MCP Servers

A collection of Model Context Protocol servers, templates, tools and more.

Catch stdout pollution and handshake failures in MCP stdio servers before clients do.

Created 5/12/2026
Updated about 4 hours ago
Repository documentation and setup instructions

mcp-stdio-guard logo

mcp-stdio-guard

Catch stdout pollution and handshake failures in MCP stdio servers before clients do.

CI npm runtime dependencies node license

mcp-stdio-guard hero showing a clean MCP stdio pipeline

MCP stdio servers use stdout as their protocol channel. Debug text, banners, progress logs, console.log, Python print, or any other stray stdout output can corrupt the stream and make clients fail in confusing ways.

mcp-stdio-guard starts your server, performs a real MCP initialize handshake, optionally sends a real post-initialize MCP request such as tools/list, validates every stdout frame, and scans source for risky stdout calls.

Why This Exists

The latest MCP docs say stdio servers must send JSON-RPC messages on stdout, may log to stderr, and must complete the initialize then notifications/initialized lifecycle before normal operation.

That is easy to get wrong in real servers. This guard turns that fragile process boundary into a fast local check and a CI gate.

Protocol flow tested by mcp-stdio-guard

Install

From npm:

npx mcp-stdio-guard -- node ./server.js

From this repo:

git clone https://github.com/1Utkarsh1/mcp-stdio-guard.git
cd mcp-stdio-guard
npm ci
npm test

Quickstart

Run your MCP server behind the guard:

mcp-stdio-guard -- node ./server.js

Exercise a real MCP operation after initialization:

mcp-stdio-guard --request tools/list -- node ./server.js

Scan source for obvious stdout writes too:

mcp-stdio-guard --scan src --fail-on-static --request tools/list -- node ./server.js

JSON output for CI:

mcp-stdio-guard --json --request tools/list -- node ./server.js

Repeat the same guard to catch cold/warm startup behavior:

mcp-stdio-guard --repeat 2 --request tools/list -- node ./server.js

What It Catches

Passing and failing terminal output examples

| Problem | Runtime check | Static scan | | --- | --- | --- | | console.log("starting") before server startup | Yes | Yes | | Dependency/import-time stdout pollution | Yes with --repeat | No | | Python print("debug") in a stdio server | Yes | Yes | | Late stdout logs after initialize | Yes | Partial | | Invalid JSON-RPC frames | Yes | No | | Server crash after notifications/initialized | Yes | No | | Missing initialize or operation response | Yes | No | | stderr diagnostics | Allowed | Allowed |

Live MCP Coverage

The test suite creates real servers with @modelcontextprotocol/sdk@1.29.0 and verifies:

| Scenario | Expected result | | --- | --- | | clean SDK stdio server through initialize and tools/list | Pass | | SDK server with startup stdout pollution | Fail | | SDK server with stderr diagnostics | Pass | | SDK server with late stdout pollution after connection | Fail | | hand-rolled server that ignores post-initialize requests | Fail | | server that crashes after initialized notification | Fail |

Commands

mcp-stdio-guard [options] -- <command> [args...]

| Option | Description | | --- | --- | | --protocol <version> | MCP protocol version to send, default 2025-11-25 | | --timeout <ms> | initialize and request timeout, default 5000 | | --repeat <count> | run the same guard multiple times to catch cold/warm startup behavior | | --request <method> | send one MCP request after initialization, for example tools/list | | --params <json> | JSON params for --request | | --scan <path> | scan source for risky stdout writes | | --fail-on-static | make static scan findings fail the command | | --json | print machine-readable output | | --cwd <path> | run the server command from a specific directory | | --help | show help |

JSON Contract

--json is intended for CI, registries, and badge ingestion. The current contract is schemaVersion: 1; new fields may be added, but these fields are stable for consumers:

| Field | Meaning | | --- | --- | | schemaVersion | JSON contract version, currently 1 | | ok | true when no error-severity issue was found | | command | command and arguments that were validated | | protocol | MCP protocol version sent by the guard | | negotiatedProtocol | protocol version returned by the server, when available | | initialized | whether the server completed the initialize handshake | | operation | post-initialize request result, or null when --request was not used | | checks | badge-friendly per-class statuses | | issues | machine-readable diagnostics with severity, code, and message; repeat mode also adds run | | staticScan | whether source scanning was enabled and whether findings fail the command | | staticFindings | source scan findings with file, line, and message | | runs | per-run results when --repeat is used |

Check statuses are pass, fail, warning, or skipped. The checks object separates the signal into initialize, stdout, jsonRpc, operation, process, pythonBuffering, staticScan, and repeat, each with stable status and issueCodes fields. When --repeat is used, checks.repeat also includes runs, passedRuns, and failedRuns; each entry in runs is a normal schema-versioned result for that individual guard run.

Example:

{
  "schemaVersion": 1,
  "ok": true,
  "checks": {
    "initialize": { "status": "pass", "issueCodes": [] },
    "stdout": { "status": "pass", "issueCodes": [] },
    "jsonRpc": { "status": "pass", "issueCodes": [] },
    "operation": { "status": "pass", "issueCodes": [] },
    "process": { "status": "pass", "issueCodes": [] },
    "pythonBuffering": { "status": "pass", "issueCodes": [] },
    "staticScan": { "status": "skipped", "issueCodes": [] },
    "repeat": { "status": "skipped", "issueCodes": [] }
  }
}

The guard is registry-agnostic. It does not care whether an install command came from Smithery, Glama, GitHub, or a private catalog; it validates the command, working directory, optional source path, and observed stdio behavior.

CI

- run: npm ci
- run: npx mcp-stdio-guard --scan src --fail-on-static --request tools/list -- node ./server.js

Output

Passing server:

PASS MCP stdio guard
initialize: ok
frames: 2 stdout / 0 invalid
stderr: 0 lines
protocol: 2025-11-25
request: tools/list responded

Polluted stdout:

FAIL MCP stdio guard
initialize: ok
frames: 2 stdout / 1 invalid
stderr: 0 lines
protocol: 2025-11-25
request: tools/list responded
[error] stdout-non-json: stdout line 1 is not JSON-RPC: "server starting..."

Design

  • Runtime dependencies: zero.
  • Default behavior: validate the real process boundary.
  • Optional static scan: intentionally simple and conservative.
  • CI posture: fail on protocol corruption, crashes, and missing responses.
  • Promotion promise: no fake stars, no spam, just a tool that catches a real MCP failure mode.

License

MIT

Quick Setup
Installation guide for this server

Install Package (if required)

npx @modelcontextprotocol/server-mcp-stdio-guard

Cursor configuration (mcp.json)

{ "mcpServers": { "1utkarsh1-mcp-stdio-guard": { "command": "npx", "args": [ "1utkarsh1-mcp-stdio-guard" ] } } }