MCP transport mechanisms compared: SSE vs Streamable HTTP vs stdio
Streamable HTTP replaces deprecated SSE as MCP's standard, enabling serverless deployment.

The MCP specification is young and has moved fast. The November 2024 release (v2024-11-05) defined two standard transports: stdio and HTTP+SSE. By March 2025 (v2025-03-26), Streamable HTTP had replaced HTTP+SSE as the current standard, with SSE formally deprecated. The November 2025 revision went further, rewriting the protocol to be fully stateless, making it possible to run a complete MCP server in a single serverless Worker without any stateful sidecar.
"Deprecated" here means something precise. HTTP+SSE still functions for clients built before March 2025 that have not been updated; backward compatibility is intact. But no new server should target it. More concretely, Cloudflare Gateway does not route SSE traffic, which means any architecture relying on Gateway for authentication, routing, or policy enforcement is structurally incompatible with SSE-only upstream servers. Deprecation carries real infrastructure consequences.
The governance story matters too, because it bears on long-term stability. Anthropic donated MCP to the Agentic AI Foundation under the Linux Foundation, with founding members including OpenAI, AWS, Google, Microsoft, and Cloudflare. Cross-vendor governance signals that the protocol is not a proprietary bet on a single vendor's roadmap. That matters when evaluating whether to build nontrivial infrastructure on top of it.
It is also worth considering how SDK version interacts with spec version. The spec version that a given SDK targets determines which transports are actually available through that SDK. Older SDKs may expose stdio and HTTP+SSE cleanly but surface Streamable HTTP awkwardly, or not at all. Checking SDK version against spec version is not optional homework. It is the first diagnostic step when a transport implementation behaves unexpectedly.
stdio: What It Does, Why It Dominates Local Development, and Where It Breaks
At the wire level, stdio is elegantly simple. The MCP client launches the server as a child subprocess and exchanges JSON-RPC messages over stdin and stdout, one message per line. Stderr handles logging. No network socket is opened, no port is bound, no auth layer is configured.
That simplicity explains its prevalence. A measurement study published on arXiv (2509.25292) found that 95 of 341 measured MCP clients supported Streamable HTTP; by implication, the overwhelming majority support stdio, making it the most broadly compatible transport in the ecosystem today. For local file system access, local tool invocation, and single-user desktop agents, stdio's properties fit the task well: near-zero latency because there is no network round trip, and a process-isolation security model simple enough to reason about without documentation.
The problems emerge at the boundary between local and shared. stdio's model is one process per client connection. On a team of fifty developers each running several MCP servers, that produces hundreds of concurrent processes with no centralized control plane, no shared audit log, and no coordinated credential rotation. A concurrency test found that the overwhelming majority of requests failed under even modest simultaneous load, not because of a bug in any particular implementation, but because single-client-per-process is not a theoretical limit. It is structural.
Research corroborates what practitioners have observed directly: the large majority of MCP servers run on developer laptops, with very few deployed in production environments. stdio's dominance is a significant reason for that gap. Teams build locally with ease and then encounter a deployment wall they did not anticipate, because the transport they built against never had production as a use case. The mistake is not choosing stdio. It is carrying it past the context it was designed for. That raises an important question: at what point does a team's reliance on stdio shift from a practical choice into a structural liability?
HTTP+SSE: The Deprecated Transport and Why It Was Replaced
HTTP+SSE solved a real problem. It gave MCP servers a network-accessible transport that could push events from server to client without requiring the client to poll, a meaningful improvement over stdio for multi-user and remote scenarios.
The wire-level design, however, introduced coordination problems that compounded under production load. A dedicated /sse endpoint held an open SSE stream; the first event on that stream delivered a separate URL for a POST-based message endpoint. Server-to-client communication traveled over the SSE stream; client-to-server communication traveled over POST requests to that second endpoint. Two endpoints, coordinated state between them.
Long-lived persistent connections of this kind do not survive load balancers gracefully. Many proxies treat idle SSE streams as timed-out connections and close them. Serverless runtimes, which allocate compute on demand and reclaim it when a request completes, have no natural model for a connection that is intentionally kept open. Coordinating state between the SSE stream and the POST endpoint introduced error-prone bookkeeping that was difficult to make robust without significant infrastructure investment.
The one legitimate reason to still care about HTTP+SSE in 2026 is backward compatibility. A client predating March 2025 that has not been updated may only speak HTTP+SSE, and that is a real constraint in enterprise environments where client software update cycles are long. But the right response is to update the client, not to build new server infrastructure around a deprecated transport. Building around it defers the problem and accumulates technical debt against a transport that is increasingly incompatible with modern routing infrastructure.
Streamable HTTP: How the Current Standard Works and What It Enables
Streamable HTTP resolves the two-endpoint coordination problem by collapsing everything into a single HTTP endpoint. A client POSTs JSON-RPC messages to that endpoint; the server responds with either a plain JSON body for simple request-response interactions, or upgrades to an SSE stream for long-running calls. The client signals its capability; the server chooses the appropriate response mode. No separate events endpoint, no persistent connection required by default.
The architectural consequences are substantial. Because no persistent connection is mandatory, stateless servers become a first-class deployment target, compatible with serverless runtimes, load balancers, and CDN edge nodes that would have been impractical with HTTP+SSE. The spec includes support for resumable streams, allowing reconnection and replay without losing progress on long-running calls, which addresses the failure mode that made long-lived SSE connections fragile under real infrastructure.
Session management is available but not mandatory. An optional MCP-Session-Id header, cryptographically secure by spec requirement, lets servers maintain stateful sessions when the application requires it, without making statefulness a precondition for deployment. Authentication runs over standard HTTP, meaning OAuth, API keys, mTLS, and existing infrastructure auth layers integrate directly without custom plumbing. The spec also mandates Origin header validation to prevent DNS rebinding attacks, a security baseline that HTTP+SSE did not enforce.
A server can launch as simple request-response and add streaming support later without changing its endpoint or breaking existing clients. That is a meaningful property for teams that need to ship quickly and iterate.
One gap to acknowledge plainly: as of the measurement study cited above (arXiv 2509.25292), only 95 of 341 measured clients supported Streamable HTTP. The client ecosystem lags the spec, as it typically does in any protocol transition. Builders should verify client support before assuming Streamable HTTP is universally available in their target environment. The transport is the current standard; "current standard" and "universally implemented" are not synonyms.
What Runtime and Language Choice Does to Streamable HTTP Server Performance
Transport choice determines the architectural envelope; runtime choice determines what happens inside it. A February 2026 multi-language MCP server benchmark measured throughput across runtimes at identical concurrency, and the spread was wide enough to affect deployment strategy.
Micronaut with native image compilation reached 2,161 requests per second, the highest throughput in the benchmark, with fast startup and low memory footprint. Those properties matter acutely in serverless and edge environments where cold-start cost is a real operational variable. Bun, at 876 RPS, was the best-performing JavaScript runtime, notably ahead of Node.js on equivalent code. Node.js came in at 423 RPS. Python running FastMCP reached 259 RPS, a ceiling set by FastMCP's session-management overhead rather than the underlying ASGI server.
Tail consistency matters as much as peak throughput in production. Go showed approximately 0.5% throughput variability across benchmark rounds; Java showed approximately 0.7%. Both are substantially more predictable than options lower in the performance stack. Python delivered roughly 18% of the throughput of the high-performance tier.
The practical read-through: for edge and serverless deployment, Micronaut native image or Bun optimizes against cold-start cost and memory pressure. For sustained high-concurrency HTTP servers under variable load, Go and Java offer consistency that is harder to achieve otherwise. Python is a defensible choice for low-traffic servers or teams where throughput ceiling is not a binding constraint, but horizontal scaling should be planned for earlier rather than treated as a fallback.
Cold-start latency compounds with deployment topology in ways that matter. Loading tool schemas, opening database connections, and reading configuration can add multiple seconds to a typical MCP tool instance's startup time in a container-based deployment. Runtime choice interacts with this; so does where the server runs.
Token Overhead: When MCP's Abstraction Costs More Than It Saves
MCP's tool-schema model is expressive. It is also not free. Every tool definition consumes context tokens before a single call executes, and for servers that wrap large API surfaces, that overhead can become the binding constraint.
Cloudflare's approach to their API MCP server illustrates the problem and one solution simultaneously. Their server covers thousands of API endpoints using two tools: search() and execute(). Rather than exposing each endpoint as a native MCP tool, the model writes JavaScript against a typed OpenAPI representation inside an isolated Worker sandbox. Token cost stays at roughly a thousand tokens regardless of the API surface. An equivalent server that exposed every endpoint as a distinct tool would exceed the context window of most current foundation models.
This pattern is sometimes cited as an argument against MCP itself, the framing being that overhead makes direct CLI or API calls more efficient. One might argue that the overhead alone disqualifies MCP as a viable integration layer — but that framing misidentifies the problem. The overhead argument is strongest against naive one-tool-per-endpoint implementations of large-surface API wrappers. It is not an argument against Streamable HTTP, or against MCP as an integration layer. The search-and-execute architecture demonstrates that the token problem is solvable at the design level.
Where MCP's overhead is clearly justified: delegated authentication with scoped tokens and refresh logic that the client does not need to reimplement; multi-tenant products where per-tenant scoping needs to be a protocol-level guarantee rather than an application-level convention; enterprise governance pipelines where audit trails, permission boundaries, and compliance logging are first-class requirements. In each case, the abstraction pays for itself.
Before deploying: audit tool count and schema size. Collapse large API surfaces into search-and-execute or similar patterns rather than registering one tool per endpoint. The token budget is finite, and how a server spends it is an architectural decision.
The Deployment Pattern That Uses Both Transports Deliberately
The binary framing of stdio versus Streamable HTTP obscures the most practical architecture for many real deployments: a local stdio server handling file system access and local resources, connected upstream to Streamable HTTP servers for cloud capabilities. Each transport does the job it is suited for.
Bridge tooling makes this composable. Frameworks like mcp-remote and Cloudflare's supergateway wrap remote Streamable HTTP servers as local stdio processes, presenting them to clients that only support local connections. This is a compatibility shim, not an architectural ideal. The goal should be moving clients to native Streamable HTTP support, but it is a practical shim that makes the mixed architecture accessible today.
Most MCP SDKs allow a single server to bind to multiple transports. A common implementation binds stdio for local development and Streamable HTTP for production, gated by an environment variable or command-line flag. Tool logic is written once; only transport initialization differs.
The underlying design principle is that the transport boundary should match the trust boundary. Local process trust is categorically different from network trust, and the transport should reflect that distinction rather than obscure it. stdio is appropriate when the client and server share a machine and a user account; Streamable HTTP is appropriate when either condition is false, when multiple users are involved, when a load balancer or reverse proxy is in path, or when audit logging and authentication need to be enforced at the protocol level.
Running Streamable HTTP Servers at the Edge with Cloudflare Workers
The November 2025 spec revision formalized what edge deployment of MCP servers requires: stateless-by-default design. A server that does not require a persistent connection can run entirely in a serverless Worker, with no stateful sidecar to provision or maintain. Streamable HTTP's single-endpoint model, combined with the optionality of session state via the MCP-Session-Id header, is precisely what makes this viable.
Cloudflare's createMcpHandler, introduced to the Agents SDK in November 2025, wraps the stateless MCP TypeScript SDK so that tools, prompts, and resources deploy directly into a Worker. No separate server process, no container lifecycle, no persistent connection to keep alive. The operational footprint is substantially reduced compared to a traditional server deployment.
The edge topology has concrete performance implications. Requests route to the data center physically closest to the user; a client in Tokyo hits a nearby edge node rather than a distant origin region. Cloudflare's network spans over 335 cities. Cold starts at the edge are measured in sub-milliseconds, which eliminates the multi-second warm-up latency that characterizes container-based deployments. For latency-sensitive agentic applications, that difference is not marginal.
Cloudflare also contributed to the broader ecosystem during this period: the MCP TypeScript SDK was ported from Node.js to Web Standards, improving interoperability across Bun, Deno, and Workers. That benefits any deployment environment targeting those runtimes, not only Cloudflare-hosted servers.
For teams building on this stack, complementary infrastructure is available in the same operational layer. AI Gateway provides caching, cost tracking, LLM fallback, and prompt-injection guardrails in front of any LLM provider, without requiring changes to client code. Workers AI runs open models without GPU management. Durable Objects are available for the subset of MCP applications that require stateful session management, allowing teams to adopt statefulness selectively rather than as a default.
What holds across all of this is not that one transport wins. stdio will remain the right answer for local-dev tooling and single-user desktop agents precisely because it is simple and requires nothing. Streamable HTTP is the right answer for anything that crosses a machine boundary, serves multiple users, or needs to integrate with standard infrastructure. The specification's evolution has been toward making the latter easier to deploy correctly, and the tooling is following. Whether any given team is keeping pace with that shift is, at bottom, a question about how carefully they are reading what the spec is actually telling them to do.


