Skip to main content
  1. Tech Blog: AI, Security, Infrastructure & Open Source/

MCP Goes Stateless — Inside the July 28 Spec Overhaul

·1288 words·7 mins
Osmond van Hemert
Author
Osmond van Hemert
Developer Tooling - This article is part of a series.
Part : This Article

Every MCP server built in the last year has quietly depended on one assumption: once a client connects, it stays pinned to that exact server instance for the life of the session. That assumption just got deleted from the protocol.

On July 28, 2026, the Model Context Protocol shipped its most significant specification revision since launch. The headline change is blunt — MCP is now stateless at the protocol layer. No more handshake, no more session ID, no more sticky routing. It’s a breaking change the MCP team spent a ten-week release-candidate window getting SDK maintainers ready for, and it directly follows up on the adoption story I wrote about back in April: the protocol got popular enough, fast enough, that its original connection model became an operational liability.

What Actually Changed
#

Under the previous spec (2025-11-25), calling a tool over Streamable HTTP meant a two-step dance. First, a client sent an initialize request and the server handed back an Mcp-Session-Id header. Every subsequent call had to carry that header, and — critically — had to land on the same server process that issued it:

POST /mcp HTTP/1.1
Mcp-Session-Id: 1868a90c-3a3f-4f5b
Content-Type: application/json

{"jsonrpc":"2.0","id":2,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"}}}

That session pinning is exactly the kind of thing that’s trivial in a demo and painful at scale — it forces load balancers into sticky routing and forces server operators to run a shared session store across every instance, just so a retried request finds the state it needs.

In the new 2026-07-28 spec, the same call is a single self-contained request any server instance can handle:

POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"},
           "_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}

The initialize/initialized handshake is gone entirely (SEP-2575). Client info and capabilities that used to be exchanged once at connection time now travel in _meta on every request, and a new server/discover method lets clients pull server capabilities up front if they need them. The Mcp-Session-Id header and the session it represented are gone too (SEP-2567). With both removed, there’s no protocol-level reason a request can’t be handled by whichever instance happens to be free.

Three smaller changes reinforce the point. Requests now carry Mcp-Method and Mcp-Name headers so gateways and rate-limiters can route on the operation without parsing the JSON body. List and resource results carry ttlMs and cacheScope, modeled directly on HTTP Cache-Control, so clients know how long a tools/list response stays valid without holding a stream open to find out. And W3C Trace Context propagation in _meta is now a documented, fixed set of keys — traceparent, tracestate, baggage — so a distributed trace can follow a tool call from host application through client SDK, through the MCP server, and into whatever it calls downstream, landing as one span tree in any OpenTelemetry backend.

The Part That Needed the Cleverest Fix
#

Removing sessions is easy when a server only answers questions. It gets harder when a server needs to ask the client something mid-call — an elicitation prompt, a sampling request, a “do you want me to delete these files?” confirmation. That flow used to lean on the persistent connection: the server pushed a request down the open channel and waited.

With no open channel to push down, MCP restructured this as multi round-trip requests (SEP-2322). Instead of holding a stream open, a server that needs input returns an input_required result:

{
  "resultType": "input_required",
  "inputRequests": {
    "confirm": {
      "type": "elicitation",
      "message": "Delete 3 files?",
      "schema": { "type": "boolean" }
    }
  },
  "requestState": "eyJzdGVwIjoxLCJmaWxlcyI6WyJhIiwiYiIsImMiXX0="
}

The client collects the answer and re-issues the original call with the response and the echoed requestState. Because everything needed to resume is in the payload itself, any server instance can pick up that retry — not just the one that asked the question. It’s a small piece of API design doing a lot of work: it’s the mechanism that lets an entire class of interactive, multi-step tool calls survive the move away from persistent connections. Server-initiated requests are also now required to happen only while actively processing a client request (SEP-2260), which closes off the possibility of a user getting prompted by a tool call they never made.

Extensions Are Now the Escape Hatch
#

The other structural change is governance, not wire format. Three companion SEPs establish a feature lifecycle policy — Active, Deprecated, Removed, with a mandatory twelve-month gap between deprecation and removal — and an Extensions framework that lets new capabilities (like MCP Apps, which let servers ship sandboxed, server-rendered UIs) ship and stabilize outside the core spec before they ever have to earn a place in it. A Standards Track proposal can no longer reach Final status without a matching scenario in the official conformance suite, the same suite the SDK tier system now scores official SDKs against.

The subtext, stated plainly by the MCP team: this breaking change was a one-time foundational cost, not the new normal. If the lifecycle policy and extensions framework hold up, the next major revision shouldn’t require anyone to rewrite their transport layer again.

Why This Matters More Than a Version Bump
#

None of this would be interesting if MCP servers were still mostly toy integrations. They aren’t. As LLM agents move from chat demos into production systems that call real tools against real infrastructure, the operational model of “one client, one server, one sticky session” stopped scaling well before this spec caught up to it. A stateless wire protocol is what lets an MCP server sit behind an ordinary autoscaling group instead of a fleet with a shared Redis session store — GitHub’s own MCP server is a concrete example: dropping session storage meant no database write on every initialize and no database read on every call, which is a straightforward latency and cost win at their scale.

It also changes the calculus for anyone designing agent architectures with governance and observability baked in. Fixed trace-context keys mean a tool call inside an agent’s reasoning loop is finally traceable end-to-end without every SDK inventing its own header convention. Cache TTLs on tool listings mean an agent orchestrator can stop re-fetching tools/list defensively on every turn. These aren’t glamorous, but they’re exactly the kind of primitive that separates a framework you can run in production from one you can only demo.

My Take
#

The practical situation is better than the phrase “breaking change” suggests. Every Tier 1 SDK — TypeScript, Python, Go, Rust, C# — shipped beta support for 2026-07-28 well before the final spec landed, and all of them preserve backward compatibility for clients still on 2025-11-25. Cloudflare’s Agents SDK, for one, serves both eras through the same handler by default; you only need a second route if your server relies on push-style server-to-client requests that don’t fit the new model. If you maintain an MCP server, upgrade your SDK, let the compatibility shim handle old clients, and only refactor the handlers that use the old elicitation/sampling push APIs directly — nobody needs to do that migration today.

What I’d actually pay attention to is the governance change bundled in with the wire-format one. MCP burned a breaking change to fix a real scaling problem, but it also shipped a feature-lifecycle policy, a conformance suite tied to Standards Track status, and an Extensions framework specifically so this doesn’t have to happen again. That’s the more interesting bet here: whether a young, fast-moving protocol can discipline itself into evolving without breaking its ecosystem every year. If it holds, 2026-07-28 becomes the version everyone builds against for a long time. If you’re standing up new MCP infrastructure now, build against the stateless model from day one — there’s no reason to inherit sticky-session plumbing the spec itself just spent a year designing away.

Developer Tooling - This article is part of a series.
Part : This Article

Related