Two ways to use it¶
mcp-stdio is one command with two distinct roles. Everything else in these docs hangs off this choice, so take a moment here.
| As a client-side gateway (default) | As a server gateway (serve) |
|
|---|---|---|
| You are… | using someone's remote MCP server | publishing your own MCP server |
| Your MCP server runs… | somewhere else, behind HTTPS | on your machine, speaking stdio |
| mcp-stdio runs… | next to your MCP client (laptop) | next to your MCP server (host) |
| It translates… | stdio → Streamable HTTP / SSE | Streamable HTTP → stdio |
| OAuth role | client: logs in, stores and refreshes your tokens | authorization server: registers clients, issues and validates tokens |
| Typical user | anyone using Claude Desktop / Claude Code with a remote server | the operator of a stdio MCP server that remote users should reach |
| Start here | Connect to a remote MCP server | Publish your stdio server |
As a client-side gateway (default mode)¶
Your MCP client (Claude Desktop, Claude Code, …) only launches local stdio processes, but the server you want lives on the network. mcp-stdio is that local process: your client talks stdio to it, and it relays every message to the remote server over HTTPS — handling the OAuth login, token cache, and refresh so the connection survives longer than an access token does.
graph TD
A["MCP client<br/>(Claude Desktop / Claude Code)"]
B["mcp-stdio<br/>OAuth client · token cache · transport translation"]
C["Remote MCP server<br/>(Streamable HTTP or legacy SSE)"]
A -- "stdio (JSON-RPC lines)" --> B
B -- "HTTPS (+ Bearer token)" --> C
C --> B
B --> A
mcp-stdio --oauth https://mcp.example.com/mcp
You want this mode when:
- a vendor / your team hosts an MCP server and you want it in Claude Desktop or Claude Code;
- the server needs an OAuth login your client cannot complete on its own;
- the server still speaks the legacy SSE transport your client dropped.
→ Continue with Connect to a remote MCP server.
As a server gateway (serve mode)¶
You wrote (or run) an MCP server that speaks stdio on your machine, and
remote users should reach it from their MCP clients. mcp-stdio serve
turns it into a proper Streamable HTTP endpoint: it accepts HTTPS on one
side, spawns an isolated stdio child process per user session on the
other, and — with --enable-oauth — acts as the OAuth 2.1 authorization
server that registers clients and issues the tokens guarding it all.
graph TD
A1["Remote user A<br/>(Claude Desktop)"]
A2["Remote user B<br/>(Claude.ai connector)"]
B["mcp-stdio serve<br/>OAuth authorization server · sessions"]
C1["stdio child for A<br/>(your MCP server)"]
C2["stdio child for B<br/>(your MCP server)"]
A1 -- "HTTPS + OAuth" --> B
A2 -- "HTTPS + OAuth" --> B
B -- "stdio" --> C1
B -- "stdio" --> C2
mcp-stdio serve --enable-oauth \
--public-url https://mcp.example.com \
--token-store /var/lib/mcp-stdio/state.json \
-- python -m my_mcp_server
You want this mode when:
- your MCP server is stdio-only and remote clients need to reach it;
- several users must share one deployment without sharing a process — each session gets its own child, bound to its authenticated user;
- you need real OAuth in front of it but do not want to run Keycloak for a single endpoint.
→ Continue with Publish your stdio server.
Both at once¶
The two roles compose. A common pattern: an operator publishes an internal
server with serve on one host, and every team member connects to it with
plain client-mode mcp-stdio from their laptop — the same package on both
ends, each side doing its half of the OAuth dance.
graph TD
A["Claude Desktop"] -- "stdio" --> B["mcp-stdio<br/>(client mode)"]
B -- "HTTPS + OAuth" --> C["mcp-stdio serve<br/>(server mode)"]
C -- "stdio" --> D["your MCP server"]