← TIL

One stray print breaks an MCP server over stdio

2 min read
#mcp#llm#debugging#python

An MCP server running over stdio talks JSON-RPC on stdout. That is the whole transport. Every byte on that stream is supposed to be a protocol message.

So a debugging print("got here") is not a harmless debugging print. It lands in the middle of the message stream, the client fails to parse it, and what you see on the other end is a generic "server disconnected" or a JSON parse error with no obvious relationship to the line you added. I lost real time to this before it clicked.

Rules that follow from it:

  • Log to stderr, always. Clients capture stderr and surface it, so it is genuinely useful, not a black hole.
  • Python's logging already defaults to stderr, which is why logging works and print does not. print needs file=sys.stderr every time.
  • Third-party libraries are the real risk. Progress bars, banner messages on import, a helpful "downloading model weights" line. Any of them can write to stdout inside your process and take the server down with it.

The defensive move, if you are pulling in dependencies you do not control, is to reassign sys.stdout to sys.stderr at startup and keep a private handle to the real one for the protocol. Ugly, effective.

Two other things that helped: the failure looks identical to a crash on startup, so check the client's MCP logs before you go debugging your own initialization, and running the server by hand in a terminal shows you immediately whether the first thing it emits is valid JSON.

Fear has killed more dreams than failure ever will.
© 2026 Amey Ambade
Houston, TX
currently obsessing over: the long catalog of bossa nova standards