Transport Types: stdio and Streamable HTTP
- Describe how stdio transport works: the server runs as a subprocess communicating through stdin and stdout
- Explain the critical stdio rule: stdout is reserved for JSON-RPC protocol messages only — use console.error() in TypeScript and stderr in Python for all logging
- Identify when to use stdio versus Streamable HTTP and understand that the standalone SSE transport was deprecated in March 2025
What a Transport Does
The MCP protocol defines what messages look like (JSON-RPC 2.0) but not how they travel between client and server. The transport layer handles that. It is the pipe through which JSON-RPC requests and responses flow.
MCP currently supports two production transports. Understanding when to use each one — and the constraints each imposes — shapes how you build and deploy your server.
stdio: Local Process Communication
When a client uses stdio transport, it launches your server as a subprocess on the same machine. Communication happens through the process's standard streams:
- The client writes JSON-RPC requests to your server's stdin
- Your server writes JSON-RPC responses to its stdout
There is no network involved. No ports, no HTTP, no sockets. The server lives and dies with the client session — when the user closes Claude Desktop, the server process is terminated.
Claude Desktop and Claude Code both use stdio transport for local MCP servers. When you add an MCP server to Claude Desktop's config, you are telling it: "launch this command, and use its stdin/stdout to communicate."
The Critical Rule: Never Write to stdout
This is the single most important constraint for stdio servers, and violating it is the most common cause of broken stdio connections:
Your server must never write anything to stdout except valid JSON-RPC messages.
Because the client reads your server's stdout as a JSON-RPC stream, any non-JSON output — a debug log, a print statement, a startup message — corrupts the stream and breaks the connection. The client cannot distinguish your log output from a protocol message.
The rule in each language:
console.error()console.log()print(..., file=sys.stderr) or logging moduleprint() without file=sys.stderrconsole.error() writes to stderr, which is a separate stream. The client does not read stderr — it is safe for diagnostic output. Only stdout is reserved for the JSON-RPC protocol.
This rule applies throughout the entire execution of your server — not just at startup. Any third-party library that writes to stdout will also break your server. Check your dependencies.
When to Use stdio
- Local development and personal tools
- Servers that access local files, local databases, or local processes
- Servers distributed as CLI tools (npm packages, Python packages with entry points)
- Any server where a single user on a single machine is the intended audience
Streamable HTTP: Remote Server Communication
Streamable HTTP transport allows your server to run anywhere with a network address — a cloud VM, a container, a serverless function. Clients connect via HTTP POST to a single endpoint.
What makes it "streamable" is optional support for Server-Sent Events (SSE): the server can open a long-lived streaming response to push multiple messages back to the client without waiting for additional requests. This is useful for long-running operations where you want to send progress updates.
Unlike stdio, an HTTP server is not tied to a single client session. Multiple users can connect to the same server simultaneously. The server process runs independently of any client connection.
When to Use Streamable HTTP
- Shared team servers — one deployment, many developers
- SaaS integrations — your server wraps a hosted API and you want users to connect without installing anything locally
- Servers that require authentication from an external identity provider
- Any server that needs to scale beyond a single user's machine
What About SSE Transport?
Earlier versions of the MCP specification included a standalone SSE transport — a separate transport type that used Server-Sent Events as its primary communication channel. This transport was deprecated in the March 2025 spec update in favour of Streamable HTTP, which handles both standard request-response and streaming in a unified design.
If you encounter documentation or tutorials mentioning SSE transport as a standalone type, they are out of date. Use Streamable HTTP for all new remote server development.
Choosing Between the Two
For the first server you build in this track, you will use stdio. It is simpler to set up, requires no infrastructure, and works directly with Claude Desktop and Claude Code out of the box. HTTP transport is covered in a dedicated lesson once you have the fundamentals down.
The JSON-RPC Layer Is the Same
One important point: the protocol does not change between transports. The same tools/call request, the same response format, the same initialization handshake — all identical whether the transport is stdio or HTTP. The SDKs expose the same tool registration API regardless of which transport you use. Switching transports later is a configuration change, not a rewrite.
- stdio transport launches your server as a local subprocess — requests come in via stdin, responses go out via stdout, no network involved
- The critical stdio rule: never write to stdout except valid JSON-RPC messages — use console.error() in TypeScript and print(..., file=sys.stderr) in Python for all logging
- Streamable HTTP transport runs your server at a network address, supports multiple simultaneous users, and uses HTTP POST with optional SSE streaming
- The standalone SSE transport is deprecated as of March 2025 — use Streamable HTTP for all new remote server development
- The MCP protocol (JSON-RPC 2.0 messages) is identical across both transports — switching transport later is a configuration change, not a code rewrite