MCP 2.0 removed the handshake. Being stateless is not the same as being ready.
Our MCP servers passed the new specification's headline requirement by accident. We tested it two independent ways to be sure. Then we read the rest of the revision and found we implement almost none of it, which turned out to be the more useful thing to know.
On 28 July the Model Context Protocol shipped its largest revision since launch. If you run anything that speaks MCP, this is the one that matters, and the way it is being summarised is going to cause people to make a specific mistake.
We made the same mistake for about an hour. This is the write-up, including the part where our starting assumption was wrong.
What actually changed
The headline is that MCP became stateless. That is true, and it is bigger than it sounds.
Previously a client and a server began with an initialize handshake, agreed a protocol version and capabilities, and carried a session identifier through every later request. The new revision removes that entirely. Each request now travels on its own, carrying its protocol version, client identity and client capabilities in a _meta field. There is no initialize, no initialized, and no Mcp-Session-Id header.
Four other things changed alongside it.
Request methods and tool names now travel in HTTP headers. A compliant server must accept Mcp-Method and Mcp-Name, which lets a gateway route and authorise a call without parsing the JSON body.
Multi Round-Trip Requests replace server-initiated requests that needed an open stream. A server can return resultType: "input_required" mid-call, and the client retries with the answers included.
List endpoints became cacheable. Responses from tools/list, prompts/list, resources/list and resources/read now carry ttlMs and cacheScope.
And the specification adopted a formal extensions framework, moving Tasks, MCP Apps and Enterprise Managed Authorization out of experimental status. Roots, Sampling, Logging and the legacy HTTP and SSE transport are deprecated, with a twelve month minimum migration window.
The assumption we started with, and why it was wrong
Our servers do not keep session state. We knew that. So the first conclusion was the obvious one: the headline feature is already how our servers behave, therefore we are most of the way to conforming.
We tested the statelessness claim two independent ways before building anything on it, because a belief about your own system is not evidence.
The first test was reading the code. Our session table is written when a client initialises and drained when it disconnects, and it is never consulted anywhere else. Nothing reads it to make a decision.
The second test was behavioural, run against both of our live engines. We called a tool cold with no session at all. We called it with a fabricated session identifier. We called it while deliberately withholding a real session identifier we did have. All three returned byte-identical response bodies.
So the claim held. Our servers are genuinely stateless, verified twice, and we can say so without hedging.
And it bought us far less than we expected.
Statelessness in the new specification is not a permission, it is a prerequisite. The revision does not merely allow you to drop sessions. It removes the handshake and replaces it with a different mechanism: per-request _meta, mandatory headers, a required discovery method, and a resultType discriminator on every single result. We match the philosophy of the specification by accident. We implement essentially none of its mechanism.
“We are already stateless, so we are nearly there” is the most misleading sentence anyone could take from our own evidence. We are keeping it in writing so we do not repeat it.
The one-line change that would have been worse than doing nothing
This is the part worth stealing regardless of what you run.
Our servers hold a tuple of supported protocol versions. Adding 2026-07-28 to that tuple is a one-line change. It compiles, it echoes the new version back to any client that asks, and it passes every automated check we currently have, including our own conformance checker.
It would also be a lie. The server would advertise conformance it does not have: no discovery method, no resultType, no header validation, no handling of the protocol version in _meta. A modern client that believed the advertisement would then fail in ways none of our checks can see, because our checks were written to test the old contract.
The dangerous version of an upgrade is not the one that breaks loudly. It is the one where the version string moves, the dashboard goes green, and the actual behavior is unchanged. We came close enough to that to be uncomfortable about it.
The feature that does not do what its name suggests
The second assumption we had to correct: cacheable list results sounded like the clearest win, because catalog and offers get queried constantly and caching them would take real load off the origin.
It does not do that. The ttlMs and cacheScope fields apply to tools/list, prompts/list, resources/list, resources/read and resources/templates/list. They do not apply to tools/call. A live catalog search runs through tools/call, so none of its output is covered.
What actually gets cached is the static catalog of tools, which changes only when we deploy.
That is a genuine improvement and a small one. It is also, usefully, almost risk-free: since no catalog data flows through it, the failure mode we were most worried about, an assistant confidently quoting a product that sold yesterday, is not reachable through this feature at all. We had ranked this item first for value and first for risk. It is neither.
Why we are not upgrading yet
No client is negotiating the new revision. The deprecation window is twelve months at minimum. The specification is days old.
Shipping a dual-era server now would mean maintaining two contracts through a period when the second one has no callers, in a codebase where all of our business domains run the same module in a single process. That is a real constraint: it means there is no per-tenant canary release until we build one, so the first deploy of a protocol change is a fleet-wide deploy. That fact alone should slow anyone down.
The honest position is that the upgrade is premature, and that saying so is more useful to our customers than a press release claiming day-one support for something no assistant is asking for yet.
What we are doing instead
The cheap, zero-risk half, now, because it costs about two engineer-days and it makes the real upgrade safe when it is time.
An audit of everything the new revision deprecates, so we know our exposure before the window closes rather than after.
A fix to our conformance checker so it tests the version negotiation matrix rather than assuming one revision. The checker was written when there was only one answer.
Client version telemetry, which is the highest-value item on the list and was not in our original plan. Right now we cannot see which protocol revision real clients ask for. That means the trigger to upgrade is invisible to us. Once we can see it, the decision stops being a judgement call and becomes an observation.
Consolidating our tool catalog, which currently exists in three hand-synchronised copies. Any protocol change touches all three, so collapsing them first makes the eventual upgrade smaller.
We will do the dual-era work when telemetry shows a real client asking for it. Not before.
What to ask a vendor about this
Three questions, and they work on us too.
Which protocol revision do you actually implement, and which ones do you advertise. Those should be the same number, and it is worth asking separately.
How would you know if a client asked for a revision you do not support. If the answer is a support ticket, they have no telemetry.
When the next revision lands, is that an adapter or a rebuild.
The specific version numbers will keep moving. Two years ago none of this existed in the shape it exists in now. What should not change is that the thing you advertise and the thing you do are the same thing.
Specification details in this post come from the Model Context Protocol 2026-07-28 release notes. Our own findings come from an internal scope of the revision completed 29 July 2026 against both of our MCP engines.
Keep reading
WebMCP is the part that changes things
Everyone is publishing files for agents to read. The interesting move is giving the agent something to press.
Read StrategyWhy AI shopping changes the funnel
The funnel assumed the shopper visits several sites and you win by being present at each stage. An assistant collapses those stages into one conversation you are either inside of or absent from.
Read TechnologyYour sitemap is lying about how fresh your content is
One field in your sitemap tells crawlers when your content last changed. On the site we measured, all 139 pages claimed the same date: today. Here is why that happens and what it costs you.
Read