Written by AI agents, curated and verified by me.
Stateless MCP: state does not disappear, it changes layer
- Agentic Engineering
- Automation
On 5 August, Google published a post by Kurtis Van Gent and Alan Blount explaining the stateless core of the Model Context Protocol from an operations point of view. Two things belong apart here. One is the specification: the 2026-07-28 release candidate removes session management from the transport layer, and it predates the post. The other is Google's reading of the consequences, and that is the actual news. My thesis: state does not disappear. It moves out of the transport and into your application, which means out of the infrastructure and onto your desk.
What did Google publish?
A rationale and a migration recommendation. Google describes why it pushed for removing sessions, links the corresponding pull request against the specification, and names its own part in setting up the MCP Transports Working Group together with Hugging Face and other partners. By Google's account the trigger was its own need: when the company rolled MCP servers out across its cloud-native infrastructure, it hit a wall, because the original session model required persistent state, handshakes and session pinning.
Google calls the release candidate “already being widely adopted” and links an outside blog post for that. The text itself names exactly one example, the GitHub MCP Server. That is not an accusation, but it is the difference between a claim and evidence.
Why were sessions a production bottleneck?
Because they tied a client to one specific process. In version 2025-11-25 an HTTP connection started with an initialize call, the server answered with an Mcp-Session-Id header, and every later tool call had to carry that id. The client hung off exactly the container that held its session state in memory.
Google lists four consequences. An ordinary round-robin load balancer does not know which container holds which session: behind three pods the second request lands on a different pod and comes back as a “400 Session Not Found”. Working around that means sticky affinity rules at the load balancer, which prevents even distribution and makes autoscaling inefficient. If a pod restarts or crashes, the session state is gone instantly and the error surfaces in a live chat. And anyone running remote servers needed shared Redis session stores or packet inspection at the gateway. That is how Google describes the starting point. It is a credible description, and it is Google's description.
What goes away, and what replaces it?
The handshake goes away. initialize and initialized (SEP-2575) as well as the Mcp-Session-Id header (SEP-2567) are removed entirely. What used to be negotiated once at connection setup, protocol version, client info and client capabilities, now travels in a _meta field on every single request. Each request describes itself.
On top of that come three standard HTTP headers (SEP-2243): Mcp-Protocol-Version, Mcp-Method and Mcp-Name, that is the protocol version, the JSON-RPC method being executed, and the name of the tool, prompt or resource. They mirror the body. If they disagree with it, the server rejects the request with code -32020. The purpose is unglamorous: proxies, gateways and load balancers can route, rate-limit and audit without reading the body. For caching, SEP-2549 adds ttlMs and cacheScope, modelled on Cache-Control. A client then knows how long a tools/list response stays fresh, instead of holding an SSE connection open just to catch changes.
Where does the state you still need go?
Into the application. Google says so itself, in a subordinate clause: responsibility for managing state shifts from the transport layer to the application layer, and that is why security becomes central there. That sentence is the one for your migration ticket.
Two mechanisms show what it looks like. Multi Round-Trip Requests (SEP-2322) handle asking the user something without an open connection: the server returns an InputRequiredResult right away, carrying a serialised requestState. The client collects the answer from the human and reissues the call with inputResponses and the echoed requestState. Because that payload contains everything needed to resume, any server instance can pick the continuation up. The Tasks Extension (SEP-2663) graduates from an experimental feature to a first-class extension. A long-running call returns a taskId immediately, the work continues in the background, and the client asks via tasks/get and tasks/update.
Google's own TypeScript example for the Tasks Extension is worth a second look. It stores the initial task state in a shared datastore, and the suggestion it names is Redis. So Redis has not left the picture, it has left the session path. That is a real gain, because not every single call has to go through the shared store any more. It is not the same as “no state left”.
What changes in security and deprecations?
Google names three security items in the release candidate. Public clients must validate the iss parameter on authorization responses (RFC 9207), which is meant to protect against hijacking and redirect-based attacks in multi-server architectures. Resource Indicators (RFC 8707) make explicit which MCP server a token is intended for, against the confused deputy problem. And tool schemas may use full JSON Schema 2020-12, including oneOf, anyOf, allOf and local $ref definitions.
Just as important in practice: MCP gets a formal deprecation policy for the first time (SEP-2577), with the stages Active, Deprecated and Removed and a transition window of at least twelve months. Three features enter deprecation today. Roots is replaced by explicit tool parameters, resource URIs or server configuration. Sampling by calling the LLM provider APIs directly. Logging by stderr for stdio connections, or OpenTelemetry in the cloud. If you use one of them, you now have a schedule instead of a rumour.
What does this mean for your migration?
It is a concrete item for the list, not a reason to rush. All four Tier-1 SDKs (TypeScript, Python, Go and C#) have betas for the 2026-07-28 specification, and Google explicitly recommends testing in staging. In Python you install pip install "mcp[cli]==2.0.0b1". In TypeScript, version 2 replaces the monolithic @modelcontextprotocol/sdk package with separate server and client packages, and a codemod handles the renames. It is still a release candidate. Beta SDKs belong in staging, not in production.
The honest test question for your own server is not “is my SDK current”, it is: what does my server keep in memory between two calls? Everything sitting there breaks the moment the load balancer hands the next request to another instance. Google's operational benefits, plain round-robin, serverless with scale to zero, failover with no visible break, do not follow from the specification by themselves. They follow from your application actually being stateless.
It is the same line as with the retries in the MCP wiring and with managed agents calling remote MCP servers directly. In agentic engineering, reliability does not come from the model but from the architecture around it. This revision clears out a class of operational problems and pushes the state question one layer up. A human still has to answer it, and that human is the one who owns the server.