What is Streamable HTTP, and which MCP transport should you use?
Streamable HTTP is the Model Context Protocol's transport for servers reached over a network: one HTTP endpoint that takes JSON-RPC messages as POSTs and answers each with either a JSON response or an event stream. Use it for anything shared or hosted. Use stdio for a single user on their own machine. Do not start a new server on HTTP+SSE.

Most explanations of the transports come from people describing the spec or selling hosting. Both are useful. We come at it differently: we run the same product server over both live transports, so this post is about what each wire is for once you have shipped them.
It covers the wire and nothing else. Deploying and authenticating a remote server has its own post. Running a server on your own machine as a deployment shape is covered here, and tool design and construction live here. What an OAuth grant actually permits, and how to revoke it, is a separate question.
The three MCP transports, side by side
| stdio | HTTP+SSE (legacy) | Streamable HTTP | |
|---|---|---|---|
| How the client reaches the server | Launches it as a subprocess and talks over stdin and stdout | Opens a long-lived GET event stream, then POSTs to a second URL the server hands back | POSTs each message to one URL; an optional GET lets the server send messages unprompted |
| Endpoints | None. No port is opened | Two: the event stream and the POST endpoint | One |
| Where session state lives | Inside the process, which dies when the client closes | Bound to the one open connection | In an Mcp-Session-Id header if the server issues one, or nowhere if it runs stateless |
| Auth | None in the protocol; the server inherits whoever is logged in | Possible, but bolted onto a two-endpoint flow | A standard Authorization: Bearer header on every request; OAuth 2.1 is the expected shape |
| Behind load balancers and serverless | Not applicable; nothing leaves the machine | Poorly. Needs sticky routing and connections that stay open | Well. Each request can stand on its own |
| Spec status | Current | Introduced 2024-11-05, deprecated 2025-03-26 | Current since 2025-03-26 |
| Pick it when | One person, their machine, or a trial with zero setup | Only to keep an existing server working for a client that still needs it | Anything shared, hosted, or that has to know who is calling |
Read the table from the bottom up. The spec status row is a consequence of the rows above it, and none of those rows is about speed.
Why was SSE deprecated?
The old transport was a reasonable first attempt, and it failed on the shape of real infrastructure rather than on anything in the protocol.
Under the 2024-11-05 spec, a client opened a GET request to an SSE endpoint and held it open. The server's first event told the client where to POST its messages. From then on, requests went up through the POST endpoint and responses came back down the stream. So the session was the open connection. If the stream dropped, the session went with it. If the POST landed on a different server instance than the one holding the stream, that instance had no idea who the client was.
That breaks in three common places. Load balancers spread requests across instances, so you needed sticky routing to keep the POST and the stream on the same box. Serverless platforms are built for requests that finish, not connections that stay open indefinitely. Corporate proxies buffer or kill idle streams. Every remote operator ended up solving the same routing problem the transport had created.
People tend to read the deprecation as "SSE was removed". It wasn't. In Streamable HTTP, SSE is still there, but as a response format a server can choose for one request, not as the channel the whole session depends on. A slow tool call can stream progress back and then close. A quick one answers in plain JSON.
What is the difference between HTTP and Streamable HTTP?
Streamable HTTP is plain HTTP with a small set of rules on top. The client POSTs a JSON-RPC message to the MCP endpoint and sends an Accept header listing both application/json and text/event-stream, which tells the server it can take either answer. The server picks one per request.
A few headers carry what the connection used to carry. If the server wants sessions, it assigns an Mcp-Session-Id during initialization and the client sends it back on each later request, so any instance behind the balancer can pick the request up. If a stream drops partway through, the client can reconnect with Last-Event-ID and resume rather than start over. Credentials travel as an ordinary bearer token on every request, which is why auth fits here in a way it never did on the old transport.
So, does MCP use HTTP? For remote servers, yes, and since 2025-03-26 that means Streamable HTTP. For local servers, no. They use stdio, and nothing goes over the network at all.
Same server, two wires, two different jobs
We ship one product server both ways on purpose, and each wire covers a case the other can't.
The stdio package is npx -y @aioproductoscom/mcp@latest. Started without a PRODUCTOS_TOKEN, it runs in demo mode against a fully seeded showcase workspace with a read-only subset of the tools: no account, no credentials, no signup. That is the job stdio does better than anything else. You can try it with no auth handshake, no redirect and no approval screen, and the client simply launches a process. For evaluating a server, or for an agent checking a claim before a human is involved, that friction floor is the point.
The hosted endpoint is https://platform.aioproductos.com/api/mcp, over Streamable HTTP, with OAuth 2.1 and PKCE (S256) and dynamic client registration. It is listed in the official MCP Registry as com.aioproductos/mcp. It covers the case stdio can't: real data, per-person identity, one install for a whole team. Every request arrives with a token that says who is calling, which a subprocess on someone's laptop cannot tell you. The full surface and every way to connect are documented here.
The lesson from running both: the transport follows from who the caller is. If the caller is "whoever is at this keyboard", stdio. If the caller is a named person with their own permissions on shared data, Streamable HTTP.
Transport, auth and deployment are three separate decisions
Most pages blur these together, so it is worth saying once. Transport is how bytes move between client and server. Auth is how the server knows who sent them. Deployment is where the server runs and who operates it. Streamable HTTP makes bearer-token auth natural, but the transport doesn't require it, and choosing stdio doesn't mean your server can't reach remote data. Decide each one on its own terms. The auth side is covered in the remote server post and the grant-and-revoke post.
When stdio (or even SSE) is still the right call
stdio is not a lesser transport. For a single-user local tool, like a filesystem server, a browser driver or anything touching a device on your desk, it is the simplest thing that works, and adding HTTP would bring an endpoint, a token and an operator for no gain. If only you need the answer and the data is on your machine, stay on stdio.
An existing SSE server that works for its only client doesn't need an emergency rewrite. If the traffic goes to one known client on one instance, the routing problems above may never reach you. Plan the move to Streamable HTTP when you next touch the server, and don't start anything new on SSE.
The awkward case is on the client side. Some clients can still only launch local subprocesses. For them, the workaround is a local bridge process that speaks stdio to the client and Streamable HTTP to the remote server. It works, but it brings back the per-machine install that hosting was supposed to remove. Check which transports your target clients actually implement before you write setup instructions that promise otherwise.
Check it yourself instead of trusting this post
Everything above can be verified without an account.
Run npx -y @aioproductoscom/mcp@latest with no token set. It starts over stdio in demo mode against the seeded workspace, which shows the zero-auth case in one command.
Fetch https://platform.aioproductos.com/.well-known/oauth-protected-resource. It names the MCP endpoint as the resource and lists the authorization server that guards it. That is the discovery document a Streamable HTTP client reads when it needs to authenticate.
POST an initialize message to https://platform.aioproductos.com/api/mcp with Accept: application/json, text/event-stream and check the Content-Type on the reply. application/json means the server answered in one shot, and text/event-stream means it opened a stream for that request. Either one is Streamable HTTP working as specified. The server manifest at /.well-known/mcp.json on our site declares "transport": "streamable-http", so an agent can read the wire from a file rather than inferring it.
If you want the same comparison for other tools' servers, the MCP guides cover what each one exposes and where it stops. If you want to run the Streamable HTTP side against your own product data, connect a client to the hosted endpoint.