The Protocol Stopped Remembering You

A while back I wrote an explainer about the Model Context Protocol. It has a section called "A client, a server, and the handshake between them", and I was pleased with it, because the handshake was the part people found confusing and I thought I had made it simple.

As of yesterday it describes something that does not exist.

The section of my own article that expired yesterday

On the 28th of July the MCP maintainers shipped revision 2026-07-28 as the stable specification. It had been sitting as a release candidate since the end of May, so nobody was ambushed.

The headline change is the one I have to eat. The initialize request and the notifications/initialized acknowledgement are gone. So is the Mcp-Session-Id header.

The connection between a client and a server no longer has a memory. That is the removal of the ceremony the whole protocol opened with, nearly two years after the first dated revision carried it.

Discovery survived, the memory did not

The lazy reading, and I saw it within an hour, is that MCP has given up and become a REST API with extra steps. That is not what happened, and the difference matters if you are about to rewrite a server.

Discovery is still there. It got promoted. There is a new server/discover call and the spec says servers must implement it, which turns what I described as a pleasant property of the handshake into a hard requirement of the protocol.

A model still asks a server what it can do before it does anything with it. The discovery-then-act flow I spent four paragraphs on is intact, and now compulsory.

What died is the remembering. Three things went at once: the handshake that established a session, the identifier that named it, and the server's ability to start a request of its own back at the client.

That last one is how sampling/createMessage, roots/list, and elicitation/create worked. They are replaced by a pattern called Multi Round-Trip Requests, where the server returns an InputRequiredResult and waits to be called again with the answer.

Read that one twice. An MCP server used to be able to interrupt you. Now it can only reply.

The reason is a load balancer, and it is a good reason

I want to be fair to this before I complain about it, because I would have made the same call. The announcement is blunt about why.

The line they lead with is that any request can now land on any server instance behind a plain round-robin load balancer without needing shared storage.

They describe better reliability and scalability as one of the most requested things from people running MCP servers. That is an operations bill somebody got tired of paying.

I know that bill. Sticky sessions are a tax you pay forever, on every deploy, every scale event, and every 3am restart. They turn a stateless box you could shoot in the head into a pet with a memory you have to preserve.

The person who runs Glama, an MCP gateway and directory indexing tens of thousands of servers, said on Hacker News that they could not tell you what portion of their bugs came from the need to persist server state. That is the kind of sentence you only write after being on call for it.

Cloudflare and AWS both put their names to supportive quotes in the announcement, and their interest is obvious and legitimate. They sell the infrastructure a stateless protocol runs well on. It happens to be true anyway.

Statelessness is not the absence of state

Statelessness in a protocol is a claim about the protocol, not about the system. The state still lives somewhere. The spec has stopped saying where.

The changelog is honest about it. Servers that need state across calls are told to use explicit, server-minted handles passed as ordinary tool arguments.

You generate an identifier, you hand it to the model, and the model passes it back to you as a function parameter next time. Which is a bearer token, in a function argument, held by a language model, in a context window, next to whatever else it read today.

The objections are on the proposal's own review thread, from people who read it before it merged. One reviewer worked the example through: if a handle leaks, the session is anyone's. User A creates a counter, the id gets out, user B increments it.

Another made the broader point, that servers can no longer filter their tool, resource, and prompt lists by who is asking, and called it a step backwards.

Both comments are public, on the pull request, and the change merged anyway. That is not a scandal. Specs merge over live objections all the time, and shipping means choosing. But the objections were specific, they were correct, and they are now everybody's homework.

A writeup from Backslash Security put the general shape of it well: the state did not vanish, it moved, and scope enforcement now lives in each developer's code, applied inconsistently.

This is the part that outlives the version number. When a protocol drops a guarantee, the requirement does not leave with it. It relocates.

It moves out of one document that dozens of people argue over in public and into every server implementation that nobody reviews at all.

The most interesting idea in MCP, and nobody shipped it

Sampling was the one that made me sit up when I first read the spec. A server could ask the client's model to think on its behalf.

Your server needs a summary, or a classification, or a scrap of judgment, and instead of holding its own API key it borrows the brain already in the room.

Elegant. Also deprecated as of yesterday, alongside Roots and Logging, on a twelve-month clock.

I went looking for someone who lost something real when that landed, and mostly failed to find them, which is its own kind of answer.

One author running an MCP server in production wrote that they found sampling elegant and had never shipped anything on it. That is roughly every conversation I have had about the feature.

The strongest objection on the review thread is economic rather than technical. A reviewer pointed out that servers lose the ability to run inference without paying for it or charging the user for it.

That is exactly right, and I suspect it is also why client support was always thin. Nobody wants to hand a third-party server a blank cheque drawn on their own token budget.

An elegant feature that nobody adopted is still a dead feature, and I do not think killing it was wrong. With sampling gone and the server unable to open a conversation, MCP is a protocol for things you call.

The servers do not think anymore. They answer.

The core got smaller and the edge got bigger on the same day

While the core was being stripped, an Extensions framework was formalised to catch what fell out. Tasks moved out of the core into an official extension and were redesigned around polling. Enterprise-Managed Authorization, stable since June, is an extension. So is MCP Apps.

MCP Apps is the one I would watch. A server can now return interactive UI, which the host typically renders inside a sandboxed iframe in the conversation, talking back over postMessage in what the documentation itself calls a dialect of MCP with its own ui/ method prefix.

It already works in Claude, Claude Desktop, VS Code Copilot, Microsoft 365 Copilot, Goose, Postman, and a couple more.

Last time I wrote that the security part is what the quickstarts skip. So here is a second execution surface with a second trust boundary, shipping on day one of the extensions framework. Backslash makes the point that a sandbox escape in this context is not a browser tab, it is an editor.

The complexity did not leave. It got a version number and moved next door.

That is a better arrangement than leaving it in the core, and an optional feature I can decline beats a mandatory one I have to implement. But "the protocol got simpler" only holds if you stop reading at the first section of the changelog.

The people eating the migration are not the ones who asked for it

Credit where it is due, this is a well-run deprecation, and I have written about badly-run ones. Removals now carry a twelve-month minimum window, written into the governance section as policy rather than goodwill.

The SDKs speak both dialects: a new server still answers the old handshake, a new client falls back when it meets an old server, and stable 2.0 releases for Python, TypeScript, and C# all landed the same week as the spec.

If you used the SDK, most of this is a version bump.

If you hand-rolled a server against the wire format, it is not. The Glama operator was clear that the new protocol is incompatible in both directions and that it will be messy.

Both things are true at the same time. The SDKs are papering over a real break, which is what SDKs are for, and the people who read the spec and wrote their own transport are the ones who find out.

That is the pattern I keep noticing in these migrations. The change was driven by operators who needed horizontal scale, which is a real need and a serious one.

The bill goes to whoever wrote a server on a weekend because the protocol looked small enough to implement by hand. It looked small enough because it was. That was the pitch.

Still moving under everyone's feet

I closed the last MCP piece by saying that standards win slowly and unevenly, and that the spec was still moving under everyone's feet. That is the safest sentence I have ever written and it has aged perfectly, which is not something to be proud of.

So here is a less safe one. A specification getting simpler is not the same as your job getting simpler, and it is frequently the opposite. Every guarantee a protocol gives up is a guarantee somebody still has to make.

The handshake is gone and I do not miss it. What I miss is it having been somebody else's job to get right, argued over in public by people who do this for a living, in a document I could point a client at when they asked me why. Now it is a handle I mint, in a function argument, in my own code.

Nobody is reviewing that but me.