mcpvet
[npm version] [npm downloads] [MCP Registry]
Vet MCP servers and agent skills before you install them.
In early 2026 the ClawHavoc wave put 1,184 malicious skills on a public skill marketplace from just 12 accounts: credential stealers and reverse shells dressed up as helpful agent skills, inheriting whatever permissions the agent itself had. Only a small minority of organizations audit the MCP servers they install, and almost none audit skill packs. The install click is the trust decision, and it usually happens with no evidence at all.
mcpvet is the pre-install check. Point it at a local MCP server directory, a SKILL.md skill directory, a client config tree, or an npm package name, and it returns a deterministic verdict with evidence for every finding: VETTED, REVIEW, or DO-NOT-INSTALL. The scan is static only. Scanned code is read as text and never executed, imported, or installed.
Since 0.2.0 there is also a separate, clearly labeled live mode (mcpvet probe). It spawns a stdio server command twice and compares what it answers: probe parity, exact-hash fingerprint matching, and a server-level risk score derived from the recorded signals. Live mode executes the target, which is exactly why it is a separate command and not folded into the static scan.
Install
Quick start
CLI exit codes: 0 when the verdict is VETTED, 1 for REVIEW or DO-NOT-INSTALL, 2 for a usage or setup error, 141 when the reader closed the output pipe before the report was delivered.
What it checks
Verdict rules
- DO-NOT-INSTALL: at least one critical finding. Reverse shells, credential stealers, install droppers, curl pipe shell autostarts, and download-and-execute steps declared in skill documentation land here.
- REVIEW: at least one high or medium finding and no criticals. Dangerous capability combos, undeclared tools, typosquats, env harvesting configs, and unpinned autostarts land here. A partial scan also lands here at minimum: when a manifest is present but could not be parsed or read, the scan did not see the whole target, so VETTED is unreachable in that state.
- VETTED: no critical, high, or medium findings. Info and low findings (such as the autostart inventory itself) are still reported as evidence.
A verdict is a scan result, never a safety claim. VETTED means these checks found nothing at or above medium severity in the files scanned; it does not certify a server safe.
Scope floor (v0.1.2): DO-NOT-INSTALL is a claim about an installable MCP bundle. If the target is not an MCP target (kind unknown, meaning no server, config, or skill as its primary subject, or a mixed monorepo root whose critical findings all come from outside every identified MCP bundle), the verdict floors at REVIEW with an explicit "not an MCP target" note telling you to scope vetting to the server or skill directory. Findings are still reported as evidence; a critical inside an identified bundle still verdicts DO-NOT-INSTALL.
Live mode (probe) has its own verdict rules, deliberately capped: any scoring signal (a parity drift or a fingerprint match) makes the verdict REVIEW, a clean double probe verdicts VETTED, and no usable evidence verdicts REVIEW as inconclusive. Live evidence never yields DO-NOT-INSTALL: a fingerprint match is grounds for a human look, not a hard fail. Two negative controls are explicit: a probe refused for auth reasons is recorded as a pass on that probe (a server demanding credentials is doing the minimum right), and a timeout records no signal at all.
MCP
mcpvet mcp serves a stdio MCP server with three tools:
Scope and limits
- Static analysis only, by design. mcpvet never executes, imports, or installs the target, and vet_package never downloads the tarball. Behavior that only appears at runtime is out of scope; vet the extracted directory for capability and inventory checks.
- Live mode is the one exception, and it is opt-in per invocation:
probe (or the probe_server MCP tool) spawns the given command twice on the machine running mcpvet. Run it only against commands you already trust enough to execute locally.
- The fingerprint file shipped in 0.2.0 is seeded from mcpvet's own test fixtures (labeled test seeds, generated by probing the fixture server with scripts/gen-seeds.ts), not from observed real-world servers. Entries are added only from recorded evidence, each with its provenance.
- Tool inventory is pattern-based: tools registered through shapes the scanner does not recognize (dynamic registration, generated code) can be missed, which is why a declared manifest with no matching registrations is itself a finding.
- The walk caps at 2,000 files and 512 KiB per file. Anything skipped is counted and reported in the report's caps block, never silently dropped. When the skipped file is package.json or server.json, the verdict is also floored at REVIEW, because the manifest itself went unaudited.
- The vet target must be a directory. A regular file, a FIFO, a device, or a nonexistent path is a usage error and exits 2, never a verdict.
- Compiled native code is opaque to this scanner by construction. A tree shipping native artifacts (.node, .dll, .so, .dylib, or a prebuilds directory) draws a NATIVE-BINARY finding at medium and verdicts at least REVIEW; the finding reports presence only and claims nothing about what the binary does.
- The typosquat list is a bundled snapshot of well-known MCP server names, not the live registry.
- Pattern literals are indistinguishable from payloads in a static text scan: the source of a security scanner (or a test fixture corpus) contains the very shapes it detects, so vetting detector tooling flags its own pattern strings (observed on the Security Wave factory repos themselves, where every such critical was hand-verified as a pattern literal or a planted fixture). Treat verdicts on scanner source as REVIEW-grade evidence, not as a compromise claim.
Part of the Security Wave: hookcheck (persistence hooks), actionpin (Actions pinning), injectcheck (prompt injection in agent-read content), tracewatch (observability and root cause), mcpvet (pre-install vetting).
MIT.
If mcpvet saved you time, star this repo: it helps other developers find it.