Guide · Tools & MCP
stdio, SSE or Streamable HTTP — which MCP transport?
Published 1 August 2026
An MCP server's transport is how the client and server exchange messages. The protocol is the same either way; the transport decides where your server can run, who can reach it, how authentication works, and what breaks in production. Picking it late is expensive, because it is the one MCP decision that constrains your deployment.
The options
- stdio. The client launches your server as a child process and talks over standard input and output. One process per client, local, no network.
- Streamable HTTP. Your server is an HTTP endpoint. The client POSTs requests and the server can stream responses back over the same connection. Remote, multi-client, the current recommendation for anything network-facing.
- HTTP + SSE. The older remote pattern: a POST channel for requests and a separate Server-Sent Events stream for responses. Superseded by Streamable HTTP, but still what many deployed servers and clients speak.
Choose by where the work happens
One question decides most cases: does the server need the user's machine?
- Yes → stdio. Reading local files, running local commands, touching local git, using credentials already on that machine. A remote server cannot do these things without you building a tunnel you do not want to own.
- No → Streamable HTTP. Wrapping a SaaS API, querying a shared database, anything you want to update centrally without asking every user to upgrade.
Second question, when both are viable: who has to install it? stdio means every user installs a runtime and your package, and their version is whatever they last pulled. HTTP means you deploy once and everyone is instantly current. For a team tool that difference dominates.
What each one costs
stdio
- No auth layer to build — the process runs as the user, with the user's rights. That is also the risk: no isolation, and a prompt-injected tool call runs with full local privileges.
- Anything your server writes to stdout that is not a protocol message corrupts the stream. Log to stderr. This single mistake accounts for a large share of "my server won't connect".
- Cold start per session, and no shared state between clients.
- Debugging is awkward: no request log, no URL to curl, failures surface as a client that silently shows no tools.
Streamable HTTP
- You own auth, and you must own it properly — token validation per request, per-user scoping, and no trusting a client-supplied user id.
- You own session identity across requests, plus the usual network concerns: timeouts, retries, proxies that buffer streamed responses.
- You get real operational tooling: logs, metrics, staged rollout, one place to fix a bug.
- Origin checks matter. A server bound to localhost over HTTP without origin validation can be reached by a web page in the user's browser.
SSE
- Two channels to keep alive, and the reconnect semantics are the part everyone gets wrong.
- Choose it only for compatibility with a client you do not control. For new work, Streamable HTTP.
Failure modes worth recognising
- Server connects, no tools listed. Almost always the handshake: a capability the client expected was not advertised, or stdout was polluted before initialization completed.
- Works locally, hangs when deployed. A proxy is buffering the stream. Check that the response is not being collected before it is forwarded.
- Intermittent disconnects on SSE. An idle-timeout on an intermediary. Keep-alives or move to Streamable HTTP.
- Works for you, fails for a colleague. stdio version skew. Pin the version in the client configuration.
A concrete pair of decisions
A server that formats and stages a git commit: stdio. It needs the working tree, the local git config and the user's signing key. Remote is not a trade-off; it is not possible without moving the repository.
A server that opens tickets in your issue tracker for the whole team: Streamable HTTP, deployed once, holding the tracker's API token server-side and mapping the authenticated user to a tracker identity. Under stdio you would be distributing that token to every laptop — a credential-management problem you can simply not create.
Can you support both?
Yes, and it is cheap if you plan for it: keep your tool handlers transport-agnostic and put the transport at the edge as a thin adapter. The trap is letting local assumptions leak into the handlers — reading a file path from the environment, assuming a single user, caching in a module-level variable. Under HTTP those become cross-user bugs. If you intend to offer both, write the handlers as if they are remote and multi-tenant from day one.
Review checklist
- Does the server need the user's machine? If not, why is it stdio?
- Under stdio: is all logging on stderr, and is the version pinned?
- Under HTTP: is every request authenticated and scoped server-side, and is the origin checked?
- Is any handler assuming a single user or a local path?
- Have you tested through the proxy you will actually deploy behind?
Where this fits
Transport choice sits in the Tools & MCP part of the track, upstream of tool schema design and versioning. The judgement being tested is deployment-shaped: who runs the process, who holds the credentials, and who has to upgrade when you fix a bug.
Related in this track
Keep going
- Tools & MCP track — the full module list this guide belongs to.
- Practise this with the coach — a Socratic session with Tools & MCP preselected.