Your MCP server faces a mixed client fleet — do you ship dual-era support, and for how long?
answer
- measure before you commit
- the redeployable side carries the bridge
- two lifecycles, opposite rules
- keep it at the edge, not in the handlers
- ship the retirement date with the feature
basics
~20 sDecide from measured client traffic, not from a wish to be safe. Dual-era support means maintaining two lifecycle implementations, so carry it only while real handshake-era clients exist, keep the stateless modern path canonical, and publish a retirement date up front.
solid answer
~50 sDual-era support is not a compatibility flag; it is a second lifecycle implementation living alongside the first, and the two have opposite rules — the legacy path negotiates once per connection while the modern path must treat every request as self-contained. So price it honestly. Decide by measuring which eras actually reach you, using the entry point a request arrives through rather than self-reported client identity, which the specification says is untrusted. If real handshake-era traffic exists and those clients are embedded in host applications you cannot upgrade, carry the bridge — servers are the side that can be redeployed, so the burden lands there. Structure it as a thin adapter in front of a modern-first, stateless core, never as branching threaded through the business logic, and never let the modern path read state the legacy handshake established. Then set an end date, advertise the supported revisions honestly so clients can plan, and retire on measured usage rather than on hope.
go deeper
Understand the basic shape: some clients still speak the older handshake model, only a server implementing both can serve them, and supporting both means maintaining two ways of doing the same thing.
Explain what the second path costs — two lifecycle implementations with opposite statefulness rules and a doubled test matrix — and why the bridge sits in the server rather than in the client.
Demonstrate the operational judgment: instrument by lifecycle path rather than client-reported identity, keep the legacy handshake in an edge adapter over a stateless core, and roll the supported-version set uniformly so clients never see node-dependent answers.
Own the whole commitment — decide from measured traffic and client upgradability, publish the supported revisions and the retirement date when you ship the bridge, and be willing to conclude that a flat legacy share is a longer obligation to plan for rather than a date to keep moving.
## The decision, framed correctly Revision 2026-07-28 removed the `initialize` handshake and protocol-level sessions and made MCP stateless: every request is self-contained and carries its own protocol version and capabilities, and servers must not rely on prior requests over the same connection to establish context. Handshake-era clients — 2025-11-25 and earlier — cannot be talked into that model, because there is no fall-forward: an old client opens with `initialize` and has no code path for per-request declaration. The only bridge is a server that implements both, and since servers are the component an operator can redeploy while clients live inside host applications on someone else's upgrade schedule, the burden lands on the server by default. That is the argument *for*. The judgment call is whether it is worth what it costs, and for how long. ## What dual-era actually costs - **Two lifecycles with contradictory rules.** One path establishes context once per connection; the other forbids relying on any prior request. Holding both correct in one codebase is the real expense. - **A doubled test matrix.** Every primitive has to be exercised under both models, and the interesting bugs live in the seam. - **A correctness trap, not just a cost.** The tempting shortcut is to let a modern request reuse capabilities the handshake negotiated. It passes tests on a single process and fails behind a load balancer, where any node may serve any request and the node holding that handshake is not the one that got this call. - **Surface area you must keep secure.** Mechanisms that only exist to serve old peers still have to be authorized, rate-limited and audited to the same standard as the ones you actually want. ## Decide on measurement, not on comfort The instinct is to support everything forever because compatibility feels responsible. Replace that instinct with data: - **Instrument by mechanism, not by claim.** Count requests by which lifecycle path served them. Client-reported identity is explicitly untrusted in the specification and is self-reported, so use it as a hint for outreach at most, never as the number a retirement decision rests on. - **Separate volume from importance.** A tail of legacy traffic from one integration you can contact is a phone call. The same volume spread across an unknown installed base is a real constraint. - **Watch the curve, not the level.** A legacy share that is falling steadily needs a date; one that is flat means clients are not upgrading and something other than time is required. ## Architecture: adapter in front, modern core behind If you carry it, contain it. Write the server modern-first — stateless, self-contained requests, state expressed as explicit server-minted handles passed as ordinary tool arguments — and put the legacy handshake in a translation layer at the edge. The adapter accepts the old opening exchange, holds whatever it must, and calls the same stateless core the modern path calls. Two properties follow: the core never learns that the old era exists, so it cannot accidentally depend on connection state; and retirement is a deletion at the edge rather than an untangling everywhere. The anti-pattern is era branches sprinkled through handlers. Those are the ones that survive their own retirement date, because nobody can prove which branches are dead. ## Announce the supported set, and mean it Whatever you decide, be legible to clients. The revisions you serve should be discoverable, and a request declaring one you will not serve should be rejected with `-32022` carrying `data.supported` and `data.requested`, so the client can pick something workable instead of failing blind. Publishing an honest set is what lets integrators plan, and it converts an outage into a retry. ## Roll uniformly A half-rolled fleet is worse than either end state. Because MCP is stateless and any node may serve any request, a client can get a modern result from one node and `-32022` from the next, which reads as flakiness and generates support load nobody can reproduce. Make the supported-version set identical across the fleet before advertising a change, and expect clients to cache an era verdict per server — they are entitled to, and a fleet that answers inconsistently punishes them for doing the right thing. ## Setting the end date Publish the retirement date when you ship the bridge, not when you are tired of it. A dated commitment converts an open-ended obligation into a plan, and it gives integrators the same courtesy the specification gives implementers when it guarantees a deprecated feature at least twelve months in the specification. Then hold to the plan: announce, watch the legacy share fall, contact the remaining identifiable callers, and cut on measured usage rather than on a calendar alone. If the number will not fall, the honest conclusion is that you have inherited a longer-lived obligation than you intended — and that is a fact to plan around, not to keep discovering every quarter. ## What a strong answer sounds like Refuse the false binary. Say that the answer depends on measured legacy traffic and on whether those clients are upgradable, that the bridge belongs in the server because that is the redeployable side, that it must be an edge adapter over a stateless core, that the fleet must roll uniformly, and that the commitment ships with a date attached. The candidates who have run this describe the load-balancer failure mode without being asked.
- Why not just count client-reported names and versions to decide when to retire the legacy path?Because client identity in MCP is self-reported and explicitly untrusted, so it is evidence for outreach, not a basis for a retirement decision. Count the mechanism instead: which lifecycle path served each request. That number cannot be misreported, it covers clients that send nothing identifying, and it is the thing you are actually turning off.
- What is the specific failure mode of letting a modern request read state the legacy handshake established?It works on one process and breaks behind a load balancer. Since 2026-07-28 MCP is stateless and any node may serve any request, so the node receiving the modern call is usually not the one that handled that connection's handshake. The result is intermittent, unreproducible failures that scale with fleet size — the reason the modern path must stay strictly self-contained.
- How do you keep the legacy path from outliving its retirement date?Structure it as an edge adapter over a modern-first core so retirement is a deletion, not an untangling, and instrument it so its traffic share is visible on a dashboard rather than inferred. Ship the end date with the feature and treat it as a commitment. Era branches spread through handlers are the ones nobody dares delete, because nobody can prove they are dead.
- What should a server do for clients that declare a revision it will not serve?Reject that request with -32022 UnsupportedProtocolVersionError, carrying data.supported with the revisions it does serve and data.requested echoing what was asked. That turns a dead end into a retry: the client intersects the list with its own revisions and re-issues. Silently failing, or dropping the connection, denies the client the one piece of information that would fix it.
saying these in an interview costs you the question
- Supports both eras indefinitely with no measurement and no end date
- Decides retirement from self-reported client names, which the spec says are untrusted
- Threads era branches through business logic instead of isolating an edge adapter
- Lets the stateless path reuse handshake-negotiated state, breaking behind a load balancer
- Advertises new era support before the whole fleet serves the same revision set