Protocol versions
MCPG negotiates two MCP revisions on one listener — the stateful 2025-11-25 release and the stateless DRAFT-2026-v1 draft. How version selection works, what changed between them, and why a single deployment serves both.
The Model Context Protocol is a moving target. MCPG's answer is to speak more than one revision of it at the same time, on the same listener, so a fleet doesn't have to fork or re-deploy to track the spec.
Today MCPG negotiates both:
2025-11-25— the current production-grade release revision. Stateful:initializehandshake, sessions, SSE.DRAFT-2026-v1— the draft of the next revision (it graduates to2026-07-28on final release). Stateless: no handshake, no sessions, MRTR.
Both are served by one gateway process. This page is the mental model; the wire details and config keys live in the configuration reference.
How version selection works
A client signals its revision; the gateway routes the request to the matching
protocol handler and serves that revision's wire shape. Internally each
revision is a typed ProtocolVersion selected from a registry, not branched at
ad-hoc call sites — so adding the next revision is a registration, not a rewrite.
- The stateful revisions (
2025-11-25and its accepted legacy peers2025-06-18,2025-03-26) signal the version via theMcp-Protocol-VersionHTTP header, runinitializenegotiation, and pin a session. - The modern revision (
DRAFT-2026-v1) is stateless. There is no handshake; the version, client info, and client capabilities travel in_metaon every request, and routing headers (Mcp-Method,Mcp-Name) let an intermediary route without parsing the body.
The default for new deployments is 2025-11-25 — a single compile-time constant
selects the default, so an operator pinning the modern revision is a one-line
change. The gateway accepts both the DRAFT-2026-v1 and the final 2026-07-28
wire strings for the modern revision, so clients on either string land on the
same handler when the spec ships final.
Why the modern revision is a rewrite, not an iteration
DRAFT-2026-v1 is structurally a rewrite. One shift drives most of the rest:
The protocol is now stateless. Sessions are gone. The handshake is gone. Every request is self-contained.
That cascades into the rest of the delta:
| Concern | 2025-11-25 (stateful) | DRAFT-2026-v1 (stateless) |
|---|---|---|
| Handshake | initialize + notifications/initialized | none — context rides in _meta per request |
| Discovery | learned from initialize result | server/discover (a callable method) |
| Sessions | Mcp-Session-Id, server-pinned | no sessions; DELETE /mcp removed |
| Server→client requests | SSE-based (elicitation/sampling/roots) | Multi Round-Trip Requests (MRTR) |
| Long-lived stream | GET /mcp (SSE) | subscriptions/listen |
| HTTP routing | body-parsed | Mcp-Method / Mcp-Name routing headers |
| Tasks | experimental core | an official extension with its own lifecycle |
roots / sampling / logging | core | deprecated |
The practical payoff of statelessness: load balancers can plain round-robin.
No sticky sessions, no shared session store required for the modern wire — each
request is independent. (Shared coordination is still needed for
subscriptions/listen streams and tasks.)
MRTR — pausing a call to ask the client
The biggest mechanical change is how the server asks the client for something
mid-call. In the stateful revision, a tool that needs input (an elicitation, an
LLM sampling, a roots/list) sends a server-initiated request over the session's
SSE channel and waits.
In the modern revision that becomes Multi Round-Trip Requests (MRTR): the
call returns an "input required" result carrying an opaque requestState; the
client answers; the call resumes. Because there's no session to hold the paused
state, MCPG persists it in a hybrid scheme — encrypted inline up to a size
threshold, KV-backed handle above it. The encryption key is operator-
configurable. In MCPG this is exactly the suspension model the
pipeline step kinds already use — the
elicitation, sampling, roots_list, and multi-entry gather steps suspend
and resume identically, mapped onto whichever wire the client is speaking.
What this means for you
You usually don't touch any of this — the gateway negotiates the right revision for each client automatically. But the model has real operational consequences:
- No forced migration. Clients on
2025-11-25keep working unchanged while newer clients adopt the modern wire against the same endpoint. You roll forward client-by-client, not all-at-once. - One deployment, two protocols. You don't run two gateways or two endpoints. A single MCPG listener serves both revisions.
- Backends never see the version. The protocol revision is entirely client-facing. A backend gets an HTTP / NATS / SQL request in its own protocol regardless of which MCP revision the client spoke — so a binding written today works under both revisions with no change.
- Statelessness simplifies scaling. Modern-wire traffic round-robins across instances with no session affinity, which is the cheaper HA story.
Where to go next
- Architecture — where protocol handling sits in the request flow.
- Configuration reference — the
mcp:block, pipeline step kinds, andrequestStatesettings. - Plugins and the plugin protocol — the plugin ABI, which versions independently of the MCP wire protocol.