Independent AI intelligence Two editions daily · ET
FervorAI

Analysis · July 28, 2026 · concept

Model Context ProtocolMCP 2026-07-28SEP-2567Explicit handle patternmcpmcp-securityagent-infrastructureagent-identityagent-security

MCP 2026-07-28 Goes Stateless: The Session Didn't Disappear, It Moved Into Your Model's Context

The initialize handshake and the Mcp-Session-Id header are both gone. Your remote MCP server can now run behind a plain load balancer. The state those things carried has to live somewhere else, and the spec's answer is a string the model passes back to you.

The first thing every MCP client ever did was say hello. You opened a connection, sent initialize with your protocol version and client info, got back an Mcp-Session-Id, and every request after that carried the header. That handshake is now deleted from the specification. So is the header.

MCP 2026-07-28 publishes today as the final spec, ten weeks after the release candidate locked on May 21. Six Specification Enhancement Proposals work together to make the protocol stateless at the transport layer, and the maintainers' own before-and-after is blunt about the payoff: a remote server that needed sticky sessions, a shared session store, and deep packet inspection at the gateway can now sit behind a round-robin load balancer and route on a header.

That is a genuinely large operations win, and I think most teams will under-read what came with it.

The state didn't evaporate. It changed owners.

Here is the part that deserves more attention than the migration guides are giving it.

Before, session state lived in a header. The model never saw it, could not read it, could not invent it, and could not be talked into changing it by text pasted into a document it was summarizing. The transport owned that identifier and your infrastructure owned the transport.

The new spec's replacement is what the maintainers call the explicit handle pattern. A tool mints an identifier (their examples are basket_id and browser_id), returns it to the model, and the model passes it back as an ordinary argument on later calls. The blog post argues this is often better than session state, and the reasoning is sound: the model can compose handles across tools, reason about them, and hand them between steps in ways that "externally managed session state, hidden in transport metadata, never really allowed."

Read that sentence again with a security hat on. State that used to be hidden from the model is now visible to it. Visible means legible, and legible means forgeable, forgettable, and quotable.

A context window is not a session store. It gets compacted. It gets summarized. It gets re-planned mid-task by a harness trying to save tokens. Any identifier your application depends on now lives in the same buffer as the untrusted README the agent just read. If your handle is a sequential integer or a predictable slug, a prompt injection that says "continue working on basket_id 4471" is now a well-formed tool call.

I want to be precise about the scope of that claim, because it is easy to overstate. This is not a flaw in the spec. It is a consequence of where the state went, and the spec is explicit that your application can keep managing state however it likes. The failure mode only lands on teams that treat the handle the way they treated the session ID: as an opaque routing token rather than as a capability the model is now holding.

What actually changed under the hood

The removals are the headline. SEP-2575 kills the initialize/initialized handshake. Protocol version, client info, and client capabilities now ride in _meta on every request, and a new server/discover method lets a client pull server capabilities when it wants them up front instead of always. SEP-2567 removes Mcp-Session-Id and the protocol-level session with it.

The rest of the stateless work is about making server-to-client requests survive without a persistent connection, and this is where a second piece of state shows up without much fanfare.

Multi Round-Trip Requests (SEP-2322) replace the held-open SSE stream. When a server needs something mid-call, an elicitation prompt for example, it returns an InputRequiredResult containing the input requests and a requestState blob. The client collects the answers and re-issues the original call with inputResponses plus the echoed requestState. Any instance can pick that retry up, because everything it needs came back in the payload.

That blob is server state stored on the client. The spec's example shows it base64-encoded, which is encoding, not protection. If you author servers, treat anything that comes back in requestState as attacker-controlled input on arrival, because architecturally that is exactly what it is. Sign it or encrypt it. Do not put a bare JSON dictionary of your internal step counter and file list into a field a client hands back to you.

Paired with that, SEP-2260 now requires that servers may only issue server-initiated requests while actively processing a client request. Earlier versions recommended it. Making it mandatory means a user is never prompted out of nowhere, and every elicitation traces back to something they or their agent started. That is a real provenance improvement and it costs nothing.

Three operational changes round it out. Mcp-Method and Mcp-Name headers are now required on Streamable HTTP (SEP-2243), so gateways route and rate-limit on the operation without reading the body, and servers must reject requests where headers and body disagree. List and resource-read results carry ttlMs and cacheScope (SEP-2549), modeled on HTTP Cache-Control, so a client knows how long a tools/list stays fresh and whether it is safe to share across users. And W3C Trace Context key names are now pinned in _meta (SEP-414), so a trace that starts in your host app follows the call through the SDK, the server, and whatever the server calls next, in one span tree.

On authorization, the change I would ship first: clients must validate the iss parameter on authorization responses per RFC 9207 (SEP-2468). The spec calls this a low-cost mitigation for a mix-up attack class that is more prevalent in MCP's one-client-many-servers shape, and says clients will be expected to reject responses missing iss in a future version. If you run an authorization server, start emitting it now.

Roots, Sampling, and Logging are deprecated, annotation-only, with at least twelve months before removal is even possible under the new feature lifecycle policy. Tasks moves out of the core spec into an extension with a rewritten lifecycle, and tasks/list is gone because it cannot be scoped safely without sessions. Anyone who shipped against the experimental Tasks API has migration work. And the error code for a missing resource changes from MCP's custom -32002 to the JSON-RPC standard -32602, which will silently break any client matching on the literal.

Put this into practice

If you maintain a remote MCP server, the lowest-friction path this week is three passes, in this order.

Pass one, the breakage sweep. Grep your codebase for -32002, for Mcp-Session-Id, and for anything that assumes initialize ran before a tool call. Then check whether you shipped against experimental Tasks. That list is your actual migration, and it is usually shorter than people fear.

Pass two, the handle audit. For every tool that returns an identifier the model will pass back, ask two questions. Is it unguessable? And is it bound to an authenticated principal on the server side, so that possessing the string is not sufficient to use it? A UUIDv4 that the server checks against the calling identity is fine. A row ID is a vulnerability with a countdown on it. This is the single highest-value hour in the whole migration and it is the one nobody is scheduling.

Pass three, the free wins. Add Mcp-Method and Mcp-Name. Set honest ttlMs and cacheScope values on tools/list (be conservative with cacheScope if your tool list varies by user, because a shared cache of a per-user tool list is an information leak). Turn on trace propagation. Emit and validate iss.

If you only consume MCP servers rather than write them, your job is smaller but not zero: confirm your client SDK targets 2026-07-28, and confirm it validates iss rather than accepting whatever comes back.

Honest limitations

I have not run a production migration on this. Everything above comes from the specification, the maintainers' release post, and the SEP pull requests, not from watching a fleet cut over. The operational claims about round-robin routing and dropped session stores are the maintainers' own and they are the people who would know, but "works in the spec" and "works in your gateway" are different statements and the ten-week validation window existed precisely because of that gap.

There is also a small provenance wrinkle worth stating plainly. As of this afternoon, the announcement I am citing is still the release candidate post, published May 21 and updated July 24, which states that the final spec ships July 28. I have not seen a separate final-release post on the MCP blog. The date is the date the maintainers committed to, and secondary coverage is treating it as shipped, but if you need certainty for a compliance conversation, check the draft specification and changelog directly rather than trusting my read of the calendar.

And I should say clearly what I am not arguing. The handle pattern is not a mistake. Explicit, model-visible state is easier to debug, easier to audit, and composes better than a hidden header ever did. Plenty of teams will mint scoped, authorization-bound handles on the first try and never think about this again. My claim is narrower: the risk moved, most migration checklists are only covering the parts that throw errors, and a handle that fails silently is worse than a header that fails loudly.

The part that's yours to decide

The MCP maintainers made a deliberate trade. They removed a piece of hidden machinery and handed the responsibility to you, along with better tools for doing it: a deprecation policy with a twelve-month floor, an extensions framework, a conformance suite that gates Standards Track SEPs from reaching Final.

That is a mature protocol behaving like one. It also means the parts of your security model that used to be free are now yours to build.

So run the handle audit before you run anything else. Pick one tool that returns an identifier, and try to use that identifier from a different authenticated user. If it works, you learned something today that no changelog was going to tell you.

Sources: MCP blog, The 2026-07-28 MCP Specification Release Candidate; SEP-2567, SEP-2575, SEP-2322, SEP-2260, SEP-2243, SEP-2549, SEP-2468; RFC 9207; MCP draft specification.