Streamable HTTP, Three Eras and the Current Wire Contract

Conçu pour la vitesse : latence d'environ 10 ms, même en cas de charge
Une méthode incroyablement rapide pour créer, suivre et déployer vos modèles !
- Gère plus de 350 RPS sur un seul processeur virtuel, aucun réglage n'est nécessaire
- Prêt pour la production avec un support complet pour les entreprises
MCP’s remote HTTP transport has gone through three distinct wire-era designs in about two years. The 2024-11-05 HTTP+SSE transport used two endpoints and a long-lived stream; it has been deprecated since 2025-03-26 and is eligible for future removal. Streamable HTTP replaced it in 2025-03-26 with a single endpoint — but that first shape still carried protocol-level sessions, a standalone GET stream, server-initiated requests on SSE, and resumable streams. The 2026-07-28 revision removed those mechanisms. What remains is unusually clean: every client JSON-RPC message is sent as its own POST, request responses are either one JSON object or an SSE stream scoped to that request, and selected request metadata is mirrored into headers so intermediaries can route and inspect traffic without parsing the body. That last design decision is the one worth reading closely.
A load balancer routes on a header while the server executes on the body, and the two disagree. That is not a hypothetical in this specification — it is the stated reason a whole validation rule exists. The 2026-07-28 transport explicitly accommodates intermediaries between client and server, and much of the new header contract only makes sense once you read it that way.
1. Three Eras
2024-11-05 — HTTP+SSE. Two endpoints. The client issued a GET that opened an SSE stream, whose first event was an endpoint event telling the client where to POST. Everything server-initiated arrived on the long-lived stream. Deprecated since 2025-03-26 under the feature lifecycle policy, with new implementations advised against adopting it.
2025-03-26 through 2025-11-25 — Streamable HTTP, first shape. A single MCP endpoint, which was the significant simplification. But four mechanisms kept it stateful: servers could assign a session via an Mcp-Session-Id header, terminated with an HTTP DELETE; clients could open a standalone SSE stream with GET to receive server-initiated messages; servers could send JSON-RPC requests on SSE streams; and streams were resumable via Last-Event-ID.
2026-07-28 — the current shape. None of those four remain. The revision notes list the removals plainly: the GET stream endpoint and protocol-level sessions are gone, and the sections below add that streams are not resumable and that servers must not send independent requests on a stream.
A server implementing only the current revision handles older traffic with specified behavior: GET or DELETE to the MCP endpoint gets 405 Method Not Allowed; an Mcp-Session-Id header is ignored and no session identifier is minted or echoed; a Last-Event-ID header is ignored.
2. The Wire Protocol
The server exposes one HTTP endpoint path — the MCP endpoint — supporting POST. The transport’s security baseline matters before any wire optimization: servers must validate the Origin header on incoming connections and return 403 for a present-but-invalid origin; locally running servers should bind to localhost rather than all interfaces; and servers should authenticate connections. Those requirements exist to reduce DNS-rebinding and unauthorized-access risk.
Sending. Every JSON-RPC message from the client is its own POST. The client must include an Accept header listing both application/json and text/event-stream, and the body must be a single JSON-RPC request or notification. Clients must not send JSON-RPC responses. One subtlety matters for implementers: although the transport defines how a notification POST behaves, the 2026-07-28 core protocol defines no client-to-server notifications over Streamable HTTP and does not define metadata-header requirements for notification POSTs.
Notifications. If the server accepts one, it returns 202 Accepted with no body; if it cannot, an HTTP error status, optionally carrying a JSON-RPC error response with no id.
Requests. The server returns either Content-Type: application/json with a single JSON object, or Content-Type: text/event-stream with an SSE stream scoped to that request. The client must support both — a server chooses per request, so a client that only handles one will break unpredictably.
What travels on a response stream. The server may send notifications related to the originating request — progress and logging messages — before the final response, and the final response should terminate the stream. The server must not send independent JSON-RPC requests on it. This is the explicit break from earlier revisions.
Cancellation. Closing the SSE response stream must be treated by the server as cancellation of that request, and because each request has its own stream the disconnect is unambiguous. The core protocol's cancellation notification is used only on stdio; on this transport there is no cancellation message and none is expected.
3. Where Server-Initiated Work Went
Two things previously needed a channel from server to client, and they were separated into different mechanisms.
Asking the client for something — sampling, elicitation, roots — is now embedded in results as input requests under the Multi Round-Trip Requests pattern. The server returns an InputRequiredResult carrying inputRequests; the client gathers what was asked and re-issues the original call with matching inputResponses. It is a second POST, not a push.
Long-lived change notifications — tool list changes, resource updates — are obtained by sending a subscriptions/listen request whose response is itself an SSE stream that stays open and delivers only the notification types the client opted into. Request-scoped notifications like progress do not appear there; they flow only on the stream of the request they belong to.
That separation is tidier than it sounds. The standardized long-lived notification stream exists because the client asked for it, and its contents are filtered by the client’s own subscription. Multi-round-trip work can remain stateless at the MCP transport layer because the server may encode needed context into requestState and the client echoes that opaque value on retry.

4. Two Details That Bite Behind a Proxy
The specification includes two operational notes that exist because SSE and reverse proxies have a difficult relationship.
Buffering. When initiating an SSE stream, servers should include X-Accel-Buffering: no. This instructs reverse proxies such as nginx to disable response buffering. Without it, a proxy may accumulate messages before forwarding them — introducing latency and undermining the point of streaming. If streamed progress updates arrive in a clump at the end, response buffering is one of the first intermediary behaviors to check.
Keep-alives. For long-lived streams, particularly the subscriptions/listen response, servers are encouraged to periodically emit an SSE comment line — a line beginning with a colon — as a keep-alive, so intermediaries and idle timeouts do not close a quiet connection. Per the SSE specification a comment carries no event data, and clients must ignore such lines rather than treating them as malformed.
5. The Header Contract
This is the part that makes the transport genuinely different from a JSON-RPC-over-HTTP convention, and the specification states its purpose directly: the transport mirrors selected JSON-RPC body fields into HTTP headers so that intermediaries — load balancers, gateways, observability tooling — can route and inspect requests without parsing the body.
MCP-Protocol-Version — required on every POST, and its value must match the protocol version carried in the body's _meta. A mismatch is rejected with 400 Bad Request and a HeaderMismatch error. A version the server does not implement gets 400 with an error listing the versions it does support. An unimplemented method gets 404 with JSON-RPC -32601, which is what distinguishes it from a legacy server's 404.
Mcp-Method — mirrors method. Required on all JSON-RPC requests.
Mcp-Name — mirrors params.name or params.uri. Required for tools/call, resources/read, and prompts/get.
For JSON-RPC requests, those standard request headers are required for compliance at the scopes above. Notification POSTs are a separate edge case: this revision defines their transport mechanics but not their metadata-header requirements. A minimal conforming tools/call request therefore looks like this on the wire:
POST to the MCP endpoint
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"get_weather","arguments":{"location":"Seattle, WA"},
"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28"}}}Servers may go further and designate specific tool parameters to be mirrored, using an x-mcp-header extension in the parameter's schema, which produces headers named Mcp-Param-{Name}. A server that annotates a region parameter gets Mcp-Param-Region: us-west1 alongside the body — which is exactly the shape a router needs to send a query to the right regional backend without reading the payload.
The constraints on that extension are strict and worth knowing before you design around it. Values must be non-empty, must match HTTP token syntax, must contain no control characters, and must be case-insensitively unique within the schema. Only primitive types are permitted — string, boolean, and integer, with number explicitly excluded. And the annotated property must be statically reachable from the schema root through a chain consisting solely of properties keys: not through array keywords, not through oneOf, anyOf, allOf, or not, not through conditionals, and not through $ref. Nested objects are fine as long as every step is a properties key. An annotation anywhere else makes the tool definition invalid, and a conforming client must exclude that tool from tools/list while logging a warning — so one malformed definition does not disable the rest.
Values that cannot be safely represented as plain ASCII are Base64-encoded inside a sentinel, =?base64?...?=, and the same encoding applies to Mcp-Name. A neat detail: a plain-ASCII value that happens to look like the sentinel must also be encoded, to remove the ambiguity.
6. Why Mismatch Is an Error
Servers that process the request body must reject requests where header values do not match the corresponding body values, returning 400 with JSON-RPC error -32020. The specification gives the reason explicitly, and it is a security argument rather than a tidiness one: this prevents vulnerabilities when different components in the network rely on different sources of truth — a load balancer routing on the header value while the MCP server executes based on the body value.
Read that as a threat model. If an intermediary makes a decision from a header and the server acts on a body that says something else, then a client that sets them differently can induce a split-source-of-truth condition — for example, routing or policy evaluated against one value while execution uses another. Required server-side validation closes that class of mismatch for the current revision, and encoded values must be decoded before comparison.
7. What This Means for a Gateway
It is rare for a protocol to specify what the thing in the middle should do. Because this one does, the mapping is unusually direct.
Route without parsing. Mcp-Method and Mcp-Name mean a proxy can tell a tool call from a resource read, and which tool, from headers alone. That is the difference between an MCP-aware layer and one that has to deserialize every payload to make a decision (MCP overview).
Expose tool identity to an authorization layer without body parsing. A conforming tools/call request carries the tool name in Mcp-Name. That gives an MCP-aware intermediary a protocol-defined input it can use when applying per-tool policy; authentication and authorization still have to be enforced by the gateway/server rather than inferred from the header itself (MCP authentication and security docs).
Rate-limit by tool or tenant. The specification names rate-limiting by tenant as an intended intermediary use of mirrored headers, with the version check as the safety condition (rate limiting).
Observe with useful dimensions. Method and tool name available without body parsing is exactly what per-tool telemetry wants, and it aligns with the tool-call conventions emerging in the telemetry standards (analytics and tracing docs).
Remove MCP-session affinity from the transport layer. With protocol-level sessions removed and response streams scoped to individual requests, the MCP transport no longer requires sticky routing or a shared MCP session store. Applications can still maintain durable state behind explicit handles or shared stores, so “stateless transport” should not be read as “stateless application.” For background on the earlier 2025 transport shape, see our stdio vs. Streamable HTTP comparison; general gateway distribution patterns are discussed in failover and load balancing.
One obligation runs the other way, and it applies to anything sitting in the path. An intermediary that does not recognize an Mcp-Param-{Name} header must forward it and otherwise ignore it. Stripping unknown headers — a common default in hardened proxies — will break conforming servers that expect them.
Where an agent harness fits. The transport still needs a caller that decides when a tool should be invoked. TrueForge, TrueFoundry's open-source agent harness, runs that execution loop across model calls, remote MCP tools, skills, sandboxing, approvals, context management, and session state. Its MCP connector supports remote servers with header authentication or OAuth, including an in-chat authorization pause when a user has not yet connected a server. That makes the separation clean: TrueForge orchestrates the agent step; Streamable HTTP defines the client-server wire contract; and a TrueFoundry MCP Gateway can govern tool traffic that is deliberately routed through it. None of those layers substitutes for the others.

8. A Deployable Posture
If you operate MCP servers, validate Origin, authenticate connections, and bind local servers conservatively before worrying about streaming polish. Then emit X-Accel-Buffering: no on SSE responses and keep-alive comments on long-lived streams; buffering and idle timeouts can masquerade as application latency or streaming failure. Validate header-body agreement rather than trusting either alone, and decode sentinel-encoded values before comparing. For traffic from earlier Streamable HTTP revisions, return the specified compatibility behavior — 405 on GET and DELETE, and ignore session and event-id headers — so older clients fail or adapt predictably.
If you operate an intermediary in the path, forward unknown Mcp-Param-* headers, check the protocol version before treating mirrored headers as authoritative, and take advantage of what the mirroring was built to enable: routing, policy inputs, rate limiting, and telemetry keyed on method and tool name without deserializing every payload. The header provides metadata; your identity and authorization layer still decides whether the caller may perform the action.
If you are writing a client, support both response content types, treat SSE comment lines as ignorable, and follow the fallback sequence — attempt a modern request, and on a 400 inspect the body before concluding anything, since modern servers also return 400 for unsupported versions and header validation failures. A recognized modern error means retry rather than fall back.
9. What the Protocol Does—and Doesn't—Solve
The 2026-07-28 transport simplifies the wire contract; it does not decide the agent's business logic. It removes MCP-level session affinity, defines how request-scoped streaming and retries work, and gives intermediaries validated metadata they can use for routing and policy inputs. It does not choose which tool an agent should call, grant the caller permission to use that tool, define approval policy, persist application memory, or prove that a downstream side effect was authorized by the system of record.
Those responsibilities belong to adjacent layers. An agent runtime such as TrueForge owns the execution loop and can manage MCP connectors, approvals, context, and session state. A TrueFoundry MCP Gateway can centralize authentication, authorization, tool access, and observability for traffic routed through it. The MCP server and downstream application still own their own business authorization and effects. Keeping those boundaries explicit is more useful than treating a cleaner transport as a complete agent-security model.
Finally, this article describes one revision of a fast-moving specification. Requirement levels here are from the 2026-07-28 text, real-world clients and servers can lag revisions, and the specification remains authoritative over any summary. This post also does not claim that a particular deployed TrueFoundry Gateway release internally consumes every 2026-07-28 mirrored header.
References
- Model Context Protocol — Streamable HTTP transport specification (2026-07-28), the source for transport mechanics, security baseline, request metadata, validation, and compatibility rules described here.
- Model Context Protocol — 2026-07-28 changelog, and the 2025-11-25 transport revision for the prior shape.
- Model Context Protocol — Multi Round-Trip Requests and subscriptions.
- TrueFoundry — stdio vs. Streamable HTTP — earlier transport context; MCP authentication and security; rate limiting; analytics.
- TrueForge — open-source agent harness and MCP server setup, documenting remote MCP connectors with header auth or OAuth, in-chat authorization, approvals, context management, and persistent sessions.
Mechanics, requirement levels, header names, error codes, compatibility rules, and MRTR state-security requirements are paraphrased from the linked 2026-07-28 specification text; the example request is adapted from the specification’s own illustration. MCP’s remote HTTP wire design has changed materially across three eras, and the specification is authoritative over this summary. TrueFoundry product claims are limited to capabilities documented on the linked current pages; this post does not assert that a particular deployed gateway version uses every 2026-07-28 mirrored header internally.
TrueFoundry AI Gateway offre une latence d'environ 3 à 4 ms, gère plus de 350 RPS sur 1 processeur virtuel, évolue horizontalement facilement et est prête pour la production, tandis que LiteLM souffre d'une latence élevée, peine à dépasser un RPS modéré, ne dispose pas d'une mise à l'échelle intégrée et convient parfaitement aux charges de travail légères ou aux prototypes.


















.webp)



.png)

.png)







