Skip to main content

MCP Went Stateless: What Killing the Session Means for an Endpoint That Deploys Apps From Chat

11 min readDora NodaDora Noda
Share
On this page

A coding agent is halfway through deploying your app — tools/call for the build just returned, the container image is pushed, and the next call creates the release — when the load balancer routes its third request to a different replica. That replica has never heard of the session. The deploy dies, not because anything failed, but because the protocol remembered which server you were talking to and your infrastructure forgot. That failure mode is now gone from the spec itself: MCP's 2026-07-28 revision removes protocol-level sessions and the Mcp-Session-Id header entirely, and every request is self-contained.

The concrete payoff is a deployment topology change, so here it is before anything else — what running a remote MCP endpoint required under the session-based protocol versus what the stateless core supports now:

ConcernBefore (session-based, through 2025-11-25)After (2026-07-28 stateless)
Load balancerSticky sessions (ALB stickiness) so each session reaches the same instancePlain round-robin; any request can land on any replica
Session stateShared session store or in-memory sessions pinned per instanceNo protocol session to store; nothing to share
First contactinitialize handshake minting an Mcp-Session-Id every client must echoNo handshake; version, client info, and capabilities ride in each request's _meta
Stale/moved clientsReplica restart or rebalance kills the session; client must re-handshakeNo session to go stale; retries can land anywhere
Routing and throttlingRoute by session affinity first, method secondRoute and throttle on the Mcp-Method and Mcp-Name headers
GET/DELETE on /mcpSession stream teardown and server-sent notificationsAnswer 405; streaming arrives via per-request SSE only

Bottom line: statelessness moves MCP servers from "stateful service with affinity rules" to "ordinary HTTP handler you can autoscale, roll, and scale to zero." Every section below earns that sentence from a different direction: how the protocol got here, the five things the removal does not fix, and the checklist for an endpoint that lets agents deploy apps through it under real load. (Spec references: SEP-2567 removes sessions and the header; SEP-2575 removes the handshake in favor of per-request _meta; server-initiated requests move to Multi Round-Trip Requests under SEP-2322.)

How the session died: a short, honest timeline

The timeline matters because the premise floating around our own topic list dates it wrong — there was no "December 2025 stateless spec." What December 2025 actually brought was Google debuting managed MCP servers for BigQuery, Maps, Compute Engine, and more, which put production-shaped load on remote MCP endpoints for the first time at hyperscaler scale. The protocol story runs separately:

  • March 2025: Streamable HTTP replaces the original SSE transport as the remote-transport path — one /mcp endpoint for bidirectional JSON-RPC instead of a standing event stream.
  • 2025-06-18 through 2025-11-25: Streamable HTTP with sessions. The client POSTs initialize, the server replies with capabilities plus an Mcp-Session-Id, and every subsequent request carries that header. The November revision made missing-versus-stale session behavior explicit — a sign the session model was already generating interop bugs.
  • Late 2025 practice runs ahead of the spec: operators who actually had to scale remote servers converged on the same answer independently. FastMCP's stateless_http=True mode — no session objects, no session IDs, plain round-robin behind the balancer — became the recommended shape for Kubernetes deployments. One team's postmortem is representative: with stateful sessions behind two replicas, tool listing failed intermittently (6 OK out of 8 attempts) because handshakes kept landing on different instances; flipping to stateless made every request self-contained and the failures disappeared. Experimenters who tried the alternatives documented the menu honestly: a Redis-backed shared session store failed against FastMCP's in-memory session manager, sticky sessions worked but pinned failure domains to instances, and stateless mode worked with the least machinery.
  • July 2026: the spec ratifies the practice. Protocol revision 2026-07-28 deletes the session, the header, and the handshake, makes list endpoints connection-independent, and deprecates the old server-initiated primitives (Roots, Sampling, logging) in favor of explicit multi-round-trip requests. SDKs followed within days — Go's StreamableHTTPOptions{Stateless: true}, fresh-server-per-request handlers in the TypeScript v2 SDK — and legacy clients simply negotiate down to 2025-11-25 and keep working.

Two things are worth noticing about that sequence. First, the ecosystem had already voted with its deployments before the spec changed; SEP-2567 describes what operators were doing, not what they were told to do. Second, AWS's two companion posts frame the stakes exactly right: the AgentCore Gateway team documents supporting the new spec as a scaling story (no more sticky sessions or shared session stores to scale horizontally), while the Architecture Blog's well-architected piece treats the old topology — ALB stickiness plus session affinity — as the thing you now get to delete.

Five things stateless does not fix

Deleting the session removes a class of infrastructure bugs. It does not remove state from your system — it relocates it, and each relocation has a bill attached. A deploy-from-chat endpoint that treats "stateless protocol" as "stateless system" will re-learn these five lessons in production:

1. Authentication now happens per request, and per-request auth has a cost curve. Under sessions, a client authenticated once at initialize and the session carried the trust forward. Now every request must stand alone — typically a bearer token verified on each call. At agent load (a deploy loop can fire dozens of tool calls in a burst), naive per-request token introspection against an identity provider becomes your new latency floor and your new rate-limit exposure. Cache verification keys locally, validate JWTs at the edge, and budget the crypto the way you used to budget the session store.

2. Multi-round trips still exist; they just have explicit handles now. The old world let a server push elicitations or sampling requests down the session's stream. The new world replaces that with Multi Round-Trip Requests — and there is a real wire-level gotcha: a request that triggers a server-initiated message now requires the client to advertise the SSE channel in its Accept header. Spec-conformant clients already send both media types and are unaffected, but a hand-rolled deploy agent with a minimal HTTP client will silently never receive its elicitation. Test your agent's Accept headers, not just its tool calls.

3. Stateful tool backends are still stateful. The protocol forgot your session; your tools did not. A Playwright-backed tool still has its browser instance bound to one process, a shell tool still has a working directory somewhere, and an upstream MCP server behind your gateway still binds its own session context to a specific pod — sticky routing at your layer does not save you if the upstream tier rebalances underneath it. Stateless transport solves replica interchangeability for the protocol layer only. Anything your tools hold in memory still needs affinity, externalized state, or explicit handles passed per request.

4. Analytics lost its free thread. Sessions were the built-in correlation ID tying "listed tools, called deploy three times, got an elicitation" into one story. Without them, a stateless deployment sees each call in isolation — which is exactly what observability vendors hit first. The honest fix is explicit state handles your application mints and correlates (the AgentCat v2 SDKs are an early example of this pattern), not longing for the header back. If your deploy audit trail currently assumes session scoping, rewrite that assumption before you migrate.

5. tools/list no longer varies per connection — and some designs depended on that. List endpoints are now connection-independent, which closes the door on per-actor tool filtering at list time. If your endpoint showed different tools to different tenants by filtering the list response per session, that enforcement moves to call time — the way GitHub's MCP server does it for OAuth-scoped tools. Related cleanup: GET/DELETE on /mcp now answer 405, and any monitoring that read a /health sessions count needs a new source of truth. None of this is hard; all of it is the kind of thing that pages you at 2am if you migrate by flag-flip alone.

What this means for an endpoint that deploys apps from chat

A deploy-from-chat endpoint is the workload this change helps most, because its traffic pattern is the worst case for sessions: bursty (idle for hours, then fifty tool calls in ninety seconds), long-lived in logical terms (a deploy spans many calls over minutes) but made of short independent operations (build, push, release, verify), and increasingly fanned out (one human's request spawning parallel sub-agents that each call tools). Map the general change onto that shape and you get a concrete checklist:

  • Scale to zero without fear. The session was the thing that made "no warm instances" dangerous — a cold start orphaned every in-flight deploy conversation. With self-contained requests, the endpoint can sit at zero replicas overnight and burst to twenty during a deploy storm, with ordinary HTTP autoscaling on CPU, queue depth, or request rate. The long logical deploy survives because its continuity lives in explicit job IDs your API returns, not in a connection your autoscaler just reaped.
  • Give every long operation a job ID, not a session. deploy should return { "job_id": ... } on the first call and let deploy_status poll it from any replica. This is the single most important application-level change: the protocol deleted its session, so anything client-visible that outlives one request needs your own durable handle backed by a store you control. Deploys, builds, log streams — all of them.
  • Retry and fail over aggressively. Any replica can serve any request now, so a 502 from one instance is retryable against the pool without re-handshaking. Teach the agent client to retry idempotent calls (status checks, listings) on a different connection immediately, and make mutating calls idempotency-keyed so a retried deploy cannot double-provision. Pre-stateless, "retry on another instance" was the exact thing that broke; now it is the default resilience strategy.
  • Throttle by method, not by connection. The AWS guidance is directly actionable here: route and throttle on Mcp-Method/Mcp-Name. A deploy-burst is fifty tools/call invocations, not fifty connections — per-IP or per-connection limits will either throttle legitimate deploy storms or miss abuse entirely. Rate-limit tools/call:deploy per tenant, let tools/list and status polls breathe, and put the expensive mutating tools behind stricter budgets than reads.
  • Treat the hyperscalers' managed servers as load-tested validation. Google's managed MCP fleet went from December 2025 debut to generally available with an Insta360 case study running a full agentic video-editing workflow through managed servers, ADK, and A2A. AWS's AgentCore Gateway ships 2026-07-28 support as a scaling feature. Neither vendor would bless a protocol revision that made their own multi-tenant gateway fleets harder to run — their adoption is the strongest signal a self-hosted operator gets that the stateless core holds up under somebody else's peak load first.

One scoping note for platform teams: none of this turns your MCP endpoint into a batch scheduler. The same season that deleted sessions also produced visual tooling for gang-scheduled training jobs and queue-fair batch workloads — a reminder that "fifty independent deploy calls" and "fifty pods that must start together" are different scheduling problems. Stateless MCP solves the first beautifully. If your agent sandboxes ever need the second, that is a Volcano-shaped conversation, not an MCP-shaped one.

The session was load-bearing scaffolding

Every maturing protocol sheds a piece of scaffolding that carried it through the awkward middle: HTTP kept cookies and dropped the rest into statelessness decades ago, and MCP just made the same trade — per-request metadata in _meta, explicit handles where continuity genuinely matters, and nothing else. The operators who flipped FastMCP's stateless_http flag in 2025 already knew the punchline; the 2026-07-28 spec just made it official and taught every SDK to speak it.

For a deploy-from-chat endpoint, the practical moral is small enough to fit on a runbook card: delete the sticky-session rules, delete the session store, mint job IDs for anything longer than one call, authenticate and throttle per request, and verify your agent sends both media types in Accept. Do that, and the next deploy storm lands on whatever replicas happen to be warm — which is the entire point.

Deploy-from-chat is the interface self-hosted platforms are converging on: an agent with tool access beats a dashboard for the 2am deploy. Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide