MCP Transports: stdio vs SSE vs Streamable HTTP
Three transports, one right answer for each situation — and one you should stop using.

MCP has three transports, and picking between MCP SSE vs Streamable HTTP vs stdio is simpler than the spec makes it look: use stdio when the server runs on your machine, use Streamable HTTP when it runs somewhere else, and treat SSE as legacy — it was deprecated in the March 2025 spec revision. That's the whole decision. The rest of this piece explains why, and where the sharp edges are.
Transport is just the pipe that carries JSON-RPC messages between your client (Cursor, Claude, VS Code) and the server. It doesn't change what a server does — a filesystem server exposes the same tools over stdio or HTTP. It changes where the server lives, how you authenticate, and who can reach it. Get it wrong and you'll waste an afternoon debugging a config that was never going to connect.
The short answer: match the transport to where the server runs
Pick by location, not preference. Roughly 90% of MCP servers run locally over stdio, because most of what people connect to — files, git, a local database, a browser — is on the same machine as the client. If the server is a process on your laptop, stdio is correct and anything else is overhead.
Use Streamable HTTP only when the server is genuinely remote: a hosted endpoint, a shared team service, or a vendor's managed API. Use SSE for exactly one reason — an old server that hasn't migrated yet and gives you no other option.
| Transport | Where the server runs | Auth | Status |
|---|---|---|---|
| stdio | Local process (same machine) | Env vars / OS | Current, default |
| Streamable HTTP | Remote / hosted endpoint | OAuth, bearer token | Current, preferred for remote |
| SSE | Remote (legacy) | Token in header | Deprecated (2025-03) |
stdio: the default, and why ~90% of servers use it
stdio is the right choice for anything local, and it's the default for a reason: the client launches the server as a child process and talks to it over stdin/stdout. No ports, no TLS, no network. The server inherits the client's user and filesystem access, which is exactly what you want for a tool reading your repo.
A stdio entry is just a command the client runs. This is what the vast majority of your config looks like:
{
"mcpServers": {
"github": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "<your-github-pat>" }
}
}
}
The GitHub MCP Server ships this way — a local Docker container over stdio — and also offers a hosted remote endpoint. That dual mode is common now: run it locally when you want your own token and zero network hops, or point at the remote when you'd rather not manage the container. Exa Search is the same story: npx for a local stdio process, or a hosted remote if you don't want a Node process running.
The one gotcha with stdio: the server writes protocol messages to stdout, so anything your server prints to stdout that isn't JSON-RPC will corrupt the stream. Log to stderr. Most "server won't start" reports on stdio are a stray print() or a banner line. If you're stuck there, the troubleshooting checklist starts with exactly this.
Streamable HTTP: the modern remote transport
Streamable HTTP is what you use for any server not on your machine, and it's the successor SSE was replaced by. It runs the whole session over a single HTTP endpoint: the client POSTs JSON-RPC requests, and the server replies with either a plain JSON response or an SSE-formatted stream when it needs to push multiple messages. One URL, one connection model, real auth.
That last part matters. Remote transports need security you don't think about locally — OAuth or bearer tokens, TLS, and a server you actually trust with your requests. When the server needs no key, a remote config is just a URL:
{
"mcpServers": {
"aws-knowledge": {
"url": "https://knowledge-mcp.global.api.aws"
}
}
}
AWS Knowledge MCP is a clean example: a fully managed remote server for AWS docs and Well-Architected guidance that needs no key at all — you just add the URL. Most remote servers do need auth, though. Jina AI Reader & Search reads web pages as Markdown and runs grounded search over HTTP, but you pass a Jina API key as a bearer token in the header rather than in the URL. Either way, someone else operates the server, patches it, and scales it. For the full local-vs-remote trade-off, see local vs remote MCP.
SSE: deprecated — don't build on it
Skip SSE for anything new. The old HTTP+SSE transport used two endpoints — one for the SSE stream, one for POSTing messages — and that split caused resumability and proxy headaches. The March 2025 spec folded both into Streamable HTTP and marked the old transport deprecated.
You'll still meet SSE in the wild: URLs ending in /sse, and older servers that only expose it. Connecting to one is fine — most clients still speak it for backward compatibility. Just don't choose it. If you're writing a server, ship Streamable HTTP. If a server you depend on is SSE-only, that's a mild signal it's not being maintained.
Transport doesn't fix your real bottleneck: the tool budget
Whichever transport you pick, it has no effect on the constraint that actually degrades your setup: the tool count. Clients feed every tool from every connected server into the model's context, and the practical ceiling is around 40 tools before selection accuracy drops and prompts get bloated. stdio vs HTTP changes none of that.
So choose transport by location, then be ruthless about which servers you connect. A remote server with 30 tools costs the same context as a local one with 30 tools. The Cursor tool limit math breaks down the budget, and browsing by capability helps you add the two tools you need instead of a server's whole surface. If you want the shortlist of servers worth the slots, start with the best MCP servers.