Checklist for a stdio server that will not start
- Spec
2026-07-28- Evidence
- E2Reproducedarm64
- TypeScript SDK (server)
@modelcontextprotocol/server@2.4.0- TypeScript SDK (client)
@modelcontextprotocol/client@2.4.0- TypeScript SDK v1
@modelcontextprotocol/sdk@1.32.2- Python SDK
mcp@2.3.0- MCP Inspector
@modelcontextprotocol/inspector@2.12.0- Verified
A stdio server is a child process. The client starts it with a command, writes JSON-RPC messages to its standard input and reads replies from its standard output. Almost every failure comes from one of four things: the process exits, its stdout carries something besides protocol messages, it gets a different environment than your terminal, or a relative path points somewhere else.
Work through the checks in order. Each one rules out a cause before the next.
1. Run the exact command
Section titled “1. Run the exact command”node /absolute/path/to/server.jsExpectedThe process starts and waits silently. Output on stderr is fine.
If it exits, the message it prints is the cause. Fix that first. Typical messages are a missing module, a missing environment variable, or a syntax error from a runtime that is too old.
If it keeps running, the server itself can start. Press Ctrl+C and continue.
2. Keep stdout for protocol messages
Section titled “2. Keep stdout for protocol messages”The specification says a stdio server must not write anything to stdout that is not a valid MCP message. To see what happens when one does, we wrote servers that break this rule in different ways and connected them with four clients:
| What the server writes to stdout | TS client v2 | TS client v1 | Python client | Claude Code |
|---|---|---|---|---|
A full text line at startup (console.log('starting…')) |
Works | Works, logs Unexpected token 'h', "hello-bann"... is not valid JSON |
Works, logs a ValidationError |
✔ Connected |
| A full text line during a tool call | Works | Works, logs … is not valid JSON |
— | — |
Text without a newline during a tool call (process.stdout.write('working... ')) |
Request timed out |
— | TimeoutError |
— |
Whole lines that are not JSON are usually skipped, but they leave error lines in the client’s log that look alarming and hide the real problem. Text without a newline is worse: it sticks to the front of the next JSON message, the client cannot parse that message, and the request waits until it times out.
So the rule stands: write nothing to stdout yourself. Use stderr for logs:
| Language | Safe | Writes to stdout, avoid |
|---|---|---|
| TypeScript / JavaScript | console.error(…) |
console.log, console.info, process.stdout.write |
| Python | print(…, file=sys.stderr), the logging module with its default handler |
print(…) |
| Java | System.err.println(…), a logging framework whose console appender targets System.err |
System.out.println |
| Go | the log package (writes to stderr by default) |
fmt.Println, fmt.Printf |
Check your dependencies too. A library that prints a banner or a progress bar to stdout breaks the protocol just as well.
3. Read the server’s stderr through the client
Section titled “3. Read the server’s stderr through the client”Clients capture what the server writes to stderr. When a server exits during startup, its last stderr lines usually name the cause. Where each client puts it is listed in where clients write MCP logs.
The Inspector prints it straight to your terminal. For a server that exits when a variable is missing:
needs-env: DEMO_API_KEY is not set{"error":{"code":"error","message":"Version negotiation probe failed: Connection closed during the version negotiation probe", …}}The first line is the server’s own message. The second is how the Inspector reports the closed connection.
4. Compare the environment
Section titled “4. Compare the environment”A client does not start the server from your shell, so it may not see the variables and the PATH your terminal has. You can see the effect by clearing the environment:
which nodeenv -i /bin/sh -c 'echo $PATH; command -v node'# PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin# /usr/bin/nodeOn our machine, the same node command resolved to Node 24 in the shell and to the system’s Node 20 without the shell’s setup. A server that needs Node 22 would work in the terminal and fail in a client that starts with a minimal environment.
What to do:
- Write runtimes as absolute paths in the configuration: the full path to
node,python,uvornpx. Get it withwhich node(orwhere nodeon Windows). - Pass variables explicitly. Put them in the client’s
envsetting, or use the client’s way of forwarding them. Codex, for example, does not forward your shell variables to stdio servers unless you list them inenv_vars; see Codex. - Test with a clean environment before blaming the client:
env -i /absolute/path/to/node /absolute/path/to/server.js. If it fails like this, it will fail in some client.
5. Use absolute paths everywhere
Section titled “5. Use absolute paths everywhere”Clients start servers in a working directory of their choosing, not your project. A relative path such as ./build/index.js or ../data then points somewhere else. Use absolute paths in args, and in any files your server opens. If the server reads a .env file, resolve it relative to the script, not to the current directory.
6. Test without the client
Section titled “6. Test without the client”If the command runs in a clean environment and stdout is clean, connect with the Inspector:
npx -y @modelcontextprotocol/inspector@2.12.0 --cli \ /absolute/path/to/node /absolute/path/to/server.js --protocol-era auto --method tools/listIf the Inspector lists the tools, the server is fine and the problem is in the client’s configuration: the file location, the JSON or TOML syntax, the server name, or an approval step. Check the page for your client.
Windows
Section titled “Windows”These points come from public reports; we have not tested them on Windows yet.
npxis a batch file (npx.cmd). Clients that cannot start batch files directly need"command": "cmd"with"args": ["/c", "npx", …].- In JSON, every backslash in a path is doubled:
"C:\\Users\\me\\server.js". In TOML, use single quotes:'C:\Users\me\server.js'. - Apps started from the Start menu may not see variables you set in a terminal session. Set them in the client’s configuration.
Related
Section titled “Related”Sources
MCPDev is community documentation. It is not affiliated with the Model Context Protocol project or with the vendors of the clients it covers.