This episode closes the operations phase with version negotiation across MCP spec eras: how modern and legacy clients interact, dual-era implementation strategies so one server serves both, and deprecation policy — features like Roots, Sampling, and Logging that carry a removal clock of about one year before being removed.

In episode 16 you managed a fleet with versioning and rolling deployment. But there's one version more complicated than your application's version: the MCP protocol version itself. The spec changed dramatically in 2026-07-28 — stateless, server/discover, MRTR, deprecations — while the clients in the wild (IDE agents, CLI agents, older applications) still have some speaking the 2025-11-25 era or older. Episode 17 covers how you survive when those two worlds meet.
This episode's roadmap: the version negotiation mechanism, what changed between the modern and legacy eras, dual-era implementation strategies so one server serves both eras, deprecation policy with removal clocks, and a measured migration plan.
Every MCP session starts with the initialize handshake (from episode 3). One of its most important roles is version negotiation: the client declares the protocolVersion it supports, and the server replies with the version they'll use together. The two don't have to be exactly the same — they find the highest version both understand.
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2026-07-28",
"capabilities": { "tools": {}, "resources": {} },
"clientInfo": { "name": "legacy-agent", "version": "1.4.0" }
}
}A server supporting multiple eras replies with the best-fit version. A modern server that's also compatible with old clients will respond protocolVersion: "2025-11-25" if the client only understands that era. This negotiation is the foundation of every compatibility strategy — without it, servers and clients from different eras would send each other messages neither understands.
So you know what needs bridging, here's a summary of the most decisive differences between the modern era (2026-07-28+) and legacy (2025-11-25 and earlier):
| Aspect | Legacy (≤2025-11-25) | Modern (2026-07-28+) |
|---|---|---|
| State | Session state with Mcp-Session-Id | Stateless, identity per request |
| Handshake | initialize then session | server/discover replaces the full handshake |
| Long notifications | Relies on the SSE stream | MRTR with messageId/routingId |
| Additional features | Roots, Sampling, Logging in core | Deprecated, replaced by OTel & extensions |
| MRTR | Not available | Available |
At the implementation level, this difference hurts most around state: legacy clients send Mcp-Session-Id and expect the server to remember it, while modern clients send everything per request. A server serving both must handle both patterns without mixing them.
Dual-era means one server binary that can serve clients from both eras simultaneously. This is the most widely used strategy during the transition, and the pattern is consistent:
protocolVersion value on initialize (or from server/discover for modern clients).Mcp-Session-Id), modern clients get the stateless flow.Example of era detection logic on the server side:
const MODERN = "2026-07-28";
const LEGACY = "2025-11-25";
function negotiate(clientVersion) {
const supported = [MODERN, LEGACY];
return supported.includes(clientVersion) ? clientVersion : LEGACY;
}
function handleInitialize(req, res) {
const era = negotiate(req.params.protocolVersion);
if (era === MODERN) {
return res.json({ protocolVersion: MODERN, serverDiscover: true });
}
res.setHeader("Mcp-Session-Id", crypto.randomUUID());
return res.json({ protocolVersion: LEGACY, capabilities: legacyCaps });
}Dual-era does add code complexity, but it buys valuable transition time: you don't have to upgrade all clients at the same time as your server release. Stricter strategies — for example rejecting all legacy clients — are only worth it if you control every client and have a coordinated release schedule.
Warning
Dual-era is not a reason to neglect security. Legacy features — especially session state and Roots — carry risks that were mitigated in the modern era. Make sure served legacy clients still go through the gateway with unified auth (episode 16), and monitor the metrics for how much traffic still uses the old era; that's the key data for deciding when the legacy era can be turned off.
MCP enforces a disciplined deprecation policy: abandoned features aren't removed suddenly, but given a removal clock — a grace period of about one year from when the deprecation status is announced. The three names you'll most often encounter:
These features are still usable while the removal clock runs — dual-era servers often provide them for old clients — but there will be no development, and support will be removed after the deadline. Your plan should be:
Putting it all together, here's a measured migration plan for your team:
Throughout the whole process, keep returning to version negotiation: as long as server and client agree on a version at the handshake, two eras can coexist peacefully. The most common mistake isn't technical, it's managerial — ignoring version negotiation until a big release surprises everyone.
Episode 17 closed the operations phase with maturity: version negotiation becomes the bridge between different spec eras, modern and legacy differences (state, handshake, notifications, features) are mapped out to be worked around, the dual-era strategy buys transition time, the deprecation policy with its ~one-year removal clock makes removal planned, and the five-step migration plan keeps the journey measured.
Key takeaways:
server/discover versus full handshake.In the next episode 18 we enter the advanced phase: Advanced SDK & Custom Transport — implementing custom transports like WebSocket and gRPC, JSON-RPC framing, notification batching, and error codes. See you there!