The enforcement-fixture commands are a program-owned synthetic compatibility
harness, not a production enforcement gateway. They are exact-pinned to
agent-governance-toolkit-core==4.1.0, reject unsupported translation, and may
write only to explicitly named fixture-state directories whose basename starts
with mcpaudit-enforcement-fixture- and whose ownership marker is valid.
Managed state and temporary paths may not be symlinks. Apply and rollback use a
persistent program-owned fixture lock; processes that ignore that lock remain
outside the harness guarantee. Do not point these commands at normal MCP
configuration or treat their proof as authority for a real server. See
docs/EVIDENCE-ENFORCEMENT-THREAT-MODEL.md.
The agent-ui commands accept only program-owned synthetic MCP Apps/OpenAI
metadata fixtures and A2UI v0.9 JSONL fixtures. They do not read widget HTML or
JavaScript, contact an MCP server, call tools, fetch resources or catalogs,
authenticate, install plugins, or process real user data. Symlink inputs are
rejected; fixture bytes are read from one identity-checked descriptor with a
post-read size bound; report outputs cannot alias each other or the input; and
no-force output commit is descriptor-bound and atomic create-if-absent.
Generic JSON nesting and A2UI traversal are bounded; duplicate JSON keys and
same-update component IDs are invalid; missing or unknown approval provenance,
out-of-domain evidence states, contradictory standard/OpenAI metadata, malformed
HTTPS authorities, and unsupported protocol or host constructs produce
UNKNOWN. Supported contradictions already established in an A2UI lifecycle
cannot be erased by replacement or surface deletion.
A passing fixture report proves only internal consistency for the supported
static contract. It does not prove host sandboxing, CSP enforcement, transport
integrity, authorization, renderer behavior, interoperability, or real-workflow
safety. See docs/AGENT-UI-CONTRACT-AUDITOR.md.
MCPAudit is itself a security tool. This document covers two distinct concerns:
- How to report vulnerabilities in MCPAudit (the auditor tool itself)
- MCPAudit's threat model — what it does and does not protect against
Please do not open a public GitHub issue for security vulnerabilities.
Use GitHub Security Advisories to report privately. This allows coordinated disclosure before a fix is published.
Include in your report:
- A description of the vulnerability and its impact
- Steps to reproduce (config snippet, command invoked, observed vs. expected behavior)
- The MCPAudit version (
mcp-audit --version) and Python version - Whether the vulnerability requires a malicious MCP config to trigger, or if it can be triggered by a benign config
We aim to acknowledge reports within 72 hours and provide an initial assessment within 7 days.
Understanding what MCPAudit does and does not do is important for evaluating its security properties.
- Reads local MCP server configuration files (e.g.,
claude_desktop_config.json, Cursor/VS Code equivalents) - During a standard
mcp-audit scan, connects to configured MCP servers to enumerate declared tools and schemas - For stdio servers, starts the configured server command with timeout guards; for HTTP/SSE servers, opens a client connection to the configured URL
- Scores permission risk and flags potential prompt injection patterns in tool, prompt, and resource text
- Detects schema drift across capability declarations with
mcp-audit pinandscan --pin-check - Supports
--skip-connectfor config-only review when you do not want MCPAudit to spawn or connect to audited servers
- MCPAudit does not call audited MCP tools; it only enumerates tool metadata
- MCPAudit does not exfiltrate config data
- MCPAudit does not modify MCP configs during scans
mcp-audit serve --installis an explicit setup command that writes an MCPAudit server entry to detected Claude config files- MCPAudit does not transmit data to a third-party API unless optional LLM analysis is explicitly requested
- The
watchmode monitors local config file changes usingwatchfiles; each re-scan follows the selected scan mode, so usewatch --skip-connectfor config-only watching
MCPAudit reads config files from your local filesystem. It trusts that the config files it reads are your own. A scenario where an attacker can write to your MCP config directory is outside MCPAudit's threat model — at that point, the attacker already has local file write access.
MCPAudit parses and displays content from MCP server configs and MCP server metadata, including tool descriptions that may be attacker-controlled. If a malicious MCP server is installed, its tool descriptions could contain content designed to manipulate AI assistants that consume MCPAudit's output. MCPAudit does not sanitize or escape tool description content before rendering it. Users should treat MCPAudit's output as untrusted data when piping it to AI systems.
When the optional anthropic dependency is installed and --llm-analysis is used, MCPAudit sends selected tool names, descriptions, and parameter names to the Anthropic API for permission classification. In this mode, content from audited MCP server configs is transmitted over the network to a third-party service. Do not use --llm-analysis if your MCP configs contain sensitive information (API keys in args, internal hostnames, etc.).
proof-before-action compares a declared boundary with observations from a
disposable Docker execution and emits a versioned offline evidence capsule.
Verification binds the staged subject, producer revision, deterministic report
projection, and an independently supplied root hash.
Its result is evidence about the declared and observed surfaces, not a general sandbox or safety guarantee. Unknown, stale, masked, unmatched, incomplete, unobservable, dirty, or authority-unverified evidence remains non-authoritative and cannot become a passing safety claim.
The observation-v2 observer emits stable machine-readable unknown receipts for transient
create-delete/write-restore attempts, no-delta SQLite queries or transactions,
requested network destinations or hostnames, and filesystem or abstract
Unix-domain sockets. It does not prove those surfaces observed, blocked, or
safe. Missing or unresolved attempt receipts are non-passing, and v2 does not
accept a producer's unsupported observed/blocked claim as proof.
Historical observation/capsule/index v1 bundles remain verifiable under their original comparison and offline-report semantics. They are not reinterpreted as v2 attempt evidence, and new observations cannot emit the v1 contract.
The observer also does not prove host-kernel isolation or safety outside the
declared surfaces. A valid capsule does not prove that an operator independently
obtained the root hash; callers must supply and protect that trust input
themselves. See
docs/SECURITY-REVIEW.md for the detailed boundary.
| Version | Supported |
|---|---|
| 2.x | Yes |
| 1.x | Security fixes only |
| 0.x and earlier | No |
| Pre-release builds (alpha/beta/RC) | No |
We appreciate responsible disclosure. Reporters of valid vulnerabilities will be credited in the release notes unless they prefer to remain anonymous.