← All guides

MCP Server Disconnects After console.log: Keep stdout Clean

Your MCP server starts, prints a friendly ready message, and disappears from the client. That ready message is the failure. A stdio server uses stdout for protocol messages, not logs.

Bottom line

  • A stdio MCP server reserves stdout for JSON-RPC.
  • One console.log() can corrupt the protocol stream.
  • Send diagnostics to stderr with console.error().
  • Test the server through an MCP client or the MCP Inspector.

The MCP transport specification says a stdio server reads messages from stdin and writes messages to stdout. It permits logging on stderr.

The official MCP TypeScript server guide makes the consequence explicit: one console.log corrupts the JSON-RPC stream. Both sources were checked on August 20, 2026.

The symptom

The server appears to start correctly:

Starting filesystem server...

Then the client reports a closed connection, invalid JSON, or a server that has no tools. Some clients hide the parser error and only show that the server disconnected.

The failure often appears after you add a startup banner:

console.log('MCP server ready');
await server.connect(transport);

The process did start. The client received the banner where it expected a JSON-RPC message.

The cause

Stdio MCP does not use stdout as a terminal display. It uses stdout as a wire.

Every line on that wire must be a valid protocol message. Your banner is plain text, so the client cannot decode it as JSON-RPC. The same failure occurs with debug objects, progress messages, and library logs.

This is why the server can work in a direct unit test and fail inside Codex, Claude Code, Cursor, or another MCP client. The unit test can call your functions without parsing the stdio stream.

The fix

Change all diagnostic output to stderr.

Node.js:

// Wrong: corrupts the MCP stdio stream.
console.log('MCP server ready');

// Correct: visible to the operator, separate from the protocol.
console.error('MCP server ready');

Python:

import sys

print("MCP server ready", file=sys.stderr)

Also check dependencies that log during import or startup. Your code can keep stdout clean while a framework banner still writes to it.

Search the server before retesting:

rg "console\.log|print\(" src

Review each match. Tool output returned through the MCP SDK is valid. A raw print to stdout is not.

Verify the repair

Run the server through the MCP Inspector:

npx @modelcontextprotocol/inspector node dist/index.js

Then verify three things:

  1. The client connects.
  2. The tool list loads.
  3. One tool call returns a valid result.

Keep the startup banner on stderr during this test. If the banner appears in the terminal and the client stays connected, the channels are separated correctly.

Do not hide the failure with retries

A restart loop does not repair a corrupted stream. It repeats the same invalid first line and creates a noisy connect-disconnect cycle.

Fix the channel first. Add restart policy only after one clean process can connect and serve a tool call.

Your agent can see the server but not its tools?

Book an agent rescue session. We trace the transport, configuration, and tool handshake live.

Need OpenClaw fixed live?

Remote rescue sessions for gateway, auth, tunnel, VPS, and model access problems.

See Rescue Session

Read next

Claude API Returns 400 on the Next Turn: Preserve Thinking Blocks
The first thinking request works, but the next returns 400. Pass thinking blocks back unchanged instead of trimming or rebuilding them.
MCP vs Skills for Agent Tools: Use This Rule
Use a skill for a repeatable workflow. Use MCP when the agent needs a new external capability. This guide shows where each boundary belongs.
OpenClaw Setup Errors and Fixes: The Index (July 2026)
The nine OpenClaw setup failures people actually hit — Ollama missing from the model picker, SearXNG silently failing, breaking after 10 prompts, endless thinking, install loops, updates wiping config. Symptom, cause, fix.
OpenClaw Docker Setup: Copy-Paste docker-compose.yml + Common Error Fixes (2026)
Copy this docker-compose.yml and run docker compose up -d. Production-ready OpenClaw container with health checks, non-root user, persistent volumes, and fixes for the 5 most common Docker errors (bind loop, permission denied, port 18789 in use).