If you're picking a transport for an MCP server, the honest answer is that there are only two live options and the choice between them is almost entirely about where the server runs, not about throughput or feature richness. The MCP spec defines exactly two transports: stdio for a server the client launches as a local subprocess, and Streamable HTTP for a server that runs as a network service behind a URL (MCP spec, Transports). As a rule of thumb, stdio is for local development and desktop integrations, and anything you run in production should be Streamable HTTP. Everything below describes the current spec, the 2026-07-28 revision, which left stdio's framing alone but reworked Streamable HTTP substantially.
You'll also run into "SSE," the older two-endpoint HTTP+SSE transport that Streamable HTTP replaced. It's been deprecated since the 2025-03-26 spec revision and only hangs around for backwards compatibility, so don't reach for it on anything new (MCP spec, Streamable HTTP). That leaves a real two-way choice: stdio or Streamable HTTP.
The short version
| Question | Answer |
|---|---|
| Server runs locally, launched by the client? | stdio |
| Server runs as a remote/network service? | Streamable HTTP |
| You have an old HTTP+SSE server or client? | Migrate to Streamable HTTP; keep the old endpoints only for back-compat |
Both current transports carry the same JSON-RPC 2.0 messages and expose the same tools, resources, and prompts. The transport doesn't change what your server can do; it changes how the client reaches it and who owns the process lifecycle.
stdio: the local subprocess transport
With stdio, the client launches the MCP server as a subprocess and talks to it over that process's standard input and output. The server reads JSON-RPC messages from stdin and writes them to stdout, one message per line, and it may use stderr for logging (MCP spec, stdio). One rule bites people who log carelessly: the server must not write anything to stdout that isn't a valid MCP message. The client, meanwhile, may ignore anything the server writes to stderr, so don't treat it as an error signal (MCP spec, stdio). A stray console.log or print to stdout is the classic way to corrupt an stdio stream.
There's no network and no port. The client owns the process: it spawns it, pipes to it, and terminates it. What it doesn't own is a session: as of the 2026-07-28 revision MCP is explicitly a stateless protocol, and an open stdio process is not a conversation, so a server must not infer anything from earlier requests on the same pipe (MCP spec, Statelessness). The upside is that a crashed server is cheap to recover from: the client just restarts it and retries.
A stdio server is configured in the client by telling it what command to run, not what URL to hit.
{
"mcpServers": {
"example": {
"command": "node",
"args": ["path/to/server.js"],
"env": { "API_KEY": "your-key" }
}
}
}Pick stdio when the server is a desktop or CLI integration, a locally installed tool, or anything a single client runs on the same machine. Because each launch is one process serving one client, "many users" means "many processes," which is fine for local development and desktop tools but wrong for anything you deploy or share. It's not a production transport. Credentials come in through environment variables (as above), since there's no HTTP layer to attach auth to. For a build walkthrough, see building an stdio MCP server.
Streamable HTTP: the remote transport
Streamable HTTP is what you use when the server is a standalone service that multiple clients reach over the network. It replaces the older HTTP+SSE transport with a single endpoint (conventionally /mcp) that accepts POST only (MCP spec, Streamable HTTP). That single endpoint is the reason for the redesign: the old transport split traffic across two endpoints, and collapsing them removed a category of routing problems.
The mechanics are more involved than stdio, but for choosing a transport you only need the shape of it: every client message is its own HTTP POST, and the server answers each one with either a single JSON object or an SSE stream scoped to that request. Long-lived server-to-client notifications, list changes and resource updates, arrive on the response stream of a subscriptions/listen request that the client opts into.
Before the 2026-07-28 revision, Streamable HTTP tied a client's requests together into a session with an Mcp-Session-Id header and let clients open a standalone GET stream for server-initiated messages. Both are gone: the protocol is now stateless, and cross-call state travels as explicit server-minted handles passed as ordinary tool arguments. A server that implements only this revision answers a GET or DELETE on the MCP endpoint with 405 Method Not Allowed.
None of that changes why you'd pick this transport. What lets Streamable HTTP do the things stdio can't, remote access, multiple clients, auth, and browser clients, was never the session layer; it's the HTTP transport itself, a URL any number of clients can reach and an Authorization header to hang OAuth 2.1 on. The full contract, the MCP-Protocol-Version, Mcp-Method, and Mcp-Name headers, Origin validation, and the per-request _meta fields that replaced the old handshake, is covered end to end in building a Streamable HTTP MCP server.
One deployment note: most shipping clients still speak pre-2026-07-28 revisions today, so a production server should serve both eras rather than the current one alone. The Tier-1 SDKs (TypeScript, Python, Go, and C#) do this for you, detecting which era a client is speaking and responding in kind.
Pick Streamable HTTP when the server is remote, shared across clients, deployed to the cloud, or reached from a browser. Concurrency, authentication, and horizontal scaling all live on this transport, because it's the only current transport with a network to hang them on. For serving many clients at once, see configuring MCP servers for multiple simultaneous connections.
SSE (HTTP+SSE): the deprecated transport
"SSE" is the original two-endpoint HTTP+SSE transport from the 2024-11-05 spec: one endpoint the client POSTed to, and a separate one holding an open SSE stream for responses. Streamable HTTP replaced it, and the 2026-07-28 revision formally classifies it as Deprecated under the spec's new feature-lifecycle policy, which makes it eligible for removal in a future revision (MCP spec, Streamable HTTP). The only reason to touch it today is interop with an old server or client you don't control and can't migrate. Migrating your own isn't a rewrite: the messages are the same JSON-RPC, and in the SDKs it's mostly a matter of swapping the legacy transport class (the TypeScript SDK's SSEServerTransport) for StreamableHTTPServerTransport, which building a Streamable HTTP MCP server walks through end to end.
How to choose
Reduce it to two questions and you rarely need a matrix:
- Does the client launch the server, or connect to it? If the client launches it as a local subprocess, that is stdio. If the client connects to a running service over a URL, that is Streamable HTTP. This single distinction decides the transport more often than any performance concern.
- Do you need remote access, multiple clients, auth, or a browser client? All four are Streamable HTTP territory. stdio has no network, so it doesn't do any of them. A production server needs at least one of these, which is the real reason production runs on Streamable HTTP and stdio stays for local development.
Performance is usually the first thing people reach for, and usually the wrong axis. stdio avoids the network stack entirely, so for a purely local integration it's the lighter path; Streamable HTTP adds HTTP parsing and network round-trips. But if your server needs to be remote, that comparison is moot, because stdio can't be remote at all. Choose on deployment shape first. Published, sourced throughput numbers for MCP transports are scarce, so treat any specific "operations per second" or "X times faster" claim you see without a cited benchmark as invented, and measure your own workload if the number matters to your decision.
Where to go from here
The transport is the smallest decision in building an MCP server, and this guide is meant to close it quickly: stdio for local, Streamable HTTP for remote, HTTP+SSE only as a back-compat bridge. From here, building a Streamable HTTP MCP server covers the remote path end to end, and building an stdio MCP server covers the local one.
Once a server is serving real clients over either transport, the operational questions take over: which tools clients actually call, what arguments they send, and where calls error or stall. That visibility is the gap AgentCat fills for production MCP servers.
Related Guides
Building a Streamable HTTP MCP server
Build a remote MCP server on the Streamable HTTP transport with the official TypeScript SDK, in the shape the 2026-07-28 spec revision requires: one POST-only endpoint, the headers that bite people, OAuth, and scaling out now that sessions are gone.
Building a stdio MCP server
Build MCP servers with stdio transport for local CLI tools and subprocess communication.
Configuring MCP servers for multiple simultaneous connections
How MCP servers actually serve concurrent clients under the 2026-07-28 spec revision: a stateless protocol with per-request metadata, no session IDs to track, and the dual-era support production servers still need for older clients.