MCP fundamentals
Transports: stdio vs SSE vs streamable HTTP
Choosing the right transport for your use case.
How the bytes get there
The previous lesson covered the host/client/server architecture as if the wire was magic. It isn't. MCP messages travel over one of three transports, each with different operational characteristics. Picking the right one matters because it shapes deployment, scaling, and security for every server you build or use.
The three transports:
- stdio. The server is a subprocess; the client communicates over stdin/stdout. Local only.
- SSE (Server-Sent Events). The server is an HTTP endpoint; the client streams responses over a long-lived HTTP connection. Remote-friendly, somewhat dated.
- Streamable HTTP. A newer HTTP-based transport that handles request/response and streaming on the same endpoint. The current recommended remote transport.
All three carry the same JSON-RPC-style messages. They only differ in the underlying I/O mechanism.
stdio
The server is a child process of the host. The host writes JSON-RPC requests to the server's stdin and reads responses from its stdout.
host process server process
| |
| spawn ─ writes to stdin ───> |
| <───── reads from stdout ─── |
| |Each line is a JSON message. Logging goes to stderr (so it doesn't pollute the protocol stream).
When stdio fits
- Local tools. A filesystem server that needs to read your local files makes sense as stdio. There's no remote endpoint.
- CLI integrations. Most Claude Code-style host integrations use stdio because the user's machine already has the tools installed.
- Development. Stdio servers are easy to write, easy to debug (just print to stderr), and easy to deploy (just a binary).
When stdio doesn't fit
- Remote tools. You can't stdio-connect to a server running on someone else's machine.
- Long-running shared state. Each host that connects spawns its own server process. They don't share state.
- Heavy initialization. If the server takes 30 seconds to load, every host pays that on every spawn.
For 90% of "tools my agent needs from my own machine," stdio is the right answer.
SSE (Server-Sent Events)
SSE is a long-standing HTTP feature: the client opens a GET request to an endpoint, the server keeps the connection open and streams events as they happen. Combined with a separate POST endpoint for client-to-server requests, you get full-duplex over HTTP.
client server (HTTPS endpoint)
| POST /mcp/messages ─────> |
| <-- 200 OK ─── |
| |
| GET /mcp/sse (long-lived)>|
| <── data: {...event...}─── |
| <── data: {...event...}─── |It works through proxies, firewalls, and CDNs because it's just HTTP. The downside is the asymmetry: client-to-server uses a different URL than server-to-client.
When SSE fits
- Remote MCP servers behind ordinary HTTP infrastructure.
- Vendor-hosted MCP where the vendor wants to deploy as a normal HTTPS service.
When SSE doesn't fit
- Bidirectional streaming. Each side has its own URL; you can't easily multiplex responses to multiple in-flight requests.
- New deployments. SSE has been around long enough that the MCP spec considers it semi-deprecated in favor of streamable HTTP for new servers.
If you're already using SSE, fine; if you're starting fresh on a remote server, prefer streamable HTTP.
Streamable HTTP
Streamable HTTP is the newer remote transport. The server exposes a single endpoint that accepts POSTs and can return either a regular JSON response (for unary calls) or a stream (for long-running calls or server-pushed notifications).
client server (HTTPS endpoint)
| POST /mcp ─────> |
| <── 200 OK + JSON ───── | (unary call)
| |
| POST /mcp ─────> |
| <── 200 OK +stream ───── | (streaming call)
| <── chunk 1 ───── |
| <── chunk 2 ───── |
| <── done ───── |One endpoint, two response shapes. The client looks at the response Content-Type to decide how to parse.
When streamable HTTP fits
- Production remote servers. This is the default recommendation for new MCP servers that aren't local.
- Mixed traffic patterns. Some calls are unary, some are streaming; one endpoint handles both.
- Modern HTTP infrastructure. It plays nicely with HTTP/2, gateways, load balancers.
For new remote servers, default to streamable HTTP.
Comparing the three
| Property | stdio | SSE | Streamable HTTP |
|---|---|---|---|
| Where servers live | Local subprocess | Remote HTTP endpoint | Remote HTTP endpoint |
| Setup complexity | Trivial | Moderate | Moderate |
| Multiple hosts can share a server | No | Yes | Yes |
| Auth | Process-level | HTTP-standard (token, OAuth) | HTTP-standard |
| Best for | Dev, local tools, CLI hosts | Older or vendor-hosted MCP | New remote servers |
| Recommended for greenfield | Local: yes | No | Remote: yes |
Picking a transport
A simple decision tree:
- Does the server need to access local resources only on the user's machine? stdio.
- Is the server hosted by a vendor / on a remote box? Use streamable HTTP for new builds.
- Inheriting an existing server? Stick with whatever it already uses.
For a single host that talks to many servers, mixing transports is normal. The filesystem server is stdio; the GitHub server is streamable HTTP; the database server is streamable HTTP. The host's client library handles each one.
Auth varies by transport
Authentication is mostly the transport's problem, not MCP's:
- stdio: the server runs as a child of the host. Auth tokens come in via env vars or process arguments. There's no "network auth"; the trust model is "the user spawned this process."
- HTTP-based: standard HTTP auth applies. Bearer tokens, OAuth, mTLS. The server validates incoming requests like any HTTPS service.
Module 4 lesson 1 of this track goes deeper on auth patterns. The thing to know now is that transport choice constrains your auth options: stdio servers can't easily verify "who's calling" because there's only one caller (the host that spawned them); HTTP servers can.
Operational differences
Beyond auth, the transports differ in operational shape:
Lifecycle
- stdio: server lives as long as the host. Crash = restart by spawning a new subprocess.
- HTTP-based: server is independent. It restarts on its own schedule. Hosts reconnect on disconnect.
Observability
- stdio: logs go to stderr, often piped into the host's log. Tracing is local.
- HTTP-based: standard HTTP observability applies (request logs, traces, metrics). Easier to monitor at scale.
Scaling
- stdio: one server process per host. To "scale" you spawn more host instances.
- HTTP-based: one server can serve many hosts. Standard horizontal scaling applies (load balance, shard, etc).
For a tool that gets used by thousands of agent instances per day, HTTP-based scales better. For a tool that one user runs locally, stdio is right.
Streamable HTTP supersedes SSE
The MCP specification has been moving toward streamable HTTP as the canonical remote transport. SSE-based servers still work and aren't going to break, but new server implementations should prefer streamable HTTP. If you're building a server today and want it to age well, that's the choice.
Key takeaway
MCP runs over three transports: stdio (local subprocess), SSE (older HTTP-based), and streamable HTTP (newer HTTP-based). Stdio fits local/CLI tools; streamable HTTP fits remote/vendor-hosted servers. SSE is supported but no longer the recommended choice for new builds. Picking the right transport shapes auth, observability, and scaling for every server you operate. The next module shifts from understanding to building: writing your first MCP server.
Done with this lesson?