Protocol revision 2026-07-28
MCP Versioning: Modern and Legacy Eras
The 2026-07-28 revision split MCP into two eras. What moved, which combinations of client and server actually work, and how to keep a page about this protocol from going stale.
On 2026-07-28 MCP removed the connection ceremony it had used since the beginning. The specification now speaks of two eras, and the word choice is honest: this is not a version bump, it is a different way of opening a conversation.
What moved, and when
2024-11-05 first public revision. HTTP+SSE transport.
2025-03-26 Streamable HTTP introduced, HTTP+SSE deprecated.
2025-06-18 MCP-Protocol-Version header introduced.
2025-11-25 last revision of the handshake era.
2026-07-28 handshake, sessions, GET stream and resumability removed.Read that list as a warning rather than trivia. In under two years the protocol has changed transport twice and connection model once. Any guide to MCP that does not say which revision it describes is describing an unknown one.
Why remove the handshake
A handshake creates a session, a session creates affinity, and affinity is what makes a service hard to scale. Sticky routing, session storage, resumption after a dropped stream: all of it existed to maintain a conversation the protocol did not otherwise need.
Removing it turns an MCP server into an ordinary stateless HTTP service. It sits behind a load balancer with no special routing, scales horizontally, and loses nothing but in-flight work when a process dies. The cost is paid by anyone who was keeping state in the connection, and it is paid most visibly in multi round-trip calls, where a tool asking the user a question is now two independent invocations.
The compatibility matrix
client server outcome
modern modern works
modern legacy fails; probe first so it fails cleanly
dual-era modern works, stays modern
dual-era legacy works, falls back to initialize
legacy modern fails; legacy clients cannot fall forward
legacy dual-era works, served under legacy semanticsThe row that causes real incidents is modern client against legacy server. A legacy server may reject the request, or stay silent, or, on an era-ambiguous method like tools/call, simply process it under legacy semantics because it never checked that initialize had happened. That last case is a success that should have been a failure. Sending server/discover first converts it into a deterministic error, which is why the specification recommends probing even for clients that only speak the modern protocol.
A server that supports only modern versions should still name its supported versions in whatever error it returns to an initialize request. Legacy clients have no way to fall forward, so that message may be the only diagnostic a user ever sees.
Extensions, and the next thing to go stale
This revision also formalised extensions: opt-in capabilities negotiated through the same per-request metadata, carrying reverse-DNS identifiers such as io.modelcontextprotocol/tasks. Tasks covers long-running work with durable handles; MCP Apps covers interactive UI returned inline.
If one party supports an extension and the other does not, the supporting party must fall back to core behaviour or reject the request cleanly. That rule is what keeps extensions from fragmenting the protocol, and it is also the seam where the next round of confusion will appear.
How to keep this from happening to you
The durable part of MCP is small and has survived every revision so far: a tool is a name, a description and a JSON Schema; tools/call takes a name and arguments; the result is content blocks with an error flag. That is the part worth committing to memory.
The ceremony around it, handshakes, sessions, streams, headers, has changed repeatedly and will change again. Read it from the revision, not from memory, and date anything you write down. That is why every page in this hub carries the revision it describes at the top.