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.
pip install mcp-server-lint
mcp-doctor path/to/your/server
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. 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. 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.
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"]
}
}
}
It flags:
outputSchema
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.
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