DEV Community

Vishal Habib
Vishal Habib

Posted on Fully Autonomous

mcp-doctor: one open-source tool for the whole life of an MCP server

An agent is only as reliable as the tools it calls, and most MCP servers are never checked against anything. A tool with no description never gets picked. A parameter typed str = None rejects the very default it advertises. A write tool says "Successfully wrote" and writes nothing.

I built mcp-doctor to catch those, and it has grown into one open-source tool that covers an MCP server's whole life: before you ship, in every PR, and inside the running agent.

mcp-doctor: one tool for the whole life of an MCP server. Build, review, run, reach.

1. Before you ship

pip install mcp-server-lint
mcp-doctor path/to/your/server
Enter fullscreen mode Exit fullscreen mode
  • Lint. Reads Python, TypeScript and Go source without running it, and grades quality and security separately, so a well-documented server can't hide a security gap behind one blended number. Quality covers what breaks an agent: missing tool descriptions, undocumented parameters, None defaults that reject null, stray stdout writes on stdio, descriptions that don't tell two tools apart. Security covers tool-poisoning phrases, dynamic exec, SSRF-prone requests, unsafe deserialization, hardcoded secrets and read-only annotations that lie.
  • --fix. Applies only the fixes that need no judgment: nullable None defaults, missing Args: entries, bare except:. On three repos with a hand-written PR for the same bug, it produced exactly the same type changes.
  • --diff-against. Compares two releases and flags a removed tool, a removed parameter, or a newly required one: the changes that silently break an agent that learned the old shape.

2. In every PR

  • GitHub Action (uses: vishalhabib99/mcp-doctor@v1): comments on each PR, suggests one-click fixes on the lines it changes, and can fail a PR that drops below your score.
  • pre-commit hook: the same checks before the commit lands.

3. Inside the agent: guard

Static checks can't see whether a call actually worked. mcp-doctor guard runs any stdio MCP server behind a live check. Every message passes through unchanged; when a "success" isn't one, it adds a warning the agent reads:

{
  "mcpServers": {
    "filesystem": {
      "command": "mcp-doctor",
      "args": ["guard", "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

It flags:

  • a write that reports success but a matching read can't find it
  • arguments the schema doesn't have, which the server silently drops
  • a wait that returned early while claiming the full time
  • an error or a refusal returned as a successful answer, an empty read, output that breaks the tool's own outputSchema
  • a likely prompt-injection leak: a secret read through one call about to be sent out by another, or text from one repo about to be written into another after the session read outside content

Same broken write_file, same prompt, in Claude Code:

Without guard: "Yes, the file was saved."

With guard: "I can't confirm the file was saved."

For coding agents, the mcp-doctor-guard Claude Code plugin applies the same idea to the agent's own tools (shell, file writes, web fetch). In a live run with Haiku in bypassPermissions, the agent without guard read a web page, then planted a .git hook and sent a token from .env to a server. With guard, both were stopped, and the agent didn't try another route.

4. Wherever you are

  • In your browser: paste a repo, npm or PyPI name. The real mcp-doctor runs in the page (Pyodide); nothing is uploaded or run.
  • As a GitHub issue: open a scan request and a bot replies with the report.
  • Claude Code plugin: ask "check my MCP server"; Claude scans, explains each finding, and fixes what it can with your OK.
  • fix-upstream: an agent that turns a finding into a tested PR, which a human reviews and sends.

How I know it works

  • Recall in the open: it finds 15,755 of 15,977 tools across 805 real servers on 5 frameworks, and every miss is listed in docs/coverage.md.
  • 113 bugs in mcp-doctor itself, found by running it on real servers and all fixed (build log). Update (Oct 16): now 115. v1.24.1 fixed two more, found by scanning a reader's repo.
  • 48 fixes merged by maintainers in 46 outside repos, including langflow and ha-mcp.
  • First outside CI user: jupyter-mcp-server added the Action today, after its maintainer asked for it.

Limits

Guard covers stdio servers only (not HTTP yet), confirms a write only when the server has a matching read tool, and matches copied text, so a paraphrased leak can get past it. It's a net for when the model is fooled, not a replacement for least-privilege tokens.

Try it on your server and tell me what it gets wrong: github.com/vishalhabib99/mcp-doctor

Top comments (0)