MCP is going stateless, and it fixes the problem I kept hitting

The 2026-07-28 MCP revision removes protocol-level sessions and the initialize handshake. State stops hiding in the transport and moves into explicit, model-visible handles. Here is what changed, what breaks, and what I would do about it today.

29 July 2026· Hüseyin Çınar


For a while now, the interesting question about the Model Context Protocol has not been what a tool can do. It has been where the state lives. MCP started out as a stateful, bidirectional protocol: the client and server did an initialize handshake, the server handed back an Mcp-Session-Id, and from then on both sides assumed there was a session sitting behind the connection. Everything - list results, subscriptions, server-initiated requests - leaned on that session being there.

That is a clean model on a laptop over stdio. It is a headache the moment you put the server behind a load balancer. A session pinned to one process means every follow-up request has to land on the same instance, which means sticky routing, or a shared session store, or both. I have built enough backend systems to know that "this request must come back to the exact box that started it" is the sentence that quietly kills your ability to scale horizontally. You end up solving a distributed-systems problem you never asked for, just to run a tool server.

The 2026-07-28 revision, published this week, takes that whole problem off the table. MCP is now stateless.

What actually changed

The protocol-level session is gone. No more initialize/initialized handshake, no more Mcp-Session-Id header on the Streamable HTTP transport. Every request now stands on its own and carries what it needs in _meta - its protocol version, the client's capabilities, who the client is:

POST /mcp HTTP/1.1
Mcp-Method: tools/call
Mcp-Name: scan_repository
Content-Type: application/json

{ "method": "tools/call",
  "params": { "name": "scan_repository", "arguments": { "repo": "acme/api" } },
  "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28" } }

Two new headers, Mcp-Method and Mcp-Name, are now required on Streamable HTTP POSTs. That detail looks small and is actually the point: a gateway, rate limiter or router can now decide what to do with a request by reading two headers, without parsing the JSON body at all. Any request can land on any instance behind a plain round-robin balancer. The sticky-session problem simply does not exist anymore.

The part I like most is what happens to state that genuinely does need to persist across calls. Instead of hiding it in the transport, the server mints an explicit handle and returns it, and the model passes it back as an ordinary tool argument on the next call. The state becomes a visible value the model can see and thread between tools, not an invisible thing bolted onto the connection. If you have read anything else I have written, you know I have a weakness for making the implicit explicit. This is that, at the protocol level.

Server-initiated requests get the same treatment through a new pattern called Multi Round-Trip Requests. Instead of the server holding a stream open to call back into the client (the old sampling/createMessage, elicitation/create, roots/list), a tool that needs more input just returns a result with resultType: "input_required" and an inputRequests field. The client answers by retrying the original request with inputResponses attached. It is a request/response protocol all the way down now, which is a much easier thing to reason about and to put behind ordinary HTTP infrastructure.

What breaks

This is not a free lunch, and the spec is honest about it.

There is a twelve-month deprecation window on the reclassified features, so nothing vanishes overnight, but the direction is unambiguous.

What I would do today

The spec is days old. That is the single most important fact about it. SDKs, gateways, proxies and every piece of tooling in between need time to catch up, and I would not point a production integration at a protocol this fresh and assume the whole path handles it cleanly.

But the design is right, and I would start getting ready for it now. If you are writing a new server, build it stateless from the start: no reliance on a session, cross-call state carried in explicit handles, no server-initiated callbacks you can replace with the MRTR pattern. Code written that way maps onto plain HTTP infrastructure with none of the sticky-routing gymnastics, and it will age into the new spec instead of fighting it. The stateful version was always a workaround for making a request/response medium pretend to hold a conversation. It is good to finally see the protocol admit that a request is just a request.

← All writing