When @jspahrsummers (Anthropic) proposed replacing the original HTTP+SSE transport with "Streamable HTTP" in specification PR #206, the PR body included an explicit "Why not WebSocket?" section. The core team's stated reasons were:
- RPC overhead -- Using MCP in a stateless "RPC-like" way (e.g., a server that just exposes basic tools) would incur unnecessary overhead if a WebSocket connection is required for each call.
- No browser header support -- From a browser, there is no way to attach
headers (like
Authorization), and unlike SSE, third-party libraries cannot reimplement WebSocket from scratch in the browser. - GET-only upgrade -- Only GET requests can be transparently upgraded to WebSocket, meaning a two-step upgrade process would be required on a POST endpoint, adding complexity and latency.
- Combinatorial compatibility -- They wanted to limit the number of officially specified transports to avoid a combinatorial compatibility problem between clients and servers.
The PR noted: "This does not preclude further exploration of WebSocket in future, if we conclude that SSE has not worked well."
The community consistently challenged these reasons across multiple repos.
@crholm opened #493 arguing the current approach "seems to be attempting to reinvent Websockets using a combination of SSE and HTTP requests" and directly rebutted each original objection:
- WebSockets reduce overhead by eliminating repeated TLS handshakes
- Auth tokens can be passed via query params, cookies, or subprotocols
- The GET upgrade mechanism is simply how the protocol works and is transparent
They also raised distributed systems problems with SSE: in a load-balanced environment, SSE connections and HTTP requests may be routed to different servers, requiring shared state and complex session management.
@Kludex (MCP Python SDK maintainer, Anthropic member) commented on #493:
I personally think WebSocket should be the only transport.
And:
I had previously suggested to use WebSockets as well, given that it seems the obvious choice for bidirectional message support. Now that I see how complex the Python SDK has become due to the handling of session, streams and issues during development about long-lived HTTP connections I can be even more sure about it.
- @rev1si0n on #493: "It's unclear to me why HTTP+SSE was originally chosen... WebSocket is clearly a more suitable communication protocol."
- @CaliViking on PR #206: Called the "Why not WebSocket?" arguments "factually incorrect or misleading."
- @RoseSecurity on #493: "The initial arguments against Websockets felt extremely brittle to me."
- @lmaccherone on #493: Highlighted that edge serverless vendors (Cloudflare, AWS, Azure) now have first-class WebSocket support, including hibernatable connections -- something Streamable HTTP cannot leverage.
WebSocket requests appeared across every SDK:
- TypeScript SDK #435 -- Proposal to make WebSocket the standard transport. Redirected to the spec level. @KKonstantinov: "Transports decisions are taken on the spec level and not on the SDK level."
- TypeScript SDK #142 -- Asked about missing
WebSocketServerTransport. @jspahrsummers responded: "we should probably remove the WS transport that's there... we don't want a proliferation of transports." - Python SDK #302 -- Asked when WebSocket would launch. @dsp-ant (Anthropic): "The specification is moving towards Streamable HTTP with additional SSE support. Websocket is an alternative protocol that can be used but we expect most clients won't implement."
- Python SDK #411 -- Community member shared a working WebSocket FastMCP implementation.
- Go SDK #652 -- Proposal for WebSocket transport. @findleyr (maintainer): "we don't want to add features to the SDK that aren't part of the official spec." Closed for inactivity.
SEP-1288 by @lmaccherone is the formal Specification Enhancement Proposal for WebSocket transport. It is currently in draft status, sponsored by @Kludex.
- Cloud-provider support -- Edge/serverless providers (Cloudflare, AWS, Azure) have mature WebSocket support including hibernatable connections. Cloudflare Durable Objects can maintain up to 32,000 hibernated WebSocket connections per instance. Streamable HTTP long-lived connections consume chargeable resources even when idle.
- Intermediary reliability -- WebSockets are well supported by network intermediaries and enterprise proxies, while streamable HTTP support "is expected to take years for parity, if ever."
- Reduced protocol overhead -- Smaller per-frame overhead vs HTTP/2 bidirectional streams.
- Simpler semantics -- HTTP/2 multiplexing and prioritization is more complex than WebSocket for little benefit in MCP's context.
- Server-initiated notifications -- WebSocket naturally supports
notifications like
notifications/resources/updatedandnotifications/*/list_changedwhich are poorly served by current transports.
- Sessions are required for WebSocket (you'd only choose it when you need stateful bidirectional communication).
- Single connection per session to eliminate ambiguity about where to deliver server-initiated notifications.
mcpSessionIdis placed in the JSON-RPC package (not headers), since WebSockets don't provide access to headers after connection establishment.
Browser WebSocket APIs don't allow setting Authorization headers. This is not only a browser limitation -- it applies to Deno, Fastly, Cloudflare Workers, etc. The SEP proposes using WebSocket subprotocol fields for token transmission, with cookie and URL query parameter support as alternatives.
@sergey-png reported running WebSocket-based MCP servers in production for several months in an enterprise environment: "It's been stable and works well for our bidirectional communication needs."
As of the latest update, the Transports Working Group is managing next steps. Progress has been slow due to a parallel debate about making MCP stateless (SEP-1442). The SEP author notes: "The Transport WG is currently gridlocked between those that want big changes to make MCP stateless and those that want to make incremental improvements."
SEP-1442 proposes making MCP stateless by default -- removing the mandatory initialization handshake. This creates tension with WebSocket transport, which is inherently connection-oriented. However, the WebSocket SEP author argues the two are complementary: WebSocket handles stateful bidirectional use cases while plain HTTP POST handles stateless ones.
| Reason Given | Community Response |
|---|---|
| RPC overhead for stateless use | WebSockets reduce overhead; stateless use can coexist via plain HTTP POST |
| No browser header support for auth | Standard workarounds: cookies, query params, subprotocol fields |
| GET-only upgrade limitation | Non-issue in practice; all WS libraries handle this transparently |
| Combinatorial transport compatibility | Community argues WS should replace SSE, not be additive |
The underlying reasons appear to be:
- A desire to keep the spec simple with fewer transports.
- Prioritizing stateless/RPC-style usage patterns over real-time bidirectional ones.
- Organizational inertia -- the HTTP+SSE transport was already built and shipped before the community voiced strong WebSocket preference.
The WebSocket SEP (1288) remains active and sponsored. Its timeline depends on resolving the broader stateful-vs-stateless debate within the protocol.