No description
  • Go 96.9%
  • Shell 1.4%
  • Dockerfile 1%
  • Makefile 0.7%
Find a file
Ole-Morten Duesund 5eeac663d8 feat(broker): wire OAuth + MCP session glue into main; e2e test (forgejo-mcp-broker-q6n)
cmd/broker/main.go now composes every phase-2-5 component into a live
binary:

  /healthz      → internal/httpserver
  /oauth/*      → internal/oauth.Server.Handler()
  /.well-known  → internal/oauth.Server.Handler()
  /mcp          → oauth.Authenticator.RequireBearer
                   over session.Registry.Handler()

The SpawnFunc passed to the registry composes supervisor + bridge: each
new MCP session forks `forgejo-mcp --transport stdio` with the user's
upstream token in env, wraps stdio with a bridge, and returns the
bridge's HandleSSE as the per-session http.Handler. The reaper is wired
with a refresh callback that calls forgejo.Client.Refresh and persists
rotated tokens back to access_tokens before the rotator swaps the
session's child.

cmd/broker/e2e_test.go is the gating local validation: builds the
binary, builds forgejo-mcp from the sibling repo (skipped if absent),
stands up a fake Forgejo, runs the broker, and walks
register → authorize → callback → token → /mcp initialize → tools/list.
This catches:

  - any component left unwired
  - the subprocess-context bug fixed in this commit (using a request
    context in supervisor.Start kills the child when the request that
    minted it returns; the fix is a long-lived childCtx)
  - the happy-path Mcp-Session-Id mint+reuse cycle that unit tests
    can't exercise without a real subprocess

docs/phase7-findings.md documents both the local automated validation
(this test) and the manual Claude.ai-side checklist (OAuth completes,
tool discovery, tool invocation, session reuse, idle reap, mid-session
token refresh, revocation). The Claude.ai half is fundamentally manual
and stays that way; the automated test catches the broker bugs that
would otherwise hide behind operator setup mistakes.

Closes forgejo-mcp-broker-q6n. Phase 7 — and the project's primary
implementation track — complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:55:18 +02:00
.beads feat(broker): wire OAuth + MCP session glue into main; e2e test (forgejo-mcp-broker-q6n) 2026-04-27 17:55:18 +02:00
.claude bd init: initialize beads issue tracking 2026-04-24 16:34:50 +02:00
cmd/broker feat(broker): wire OAuth + MCP session glue into main; e2e test (forgejo-mcp-broker-q6n) 2026-04-27 17:55:18 +02:00
deploy docs(deploy): Caddy front-end example + walkthrough (forgejo-mcp-broker-r2c) 2026-04-27 17:49:25 +02:00
docs feat(broker): wire OAuth + MCP session glue into main; e2e test (forgejo-mcp-broker-q6n) 2026-04-27 17:55:18 +02:00
internal sec(oauth): phase-2 attack-path review (forgejo-mcp-broker-wgo) 2026-04-27 17:37:00 +02:00
.gitignore bd init: initialize beads issue tracking 2026-04-24 16:34:50 +02:00
AGENTS.md bd init: initialize beads issue tracking 2026-04-24 16:34:50 +02:00
CLAUDE.md docs: fill CLAUDE.md with phase-1 session learnings 2026-04-24 17:37:36 +02:00
Containerfile feat(deploy): rootless podman + Quadlet deployment (forgejo-mcp-broker-8yd) 2026-04-27 17:42:09 +02:00
go.mod feat(store): SQLite with embedded migrations (forgejo-mcp-broker-9jh) 2026-04-24 17:22:47 +02:00
go.sum feat(store): SQLite with embedded migrations (forgejo-mcp-broker-9jh) 2026-04-24 17:22:47 +02:00
LICENSE bd init: initialize beads issue tracking 2026-04-24 16:34:50 +02:00
Makefile feat(deploy): rootless podman + Quadlet deployment (forgejo-mcp-broker-8yd) 2026-04-27 17:42:09 +02:00
README.md docs(deploy): Caddy front-end example + walkthrough (forgejo-mcp-broker-r2c) 2026-04-27 17:49:25 +02:00

forgejo-mcp-broker

OAuth 2.1 authorization server and MCP session broker for forgejo-mcp.

Lets MCP clients such as Claude.ai connect to a Forgejo instance through a single public HTTPS endpoint, with per-user authentication delegated to Forgejo's own OAuth2 provider. The broker handles the OAuth dance, then spawns a dedicated forgejo-mcp --transport stdio subprocess for each authenticated session, scoped to the authenticated user's Forgejo access token.

Status: Planning. No code yet. See docs/design.md for the architecture and docs/plan.md for the phased implementation plan.

How it fits

Claude.ai ──HTTPS──▶ Caddy ──▶ fjmcp-broker ──stdio──▶ forgejo-mcp  ──▶ Forgejo API
                                  (this)              (one per user     (per-user
                                                       session)          token)
  • fjmcp-broker (this project): one long-running process. Handles OAuth discovery, dynamic client registration, the authorization-code flow against Forgejo, session lifecycle, and stdio-to-streamable-HTTP bridging.
  • forgejo-mcp (existing project): used as-is. Spawned per-session with the authenticated user's FORGEJO_ACCESS_TOKEN in the environment.
  • Caddy: terminates TLS for the public hostname and reverse-proxies to the broker.

Why a broker instead of adding OAuth to forgejo-mcp?

Process-level isolation. Each user's Forgejo token lives in exactly one subprocess — the broker never needs to demultiplex tokens inside a single shared client. This keeps forgejo-mcp's sync.Once singleton-client pattern valid and avoids a refactor of every tool handler. Full trade-off in docs/design.md.

Quick map

File What
docs/design.md Architecture, components, token flow, deployment, security
docs/plan.md Seven-phase implementation plan with acceptance criteria
docs/deploy-podman.md End-to-end production deploy with rootless podman + Quadlet
docs/deploy-caddy.md Caddy reverse-proxy front-end (TLS, SSE, host-header defense)
Containerfile Multi-stage build; bundles broker + pinned forgejo-mcp
deploy/podman/ Quadlet unit and example env file
deploy/caddy/ Example Caddyfile

License

MIT © 2026 Ole-Morten Duesund.