MCP 2026-07-28 is out, and sessions are gone. Here is the whole story in plain English.
There is a restaurant in your city with one amazing waiter. He remembers everything. Your table number, your no-onions rule, the fact that your friend wants the bill split in two. You never repeat yourself. It feels wonderful.
Then the waiter gets sick.
The new waiter knows nothing. Your order is lost. Your no-onions rule is lost. You start again from zero, and you are annoyed. The whole restaurant depended on one person's memory.
Until this week, the Model Context Protocol worked exactly like that restaurant.
First: what is MCP?
If you have never heard of MCP, the docs' own one-line pitch is the fastest start: a USB-C port for AI applications. It is an open standard. It lets AI applications (Claude, IDEs, chatbots, agents) plug into tools and data the same way, no matter who built them.
In our restaurant: you, the customer, are the client (the AI app). The kitchen is the server, a program that offers tools, resources (files and data), and prompts. The dishes on the menu are the tools. And MCP itself is the ordering system: how you ask, how the kitchen answers, what happens when something goes wrong.
On July 28, the MCP team released the 2026-07-28 revision (their word for a version). When they announced the release candidate in May, they called it "the largest revision of the protocol since launch"; in the release post, MCP co-inventor David Soria Parra calls it MCP's most important release since remote MCP first launched over a year ago. It changes the ordering system from top to bottom.
The waiter is gone: MCP is now stateless
In the old protocol, a client and a server first did a handshake called initialize, a "hello, who are you?" step. After that, the server kept a session for you. On HTTP there was even a special header, Mcp-Session-Id, that locked you to one specific server instance (one running copy of the server).
And it had the same weakness. If that one instance crashed, your session died with it. Running three instances behind a load balancer (the machine that spreads requests across copies, like the host at the restaurant door deciding which waiter takes you) meant the balancer had to be "sticky": the host had to send you back to the same waiter every time. Serverless platforms (cloud services such as AWS Lambda that run your code only when a request arrives) start and stop instances all the time, so they did not work well with this.
The new version removes sessions from the protocol completely. initialize is gone. Instead, every request carries everything the server needs, inside a small _meta block: which protocol version you speak and what your client can do. Just the technical facts, not your conversation; anything else the server needs arrives as normal request parameters.
In restaurant terms: the restaurant reopened as a counter-service place. Table service is gone; you order at the counter, receipt in hand. Your whole order is printed on the receipt, and any cashier can pick it up and serve it. If one cashier goes home, the next one reads the same receipt and continues. Nobody has to remember you.
The smallest possible before and after:
// Before (2025-era): a handshake first, then a session to protect
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "my-app", "version": "1.0.0" }
}
}
// After (2026-07-28): no handshake, the receipt carries everything
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
The official 15-second animation shows the same before-and-after. On the left, a sticky load balancer sends every request only to Instance A, with a session tag pinned to it; when A dies, the client fails with it. On the right, requests take turns going to A, B, or C (round-robin), and when A dies they simply continue through B and C.
This is the big win of the whole release. You can now deploy an MCP server on serverless platforms or edge platforms (small servers close to the user), or scale it behind any ordinary load balancer.
The card at the door: server/discover
Without a handshake, how do you learn what a server offers? One new call: server/discover. Every 2026-07-28 server must implement it. It returns the server's capabilities (the list of things it can do) and the protocol versions it supports, in a single response.
Before, you walked in, sat down, and went back and forth with the waiter before you learned anything. Now a card at the door tells you what kinds of service this branch offers; the dishes themselves you still ask for with tools/list. The card even says how long it stays fresh (more on that below).
The buzzer: subscriptions/listen
Sometimes the kitchen needs to tell you something: a resource changed, the tool list was updated. The old protocol did this with a stream over HTTP GET (a stream is a connection that stays open so the server can keep sending), plus separate resources/subscribe and resources/unsubscribe calls. All of that is replaced by one method: subscriptions/listen.
You tell the server what you care about (resource updates, tool-list changes, prompt-list changes), and the server opens a stream tagged with your subscription ID. It is the buzzer the restaurant hands you: it sits quietly on your table and rings only when your order changes. When the server wants to close the stream on purpose, it should now say a proper goodbye: a small "complete" result with your subscription ID. That way your client knows the difference between a normal close and a broken connection.
One more simplification hides here: SSE (Server-Sent Events, the one-way streaming technology) resumability, the resume feature for broken streams, is gone. In the old spec, a broken stream could be resumed with a Last-Event-ID header, which quietly required server-side memory again. Now, if a stream breaks, you just send the request again with a new ID. If your buzzer breaks, nobody repairs it mid-ring; you take a new buzzer from the counter. Simple and cheap.
The question written on your receipt: MRTR
Nobody can come to your table anymore; the waiter who would have carried the kitchen's question is gone. So the cashier writes on your receipt: "Which sauce?" You come back to the counter with the same receipt and the answer written on it. Any cashier can complete the order from there. That is the smartest mechanism in the release, and in the spec it is called Multi Round-Trip Requests (MRTR).
Here is the problem it solves. In the old protocol, a server could send requests back to the client in the middle of an operation. For example: "please ask the user a question" or "please run this through your model". That needs a two-way, stateful channel (stateful means the server must remember you between messages), which conflicts with everything above.
The replacement: when a server needs something from you, it does not call you. It finishes the current request with a result marked resultType: "input_required", and inside that result it lists its questions. Your client then retries the same request, with the answers attached and, when the server sent one, a state token (the note on your receipt; the client carries it back without reading it) so the server can continue from the same place.
This is also why every result must now declare a resultType, either "complete" or "input_required". If an older server sends nothing, clients treat it as "complete".
The menu photo has an expiry time: caching hints
Being stateless has a cost: clients repeat questions like "what tools do you have?" more often. So the spec adds standard caching hints. Five results (tools/list, prompts/list, resources/list, resources/read, resources/templates/list) plus server/discover now carry two required fields: ttlMs and cacheScope.
ttlMs says how many milliseconds the result stays valid. cacheScope says who may reuse it: "private" means only the same logged-in user (the same auth token) may reuse it, and "public" means anyone may. It is a photo of the menu with a note on it: "valid for 10 minutes." The photo is either pinned to the public board by the door or left privately at your table. Only final "complete" results carry these hints; an "input_required" half-answer can never be saved for reuse.
The complaint codes on the wall
Every branch of the restaurant now prints the same complaint codes on the wall. Error codes were renumbered and, for the first time, given a real rule about who owns which numbers: header mismatch is -32020, missing required client capability is -32021, unsupported protocol version is -32022. The range -32020 to -32099 now belongs to the spec itself, and servers and clients must not invent their own codes inside it. Your client can react to a number instead of reading an error message and guessing.
The old side door is closing
A release this big also says goodbye to things:
-
Roots and Sampling, two client features (Roots let a server see your folders; Sampling let a server borrow your model), and Logging, a server utility, are deprecated (deprecated means: it still works today, but it is marked to be removed). Logging control moved into
_meta: you set alogLevelper request instead of callinglogging/setLevel. -
ping,logging/setLevel, and the roots list-changed notification are removed. - The original HTTP+SSE transport from 2024 (a transport is the way messages travel) is now formally deprecated under the new lifecycle policy.
- Tasks moved out of the core into an extension, so the core spec stays smaller.
- OAuth Dynamic Client Registration is deprecated and replaced by Client ID Metadata Documents. Skip this bullet if you do not touch OAuth.
The old side door still opens for a while, because the spec defines careful rules for talking to older servers. But the sign on it now says: please use the main entrance.
Where I am standing while I write this
A short honesty note. I have been working on this migration for weeks. I am moving the Dart team's MCP SDK (package:dart_mcp) to this version, with five merged pull requests so far in dart-lang/ai. The work is tracked publicly in dart-lang/ai#162. When the final spec came out, I compared it line by line with the release candidate (the near-final draft) and posted the report there. So everything above comes from the schema and the docs themselves, not from a summary of a summary.
What this means for you
If you build apps that use MCP servers: wait for your SDK to update, then enjoy the simpler model. No handshake to get right, no session to lose. Version and capabilities travel with every request.
If you write MCP servers: tape this to the kitchen wall. Implement server/discover. Put resultType on every result. Add ttlMs and cacheScope to your list and read results. If you needed to ask the client questions, redesign that flow around input_required. Your reward is that the server can finally run anywhere.
If you are just curious: the practical news is freedom in where servers can run. MCP servers used to need a machine that stays up and remembers things. Now they can run as short-lived functions close to users, and scale like any normal web service.
Also changed, in one breath
A few smaller items, so you know they exist:
- OpenTelemetry trace keys (monitoring IDs) got reserved slots in
_meta. -
tools/listshould now return tools in a stable order. - A tool parameter can now be mirrored into an HTTP header with the new
x-mcp-headerannotation, under strict naming rules. -
clientInfobecame optional on requests, whileserverInfonow travels in every result's_meta. - JSON Schema
$refresolution over the network (fetching schema pieces from other servers) is now banned by default.
None of these change the story above; all of them make life more predictable.
Your receipt, please
A restaurant that depends on one waiter's perfect memory can be a lovely place, right up until the day he is not there. A restaurant that writes everything on the receipt is less romantic, but it never loses your order, and it can open a hundred branches without breaking.
MCP just made that trade-off, and I think it chose well. If you want to see the change happen in real code, the Dart migration in dart-lang/ai#162 is open to read, one pull request at a time.
Top comments (0)