MCP 2026-07-28 spec

On 28 July 2026 the Model Context Protocol shipped its largest revision yet, protocol version 2026-07-28. Most write-ups read it from the client or server author’s chair. This page reads it from a different seat: the gateway — the proxy, control plane, or edge that sits between an agent and the tool servers it calls. If your job is to route, meter, authorize, cache, or audit MCP traffic that you did not write on either end, this release changes your work more than almost anyone’s, and mostly in your favor. Everything below is drawn from the official 2026-07-28 announcement and the specification changelog; sources are linked inline so you can verify each claim at its origin.

The shape of the release

The headline of 2026-07-28 is that MCP became a stateless protocol built to run on ordinary HTTP infrastructure. Around that core the release also hardens OAuth to match how enterprises actually run identity, formalizes an extensions mechanism so optional features live outside the core, adds a real deprecation policy with a twelve-month clock, and cleans up transport, caching, and error semantics. It supersedes the prior revision, 2025-11-25, and Tier-1 SDKs (TypeScript, Python, Go, C#) plus a beta Rust SDK ship with migration guidance.

For a gateway operator, the practical takeaway is that a lot of the awkward gymnastics the old protocol forced on intermediaries — sticky sessions, shared session stores, body inspection to figure out what a request even was, holding a bidirectional stream open so a server could ask the user a question — are either gone or optional. The rest of this page walks the specific changes and what each one means when you are the thing in the middle.

Statelessness: the change that matters most in the middle

The old Streamable HTTP transport carried a per-connection Mcp-Session-Id header and an initialize/notifications/initialized handshake. That session was the source of most of a gateway’s pain: to route a follow-up request to the right backend you needed session affinity or a shared session store, and list results could vary per connection, so you could not safely cache them across callers.

2026-07-28 removes both. Protocol-level sessions and the Mcp-Session-Id header are gone (SEP-2567), and the initialize handshake is gone too (SEP-2575). Every request is now self-contained: it carries its protocol version and the client’s capabilities inside a _meta object (io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientCapabilities), clients identify themselves per request, and servers identify themselves in each result. A version mismatch returns a defined UnsupportedProtocolVersionError. Because the endpoints no longer vary per connection, a server that genuinely needs cross-call state now hands the client an explicit, server-minted handle and expects it back as an ordinary tool argument — state moves into the payload, out of the transport.

What this buys a gateway is blunt and large: any request can land on any backend instance behind a plain round-robin load balancer, with no shared session storage and no affinity rules. Horizontal scaling of MCP stops being a special case. The flip side, worth planning for, is that the safety net of session resumption is also gone — the spec removes SSE stream resumability and message redelivery (the Last-Event-ID header and SSE event IDs), so a dropped response stream loses the in-flight request and the client must re-issue it as a brand-new request with a new ID (SEP-2575). A gateway should treat a broken upstream stream as a clean failure to surface, not something to silently stitch back together.

Two related cleanups fall out of the same work. Servers now expose a server/discover RPC so a client (or an intermediary) can learn supported versions, capabilities, and identity up front instead of inferring them from a handshake. And the old HTTP GET notification channel plus resources/subscribe/unsubscribe are replaced by a single opt-in subscriptions/listen stream for server-to-client change notifications — one long-lived stream a client explicitly subscribes to, rather than an implicit side channel a proxy has to know about.

Header-based routing: Mcp-Method and Mcp-Name

Statelessness solves where a request can go. The new routing headers solve knowing what it is without opening it. Streamable HTTP POST requests now must carry standard Mcp-Method and Mcp-Name headers (SEP-2243), naming the operation (for example a tools/call) and the specific tool at the HTTP layer.

For a gateway this is the difference between parsing a JSON-RPC body on every hop and reading two headers. Rate limiters, WAFs, routers, and metering can now key on the operation and tool name directly — decide, throttle, and count at the edge without deserializing the payload, which is faster and keeps the sensitive body untouched for components that have no business reading it. The same SEP adds an x-mcp-header convention so a tool can accept values that are carried as custom headers rather than buried in arguments. A mismatch between the declared header and the body is itself a defined error, so an intermediary can reject inconsistent requests rather than guess. This is the change that most directly aligns MCP with the ordinary HTTP infrastructure a gateway is usually built from.

Multi Round-Trip Requests: elicitation and sampling stop being server-initiated

In earlier MCP, when a server needed something back from the human or the client mid-call — a prompt to the model via sampling/createMessage, a question to the user via elicitation/create, a directory listing via roots/list — it initiated a request back toward the client. That inversion is exactly what forces a proxy to hold a bidirectional channel open and correlate two directions of traffic on one logical call. It is the single hardest thing to relay cleanly.

2026-07-28 replaces the whole pattern with Multi Round-Trip Requests (MRTR) (SEP-2322). Instead of calling back, a server that needs more input returns an ordinary result marked resultType: "input_required", whose inputRequests field carries what it needs. The client gathers the answers and retries the original request with inputResponses attached. To make this legible, every result now carries a required resultType field — "complete" for a normal result, "input_required" for an interim one — and results from older servers that omit it must be treated as "complete". Because the exchange is now a sequence of independent request/response round trips rather than a live callback, a server no longer needs a completion notification to signal the end of an out-of-band interaction; the client simply learns the outcome on retry, which is why the notifications/elicitation/complete signal and its correlation id were removed.

For a gateway this is a genuine simplification. Every leg of an MRTR exchange is a plain stateless request/response you already know how to route, meter, and log; a server instance handling the retry need not be the one that started the exchange, because the continuation is encoded in an opaque requestState token the server round-trips through the client. Elicitation and sampling stop being a special bidirectional mode you have to support and become normal traffic. If your gateway offers a human-in-the-loop or approval step, note that the human interaction now naturally slots between two round trips rather than inside a held-open stream — a shape that is generally easier to insert a control point into.

Authorization hardening: OAuth that matches how enterprises run identity

The auth changes are individually small and collectively significant, because they close well-known OAuth failure modes rather than inventing new mechanisms. None of them change whether your gateway does OAuth or IAM — they tighten how it is done:

  • Issuer validation (RFC 9207). Authorization servers should return the iss parameter, and clients must validate it against the recorded issuer before redeeming an authorization code (SEP-2468). This closes the classic authorization-server mix-up attack, where a code minted by one server is redeemed against another.
  • Credentials bound to their issuer. Client credentials are keyed to the authorization server that issued them: persist them by issuer identifier, never reuse them against a different server, and re-register when the server changes (SEP-2352). A gateway that stores upstream credentials should scope them the same way.
  • Application type in registration. Clients must declare an application_type during Dynamic Client Registration so an authorization server can, for instance, accept localhost redirects for desktop and CLI apps without the redirect-URI ambiguity that otherwise creates (SEP-837).
  • DCR is on the way out. OAuth Dynamic Client Registration (RFC 7591) is now deprecated as the registration mechanism in favor of Client ID Metadata Documents (CIMD), where a client is identified by a URL that resolves to its metadata (PR #2858). DCR still works for backward compatibility, but new work should plan for CIMD.

The gateway-relevant point is that upgrading the protocol version does not, by itself, require you to change your inbound authorizer or your outbound credential providers — those layers sit alongside the protocol. What the spec does is raise the floor on how the OAuth handshake behaves, so a gateway acting as an OAuth client (or as an authorization server for its own callers) should audit its handshake against these four items rather than assume the old flow is still compliant.

Cacheable list results

List and read results — tools/list, prompts/list, resources/list, resources/read, and resources/templates/list — now carry ttlMs and cacheScope fields via a CacheableResult interface (SEP-2549). ttlMs is a freshness hint in milliseconds; cacheScope is either "public" or "private", and it controls whether a shared intermediary is allowed to cache the response at all. The spec also asks servers to return tools in a deterministic order to help client-side and prompt caches.

This is written for the thing in the middle. A gateway can now cache a tool catalog for exactly as long as the server says is safe, and — crucially — the "public" versus "private" scope tells it whether a cached list may be served to a different caller or must stay scoped to the one it was fetched for. In a stateless world where lists no longer vary per session, honoring these two fields lets a gateway cut redundant upstream list traffic without guessing at correctness. Treat cacheScope: "private" as a hard boundary, not a hint.

Governed extensions and a deprecation clock

Two structural changes shape everything above. First, the release adds a formal extensions mechanism: ClientCapabilities and ServerCapabilities gain an extensions field, and optional features now live in named extensions rather than bloating the core. The clearest example is Tasks, which moved out of the experimental core into an official extension, io.modelcontextprotocol/tasks, and was redesigned around polling (tasks/get, tasks/update) instead of a blocking call (SEP-2663). For a gateway, extensions are a capability-negotiation problem: read the extensions field to know what a given pair actually supports, and pass through what you do not need to interpret.

Second, MCP now has a real feature lifecycle and deprecation policy with Active, Deprecated, and Removed states and a minimum twelve-month deprecation window (SEP-2596). Several things enter the Deprecated state on this clock: Roots, Sampling, and Logging as features (SEP-2577), and the legacy HTTP+SSE transport, which finally gets an official one-year offramp toward Streamable HTTP. Deprecated does not mean removed — these remain functional through the window — but a gateway that still relies on HTTP+SSE, or that special-cases Sampling and Roots, now has a dated runway to migrate rather than an open-ended one.

A handful of smaller cleanups round out the release and are worth a gateway author’s attention: OpenTelemetry trace-context keys (traceparent, tracestate, baggage) are now documented conventions in _meta (SEP-414), which makes end-to-end tracing across a proxy a first-class concern; tool inputSchema/outputSchema loosen to full JSON Schema 2020-12 and structuredContent to any JSON value (SEP-2106); and the JSON-RPC error range gets a formal allocation policy so custom error codes and spec codes stop colliding.

What to check when you upgrade a gateway

Read as a checklist, the release asks a gateway operator a short list of concrete questions. Do you still depend on Mcp-Session-Id or session affinity anywhere — and can you drop it now that any instance can serve any request? Are you routing and metering on the new Mcp-Method/Mcp-Name headers instead of parsing bodies? Do you handle a broken stream as a clean failure the client re-issues, rather than trying to resume it? If you relay elicitation or sampling, have you moved from bidirectional callbacks to the MRTR round-trip shape? Does your OAuth handshake validate iss, bind credentials to their issuer, and have a CIMD path? And are you honoring ttlMs and cacheScope — especially the public/private boundary — before caching a list across callers?

Two comforts are worth stating plainly. The protocol version and your authorization configuration are separate concerns — upgrading one does not force you to re-issue credentials or reconfigure your authorizer — and the twelve-month deprecation window means nothing on the deprecated list breaks under you without warning.

Where a gateway fits

Most of what 2026-07-28 did — stateless requests, header routing, MRTR, cacheable lists — is, in effect, the spec meeting gateways halfway: it makes MCP behave like the routable, meterable, cacheable HTTP traffic that a control plane in the middle already knows how to handle. If you are weighing whether that middle layer should be something you build or something you run off the shelf, our plain-English primer on what an MCP gateway is covers the role itself, and the Meandr gateway page describes one implementation of it. To be exact about status: Meandr negotiates the MCP generation its upstreams and clients currently speak, and the 2026-07-28 changes described here are the direction the ecosystem is moving, not a checkbox any single product should claim wholesale. Read this page as a map of the spec, and judge any gateway — ours included — by how cleanly it lets these changes pass through.

Primary sources, all official: the 2026-07-28 release announcement, the specification changelog, and the 2026-07-28 specification itself. Each SEP linked above resolves to its pull request on the modelcontextprotocol GitHub.