STDIO Transport Overview
Understand how MCP communicates through standard input and output streams.
What You'll Build
In this lesson, you'll build stdio_server.py — an MCP server
that communicates through standard input/output streams. By the end, your server will:
- Read JSON-RPC messages from stdin (one per line)
- Write JSON-RPC responses to stdout
- Log debug information to stderr
- Handle the MCP initialization handshake
- Expose tools that can be called by MCP clients
Each step builds on the previous one, and you'll test your server with echo commands.
What is STDIO Transport?
STDIO (Standard Input/Output) Transport is one of the two primary
transport mechanisms in MCP. It uses the process's standard input (stdin)
and standard output (stdout) streams for communication.
STDIO transport is simple, secure, and universally available. Every process has stdin/stdout, making it perfect for local MCP servers that run as child processes of the client application.
How It Works
In STDIO transport, the MCP client spawns your server as a subprocess and communicates by writing to your stdin and reading from your stdout:
┌─────────────────────────────────────────────────────────────┐
│ MCP Client (Parent Process) │
│ │
│ Writes JSON-RPC ──────────► Child's stdin │
│ Reads JSON-RPC ◄────────── Child's stdout │
│ │
└─────────────────────────────────────────────────────────────┘
│
(subprocess pipe)
│
┌─────────────────────────────────────────────────────────────┐
│ MCP Server (Child Process) │
│ │
│ sys.stdin ◄──── Receives JSON-RPC requests │
│ sys.stdout ────► Sends JSON-RPC responses │
│ sys.stderr ────► Debug logging (not parsed by client) │
│ │
└─────────────────────────────────────────────────────────────┘
Message Format
Each JSON-RPC message is sent as a single line terminated by a newline
character (\n). This is critical for proper message parsing:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}\n
{"jsonrpc":"2.0","id":1,"result":{...}}\n
{"jsonrpc":"2.0","id":2,"method":"tools/list"}\n
{"jsonrpc":"2.0","id":2,"result":{"tools":[...]}}\n
Each JSON message must be on a single line with no embedded newlines. The newline character marks the end of the message. Pretty-printing JSON will break the protocol!
STDIO vs HTTP Transport
Understanding when to use each transport:
| Feature | STDIO | HTTP/SSE |
|---|---|---|
| Deployment | Local subprocess | Local or remote server |
| Setup | Simpler | More complex |
| Security | Process isolation | Requires auth/TLS |
| Multiple Clients | No (1:1) | Yes (1:many) |
| Use Case | Desktop apps, CLI tools | Web services, shared servers |
The Three Streams
Every process has three standard streams. In MCP STDIO transport, each has a specific purpose:
import sys
# stdin - READ incoming JSON-RPC messages from client
request = sys.stdin.readline()
# stdout - WRITE JSON-RPC responses to client
sys.stdout.write(response + "\n")
sys.stdout.flush()
# stderr - Debug logging ONLY (client ignores this)
sys.stderr.write("Debug: Processing request...\n")
stdout is sacred! Only write valid JSON-RPC messages to stdout. Any debug output, print statements, or logging must go to stderr. Polluting stdout will break the protocol.
What You'll Learn
In this lesson, you'll master STDIO transport:
- Step 2: Reading JSON-RPC messages from stdin
- Step 3: Writing responses to stdout correctly
- Step 4: Using stderr for debug logging
- Step 5: Building a complete STDIO MCP server
By the end of this lesson, you'll have a fully functional STDIO-based MCP server that can be used with Claude Desktop or any MCP client.