What actually changed
MCP versions are dates, and the date only moves when something breaks backwards compatibility. Ours said 2025-06-18. The current revision is 2026-07-28, and the gap is not cosmetic: it removes the initialize handshake entirely, removes ping, moves the protocol version and client capabilities into per-request metadata, requires a resultType field on every result, drops session ids and stream resumability, and replaces server-initiated requests with a retry-based pattern.
In other words: the thing our servers are built around — a handshake that establishes a session — no longer exists in the protocol. That is a transport rewrite, not an upgrade.
Why you must not just bump the string
A version number in a protocol handshake is not documentation. It is an assertion to the other party about which messages you will understand and which shapes you will return. The client reads it and changes its behaviour — it stops sending things you would have needed, starts expecting fields you do not emit, and skips fallbacks it would otherwise have used.
So a version you have not implemented converts a clean, early, legible failure into a late and confusing one. Advertise 2025-06-18 honestly and a modern client knows exactly where it stands at handshake time. Advertise 2026-07-28 falsely and it finds out halfway through a tool call, with a missing field and no obvious culprit.
An old version that is true degrades gracefully. A new version that is false breaks mysteriously.
Check who owns compatibility before you panic
Before scheduling any migration, read the specification's compatibility clause — it often says the work is not yours. MCP's new revision puts it on the client: a result arriving without the required resultType field must be treated as a complete result, precisely so that servers on older revisions keep working.
That single sentence changes the priority of the whole task. Our servers were not broken and no integration was at risk. “We are a revision behind” and “we are broken” are very different statements, and conflating them is how teams get talked into rewrites they did not need.
The cheap middle path: implement discovery, keep the version honest
There was still a real gap. The new revision makes a discovery method — server/discover — mandatory, because that is how a modern client asks a server which revisions it speaks before committing to one. A server that only knows initialize cannot answer, so the client probes blind and fails without knowing why.
So we implemented that one method on both servers and left the version string alone. A 2026-07-28 client now asks what we speak, hears 2025-06-18, and negotiates down cleanly. An older client never calls it and is unaffected. Thirty minutes, no transport rewrite, no false promise.
The general shape: when you are behind on a protocol, implement its discovery or capability-advertisement surface first. It is almost always the smallest piece, and it is the piece that turns a silent incompatibility into a negotiation.
Two things that saved us time
Read the schema, not the changelog. The prose release notes named the concepts but not the field names, and the human-facing spec page returned an empty body twice. The machine-readable schema file gave exact interfaces — required fields, types, which result type each method returns — in one fetch. For anything you are going to implement rather than merely understand, the schema is both faster and authoritative.
Write the deferral down. Our commit names precisely what is still missing: the handshake removal, per-request metadata, resultType everywhere, and the new multi-round-trip pattern. A deferral you documented is a decision someone can revisit. An undocumented one is just drift that will be rediscovered as a surprise — which is the same failure mode as a filter nobody looked at for ten weeks.
One limit worth stating: we verified the discovery response against the published schema and confirmed nothing else regressed, but no 2026-07-28 client has yet negotiated against it in the wild. That is the untested seam, and we would rather name it than imply a completeness we have not earned.