Serve MCP over stateless Streamable HTTP with JSON responses
Supersedes ADR-0066.
Decision
Run rmcp's Streamable HTTP service in stateless mode with JSON responses. Each POST is an independent JSON-RPC request with a JSON response. The server issues no session id, holds no transport state in the process and needs no sticky routing. Authentication middleware resolves the user from each request's bearer token.
Why
In stateful mode rmcp keeps a session's channels and tasks in the memory of one process, so a session started on one replica cannot continue on another. ADR-0066 pinned sessions to a replica with a Traefik sticky cookie. Production runs two replicas, and the real MCP clients we tested, Claude Code and Claude Desktop, don't persist cookies. Traefik spread their follow-up requests across both replicas, and clients got "Session not found". Preview environments run one replica, which hid the failure there.
Stateful mode buys one capability: server-initiated messages to the MCP client. No client we support consumed them. Live updates reach the iOS app through GraphQL subscriptions over Redis pub/sub (ADR-0041), which do not depend on the MCP transport.
Rejected alternatives
- Stateful sessions pinned with a sticky cookie. They depend on cookie handling the real clients don't have.
- Shared session state across replicas. rmcp's stateful transport lives in process memory, so a shared store would track sessions without letting another replica serve them.
Consequences
Any replica serves any request, and a deploy no longer drops live MCP sessions. The MCP server can no longer push notifications to its clients. MCP writes still publish the trip signal, so an iOS client subscribed on any replica sees the change.