Skip to content

feat(mcp): opt-in HTTP (streamable) transport so clients auto-reconnect (#440) - #441

Merged
CheeryProgrammer merged 1 commit into
mainfrom
feat/mcp-http-transport
Jul 21, 2026
Merged

feat(mcp): opt-in HTTP (streamable) transport so clients auto-reconnect (#440)#441
CheeryProgrammer merged 1 commit into
mainfrom
feat/mcp-http-transport

Conversation

@CheeryProgrammer

Copy link
Copy Markdown
Contributor

Closes #440.

Problem

MCP is stdio-only; the documented deploy runs docker compose exec app /brainiac-mcp — a subprocess inside the app container. Every docker compose up -d / brainiac update recreates the container, killing that subprocess in every connected client. MCP clients do not auto-restart stdio servers (they do auto-reconnect HTTP/SSE ones), so every routine update forced a manual reconnect in every session.

Change

Serve the same tool surface over streamable HTTP at POST/GET /mcp on the app's existing chi router (mcp.NewStreamableHTTPHandler wrapping mcpserver.New). Clients register it as an HTTP transport and reconnect automatically across restarts.

Verification (live, built from source)

$ curl -s -o /dev/null -w '%{http_code}' -XPOST localhost:8088/mcp -d '<initialize>'      # 401  (no token)
$ curl -s -XPOST localhost:8088/mcp -H 'Authorization: Bearer <token>' -d '<initialize>'   # {"serverInfo":{"name":"brainiac",...}}

Startup logs MCP over HTTP ON: …. Unit tests: /mcp bearer gate (401 without/with bad token; initialize returns serverInfo with a valid token), not-mounted-by-default, and config fail-closed guards. Full suite green (config/server/mcpserver/core/store).

Register it

claude mcp add --transport http brainiac http://localhost:8088/mcp \
  --header "Authorization: Bearer $AUTH_TOKEN"

→ survives brainiac update with no manual /mcp.

Follow-ups (in #440, out of scope here)

  • Per-connection principal resolved from the bearer token (hard-isolation over HTTP).
  • ingest tool parity over HTTP MCP.

🤖 Generated with Claude Code

…ct (#440)

The MCP server was stdio-only and the documented deploy runs it as
`docker compose exec app /brainiac-mcp` — a subprocess inside the app
container. Every `docker compose up -d` / `brainiac update` recreates
that container and kills the subprocess in every client; MCP clients do
not auto-restart stdio servers (they do auto-reconnect HTTP/SSE ones), so
each session had to manually reconnect.

Serve the same tool surface over streamable HTTP at POST/GET /mcp on the
app's chi router (mcp.NewStreamableHTTPHandler wrapping mcpserver.New).
Clients register an HTTP transport that reconnects on its own across app
restarts.

- Opt-in: HTTP.mcp / MCP_HTTP env (+ compose passthrough). Off by
  default; stdio stays the default transport.
- Gated by the existing bearerAuth(AUTH_TOKEN) — the endpoint exposes
  write tools and the app binds localhost.
- Fail-closed config: MCP_HTTP requires AUTH_TOKEN, and is Layer-1 only
  (rejected with configured principals — a single-principal HTTP surface
  would bypass the per-request isolation the /api routes enforce).
- v1 omits the ingest tool over HTTP (nil importFn; stdio/CLI keep it).

Tests: /mcp bearer gate (401 without/with bad token, initialize past the
gate) and not-mounted-by-default; config validation guards. SYSTEM.md +
.env.example updated. Follow-ups (#440): per-connection principal from the
bearer token; ingest parity.

Register: claude mcp add --transport http brainiac
http://localhost:8088/mcp --header "Authorization: Bearer $AUTH_TOKEN"

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@CheeryProgrammer
CheeryProgrammer merged commit d2467d3 into main Jul 21, 2026
5 checks passed
@CheeryProgrammer
CheeryProgrammer deleted the feat/mcp-http-transport branch July 21, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Opt-in HTTP (streamable) MCP transport so clients auto-reconnect across app restarts

1 participant