Skip to content
awesome-applied-ai
← Design problems

08Orchestration & Protocols

Migrating an MCP fleet to the stateless specification

We run 40 internal MCP servers on the 2025-11-25 revision. The 2026-07-28 revision removes protocol sessions and the initialize handshake entirely. Plan the migration for a fleet where several servers hold per-connection state.

What changed, precisely. Revision 2026-07-28 removes protocol-level sessions and the Mcp-Session-Id header. It removes the initialize and notifications/initialized handshake; every request now carries its protocol version and client capabilities in _meta under io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities. List endpoints no longer vary per connection. Servers needing cross-call state must mint explicit handles and accept them back as ordinary tool arguments.

It also removes SSE stream resumability — Last-Event-ID and event IDs are gone. A broken response stream loses the in-flight request, and the client must re-issue it with a new request ID.

The constraint. "Per-connection state" and "list endpoints vary per connection" are the two patterns the revision deliberately breaks. Any server whose tools/list depends on who is asking, or which accumulates state across calls on one connection, needs redesign rather than a version bump.

Architecture.

  1. 01Inventory by state dependency, not by server. Three buckets: stateless already; state that can become an explicit handle; state that was really per-user authorization. The third bucket is the trap — servers that varied their tool list by caller were doing authorization through the protocol, and that now has to move into the authorization layer proper.
  2. 02Convert sessions to server-minted handles. A handle is an opaque, signed, expiring token that the server issues from one tool call and accepts as an argument to the next. Sign it, scope it to a principal, and give it a TTL. It is now part of your tool schema and therefore visible to the model, so it must carry no secrets.
  3. 03Make list results genuinely uniform. If the tool list must differ by caller, return the union and enforce permission at call time with a clear denial. Uniform lists are also what makes the caching contract in item 10 work.
  4. 04Handle the resumability loss explicitly. Long-running work can no longer survive a dropped stream. Either make the operation idempotent and safe to re-issue, or move it to the tasks extension and poll. Idempotency keys on write-capable tools become mandatory rather than good practice.
  5. 05Dual-stack during the window. Servers accept both revisions; clients negotiate via server/discover. The version now travels per request, so a server can support both without connection-scoped branching.

Deprecations to plan around in the same pass. Roots, Sampling, and Logging are deprecated with a twelve-month minimum window. Migrations: pass directories via tool parameters or resource URIs instead of Roots; call the LLM provider directly instead of Sampling; log to stderr or OpenTelemetry instead of Logging. Dynamic Client Registration is deprecated in favour of Client ID Metadata Documents. HTTP+SSE transport is now formally Deprecated rather than merely discouraged.

Stack. A private MCP registry is the precondition for a fleet migration of this shape — you cannot roll a version boundary across 40 servers you do not have an inventory of. Pin by digest. The official SDKs at 2.0 track the new revision. OpenTelemetry for the logging migration, using the newly documented traceparent, tracestate and baggage conventions in _meta.

Where answers fail. Treating it as a library upgrade. Removing sessions is an architectural change to any server that had them, and the servers that varied their tool list per caller have a security review to do, not a migration.