@royalpinto007/mcp-audit
v0.1.2
Published
A security scanner and linter for Model Context Protocol (MCP) servers. Like npm audit / eslint, but for MCP.
Maintainers
Readme
mcp-audit
Security scanner and linter for Model Context Protocol (MCP) servers. Like
npm auditandeslint, but for the tools you hand your AI agents.
npx @royalpinto007/mcp-audit stdio "node my-mcp-server.js"Why
MCP servers give language models real capabilities: run commands, read files, hit
internal URLs, mutate databases. A single over-scoped tool or a .env exposed as
a resource turns a helpful agent into a remote code execution or data-exfiltration
path. Yet most MCP servers ship with no security review at all.
mcp-audit connects to an MCP server (or lints its manifest), enumerates every
tool, resource, and prompt, and runs a catalog of security rules over that surface.
It finds the dangerous stuff before your agent does:
- arbitrary command / shell execution tools,
- destructive actions with no confirmation or scoping,
- prompt-injection sinks planted in tool descriptions,
- secrets and system paths exposed as resources,
- SSRF-prone URL arguments and unauthenticated HTTP transports,
- unconstrained input schemas that let the model pass anything, anywhere.
It runs offline, is fully deterministic, and drops straight into CI with JSON and SARIF output for GitHub code scanning.
Quickstart
# Audit a server you spawn over stdio
npx @royalpinto007/mcp-audit stdio "node my-mcp-server.js"
# Audit a remote server over HTTP (with a bearer token)
npx @royalpinto007/mcp-audit http https://mcp.example.com/mcp --token "$MCP_TOKEN"
# Lint a server's declared surface from a manifest, without running it
npx @royalpinto007/mcp-audit static ./mcp-manifest.json
# List every built-in rule
npx @royalpinto007/mcp-audit rulesExample output
mcp-audit — stdio target node my-mcp-server.js
server: insecure-demo-server v0.0.0-internal
surface: 5 tools, 2 resources, 1 prompts
CRITICAL (3)
✖ MCP002 Arbitrary execution tool detected @ run_shell
Tool "run_shell" appears to execute commands or code (matched "shell").
fix: Avoid exposing raw exec. If unavoidable, allowlist commands, drop privileges, sandbox execution, and require human approval.
✖ MCP030 Resource surfaces sensitive material @ file:///home/app/.env
Resource "file:///home/app/.env" references ".env", which commonly holds secrets or system credentials.
fix: Remove the resource or restrict it to non-sensitive content.
HIGH (4)
✖ MCP001 Destructive tool has no confirmation argument @ delete_file
✖ MCP031 Unconstrained filesystem path argument @ delete_file.path
...
20 findings 3 critical, 4 high, 5 medium, 6 low, 2 infoThe process exits 1 when any finding reaches the --fail-on threshold (default
high), so a failing audit breaks the build.
Rule catalog
18 built-in rules, each with a stable id, severity, and remediation. Toggle any of them via config.
| Rule | Severity | Category | Description |
| ---- | -------- | -------- | ----------- |
| MCP001 | high | permissions | Destructive tool lacks scoping or confirmation |
| MCP002 | critical | permissions | Tool exposes arbitrary command execution |
| MCP003 | medium | permissions | Mutating tool has an unscoped input surface |
| MCP010 | medium | schema | Tool is missing an input schema |
| MCP011 | low | schema | Schema allows unbounded additional properties |
| MCP012 | low | schema | Unconstrained string argument |
| MCP013 | info | schema | Object schema has no required properties |
| MCP020 | high | injection | Description contains probable prompt-injection text |
| MCP021 | medium | injection | Tool advertises overly broad capability |
| MCP030 | critical | secrets | Resource exposes secrets or sensitive paths |
| MCP031 | high | secrets | Path argument is vulnerable to traversal |
| MCP040 | high | transport | HTTP transport has no authentication |
| MCP041 | medium | transport | Tool accepts a caller-controlled URL (SSRF risk) |
| MCP050 | medium | metadata | Server metadata leaks internal information |
| MCP051 | info | metadata | Server does not report a version |
| MCP060 | high | hygiene | Duplicate tool names |
| MCP061 | low | hygiene | Excessive number of tools |
| MCP062 | info | hygiene | Tool has no description |
Configuration
Drop a .mcpauditrc (or .mcpauditrc.json / mcpaudit.config.json) in your repo.
mcp-audit discovers the nearest one walking up from the working directory.
{
"failOn": "high",
"disabledRules": ["MCP013"],
"severityOverrides": {
"MCP011": "info"
},
"ignore": ["fetch_url.note"]
}| Key | Meaning |
| --- | ------- |
| failOn | Findings at or above this severity cause a non-zero exit. Default high. |
| disabledRules | Rule ids to skip entirely. |
| enabledRules | If set, run only these rule ids. |
| severityOverrides | Remap a rule's severity, e.g. downgrade a noisy check. |
| ignore | Substrings matched against a finding's location to suppress it. |
Severities, lowest to highest: info, low, medium, high, critical.
CLI flags
--json Emit JSON instead of terminal output
--sarif Emit SARIF 2.1.0 (for GitHub code scanning)
--output <file> Write the report to a file instead of stdout
--config <file> Use a specific config file
--fail-on <severity> Exit non-zero at/above this severity
--disable <ids> Comma-separated rule ids to disable
--only <ids> Comma-separated rule ids to run exclusively
--token <token> Bearer token for http transport
--header <k:v> Extra header for http transport (repeatable)
--sse Use the legacy SSE transport for http
--no-color Disable colored outputSARIF and CI usage
mcp-audit emits SARIF 2.1.0 so findings
show up as annotations in GitHub code scanning.
- run: npx @royalpinto007/mcp-audit static ./mcp-manifest.json --sarif --output mcp-audit.sarif --fail-on critical
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: mcp-audit.sarifExit codes:
| Code | Meaning |
| ---- | ------- |
| 0 | No findings at or above --fail-on. |
| 1 | Findings at or above --fail-on. |
| 2 | Usage or runtime error. |
Static mode
Static mode lints a server's declared surface from a JSON manifest without ever executing it, which is ideal for scanning untrusted servers in code review. A manifest is just the tools/resources/prompts the server would advertise:
{
"transport": "http",
"url": "https://mcp.example.com/mcp",
"authProvided": false,
"server": { "name": "billing-mcp", "version": "1.0.0" },
"tools": [
{
"name": "charge_card",
"description": "Charge a saved payment method.",
"inputSchema": {
"type": "object",
"properties": { "amount": { "type": "number" } },
"required": ["amount"],
"additionalProperties": false
}
}
]
}A bare JSON array of tools is also accepted.
Programmatic API
import { connectStdio, runAudit, DEFAULT_CONFIG, renderJson } from "mcp-audit";
const target = await connectStdio({ command: "node", args: ["server.js"] });
const result = runAudit(target, DEFAULT_CONFIG);
console.log(renderJson(result));Development
npm install
npm run build # compile TypeScript to dist/
npm test # run the vitest suite (includes a live mock server)
npm run typecheck # type-only checkThe repo ships a real mock MCP server (fixtures/mock-server.mjs) so the test
suite is hermetic and reproducible.
Contributing
Contributions are welcome. A good new rule is: independently coded, has a stable
MCPxxx id, a clear severity and remediation, and a test that fires it against a
fixture (plus one that proves it stays quiet on a clean surface). Add the rule
module under src/rules/, register it in src/rules/index.ts, and cover it in
test/rules.test.ts.
License
MIT © royalpinto007
