Skip to content
July 28, 20265 min read

MCP Deleted Protocol Sessions. Your Code Now Owns Them

Checking identity, reclaiming abandoned state and bounding revocation were free under the handshake; all three are now code you write

Ihor K

CEO

MCP
stateless HTTP
authorization
protocol design

Deletions in a protocol are harder to plan for than additions. An addition goes in the backlog. A deletion moves work out of the specification and into your code, and it does not tell you which code.

The 2026-07-28 MCP revision removes the initialize handshake and its initialized notification, and removes protocol-level sessions along with the Mcp-Session-Id header from Streamable HTTP. The negotiated protocol version and the client's capabilities now ride in _meta on every request, and a new server/discover call lets a client ask for a server's capabilities up front when it wants them. The routing consequence is the easy half: any request can land on any replica, so sticky routing goes. The harder half is what the handshake was. It was the one moment where three things got written down once — version, capabilities, identity — and two of the three were given a new home. Identity was not. There is no longer a point at which your server learns who is on the other end and gets to hold that answer.

Identity is where the deletion is felt first. Under the handshake a token was verified once, when the connection came up, and every later call inherited that decision. Now each call arrives with its own credential and nothing standing behind it. Verify in full on every request and you have put a network call to an introspection endpoint in front of work that is often shorter than the call itself; the ratio is worst on the cheapest tools, the ones a model invokes in a loop. Cache the decision and that cost collapses into a single config line — and that line is now the length of time a revoked credential keeps being served. It is not a performance setting with a security side effect; it is the revocation window, expressed in milliseconds, in a file that gets edited for latency reasons. The same revision hardens the client side in two ways that hold even if you cache nothing: validate a present iss parameter against the recorded issuer before redeeming an authorization code, per RFC 9207, and key persisted credentials by the issuer that granted them rather than reusing them against a different authorization server.

Continuity got an explicit replacement rather than a removal. A server that needs one call to know about another mints a handle and returns it as an ordinary tool argument, and the model passes it back on the next call. Read that literally. The handle is a string sitting in a model's context next to everything else the model has seen, and strings in that position get copied into other calls, replayed on a retry, folded into a transcript, and occasionally handed to a tool you never designed to receive them. The session id it replaces at least travelled in a header the model never read. This one travels through the part of the system an attacker has the best chance of influencing. Unguessable is not the same as authorized.

So the handle carries an identity, and every call presenting it is checked against that identity at use, not only at mint. That distinction is the load-bearing one. A check at mint asks whether this caller was allowed to open the operation; a check at use asks whether this caller is allowed to continue it, and those stop being the same question the moment the two callers differ. The code is short: take the principal off the credential on this request, take the principal bound into the handle, compare them, refuse on mismatch. It is the cheap half, and it is the half most often skipped, because a handle that is hard to guess feels finished.

Expiry is the expensive half, because the free lifecycle hook left with the handshake. No socket teardown remains to hang cleanup on, and the same revision removes SSE stream resumability and message redelivery: the changelog states that a broken response stream loses the in-flight request and that the client must re-issue it as a new request with a new request id. Read that as a fact about bookkeeping — the retry does not arrive labelled as a retry, so your server cannot recognise it and close out the first attempt. Nothing will tell you a client walked away. Whatever a handle holds open — a cursor, a scratch table, a partial upload, a lease on a row — is reclaimed by a clock you run yourself, reading a store any replica can reach, and the reclaim path has to be exercised in tests, because the disconnect you used to infer it from does not arrive any more.

The longest-running item in the migration is the one that never becomes a ticket: carrying two protocol surfaces at once. The same revision deprecates Roots, Sampling and Logging, reclassifies the legacy HTTP+SSE transport as deprecated, and deprecates Dynamic Client Registration in favour of Client ID Metadata Documents, keeping DCR available only for authorization servers that cannot do CIMD. The lifecycle policy adopted alongside it sets a minimum twelve-month deprecation window before a feature is even eligible for removal. That is a lower bound on the specification's calendar, not a date on yours: yours is set by your slowest client, and until that client crosses, every deprecated path is a second implementation, a second set of tests, and a second thing that breaks while you are looking at the first.

One migration is worse than not migrating: a server-side map keyed by handle, so the second call has to reach the replica that minted the first. That is affinity again, rebuilt one layer above where any load balancer can see it. It passes staging, where a single replica serves everything, and it dies on the next rolling deploy, when a share of handles starts resolving to nothing on a box that never minted them. Put the state where any replica can read it, or put it inside the handle. Then watch what that same deploy does to the other two: every authorization cache in the fleet goes cold at once, so the introspection traffic you had amortised arrives in one burst, and every handle held open by a replica that just went away is left to the only clock you have. A rolling deploy exercises all three at once, on a date you picked. It is the gentle version of that test.