Forgejo MCP
  • Rust 75.5%
  • Just 13.9%
  • Dockerfile 7.2%
  • Nushell 3.4%
Find a file
nrupard e7388686d8
All checks were successful
Check / fmt + clippy + build + tests (push) Successful in 1m36s
Build binary (Linux) / Build and publish forgejo-mcp binary (Linux x86_64) (push) Successful in 13m44s
Build binary (Windows) / Build and publish forgejo-mcp binary (Windows x86_64) (push) Successful in 14m14s
Build binary (Linux static musl) / Build and publish forgejo-mcp binary (Linux x86_64 static musl) (push) Successful in 14m40s
Merge pull request 'fix(build): vendor OpenSSL for Windows cross-build' (#10) from fix/windows-openssl-vendored-fjmcp-8 into main
Reviewed-on: #10
2026-06-08 01:31:55 +02:00
.forgejo/workflows fix(ci): generate release notes from commits since previous tag 2026-05-31 13:27:45 -04:00
oci-build fix(build): install OpenSSL build toolchain in Windows builder 2026-06-08 01:29:20 +02:00
src feat(http): add Streamable HTTP transport with bearer auth (FJMCP-6) 2026-05-28 05:20:12 -04:00
.dockerignore chore: bring repo to full governance compliance (FJMCP-1) 2026-05-26 21:37:15 -04:00
.gitignore chore: Sync .gitignore 2026-06-06 13:21:35 -04:00
build.rs feat: add clap CLI with serve and version self-updater (FJMCP-3) 2026-05-28 04:46:17 -04:00
Cargo.lock Merge branch 'main' into feat/static-musl-binary-fjmcp-4 2026-05-28 06:52:50 -04:00
Cargo.toml fix(build): vendor OpenSSL for Windows cross-build 2026-06-08 01:07:57 +02:00
CLAUDE.md chore(build): bump rust-builder-glibc to v1.0.1 2026-06-06 22:27:42 -04:00
justfile chore(build): bump rust-builder-glibc to v1.0.1 2026-06-06 22:27:42 -04:00
LICENSE-APACHE chore: bring repo to full governance compliance (FJMCP-1) 2026-05-26 21:37:15 -04:00
LICENSE-MIT chore: bring repo to full governance compliance (FJMCP-1) 2026-05-26 21:37:15 -04:00
README.md docs(readme): link other forgejo-mcp implementations 2026-06-05 19:39:53 -04:00

forgejo-mcp

A Model Context Protocol server that exposes Forgejo operations to MCP clients. It is built on the forgejo-cli crates (fj-client, fj-core), so authentication and API calls reuse the same code path as the fj CLI rather than reimplementing them.

What it does

Speaks MCP over stdio (default) or Streamable HTTP (MCP_TRANSPORT=http, see Transports) and exposes a small set of tools:

Tool Arguments Returns
whoami none The authenticated user for the configured host.
get_repo owner, repo Repository metadata.
list_issues owner, repo, state? (open/closed/all, default open) One page of issues.
get_issue owner, repo, index A single issue by number.

Each tool returns the raw forgejo-api struct as pretty JSON text.

Authentication

Credentials come from the fj CLI's keys.json (managed by directories under the Cyborus/forgejo-cli data dir). Authenticate once with the CLI:

fj auth add-key --host dev.a8n.run

The server loads keys.json on each call, so token refreshes performed by the CLI are picked up automatically. Hosts with no stored login fall back to an unauthenticated client.

Configuration

Variable Default Purpose
FORGEJO_HOST https://dev.a8n.run Base URL of the target Forgejo instance.
RUST_LOG info Log filter (logs go to stderr; over stdio, stdout carries the JSON-RPC stream).
MCP_TRANSPORT stdio Transport to start: stdio or http. Unset or unrecognized values use stdio.
MCP_HTTP_ADDR 127.0.0.1:8080 HTTP-mode bind address. Override to 0.0.0.0:<port> for a container or daemon.
MCP_HTTP_TOKEN (none) HTTP-mode bearer token. Required in http mode: the server refuses to start without it.

Transports

The serve subcommand selects its transport from MCP_TRANSPORT at startup.

stdio (default)

The server reads JSON-RPC from stdin and writes it to stdout; an MCP client launches one process per session and it exits when the client disconnects. This is the mode the client registration example below uses.

Streamable HTTP

MCP_TRANSPORT=http runs a long-lived, network-reachable process that serves the Streamable HTTP MCP endpoint at POST/GET/DELETE {MCP_HTTP_ADDR}/mcp. It binds MCP_HTTP_ADDR and shuts down gracefully on SIGINT/SIGTERM.

Because the server loads stored Forgejo credentials from the fj CLI's keys.json, any reachable endpoint grants authenticated Forgejo access. The HTTP listener is therefore authenticated by default and fails closed: every request must carry Authorization: Bearer <MCP_HTTP_TOKEN> or it gets 401 Unauthorized, and the server refuses to start when MCP_HTTP_TOKEN is unset or empty. TLS is not terminated in-process; put a reverse proxy in front for a public deployment.

$env.FORGEJO_HOST = "https://dev.a8n.run"
$env.MCP_TRANSPORT = "http"
$env.MCP_HTTP_ADDR = "127.0.0.1:8080"
$env.MCP_HTTP_TOKEN = "a-long-random-secret"
./target/release/forgejo-mcp serve

Build and run

cargo build --release
$env.FORGEJO_HOST = "https://dev.a8n.run"
./target/release/forgejo-mcp serve

The server reads JSON-RPC from stdin and writes to stdout, so run it directly only for a smoke test. Normally an MCP client launches it. A bare forgejo-mcp with no subcommand prints usage and exits; the stdio server lives behind serve.

CLI

forgejo-mcp --help            # usage for all subcommands
forgejo-mcp --version         # forgejo-mcp <semver> (<git-hash>, built <build-date>)
forgejo-mcp serve             # start the MCP stdio server
forgejo-mcp version           # same banner as --version (alias: version show)
forgejo-mcp version check     # ask the registry whether a newer build is published
forgejo-mcp version update    # download + verify + self-replace the running binary

version update accepts --dry-run (probe + report, no download), --force (reinstall even when not newer), and --url <base> (override the baked registry URL). It verifies a published .sha256 sidecar when present (hard-fail on mismatch, skip when absent) and smoke-tests the swapped binary with --version before reporting success.

Register with an MCP client

Example claude registration (stdio transport):

claude mcp add forgejo --env FORGEJO_HOST=https://dev.a8n.run -- /absolute/path/to/forgejo-mcp/target/release/forgejo-mcp serve

For a running Streamable HTTP server, register the URL transport and pass the bearer token as a header:

claude mcp add --transport http forgejo http://127.0.0.1:8080/mcp --header "Authorization: Bearer a-long-random-secret"

Adding a tool

  1. Add a #[derive(Deserialize, JsonSchema)] params struct in src/server.rs.
  2. Add a #[tool] method on the #[tool_router] impl that builds the client with self.api(), calls the matching fj_core::* wrapper, and returns json(&result).
  3. If the operation does not yet exist in fj-core, add it there first rather than calling forgejo-api directly, so the CLI and the MCP server stay in sync.

Development

This repo uses a justfile for the standard dev workflow. Run just (or just --list) to see every recipe.

just install-hooks   # one-time per clone: install the pre-commit hook
just check           # fmt + clippy + build + builder-stage docker compile
just test            # cargo test
just build           # release binary
just build-docker-export   # extract the dynamic glibc Linux binary via the OCI build
just build-static          # build the fully static musl Linux binary via Docker
just build-windows         # cross-compile the Windows .exe
just create-release minor  # bump version, push release branch, open the PR via fj

just pre-commit runs the same fmt + clippy + build + test steps as .forgejo/workflows/check.yml, inside the rust-builder-glibc image so the toolchain matches CI. Conventions follow the a8n-run/governance repo; see CLAUDE.md for forgejo-mcp-specific notes.

Releases

CI publishes binaries to the Forgejo Generic Packages registry on every push to main and on v* tags:

Artifact Target Linking
forgejo-mcp-linux-x86_64 x86_64-unknown-linux-gnu Dynamic glibc (needs libssl3 at runtime).
forgejo-mcp-linux-x86_64-static x86_64-unknown-linux-musl Fully static (vendored OpenSSL/libgit2/libssh2; runs on any Linux).
forgejo-mcp-windows-x86_64.exe x86_64-pc-windows-gnu mingw cross-compile.

just create-release <major|minor|hotfix> opens the release PR; once merged, .forgejo/workflows/create-release.yml tags and publishes automatically.

Other implementations

Unrelated projects that also implement an MCP server for Forgejo under the same name:

License

Dual-licensed under Apache-2.0 OR MIT (LICENSE-APACHE, LICENSE-MIT), matching the forgejo-cli crates this depends on.