Skip to content

Instantly share code, notes, and snippets.

@codefromthecrypt
Created February 2, 2026 00:59
Show Gist options
  • Select an option

  • Save codefromthecrypt/4850f398d3bf3971ec1b50e042ba0390 to your computer and use it in GitHub Desktop.

Select an option

Save codefromthecrypt/4850f398d3bf3971ec1b50e042ba0390 to your computer and use it in GitHub Desktop.
WebSocket Transport in MCP: History of Discussions

WebSocket Transport in MCP: History of Discussions

The Original Decision (PR #206, March 2025)

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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."

Community Pushback

The community consistently challenged these reasons across multiple repos.

Issue #493: "Simplify HTTP transport with Websockets" (Aug 2025)

@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.

MCP Python SDK Maintainer Support

@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.

Other Community Voices

  • @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.

SDK-Level Requests

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.

The Formal SEP: SEP-1288 (Aug 2025 -- Present)

SEP-1288 by @lmaccherone is the formal Specification Enhancement Proposal for WebSocket transport. It is currently in draft status, sponsored by @Kludex.

Arguments For WebSocket Transport

  1. 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.
  2. 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."
  3. Reduced protocol overhead -- Smaller per-frame overhead vs HTTP/2 bidirectional streams.
  4. Simpler semantics -- HTTP/2 multiplexing and prioritization is more complex than WebSocket for little benefit in MCP's context.
  5. Server-initiated notifications -- WebSocket naturally supports notifications like notifications/resources/updated and notifications/*/list_changed which are poorly served by current transports.

Design Decisions

  • 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.
  • mcpSessionId is placed in the JSON-RPC package (not headers), since WebSockets don't provide access to headers after connection establishment.

Auth Challenge

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.

Production Validation

@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."

Current Status

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."

The Stateless MCP Tension (SEP-1442)

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.

Summary: Why WebSocket Wasn't Chosen

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:

  1. A desire to keep the spec simple with fewer transports.
  2. Prioritizing stateless/RPC-style usage patterns over real-time bidirectional ones.
  3. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment