Skip to main content

cleanpipe

A stdio MCP server talks JSON-RPC on stdout, so a single banner, log line or print() there breaks the stream: the client times out handshaking, shows Unexpected token errors, or drops the server. cleanpipe wraps any server so only JSON-RPC messages reach the client and everything else goes to stderr, without waiting for the server's maintainers to ship a fix.

pip install cleanpipe        # or run it with no install: uvx cleanpipe -- <server command>

No dependencies. Python 3.9+. Works with servers written in any language.

Use it

Put cleanpipe -- in front of the server command in your client's config.

Codex (~/.codex/config.toml):

[mcp_servers.gitnexus]
command = "uvx"
args = ["cleanpipe", "--", "npx", "-y", "gitnexus@latest", "mcp"]

Claude Code:

claude mcp add gitnexus -- uvx cleanpipe -- npx -y gitnexus@latest mcp

Claude Desktop, Cursor, VS Code and other JSON configs:

{
  "mcpServers": {
    "gitnexus": {
      "command": "uvx",
      "args": ["cleanpipe", "--", "npx", "-y", "gitnexus@latest", "mcp"]
    }
  }
}

The first time the server writes something that isn't JSON-RPC, cleanpipe says so on stderr (your client's MCP log), and when the server exits it prints how many lines it moved.

Options

cleanpipe [--log FILE | --drop] [--quiet] -- <server command> [args...]
Option What it does
--log FILE Append the moved output to FILE instead of stderr.
--drop Throw the moved output away.
--quiet Don't print cleanpipe's own notes.

The server's exit code is passed on, its stderr is left alone, and SIGTERM / Ctrl-C are passed to it. python -m cleanpipe works too.

What counts as noise

cleanpipe reads the server's stdout one line at a time (MCP stdio is newline-delimited JSON):

The server writes The client gets stderr gets
{"jsonrpc":"2.0","id":1,"result":{...}} the line, byte for byte
Server started on stdio nothing the line
{"level":"info","event":"startup"} (a JSON log line) nothing the line
Loading...{"jsonrpc":"2.0","id":1,...} (a log printed without a newline) the message Loading...
two messages on one line both, one per line
a UTF-8 byte order mark, \r\n, ANSI colors, invalid UTF-8 in a log line the messages only the rest

A message is a JSON object with "jsonrpc": "2.0" and a method, or an id with a result or error, or a batch (array) of them. A JSON-RPC message nested inside a log object is not forwarded. What the client sends to the server is copied unchanged.

For server maintainers: cleanpipe check

$ cleanpipe check -- python my_server.py
handshake: ok, 4 tool(s)
stdout noise: 2 line(s) that are not JSON-RPC (clients may fail the handshake; log to stderr instead):
  | Loading model weights...
  | [INFO] ready

It starts the server, sends initialize, notifications/initialized and tools/list, keeps listening for half a second more, and exits 0 when stdout is clean, 1 when it found noise and 2 when the server didn't answer (--timeout S, default 10). --json prints the report as JSON for CI. From Python:

from cleanpipe import check, split

report = check(["python", "my_server.py"])
assert report.clean, report.noise

split(b'Loading...{"jsonrpc":"2.0","id":1,"result":{}}\n')
# ([b'{"jsonrpc":"2.0","id":1,"result":{}}'], [b'Loading...'])

Limits

  • Only stdio servers. Streamable HTTP servers don't share a channel with their logs.
  • A message pretty-printed over several lines is not put back together; each line is noise. No MCP SDK writes messages that way.
  • Output the server buffers and only flushes at exit (Python's print() to a pipe without flush=True) can't be moved until it is flushed; it still never reaches the client.
  • cleanpipe needs Python on the machine. With uvx it is fetched on first use.
  • Some clients tolerate stray lines (the official Python SDK client logs them and carries on); others fail the handshake. cleanpipe is for the second kind and harmless with the first.
  • The real fix is in the server: log to stderr. cleanpipe check in CI keeps it fixed.

Why this exists

The same bug is reported again and again, one server at a time, and users wait for each fix:

GitHub has over 70 such issues opened since September 2026 alone (titles with "stdout", "stdio" and "MCP"). The existing tools detect the problem; cleanpipe also works around it from the client side, so you can use the server today.

License

MIT

Metadata

Release files for cleanpipe 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cleanpipe 0.1.0
File Size Uploaded Provenance Actions
cleanpipe-0.1.0.tar.gz 14.8 kB Without provenance Details for cleanpipe-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cleanpipe 0.1.0
File Interpreter ABI Platform Actions
cleanpipe-0.1.0-py3-none-any.whl Python 3 none any Details for cleanpipe-0.1.0-py3-none-any.whl

Total release size: 27.6 kB

Release files / cleanpipe-0.1.0.tar.gz

Download URL cleanpipe-0.1.0.tar.gz
Size 14.8 kB
Tags Source
SHA-256 checksum
How to use checksums
53caa6528d104209dd6f687594f0a2a20f3fcd4016c6bb40069a7228815e6f30
BLAKE2b-256 checksum
How to use checksums
2b0168d887d4a800fa103bd929af8be08ec26922d01ae7a8cf1cdadd176d4d60
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / cleanpipe-0.1.0-py3-none-any.whl

Download URL cleanpipe-0.1.0-py3-none-any.whl
Size 12.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
517eadc8b1b210b33df5b033050f56789fdfb1b51db104189c338c681bd982d1
BLAKE2b-256 checksum
How to use checksums
db57bdeec06d05edb3c6e3a0fdd54d383e3df4cc628e732922ef0cdbac903248
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Security for cleanpipe 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page