MCP is stateless now: what the 2026-07-28 spec changes
The new MCP spec deletes the initialize handshake, sessions, and resumable streams. Your server becomes an ordinary HTTP service - and scales like one.
8 min read
The 2026-07-28 revision of the Model Context Protocol deletes its session layer. There's no initialize handshake, no Mcp-Session-Id, and no resumable streams — every request now carries its own protocol version, client identity, and capabilities. The practical effect is that an MCP server stops being a stateful process you have to pin traffic to, and becomes an ordinary HTTP service you can put behind a load balancer and a cache. If you run an MCP server, this is a breaking change, and it's the good kind.
What actually changed
From the official changelog, the removals are the headline:
- Protocol-level sessions and the
Mcp-Session-Idheader are gone. Servers needing cross-call state now mint explicit handles and pass them as ordinary tool arguments. - The
initialize/notifications/initializedhandshake is gone. Version,clientInfo, andclientCapabilitiesride along in_metaon every request. A newserver/discoverRPC — which servers MUST implement — handles up-front capability negotiation when a client wants it. - The standalone HTTP GET stream is gone, along with
resources/subscribe. Both are replaced by a singlesubscriptions/listenrequest whose response stream stays open and carries only the notification types the client opted into. - SSE resumability is gone.
Last-Event-IDand event IDs are removed: a broken stream loses the in-flight request, and the client MUST re-issue it as a new request with a new ID. ping,logging/setLevel, andnotifications/roots/list_changedare removed, and Roots, Sampling, and Logging are all deprecated — functional for now, but new implementations shouldn't adopt them.
That's a lot of deletion for one revision. The interesting question is what it's in service of.
The mechanism: MCP became a web protocol
Under the old model, the session ID pinned a client to whichever instance issued it. Scaling horizontally meant either sticky routing or real work to share session state between instances — the tax any stateful protocol imposes. Remove the session and that constraint evaporates: any request can land on any instance, because every request is self-describing.
But the tell isn't the removal. It's what got added.
The transport now requires HTTP headers mirroring fields from the JSON-RPC body — MCP-Protocol-Version, Mcp-Method, and Mcp-Name (the tool name or resource URI). The spec states the purpose plainly: so that intermediaries can route and inspect requests without parsing the body.
Alongside that, list and read results carry a required CacheableResult interface: ttlMs, a freshness hint in milliseconds, and cacheScope, either "public" or "private", controlling whether shared intermediaries may cache the response. Servers are also told to return tools from tools/list in a deterministic order, explicitly to improve client-side caching and prompt-cache hit rates.
Freshness lifetimes. Public versus private cacheability. Routable headers. Deterministic ordering for cache stability. These are HTTP's own semantics, restated. MCP has stopped trying to be a session protocol that happens to run over HTTP and started being something the existing web tier — proxies, gateways, CDNs, rate limiters — can actually see and act on.
A protocol that hides everything in a POST body is invisible to your infrastructure. This revision drags the routing-relevant parts up into headers, which is the difference between a service you can operate and one you can only host.
The consequences you have to design for
Three changes bite in production, and two of them are easy to miss.
Servers can no longer call back into a client mid-request. Sampling, elicitation, and roots used to be server-initiated JSON-RPC requests sent down an SSE stream. They're now the Multi Round-Trip Requests pattern: the server returns an InputRequiredResult (resultType: "input_required") carrying inputRequests, and the client retries the original request with inputResponses attached. Every result now has a required resultType field, and results from older servers that omit it must be treated as "complete". If your handler assumed it could block waiting on a client answer, that control flow no longer exists — the request ends and a new one arrives.
No resumability makes idempotency mandatory. A dropped stream means the client re-issues the work as a brand-new request with a new ID. If your tool charges a card, sends a message, or writes a row, "the client will retry it" is now a protocol-level guarantee rather than an edge case. This is exactly the discipline we argued for in designing a backend an AI agent can actually use, and the new spec removes the option of ignoring it.
Header/body mismatch is a security control, not a formality. Servers MUST reject requests whose headers disagree with the body, returning 400 with error code -32020 (HeaderMismatch). The spec is explicit about why: if a load balancer routes on the header value while the server executes on the body value, two components are trusting different sources of truth. That's a confused-deputy bug with a clean exploit path — route as a harmless tool, execute as a dangerous one. Intermediaries enforcing policy on mirrored headers are also told to verify the protocol version first, and reject rather than trust unvalidated headers from older clients.
If you run an MCP server today
- Decide your compatibility window. A client can probe by trying a modern request first — but on a
400it must inspect the body, because modern servers also return400for unsupported-version and header-validation errors. Falling back toinitializeon any400will misfire. - Answer GET and DELETE with
405. Reject the old GET stream and session-delete calls outright, ignore anyMcp-Session-Idyou receive, and never mint or echo one. - Convert implicit session state to explicit handles. Anything you were hanging off the session ID becomes a server-minted handle passed as a tool argument. Do this deliberately — it's the migration most likely to leak state bugs.
- Audit every side-effecting tool for idempotency. Assume each one gets called twice.
- Set
ttlMsandcacheScopehonestly. Marking a per-user result"public"is a data-leak waiting for the first shared proxy. - Note the deprecation clock. A new feature lifecycle policy guarantees a minimum twelve-month window, so Roots, Sampling, and Logging keep working — but the suggested migrations (tool parameters instead of Roots, direct provider APIs instead of Sampling, stderr or OpenTelemetry instead of Logging) are the direction of travel.
Our opinion
This revision is MCP admitting it's infrastructure. The early versions were shaped by the local case — one client, one server, a process on your laptop — where a handshake and a session are free. Once MCP servers became multi-tenant network services, those choices turned into a scaling tax paid by everyone, and the honest fix was to delete them rather than paper over them with sticky routing.
We'd also say the deprecations matter more than the removals. Taking Sampling out of the protocol's future is a statement: an MCP server shouldn't reach back through the client to borrow an LLM. It should do its job and return. That's a cleaner boundary, and it makes the security story we've written about before easier to reason about — fewer inversions of control mean fewer places for a malicious tool description to do something surprising.
The one thing we'd push back on: losing stream resumability is a real cost for long-running tools, and "re-issue it as a new request" is only free if you've done the idempotency work. Most teams haven't. Expect this to be the change that generates the production incidents.
How Ashvara helps
We build backends that agents call — MCP servers, tool APIs, and the auth and audit layer around them — with idempotency, explicit state handles, and cache semantics designed in rather than retrofitted. This revision rewards exactly that shape of system and punishes the stateful shortcut.
If you have an MCP server on an older revision and want a migration path that doesn't break your clients, or you're adding one to an existing product, that's core backend and API work for us. Tell us what your server exposes and we'll map the upgrade.
Sources: Model Context Protocol specification, revision 2026-07-28 — changelog and Streamable HTTP transport (modelcontextprotocol.io).