
How MCP Transports Work — stdio, HTTP, and Message Framing
The MCP data layer defines WHAT messages look like (JSON-RPC requests, responses, notifications). The transport layer defines HOW those messages move between client and server.
MCP supports two transports. The same JSON-RPC messages work over both — the transport is pluggable.
stdio — Local Servers
The simplest transport. The client launches the server as a child process and communicates through stdin/stdout.
How it works:
- Client spawns the server as a subprocess (like running a command)
- Client writes JSON-RPC messages to the server's stdin
- Server writes JSON-RPC messages to its stdout
- Each message is one line of JSON, delimited by
\n - Server can write logs to stderr (client may capture or ignore them)
- Client closes stdin to terminate the connection
Rules:
- Messages MUST NOT contain embedded newlines (one JSON object per line)
- Server MUST NOT write non-JSON-RPC content to stdout
- Client MUST NOT write non-JSON-RPC content to the server's stdin
- The server's stderr is for logging only — not protocol messages
When to use: local MCP servers that run on the same machine. Filesystem access, local databases, git operations, code analysis, local build tools.
Advantages: zero network overhead, simple to implement, natural process lifecycle (client kills the subprocess on shutdown).
Limitations: single client only (one stdin), local only (no network access), subprocess management adds complexity.
Streamable HTTP — Remote Servers
For servers that need to run remotely, serve multiple clients, or persist across sessions. Uses standard HTTP with optional Server-Sent Events (SSE) for streaming.
How it works:
The server exposes a single HTTP endpoint (e.g., https://example.com/mcp). This endpoint handles both POST and GET.
Sending messages (client → server): Every JSON-RPC message from the client is an HTTP POST to the MCP endpoint. One message per request.
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
The server responds either with:
Content-Type: application/json— a single JSON responseContent-Type: text/event-stream— an SSE stream that may contain multiple messages (the response plus any server-initiated requests or notifications)
Receiving messages (server → client): The client can open an SSE stream via HTTP GET to receive server-initiated messages (notifications, requests) without first sending a POST.
GET /mcp HTTP/1.1
Accept: text/event-stream
When to use: remote MCP servers, SaaS integrations (GitHub, Sentry, Slack), shared team tools, any server that needs to serve multiple clients.
Session Management
Streamable HTTP supports stateful sessions:
- During initialization, the server returns an
Mcp-Session-Idheader - The client includes this header on all subsequent requests
- The server uses it to maintain state across requests
- Either side can terminate the session (client sends HTTP DELETE, or server returns 404)
Session IDs should be cryptographically secure (UUIDs or JWTs). If a client receives 404 for its session ID, the session expired and it must re-initialize.
Resumability
Network connections break. SSE streams disconnect. The Streamable HTTP transport supports resuming:
- The server attaches an
idfield to each SSE event - If the client reconnects, it sends
Last-Event-IDheader with the last event ID received - The server replays missed events from that point
This prevents message loss during temporary network interruptions — critical for long-running tool executions.
Authentication
The stdio transport doesn't need authentication — if you can launch a subprocess, you have access.
The Streamable HTTP transport uses standard HTTP authentication:
- Bearer tokens
- API keys
- OAuth 2.0 (recommended by the spec)
- Custom headers
MCP recommends OAuth for production deployments. The server validates credentials on every request.
Security for HTTP Transport
Remote servers face additional threats:
DNS rebinding — a malicious website tricks the browser into connecting to a local MCP server. Defense: servers MUST validate the Origin header on all requests.
Localhost binding — local HTTP servers should bind to 127.0.0.1, not 0.0.0.0. Binding to all interfaces exposes the server to the network.
TLS — remote servers should use HTTPS. Without it, tool calls and responses (potentially containing secrets) travel in plaintext.
Choosing a Transport
| stdio | Streamable HTTP | |
|---|---|---|
| Network | Local only | Local or remote |
| Clients | Single | Multiple |
| Latency | Microseconds | Milliseconds |
| Authentication | Process-level | HTTP-based |
| Resumability | N/A | Supported |
| Sessions | Implicit (process lifetime) | Explicit (session ID) |
| Best for | Local tools | Remote services, SaaS |
Most MCP servers today use stdio because most use cases are local (Claude Code running a filesystem server, Cursor running a database server). As the ecosystem matures, Streamable HTTP will grow for shared, remote, and team-level tools.
Next Steps
- How AI Agents Work — the bigger picture: AI that uses MCP tools to accomplish multi-step tasks.
- How HTTP Works — the protocol underneath Streamable HTTP transport.
- How Processes Work — the OS primitive underneath stdio transport.