MCP Server Fuzzer
Black-box security assessment for live Model Context Protocol servers. It drives an authorized target over its real transport, sends realistic and malformed input, classifies the responses, and writes findings and reproduction data to disk.
Documentation | Assessment workflow | CLI reference | Releases
Authorization
Use this tool only against MCP servers you own or are explicitly authorized to
test. It sends attack-pattern input, can start local processes, and with
--auth-audit-intrusive will register OAuth clients on the target's
authorization server. The built-in safety controls reduce accidental impact;
they are not a substitute for a container, a VM, or an engagement-specific
network boundary.
What it does
The fuzzer connects over stdio, HTTP, SSE, or Streamable HTTP and answers a fixed set of assessment questions:
- What tools, resources, prompts, and protocol methods does the server expose?
- Does it reject malformed and out-of-contract input, or accept it?
- Do tool descriptions or schemas carry poisoning markers, hidden instructions, duplicate definitions, or dangerous capability combinations?
- Does an advertised OAuth boundary publish unsafe metadata or serve tools without the expected authentication?
- What does a local stdio server execute, read, write, or connect to while the test runs?
Install
Requires Python 3.10 or newer.
python -m pip install mcp-fuzzer
mcp-fuzzer --version
Optional runtime observation of local stdio processes:
python -m pip install "mcp-fuzzer[mcpfz-probe]"
Docker image (princekrroshan01/mcp-fuzzer):
docker pull princekrroshan01/mcp-fuzzer:latest
docker run --rm princekrroshan01/mcp-fuzzer:latest --help
Run an assessment
Against a remote HTTP or Streamable HTTP target:
mcp-fuzzer \
--mode tools \
--protocol streamablehttp \
--endpoint https://target.example.com/mcp \
--phase realistic \
--runs 10 \
--security-audit \
--seed 42 \
--output-dir reports/baseline
Against a local stdio target, confine file operations and deny non-local network access:
mcp-fuzzer \
--mode all \
--protocol stdio \
--endpoint "python my_server.py" \
--enable-safety-system \
--fs-root "$PWD/fuzz-sandbox" \
--no-network \
--runs 5 \
--seed 42 \
--output-dir reports/baseline
To try the tool without a target, use the bundled fixtures in
examples/.
Start with low run counts, bounded timeouts, dedicated credentials, and an isolated target. The assessment workflow describes what to record at each stage.
What it produces
Every session writes to --output-dir (default reports/):
| Artifact | Contents |
|---|---|
findings.json |
Every finding, with category, severity, kind, target, run, detail, and an evidence object |
run_summary.json |
Mode, status, tool discovery result, per-tool run counts and outcome buckets, finding totals by category |
sessions/<session-id>/<timestamp>_fuzzing_results.json |
Per-run request and response records for the session |
crashes/ |
One JSON repro per run that terminated the server process, containing the input and crash context. Written only when a run crashes the target |
Severities are critical, high, medium, low, and info. Fuzz
classifications include accepted_malformed, injection_reflection, crash,
hang, error_leakage, internal_error, non_determinism, memory_growth,
oversized_response, and performance_outlier.
A finding from --security-audit carries a stable check ID and maps to
published sources:
{
"category": "tool_poisoning",
"severity": "high",
"kind": "tool",
"target": "read_notes",
"detail": "Tool name or description contains injection/poisoning markers (hidden instructions or secret-path references).",
"evidence": {
"check_id": "TP1",
"paper_arxiv_id": "2503.23278",
"owasp_mcp_top_10": "MCP03:2025",
"markers": ["ignore\\s+(all\\s+)?previous\\s+instructions?", "id_rsa"],
"tool_definition_hash": "18cd91e6…"
}
}
--export-csv, --export-xml, --export-html, and --export-markdown write
additional per-run tables into the same directory. These exports carry a
metadata block (session ID, mode, protocol, endpoint, timestamps) and per-run
pass/fail rows. They do not carry severities or findings, and only the CSV
export includes the arguments that produced each run. Treat findings.json as
the authoritative output.
Exit codes: 0 success, 1 validation or execution failure, 2 no tools
discovered with --fail-if-no-tools, 130 interrupted.
What it does not do
- It does not read your source. This is a black-box client, not a SAST tool.
- It does not prove exploitability.
accepted_malformedmeans the server returned a non-error response to schema-invalid or attack-pattern input. That is a contract observation and a lead, not a demonstrated vulnerability. - It does not record the seed in any output file.
--seedmakes payload generation reproducible, but you must record the value yourself alongside the report for a run to be replayable. - It does not record the endpoint or protocol in
run_summary.json. That metadata appears only in the CSV, XML, HTML, and Markdown exports. - It does not assign CVEs, CVSS scores, or remediation guidance, and it is not a certification.
- It does not test authorization logic between users or tenants. The auth checks cover metadata, the authorization endpoint, and unauthenticated tool exposure.
- It only reaches what the server advertises through
tools/list,resources/list, andprompts/list. Undiscoverable surface is not tested. - Schema-driven fuzzing via
--spec-schema-versionreads MCP schemas from theschemas/mcp-specsubmodule, which is not shipped in the PyPI package. From a released install, pointMCP_SPEC_SCHEMA_ROOTat a schema directory or work from a git checkout.
Audit surfaces
--security-audit runs tool and schema checks plus active output oracles
against the same run's results: poisoning markers, hidden or encoded
instructions, tool shadowing, dangerous capability combinations, cleartext
transport, and command, path, SQL, and prompt-injection oracles.
--auth-audit runs read-only OAuth checks against an HTTP or SSE endpoint:
metadata review, authorization-endpoint probes, and unauthenticated tool
exposure where authentication is advertised. Add --auth-audit-intrusive only
when your authorization explicitly covers dynamic client registration and
redirect handling.
--runtime-probe observes process, filesystem, network, credential, privilege,
and ptrace activity for a local stdio target. The mcpfz-probe sidecar is
optional, disabled by default, and fails open if it cannot observe the target.
Handling evidence
Reports embed generated arguments, server responses, paths, and runtime
observations. Values under credential-named keys (token, secret,
authorization, api_key, and similar) are redacted in console output and in
written artifacts, and credentials embedded in URLs are stripped. Redaction is
key-driven, so a secret echoed inside a response body under an unrelated key is
not caught. Review and redact artifacts before sharing them, and store them
under the same restrictions as the engagement's other evidence. See
understand run results.
Documentation
- Choose an assessment path
- Run the assessment workflow
- Focused audit recipes
- Interpret evidence and findings
- Configure repeatable assessments
- Contain local targets
- Collect evidence in CI
- CLI reference
References
The checks in this project complement, and do not replace, the MCP Security Best Practices, the MCP authorization specification, and the OWASP MCP Top 10.
License
MIT. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_fuzzer-0.6.0.tar.gz.
File metadata
- Download URL: mcp_fuzzer-0.6.0.tar.gz
- Upload date:
- Size: 373.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f4794a55c2879f2138af6a576a383dd203911fe26294903007899efd5822ce6
|
|
| MD5 |
75c9ae71ca83be8058a8fa60a2a592df
|
|
| BLAKE2b-256 |
8309a1b8be54e323dffb290548ff34892d9716d4d50dabab86b5c4e0bdea625a
|
File details
Details for the file mcp_fuzzer-0.6.0-py3-none-any.whl.
File metadata
- Download URL: mcp_fuzzer-0.6.0-py3-none-any.whl
- Upload date:
- Size: 463.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c55349ee321e4a3e3205e184af0192a3f8769a6e93cfdaa1ec9821d06979378
|
|
| MD5 |
d200afff3083cc62eb9b2f8fd5a6c679
|
|
| BLAKE2b-256 |
353d82a551e7e07d87f6b1ba3f925603958de52155029d572d4d279b3278a383
|