mcp-supply-audit
Allows auditing npm package versions by querying npm registry metadata for package and version existence, deprecation state, install scripts, provenance, and declared repository, along with OSV.dev advisories and typosquat checks.
Allows auditing PyPI package versions by querying PyPI metadata for package and version existence, yanked state, provenance, publish recency, and declared repository, along with OSV.dev advisories and typosquat checks.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-supply-audit Is the MCP server modelcontextprotocol/filesystem safe to install?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-supply-audit
Audits MCP servers as software artifacts: registry entries, packages, configs, tool definitions.
An MCP server. Static checks only. It never runs the code it inspects.
Status: pre-release, in development. Not yet published. Nothing here is a security guarantee.
Why this exists
Most MCP security scanners focus on tool descriptions. This project looks at the server as a supply-chain artifact: does the registry entry match its package and repo, is the name close to a popular one, what do the package version's own metadata and advisories say, and what does the config you are about to copy actually do.
Related MCP server: mcpaudit
What works today
Six tools, served over stdio by a FastMCP server (tool surface). All are read-only and take names or pasted text, never a path.
audit_server: runs the checks below that apply to one server (a registry name, or a package name pluspypi/npm) concurrently under a 30 s deadline, and returns one merged report. A check that fails or times out is listed as skipped; the rest of the report still returns.audit_registry_entry: an MCP Registry entry's repository consistency with its packages, owner match, package and version existence, ownership markers, status, and remote URLs (as text).audit_package: one PyPI or npm package version's OSV.dev advisories, yanked or deprecated state, install scripts (npm only), provenance, publish recency (PyPI only) and declared repository.check_typosquat: whether a name is close to one in a dated reference list (curated PyPI and npm names; for registry names, also names crawled from the MCP Registry).scan_mcp_config: an MCP client config's servers, for unpinned runners, plain-http remotes, hardcoded secrets, shell wrappers, broad paths, non-registry sources and risky env.scan_tool_definitions: checks atools/listsnapshot for hidden Unicode, instruction-like phrasing, cross-tool references, exfiltration phrasing, encoded blobs, and other indicators.
Outbound requests go only to registry.modelcontextprotocol.io, pypi.org, registry.npmjs.org and api.osv.dev.
Quick start
Not on PyPI yet: these commands work once the package is published. Until then, run it from a source checkout.
For Claude Code, with uv:
claude mcp add mcp-supply-audit -- uvx mcp-supply-auditPinned to one version (uvx's package@version form):
claude mcp add mcp-supply-audit -- uvx mcp-supply-audit@0.1.0scan_mcp_config flags the first form in the configs it scans (CF001, unpinned package runner).
From a source checkout
Run it from a checkout of this repository with uv:
cd mcp-supply-audit
uv syncThen point an MCP client at the mcp-supply-audit console script in that checkout. For Claude
Code:
claude mcp add mcp-supply-audit -- uv --directory /path/to/mcp-supply-audit run mcp-supply-auditFor clients configured with JSON (e.g. Claude Desktop's mcpServers):
{
"mcpServers": {
"mcp-supply-audit": {
"command": "uv",
"args": ["--directory", "/path/to/mcp-supply-audit", "run", "mcp-supply-audit"]
}
}
}Then ask, for example, "Is the MCP server <namespace>/<name> safe to install?" and the client
can call audit_server. Read the report's skipped checks: a report with no findings is not proof
of safety.
Design principles
Never executes a scan target. Static analysis and metadata only. No tool accepts a filesystem path.
Hostile input by default. Text from a target is sanitized, capped, and returned only as quoted evidence under
untrusted_text. Hidden characters are shown as visible\uXXXXescapes.Deterministic rules, no LLM in detection. Results are reproducible and unit-tested.
Findings are indicators, not verdicts. Every report lists which checks ran and which were skipped, and states that no findings is not proof of safety.
Mapped to the OWASP MCP Top 10 (beta, v0.1), by ID.
Limits
Static checks cannot see runtime behavior. Audit and telemetry gaps, shadow servers, and context over-sharing are outside what this tool can detect.
Per-check limits: registry checks, package checks, typosquat checks, config checks.
Evaluation
No benchmark has been run yet.
Development
uv sync
uv run pytest -q
uv run ruff check
uv run ruff format --checkSecurity
To report a vulnerability in this project, see SECURITY.md.
License
MIT
Available Tools
6 toolsaudit_packageAudit PackageARead-only
Check one PyPI or npm package version's supply-chain signals: known vulnerabilities (OSV.dev), yanked or deprecated state, install scripts (npm), provenance, publish recency (PyPI) and declared repository (PK001-PK006).
Use when: the user asks about a specific package (and version), e.g. one a config pins. Prefer another tool when: the user wants a full check of a server (audit_server).
Limits: large PyPI projects need an explicit version, because their project document exceeds the size cap used to resolve the latest. There is no install-script check for PyPI (build code is only in the sdist, which is never downloaded). npm version documents have no publish time, so recency is not checked for npm. Download counts, package age and owner history are not available. Findings are indicators, not verdicts, and a report with no findings is not proof of safety. coverage lists the checks that ran and those skipped, with reasons.
Inputs: name (required), ecosystem (required), version. Returns: object
audit_registry_entryAudit Registry EntryARead-only
Check an MCP Registry entry: declared repository URLs are consistent with its packages, the entry's owner matches its repository, its packages and versions exist, ownership markers, status (deleted, deprecated), and remote URLs (RG001-RG011).
Use when: checking a registry listing's consistency, status, ownership markers or remote URLs. Prefer another tool when: the user wants a full check of a server (audit_server).
Names are case-sensitive: copy the name exactly as the registry shows it; a name in another case is reported as not found. Remote and repository URLs are checked as text and never contacted. Findings are indicators, not verdicts, and a report with no findings is not proof of safety. coverage lists the checks that ran and those skipped, with reasons.
Inputs: name (required). Returns: object
audit_serverAudit ServerARead-only
Run every check that applies to one MCP server and return one merged report.
Use when: any "is this MCP server safe to install?" question. Prefer another tool when: the user wants only one specific check.
For a registry name: audit_registry_entry, then audit_package for each pypi or npm package the entry lists (at most 3, at the listed version), and check_typosquat on the registry name and each package name. For a package name: audit_package and check_typosquat; the registry checks are recorded as skipped (a package name does not identify a registry entry). With tools_snapshot (JSON text only; an object is rejected), scan_tool_definitions too. Checks run concurrently under a 30 s deadline; a check that fails or does not finish is a skipped check naming it, and the rest of the report still returns. Coverage, findings and locations are prefixed with the sub-check name. The limits of each sub-check apply (see audit_package, audit_registry_entry and check_typosquat). Findings are indicators, not verdicts, and a report with no findings is not proof of safety. coverage lists the checks that ran and those skipped, with reasons.
Inputs: name (required), version, ecosystem, tools_snapshot. Returns: object
check_typosquatCheck TyposquatARead-only
Check whether a package or MCP registry server name is suspiciously close to a known MCP server or package name: edit distance, confusable characters, affixes, and scope or namespace changes (TS001-TS006).
Use when: the user asks whether a name looks like a known MCP server or package, or is about to install something by a name they typed or were given. Prefer another tool when: the name is one the user already trusts and has verified, or the user wants a full check of a server (audit_server).
The name is compared against a dated reference list, never live popularity data. The reference list (data/reference_names.json) is dated 2026-11-03 (its generated_at field, returned in each report as data_sources[].as_of) and has 55988 entries: 6 PyPI and 4 npm names, which are curated names only, and 55978 registry names, crawled from the MCP Registry plus curated ones. A name close to none of them is not shown to be legitimate: the list is incomplete by nature. Findings are indicators, not verdicts, and a report with no findings is not proof of safety. coverage lists the checks that ran and those skipped, with reasons.
Inputs: name (required), ecosystem (required). Returns: object
scan_mcp_configScan Mcp ConfigARead-only
Check an MCP client config (Claude Code, Claude Desktop, Cursor or VS Code) for unpinned package runners, plain-http remotes, hardcoded secrets, shell wrappers, broad filesystem paths, non-registry package sources and risky env entries (CF001-CF007).
Use when: the user is reviewing a config they have or are about to copy. Prefer another tool when: the user asks about one server's tools (scan_tool_definitions), or whether a pinned package is safe (audit_package).
Takes pasted text only, never a path; no config value appears in the report. Findings are indicators, not verdicts, and a report with no findings is not proof of safety. coverage lists the checks that ran and those skipped, with reasons.
Inputs: config_text (required). Returns: object
scan_tool_definitionsScan Tool DefinitionsARead-only
Check a server's tool definitions (a tools/list snapshot) for poisoning indicators: hidden Unicode, instruction-like phrasing aimed at the model, references to other tools, exfiltration phrasing, encoded blobs, overlong descriptions, free-text parameters that invite conversation content, and duplicate names (TD001-TD008).
Use when: the user has a server's tool list (pasted JSON) and wants it checked. Prefer another tool when: the user only has a package or registry name, or wants a full check of a server (audit_server, which also takes the snapshot).
Takes pasted JSON text only (an object is rejected), never a path; nothing is fetched. Findings are indicators, not verdicts, and a report with no findings is not proof of safety. coverage lists the checks that ran and those skipped, with reasons.
Inputs: snapshot (required). Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 6 tools
Each tool targets a distinct input artifact: a registry listing, a tools/list snapshot, a package name, a client config, a name for typosquat comparison, or the aggregate server audit. The descriptions explicitly state when to prefer another tool, and audit_server's overlap with the individual checks is disambiguated by its 'prefer another tool when the user wants only one specific check' guidance.
All names are lowercase snake_case with a verb_noun shape, so the pattern is predictable. The mix of audit_ (composite analysis), scan_ (pattern detection over pasted text) and check_ (single yes/no question) is meaningful rather than arbitrary, though three different verbs is a minor deviation from a single consistent verb.
Six tools map cleanly onto the five distinct supply-chain inputs plus one aggregate entry point, with no redundant members. Each tool earns its place and the count is well within a comfortable range.
The surface covers registry entries, package metadata, typosquatting, tool-definition poisoning, client configs, and an aggregator that composes them with documented limits and skipped-check reporting. Minor gaps exist around server runtime/behavioral auditing and bulk multi-server sweeps, but these are outside the stated static-audit scope and are workable around.
Maintenance
Related MCP Connectors
Scan any MCP server for tool-poisoning, security, auth & license. Trust score before install.
Audit your MCP servers for poisoned tools and leaked secrets, and check AI-generated code for flaws.
Audit MCP servers for tool poisoning and shadowing. Scan a remote server by URL or paste its tools.
Security scanner for MCP servers. Detect vulnerabilities, prompt injection, and tool poisoning.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceAudits MCP server configurations for security risks including capability inventory, SSRF, prompt injection, and drift detection. Works in read-only mode and can also be used as an MCP server to let AI agents audit their own attack surface.259 PyPI4MIT
- AlicenseNot gradedqualityBmaintenanceAudits MCP server configurations and packages for security risks such as typosquats, credential exposure, and malicious code, with zero dependencies and no execution.1MIT
- AlicenseAqualityBmaintenanceEnables agents and CI pipelines to audit MCP servers and agent tool-chains by statically scanning repositories, local checkouts, tools/list exports, or live endpoints for risks such as destructive actions without confirmation, mismatched safety annotations, injection surfaces, credential or PII exposure, and unguarded command, path, or URL sinks. All checks are read-only and never execute the scanned code or call tools/call.3MIT
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients to run deterministic, read-only local repository audits for risky GitHub workflows, undocumented environment variables, possible credentials, and conflicting dependency configuration, returning structured findings without sending source code to a service.50 npmMIT