Run a defensible MCP audit¶
This workflow is for an authorized security assessment where another analyst must be able to understand what was tested, what happened, and what remains uncertain.
flowchart LR
S[Scope and authorization] --> R[Record target and controls]
R --> B[Bounded realistic baseline]
B --> M[Map tools, schemas, auth, and transport]
M --> X[Run focused probes]
X --> T[Triage evidence and reproduce]
T --> H[Report, retain, and hand off] 1. Create the engagement record¶
Before the first request, record:
| Field | Example |
|---|---|
| Authorization | Ticket, assessment letter, or owner approval |
| Target identity | Server name, commit/image, environment, endpoint |
| Transport | https, streamablehttp, sse, or stdio |
| Authentication | Dedicated identity, scope, expiry, and auth source |
| Test window | Start/end time and timezone |
| Allowed effects | Read-only, test writes, registration, redirect probes, runtime observation |
| Data handling | Report location, retention, redaction, disclosure owner |
| Reproduction | CLI version, schema version, seed, config hash, command line |
Do not use production credentials or unapproved intrusive probes. The official MCP Security Best Practices and authorization specification are useful review context, but the engagement's authorization and target behavior determine what you may run.
2. Establish a baseline¶
Start with low volume and realistic values:
mcp-fuzzer \
--mode tools \
--protocol streamablehttp \
--endpoint https://target.example/mcp \
--phase realistic \
--runs 10 \
--seed 42 \
--output-dir reports/01-baseline
For stdio, add --enable-safety-system, --fs-root, and usually --no-network. For CI or target inventories, add --fail-if-no-tools so an unreachable or protected endpoint cannot look like an empty clean result.
The baseline should answer:
- Was the endpoint reachable and correctly negotiated?
- What tools, resources, prompts, and protocol version were exposed?
- Which credentials and tool mappings were actually used?
- What normal success, rejection, timeout, and error responses look like?
3. Build a test matrix¶
Do not run every flag by default. Select the smallest matrix that answers the research question:
| Surface | First pass | Expansion |
|---|---|---|
| Tool arguments/results | --mode tools --phase realistic | --phase aggressive, --tool NAME, larger bounded --runs |
| Protocol messages | --mode protocol --protocol-phase realistic | Aggressive phase, --protocol-type, --stateful |
| Resources/prompts | --mode resources or --mode prompts | Supply the target-specific URI/name/arguments |
| Tool and schema metadata | --security-audit | Review each check's evidence and reproduce manually |
| OAuth boundary | --auth-audit | --auth-audit-intrusive only with explicit authorization |
| Local process behavior | stdio safety controls | --runtime-probe with explicit exec/host allowlists |
| Version behavior | Default negotiated version | --spec-schema-version VERSION for a target-specific comparison |
Use separate output directories for baseline, aggressive, auth, and runtime runs. This preserves the comparison between normal behavior and probe-induced behavior.
4. Exercise input and protocol boundaries¶
Aggressive inputs are useful when the target is isolated and the authorization allows them:
mcp-fuzzer \
--mode tools \
--protocol https \
--endpoint https://target.example/mcp \
--phase aggressive \
--security-audit \
--runs 20 \
--seed 42 \
--output-dir reports/02-tools-aggressive
Interpret outcomes carefully:
- A well-formed protocol error or schema rejection is normally validation evidence, not a vulnerability.
- Accepted malformed input, unexpected side effects, sensitive output, transport anomalies, crashes, and reproducible state changes deserve review.
- A timeout or server error is a symptom to reproduce, not automatically a security finding.
5. Run security checks with clear boundaries¶
--security-audit combines read-only inspection of advertised tool/schema content with selected evidence-backed output oracles. It can report signals for poisoning, hidden instructions, ANSI/control content, duplicate or drifting definitions, typosquatted names, dangerous capability combinations, insecure remote transport, and command/path/SQL/output-injection evidence from the same fuzz run.
Add --security-audit-intrusive when an authorized HTTP/SSE probe is in scope. It sends a foreign Origin and reports only a successful response as missing_origin_validation; same-host redirects are followed first so that endpoint canonicalization is not mistaken for acceptance, and a probe refused with HTTP 401 is recorded as origin_validation_inconclusive. Use a separate assessment phase if the target owner treats this extra request as intrusive.
--auth-audit reviews published OAuth metadata, authorization behavior, and unauthenticated tool exposure when authentication is advertised. The default path is read-only. Intrusive registration and redirect probes can change target state and must be separately authorized.
These checks map to security research vocabulary and source material, including the OWASP MCP Top 10. A mapping is not a proof of exploitability or a final severity rating.
6. Reproduce before reporting¶
For each candidate finding, preserve:
- The target revision and authorization scope.
- The exact command, configuration path, schema version, seed, and tool or protocol type.
- The finding ID/category, severity emitted by the tool, and evidence fields.
- The request/input and the smallest relevant server response or runtime observation.
- The expected secure behavior and the observed behavior.
- A second run showing whether the behavior is deterministic, stateful, or intermittent.
- Manual validation of exploitability, privilege, data sensitivity, and business impact.
Use Interpret evidence and findings for artifact handling. Keep reports restricted until credentials, tokens, private paths, server responses, and generated payloads have been reviewed and redacted.
7. Hand off or automate¶
An assessment handoff should include the scope record, test matrix, blocked or completed status, findings, reproductions, limitations, and recommended next tests. For repeatable checks, use evidence collection in CI with private artifacts and an explicit policy for which reviewed categories should fail a build.