Lesson 7 - Step 1 of 5

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.

Why STDIO?

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:

diagram
┌─────────────────────────────────────────────────────────────┐
│                    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:

text
{"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
Critical: One Message Per Line

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:

python
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")
Golden Rule

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.