What is the recommended way to log/debug an MCP server without corrupting the stdio transport?

5 minintermediatemcpdebuggingloggingstdio

Quick Answer

Never use print() or anything else that writes to stdout for debugging in a stdio-transport server, since that's the exact channel carrying JSON-RPC messages. Instead, write diagnostic output to stderr (most hosts capture and surface it, or leave it for you to redirect to a file), use a proper logging library configured to a non-stdout handler, or use MCP's own logging capability (notifications/message), which lets a server emit structured log messages through the protocol itself at a given severity level, visible to any client that supports displaying them. For deeper interactive debugging during development, use the MCP Inspector, which lets you call tools/resources/prompts directly and see exactly what's sent and received, without needing a full host in the loop at all.

Detailed Answer

Debugging an MCP server has one hard constraint that doesn't exist in most other server-side debugging: the most obvious tool (print()) is actively dangerous over stdio.

Wrong way

@mcp.tool()
def fetch_price(symbol: str) -> str:
    print(f"DEBUG: fetching {symbol}")   # corrupts the stdio transport
    ...

Right way #1: log to stderr

import logging, sys
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logger = logging.getLogger("price-server")

@mcp.tool()
def fetch_price(symbol: str) -> str:
    logger.debug(f"fetching {symbol}")
    ...

Right way #2: MCP's own logging capability

# Sends a notifications/message the connected client can display,
# instead of writing to a local stream the client never sees at all.
await ctx.session.send_log_message(level="info", data=f"fetching {symbol}")

This is useful specifically because it surfaces log messages inside the host's own UI, rather than requiring the user to go find a separate log file.

Right way #3: MCP Inspector for interactive debugging

npx @modelcontextprotocol/inspector python server.py

The Inspector gives you a browser-based UI to send raw tools/call/resources/read requests and inspect exact request/response payloads. That's far faster for iterating on a new tool than repeatedly reconfiguring a full host and restarting it for every change.

The habit to build

Before shipping any server built with a language or library you haven't used for MCP before, double check whether any dependency defaults to logging to stdout. Some CLI-oriented libraries do, and that's the single most common source of "my server works standalone but breaks the moment a host connects to it" bugs.

Related Resources