MCP servers need a way to talk to clients. That communication layer is called a transport, and for most of 2025, developers had two choices: stdio for local servers and Server-Sent Events (SSE) for remote ones. SSE worked, but it came with friction. It required two endpoints, long-lived connections, and careful session management that made deployment harder than it needed to be.
The MCP specification now defines a third option called Streamable HTTP. It is the recommended transport for any MCP server that runs over the network, and it is designed to replace SSE entirely.
The problem with SSE
SSE transport required clients to open a long-lived GET connection to an SSE endpoint, then send JSON-RPC messages to a separate POST endpoint. The server pushed responses and notifications back through the SSE stream. This two-endpoint design meant that:
- Load balancers and reverse proxies had to handle persistent connections differently from normal HTTP traffic.
- Serverless platforms like Cloudflare Workers and AWS Lambda struggled with the long-lived connection requirement.
- Session state had to be maintained across the SSE connection, adding complexity to horizontal scaling.
- Connection drops required reconnection logic and state recovery on both sides.
Developers building remote MCP servers spent more time managing the transport layer than building the actual server logic. Streamable HTTP fixes this by working with standard HTTP instead of against it.
How Streamable HTTP works
Streamable HTTP uses a single endpoint. Clients send JSON-RPC requests as HTTP POST bodies, and servers respond with standard HTTP responses. That is the entire baseline. No persistent connections, no special endpoints, no SSE stream required for basic operation.
The “streamable” part is optional. When a server needs to send multiple messages in response to a single request — progress updates during a long-running tool call, for example — it can upgrade the response to an SSE stream within that same HTTP response. The client knows this is happening because the response Content-Type switches to text/event-stream.
Here is what a basic request-response looks like:
POST /mcp HTTP/1.1
Content-Type: application/json
{"jsonrpc": "2.0", "method": "tools/list", "id": 1}
The server responds with:
HTTP/1.1 200 OK
Content-Type: application/json
{"jsonrpc": "2.0", "result": {"tools": [...]}, "id": 1}
No handshake. No session setup. Just a POST and a response.
For streaming, the same endpoint handles the upgrade:
POST /mcp HTTP/1.1
Content-Type: application/json
{"jsonrpc": "2.0", "method": "tools/call", "params": {...}, "id": 2}
HTTP/1.1 200 OK
Content-Type: text/event-stream
event: message
data: {"jsonrpc": "2.0", "method": "notifications/progress", "params": {...}}
event: message
data: {"jsonrpc": "2.0", "result": {...}, "id": 2}
The server decides per-request whether to stream. Simple responses return JSON. Long-running operations stream. Clients handle both.
Session management
Streamable HTTP supports optional sessions through the Mcp-Session-Id header. A server can include this header in its response to the initialize request. If it does, the client sends that header with every subsequent request, and the server uses it to maintain state.
Sessions are optional on both sides. A stateless server that just wraps an API does not need them. A server that tracks conversation context or manages subscriptions to resources does.
When a client wants to end a session, it sends a DELETE request to the endpoint with the session ID header. The server cleans up any state and returns 200.
Server-initiated messages
SSE’s persistent connection had one clear advantage: the server could push messages to the client at any time. Streamable HTTP handles this through an optional GET endpoint. Clients that want to receive server-initiated messages open a GET request to the same MCP endpoint, and the server holds it open as an SSE stream for pushing notifications.
This is only needed when the server uses sampling, sends log messages outside of request handling, or pushes resource change notifications. Most servers do not need it.
Why this matters for deployment
Streamable HTTP turns MCP servers into normal HTTP services. That change has practical consequences:
Serverless works. A Streamable HTTP server can run on Cloudflare Workers, AWS Lambda, or Vercel Functions without workarounds. Each request is independent. There is no persistent connection to maintain.
Standard infrastructure applies. Load balancers, CDNs, API gateways, and rate limiters all work without special configuration. The server is just an HTTP endpoint.
Scaling is straightforward. Without long-lived connections tying clients to specific server instances, horizontal scaling follows the same patterns as any other web service.
Authentication is simpler. Standard HTTP auth patterns (Bearer tokens, API keys in headers, OAuth 2.1) work on every request. No need to authenticate the SSE connection separately.
Streamable HTTP vs stdio vs SSE
| stdio | SSE (deprecated) | Streamable HTTP | |
|---|---|---|---|
| Use case | Local servers | Remote servers | Remote servers |
| Connection | Process pipes | Long-lived GET + POST | Standard HTTP |
| Streaming | Built in | Built in | Optional per-request |
| Serverless | No | Difficult | Yes |
| Session state | Implicit (process) | Required | Optional |
| Status | Active | Deprecated | Recommended |
stdio remains the right choice for local servers that run as child processes. For anything that runs over a network, Streamable HTTP is the path forward.
Migration from SSE
If you have an existing SSE-based MCP server, the migration is mechanical:
- Collapse your two endpoints (GET for SSE, POST for messages) into a single endpoint that handles POST.
- Return JSON responses for simple requests instead of pushing them through the SSE stream.
- For long-running operations, return
text/event-streamresponses from the POST handler. - Add the
Mcp-Session-Idheader to your initialize response if you need sessions. - Optionally support GET requests on the same endpoint for server-initiated messages.
Most MCP SDKs (TypeScript, Python) already support Streamable HTTP. If you are using an SDK, the change may be a configuration flag rather than a rewrite.
FAQ
Q: Is SSE transport going away? A: The MCP specification has deprecated SSE transport in favor of Streamable HTTP. Existing SSE servers will continue to work with clients that support them, but new servers should use Streamable HTTP. Client implementations are expected to drop SSE support over time.
Q: Do I need to support streaming in my Streamable HTTP server? A: No. The “streamable” part is optional. A server that only returns plain JSON responses over POST is a valid Streamable HTTP server. Streaming is there for when you need it, not as a requirement.
Q: Can I use Streamable HTTP for local servers? A: You can, but stdio is simpler for local use. Streamable HTTP adds HTTP overhead that does not buy you anything when the server runs as a child process on the same machine. Use stdio for local, Streamable HTTP for remote.
Q: What about WebSocket transport? A: The MCP specification does not define a WebSocket transport. Some third-party implementations use WebSockets, but Streamable HTTP covers the same use cases (bidirectional communication, streaming) while staying compatible with standard HTTP infrastructure. The transport comparison guide covers the tradeoffs in more detail.