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>
Final phase-1 step: the broker now starts. run() parses config, opens
the store, builds the httpserver, and blocks on signal.NotifyContext
until SIGTERM/SIGINT fires, at which point it drains through
httpserver.Run's graceful-shutdown path and closes the store.
--version is handled before config.Load so operators can inspect a
binary without providing the rest of the config. flag.ErrHelp is passed
through so -h exits 0. Config failure exits 2; runtime failure exits 1.
Integration tests build the binary once in TestMain and exercise three
acceptance scenarios against it:
- --version: prints build info, exits 0
- no config: exits nonzero with stderr listing every missing field
- full startup: /healthz returns 200 with correct JSON; SIGTERM triggers
clean exit within 2s
Closes forgejo-mcp-broker-t37. Phase 1 complete.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Initialize go.mod with module path kode.naiv.no/olemd/forgejo-mcp-broker
- Create directory layout: cmd/broker + internal/{buildinfo,config,log,store,httpserver}
- Add Makefile with build/test/lint/tidy/clean targets and ldflags-injected build info
- Stub cmd/broker/main.go with --version support; real wiring follows in -t37
- Stub doc.go for each internal/* package, pointing to the issue that fills it in
Closes forgejo-mcp-broker-n84.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>