forgejo-mcp/DEVELOPER.md
Tristin Stagg d3cb629db7
feat: add user agent configuration support
Add --user-agent CLI flag and FORGEJO_USER_AGENT environment variable
to allow custom HTTP User-Agent headers.

Changes:
- Add UserAgent flag to pkg/flag
- Add --user-agent CLI option and FORGEJO_USER_AGENT env var
- Default to 'forgejo-mcp/<version>' if not configured
- Update forgejo client to use configured user agent
- Update documentation in README.md and DEVELOPER.md
2026-03-11 12:41:15 +01:00

5.8 KiB

Developer Guide

This guide covers building, developing, and contributing to the Forgejo MCP Server.

Prerequisites

  • Go 1.24 or later
  • make (optional, for convenience commands)

Building

Using Make

make build          # Build the binary (outputs ./forgejo-mcp)
make vendor         # Tidy and verify Go module dependencies

Using Go Directly

go build -v         # Build the binary
go mod tidy         # Tidy dependencies

Running Locally

# stdio mode (for MCP client integration)
./forgejo-mcp --transport stdio --url https://forgejo.example.org --token <token>

# SSE mode (for HTTP-based clients)
./forgejo-mcp --transport sse --url https://forgejo.example.org --token <token> --sse-port 8080

# With debug logging
./forgejo-mcp --transport sse --url <url> --token <token> --debug

Environment variables: FORGEJO_URL, FORGEJO_ACCESS_TOKEN, FORGEJO_DEBUG, FORGEJO_USER_AGENT

CLI options: --url, --token, --transport, --sse-port, --user-agent

Architecture

This is an MCP (Model Context Protocol) server that exposes Forgejo API operations as tools for AI assistants.

Core Flow

main.go → cmd/cmd.go (CLI parsing) → operation/operation.go (tool registration) → operation/{domain}/*.go (tool handlers)

Directory Structure

Directory Purpose
cmd/ CLI entry point and command parsing
operation/ MCP tool definitions and handlers, organized by domain
operation/issue/ Issue-related tools
operation/pull/ Pull request tools
operation/repo/ Repository and branch tools
operation/search/ Search tools (users, repos, teams)
operation/user/ User info tools
operation/version/ Server version tool
pkg/forgejo/ Singleton Forgejo SDK client wrapper
pkg/to/ Response formatting helpers (TextResult, ErrorResult)
pkg/params/ Shared parameter descriptions for tool definitions
pkg/flag/ Global configuration state
pkg/log/ Structured logging utilities

Adding a New Tool

Step 1: Create or Modify a Domain File

Tools are organized by domain in operation/{domain}/. Create a new file or add to an existing one.

Step 2: Define the Tool

package mydomain

import (
    "context"
    "fmt"

    "codeberg.org/goern/forgejo-mcp/v2/pkg/forgejo"
    "codeberg.org/goern/forgejo-mcp/v2/pkg/params"
    "codeberg.org/goern/forgejo-mcp/v2/pkg/to"
    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)

// Tool definition
var MyTool = mcp.NewTool(
    "my_tool_name",
    mcp.WithDescription("What this tool does"),
    mcp.WithString("owner", mcp.Required(), mcp.Description(params.Owner)),
    mcp.WithString("repo", mcp.Required(), mcp.Description(params.Repo)),
    mcp.WithNumber("limit", mcp.Description("Page size"), mcp.DefaultNumber(20)),
)

// Handler function
func MyToolFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    // Extract parameters (numbers come as float64)
    owner, _ := req.Params.Arguments["owner"].(string)
    repo, _ := req.Params.Arguments["repo"].(string)
    limit, _ := req.Params.Arguments["limit"].(float64)

    // Call Forgejo API
    result, _, err := forgejo.Client().SomeMethod(owner, repo, int(limit))
    if err != nil {
        return to.ErrorResult(fmt.Errorf("operation failed: %v", err))
    }

    // Return formatted result
    return to.TextResult(result)
}

Step 3: Register the Tool

Add registration in the domain's file:

func RegisterTool(s *server.MCPServer) {
    s.AddTool(MyTool, MyToolFn)
}

Step 4: Wire Up New Domains

If you created a new domain, import and register it in operation/operation.go:

import "codeberg.org/goern/forgejo-mcp/v2/operation/mydomain"

func RegisterTools(s *server.MCPServer) {
    // ... existing registrations
    mydomain.RegisterTool(s)
}

Key Patterns

Parameter Handling

  • String parameters: value, _ := req.Params.Arguments["param"].(string)
  • Number parameters: value, _ := req.Params.Arguments["num"].(float64) (always float64)
  • Optional with defaults: Check if value exists before using

Response Formatting

Use helpers from pkg/to/:

// Success response
return to.TextResult(data)

// Error response
return to.ErrorResult(fmt.Errorf("something went wrong: %v", err))

Shared Parameter Descriptions

Reuse descriptions from pkg/params/ for consistency:

mcp.WithString("owner", mcp.Required(), mcp.Description(params.Owner))
mcp.WithString("repo", mcp.Required(), mcp.Description(params.Repo))
mcp.WithNumber("page", mcp.Description(params.Page), mcp.DefaultNumber(1))

Dependencies

Package Purpose
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 Forgejo API client
github.com/mark3labs/mcp-go MCP protocol implementation
github.com/spf13/cobra CLI framework

Testing

Run with debug mode to troubleshoot issues:

FORGEJO_DEBUG=true ./forgejo-mcp --transport stdio --url <url> --token <token>

Blocked Features

Some planned features are blocked on upstream API or SDK support:

Feature Status Details
Wiki support Blocked Waiting for forgejo-sdk wiki API
Projects/Kanban Blocked Requires Gitea 1.26.0 API

See docs/plans/ for detailed status:

  • wiki-support.md - Wiki API implementation plan
  • projects-support.md - Projects/Kanban implementation plan

Contributing

  1. Fork the repository on Codeberg
  2. Create a feature branch
  3. Make your changes following the patterns above
  4. Test locally with both stdio and SSE modes
  5. Submit a pull request

Code Style

  • Follow standard Go conventions
  • Use meaningful variable names
  • Add tool descriptions that clearly explain what each tool does
  • Reuse parameter descriptions from pkg/params/ where applicable