Scope. Implement the MCP Streamable HTTP transport (POST/GET/DELETE /mcp), session management (Mcp-Session-Id), required headers (MCP-Protocol-Version, Origin), and the full initialize / notifications/initialized lifecycle. Reuse Harper's existing Request/Headers abstraction and the text/event-stream serializer in contentTypes.ts.
Design reference. Sections "Transports → Streamable HTTP", "Security headers (MCP §transports → Security Warning)", "Server-side integration with existing Harper machinery", "Lifecycle Notes", and "HTTP Headers" in #465.
Acceptance criteria
Out of scope. No tools, no resources, no rate limiting, no audit. Resumability via Last-Event-ID is deferred to v2 — the SSE serializer's id field is reserved but not used.
Stacks on. #613 (component scaffold).
Branch & PR conventions
Smoke test
curl -sS -X POST http://localhost:9925/mcp \
-H 'Origin: http://localhost' \
-H 'Accept: application/json, text/event-stream' \
-H 'Authorization: Bearer ...' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
-i
# Expected: 200, `Mcp-Session-Id` response header, body with serverInfo + capabilities.
Tracking. Part of #465. Sub-issue #2 of 11.
Scope. Implement the MCP Streamable HTTP transport (POST/GET/DELETE
/mcp), session management (Mcp-Session-Id), required headers (MCP-Protocol-Version,Origin), and the fullinitialize/notifications/initializedlifecycle. Reuse Harper's existingRequest/Headersabstraction and thetext/event-streamserializer incontentTypes.ts.Design reference. Sections "Transports → Streamable HTTP", "Security headers (MCP §transports → Security Warning)", "Server-side integration with existing Harper machinery", "Lifecycle Notes", and "HTTP Headers" in #465.
Acceptance criteria
POST /mcpaccepts JSON-RPC, returns eitherapplication/jsonor upgrades totext/event-streamvia the existing serializer atserver/serverHelpers/contentTypes.ts:127-161. No new Fastify API surface added.GET /mcpreturns 405 when no SSE channel is offered, or opens atext/event-streamchannel for server-initiated messages.DELETE /mcpterminates the session whensession.allowClientDelete: true; returns 405 otherwise.Mcp-Session-Idissued oninitialize(UUIDv4); enforced on subsequent requests; 400 if missing, 404 if terminated.MCP-Protocol-Versionrequired afterinitialize; 400 on unsupported. v1 supports2025-06-18(preferred) and2025-03-26(backcompat).Originheader validated against existinghttp_corsAccessList/operationsApi_network_corsAccessList; 403 on mismatch. No newmcp.allowedOriginskey.notifications/initialized).initializereturns servercapabilities(tools.listChanged: true,resources.listChanged: true,logging: {}); subsequent requests reach a stub handler.initialize→notifications/initializedover HTTP.Out of scope. No tools, no resources, no rate limiting, no audit. Resumability via
Last-Event-IDis deferred to v2 — the SSE serializer'sidfield is reserved but not used.Stacks on. #613 (component scaffold).
Branch & PR conventions
feat/mcp-transportmain(after [MCP] Component scaffold + config schema + boot gating #613 merges).Closes #<self>; references Expose Operations (API) through an MCP Wrapper #465.Smoke test
Tracking. Part of #465. Sub-issue #2 of 11.