
This page is part of our MCP specification reference, a plain-English guide to the Model Context Protocol spec (version 2026-07-28), within The MCP Guide. Building a server? See MCP development.
The MCP stdio transport is the way an AI app talks to an MCP server running on the same computer. The app launches the server as a child process, writes JSON-RPC messages to its standard input (stdin), and reads replies from its standard output (stdout). Each message is one line of JSON.
Status in the 2026-07-28 spec: core
stdio is the simplest MCP transport and the usual choice for local tools, such as a server that reads files in your project. This page follows the stdio binding in the 2026-07-28 specification, checked on 28 October 2026.
Key Takeaways
- With stdio, the MCP client launches the server as a subprocess and talks to it over stdin and stdout.
- Each message is a single JSON-RPC message on its own line, and messages must not contain embedded newlines.
- Only valid MCP messages may go to stdout; the server may write logs to stderr.
- To cancel a request, the client sends
notifications/cancelled; to shut down, it closes the server's stdin. - stdio servers should take credentials from the environment rather than use the OAuth flow in the authorization spec.
Why stdio exists
Local servers do not need a network. A desktop AI app or code editor can start a small program, pipe messages in and out, and stop it when done. There is no port to open, no URL to secure and no sign-in page.
That simplicity is also a security feature. The MCP security best practices tell local server authors to "use the stdio transport to limit access to just the MCP client." A server reachable only through a pipe cannot be reached by a random website.
How it works
The spec lists the core rules:
- "The server reads JSON-RPC messages from
stdinand writes JSON-RPC messages tostdout." - "Messages are delimited by newlines, and MUST NOT contain embedded newlines."
- "The server MAY write UTF-8 strings to
stderrfor any logging purposes." - "The server MUST NOT write anything to its
stdoutthat is not a valid MCP message." - "The client MUST NOT write anything to the server's
stdinthat is not a valid MCP message."
A request written to stdin is a single line. Here is a tool call, adapted from the examples in the spec and pretty-printed for reading. On the wire it would sit on one line:
{"jsonrpc": "2.0", "id": 7, "method": "tools/call",
"params": {"name": "read_file", "arguments": {"path": "README.md"},
"_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}}}}
What the server sends back
All server messages share stdout. "There are no per-request streams." The spec names three kinds:
- Responses to client requests, matched by JSON-RPC
id. - Notifications tied to an in-flight request, such as
notifications/progress. - Notifications for an active
subscriptions/listenrequest, which the client matches using theio.modelcontextprotocol/subscriptionIdfield.
The server "MUST NOT write JSON-RPC requests to stdout." When it needs something from the client, such as user input, it returns an InputRequiredResult under the multi-round-trip request pattern.
Metadata, cancellation and shutdown
stdio has "no header layer." The protocol version and client capabilities live in _meta inside each message.
To cancel, "the client MUST send a notifications/cancelled notification referencing the request's ID." Our page on cancelling MCP requests covers the full rules.
To shut down, the client "SHOULD" close the server's stdin, wait for it to exit, then force-terminate it if it does not. Servers "SHOULD exit promptly" when stdin closes. If the server crashes, the client "SHOULD restart it." Because the protocol is now stateless, in-flight requests are lost and can simply be retried.
Beyond pipes
The wire format is "one newline-delimited JSON-RPC message per line over a reliable bidirectional byte stream." The spec says it "works unchanged over Unix domain sockets, TCP connections, or any similar channel." Custom transports on such streams "SHOULD reuse this framing."
What changed across spec versions
The core stdio rules have barely changed since the 2024-11-05 transports page. That page already required newline-delimited messages and a clean stdout. The changes since then:
| Version | Change |
|---|---|
| 2025-11-25 | Clarified that stderr is for all types of logging, not just errors, per the 2025-11-25 changelog |
| 2026-07-28 | No initialize handshake; metadata in every message's _meta |
| 2026-07-28 | Server may not send JSON-RPC requests; uses InputRequiredResult instead |
| 2026-07-28 | Client "SHOULD NOT assume stderr output indicates error conditions" |
| 2026-07-28 | Logging feature deprecated; the changelog suggests logging to stderr instead |
Talking to older stdio servers
A client that supports both modern and legacy versions "SHOULD probe with server/discover" before anything else. If the server answers with a DiscoverResult or a recognized modern error, it is modern. If it returns any other error or no reply, it is legacy, and the client falls back to initialize. The fallback "MUST NOT be keyed to one specific error code." See MCP versioning and deprecation for the version model.
Security notes
A stdio server is a program running with your user account's privileges. The best practices page warns that local servers "may have direct access to the user's system." It says clients offering one-click local setup "MUST" show the exact command, warn that it runs code, and require explicit approval.
The same page says stdio "is not inherently vulnerable," but proxy services that spawn stdio servers can turn a web bug into code execution. Our pages on command injection in local MCP servers and sandboxing local MCP servers cover the defenses.
Common mistakes
- Printing debug output to stdout. A stray
print()corrupts the message stream. Send logs to stderr. - Pretty-printing JSON on the wire. Embedded newlines break framing. Serialize each message on one line.
- Treating stderr as an error signal. The 2026-07-28 spec says clients should not assume that.
- Ignoring end-of-file on stdin. Servers that keep running after stdin closes force the client to kill them.
- Using OAuth for a stdio server. The authorization spec says stdio implementations should get credentials from the environment.
Next step
When your server needs to serve many users over the web, it moves to the other standard transport. Read MCP Streamable HTTP transport next, or return to the MCP specification reference.
Frequently asked questions
Is stdio only for local servers?
Yes, in its standard form. The client launches the server as a subprocess on the same machine. The same line-based framing can be reused over sockets as a custom transport, but that is no longer the stdio binding itself.
How do stdio MCP servers get API keys?
The authorization spec says stdio implementations "SHOULD NOT" follow the OAuth flow and should "retrieve credentials from the environment." In practice that usually means environment variables set in the client's server configuration.
Can a stdio server send requests to the client?
Not in the 2026-07-28 spec. The server "MUST NOT" write JSON-RPC requests to stdout. It returns an InputRequiredResult and the client retries the original request with the answers.
Why does my stdio server hang or show parse errors?
A common cause is non-MCP output on stdout, such as log lines or banners. Another is JSON that spans several lines. Both break the newline framing the spec requires.
Do I need to restart a stdio server after a crash?
The spec says the client "SHOULD" restart it. Requests that were in flight are lost and can be retried, and any subscriptions/listen streams must be opened again.











