The MCP ecosystem has been growing fast, but the supply-chain hygiene has not kept up. MCPwn (CVE-2026-33032, CVSS 9.8) exposed 2,600+ instances. The Shai-Hulud npm worm stole MCP auth tokens from 172 packages. MCPSafe found high-severity bugs in official MCPs from Atlassian, GitHub, Cloudflare, and Microsoft. Perplexity open-sourced Bumblebee in May 2026 specifically because no good scanner existed.
So I built one. Today I'm shipping @weiseer/mcp-doctor — an open-source install-time trust gate for MCP server packages — together with the validation dataset that surfaced its first real finding.
TL;DR
npx @weiseer/mcp-doctor @some/mcp-server
Returns PASS / WARN / BLOCK with cited evidence per signal. The full scoring rubric is open-source so you can argue with the methodology rather than trust a black-box. Free public scan endpoint at https://api.weiseer.com/scan, 60 requests/min/IP, no auth.
Live dataset of 200 popular MCP-related packages at https://api.weiseer.com/dataset/scan_200.json. Leaderboard view at https://api.weiseer.com/leaderboard.
What the 200-package scan found
| Verdict | Count |
|---|---|
| PASS | 138 (69%) |
| WARN | 58 (29%) |
| BLOCK | 3 (1.5%) |
| ERROR | 1 (npm 404) |
1 package had a hardcoded LLM API key
The scanner's D3_hardcoded_credentials_in_source signal fires on common provider key patterns (sk-ant-*, sk-*, AKIA*, ghp_*, npm_*, AIza*) in published source. It is a hard-block: −50 points, no questions.
One of the 200 packages tripped it with a real-looking sk-ant-... Anthropic key embedded in its bundled JavaScript source. The maintainer was emailed within the hour using their npm publisher contact. They have 7 days to rotate the key, deprecate the bad version, and republish reading from process.env. After that window closes (2026-06-06), I'll reference the pattern anonymously — but I'll keep the specific package name private indefinitely if they ask.
This is the Shai-Hulud-class risk in concrete form: a single embedded key, in a single npm package, that any tool scanning the agent's dependency tree could exfiltrate.
Six "official" MCP servers are silently abandoned
This one surprised me:
| Package | Days since last release | Repository URL |
|---|---|---|
@modelcontextprotocol/create-server |
550 | none |
@modelcontextprotocol/server-postgres |
541 | none |
@modelcontextprotocol/server-gdrive |
501 | none |
@modelcontextprotocol/server-github |
416 | none |
@modelcontextprotocol/server-slack |
399 | none |
@modelcontextprotocol/server-puppeteer |
382 | none |
These are still cited in nearly every MCP tutorial. None have a repository field in package.json, so source-to-binary verification is impossible. If you depend on any of them in production, mirror the source today.
@google/generative-ai is also installed broadly via npm but Google has archived its GitHub repo in favor of @google/genai.
2 typosquats of official servers
Self-explanatory — both blocked with −40 HARD C4_name_typosquats_official. Short-name comparison catches packages within edit-distance 1 of well-known official names.
The rubric is open-source by design
I am not a security vendor and these scores are not a black box. Every signal in rubric.yaml has:
- An ID (e.g.
D3_hardcoded_credentials_in_source) - A deduction value (how much it costs you)
- A rationale (why we think it matters)
If you think A1_unpinned_deps is too aggressive, open a PR. If you think B2_single_maintainer unfairly punishes new packages, open a PR. The whole point of an open rubric is that ecosystem trust is a public good, not vendor secret sauce.
I ran the scanner on my own 9 packages first (@weiseer/*) and published the results in the same leaderboard. They all PASS at 100/100 — but two signals (B2_single_maintainer, B3_repo_under_60d_old) are explicitly suppressed via the self_disclosure flag because they're trivially expected on packages published the same day. I'd rather show the suppression than score myself perfect with a rigged rubric.
How to use it
Single package:
npx @weiseer/mcp-doctor @some/package
Audit your existing MCP config:
npx @weiseer/mcp-doctor --config ~/Library/Application\ Support/Claude/claude_desktop_config.json
CI gate to block bad MCPs in PRs:
- uses: weiseer/mcp-doctor-action@v1
with:
config-path: '.mcp/claude_desktop_config.json'
policy: 'block-only' # or strict, or report
README trust badge:

Pricing
| Tier | Price | Get |
|---|---|---|
| Free | $0 | Single scan + Trust Badge + leaderboard, 60 req/min/IP |
| Pro | $19/mo | Repo monitoring, drift alerts, badge history |
| Team | $49/mo | 5 repos, Slack/Webhook alerts, custom policy YAML |
| Enterprise | $299/mo | Unlimited repos, audit log export, SLA |
What is broken / what I want feedback on
-
A1_unpinned_deps calibration — npm convention is
^ranges. v0.2 raised the threshold to >5 deps AND >70% caret, but I might still be over-firing. - B3_repo_under_60d_old — suppressed for self_disclosure packages but maybe should be more nuanced (new fork vs new project).
- Typosquat detection — currently short-name edit-distance ≤1. Might miss creative variants.
- MCP-specific signals (D series) — capability-declaration mismatches are very domain-specific and the rule layer feels thin.
If you spot a false positive when you run it on your packages, please open an issue with the package name + which signal you think is misfiring. The faster the rubric matures, the more useful this is for everyone.
Links
- npm:
@weiseer/mcp-doctor - GitHub: github.com/weiseer/mcp-doctor
- Public API: api.weiseer.com
- Postmortem repo (story of the build): github.com/weiseer/launch-postmortem
- Author: wei@weiseer.com · github.com/weiseer
Apache-2.0.
Top comments (0)