MCP Server Fuzzer

CLI fuzzing for MCP servers

Tool fuzzing • Protocol fuzzing • HTTP/SSE/stdio/StreamableHTTP • Safety controls • Rich reporting

CI codecov PyPI - Version PyPI Downloads Supports MCP 2025-11-25 Docker Pulls License: MIT Python 3.10+

Docs SiteGetting StartedCLI Reference

What It Does

MCP Server Fuzzer tests MCP servers by fuzzing:

  • tool arguments
  • protocol request types
  • resource and prompt request flows
  • multiple transports: http, sse, stdio, and streamablehttp

It includes optional safety controls such as filesystem sandboxing, PATH-based command blocking, and network restrictions for safer local testing.

Findings It Reports

Beyond protocol/transport errors, runs are classified into categorized findings (written to <output-dir>/findings.json, with per-crash repros under <output-dir>/crashes/, a compact CI summary at <output-dir>/run_summary.json, and summarized on stdout):

  • crash — server process terminated abnormally (segfault/abort/panic or non-zero exit); captures the signal and a stderr tail
  • auth_bypass — a protected tool answered an unauthenticated call
  • injection_reflection — a dangerous input token was echoed back verbatim
  • oversized_response — response exceeded the read cap (resource exhaustion)
  • hang — request timed out (deadlock / infinite loop / ReDoS)
  • internal_error — JSON-RPC -32603 (unhandled server-side exception)
  • error_leakage — stack trace / panic leaked in output
  • memory_growth — server RSS grew multi-fold across runs (stdio targets)
  • non_determinism — identical input produced differing outcomes
  • accepted_malformed — server returned a non-error response to an attack-pattern or schema-invalid fuzz input; repeated identical evidence is collapsed into one finding with run counts and a response sample
  • performance_outlier — response time far above the per-target median

findings.json is always written for completed sessions, even when no findings are present ({"findings": [], "count": 0}), so CI jobs can assert that report generation succeeded separately from whether issues were discovered. run_summary.json records whether the run completed or was blocked, plus tool/protocol counts and run totals for simple CI assertions.

Tip: for compiled-language servers (Go/C/C++/Rust), run the target under a sanitizer or race build so memory bugs surface as observable crashes.

Install

Requires Python 3.10+.

# PyPI
pip install mcp-fuzzer

# From source
git clone --recursive https://github.com/Agent-Hellboy/mcp-server-fuzzer.git
cd mcp-server-fuzzer
pip install -e .

Docker is also supported:

docker build -t mcp-fuzzer:latest .
docker run --rm mcp-fuzzer:latest --help

Quick Start

1. Run the bundled HTTP example server

pip install "mcp[cli]" uvicorn
python3 examples/test_server.py

That server uses the official Python MCP SDK, listens on http://localhost:8000/mcp/, and exposes:

  • test_tool
  • echo_tool
  • secure_tool requiring Authorization: Bearer secret123

2. Fuzz tools

mcp-fuzzer --mode tools --protocol http --endpoint http://localhost:8000/mcp/ --runs 10

3. Fuzz protocol requests

mcp-fuzzer --mode protocol --protocol-type InitializeRequest \
  --protocol http --endpoint http://localhost:8000/mcp/ --runs-per-type 5

4. Run tools and protocol together

mcp-fuzzer --mode all --phase both --protocol http --endpoint http://localhost:8000/mcp/

Common Commands

# Enable command blocking + safety reporting
mcp-fuzzer --mode tools --protocol http --endpoint http://localhost:8000/mcp/ \
  --enable-safety-system --safety-report

# Export results
mcp-fuzzer --mode tools --protocol http --endpoint http://localhost:8000/mcp/ \
  --export-csv results.csv --export-html results.html

# Use auth config for the bundled secure_tool example
mcp-fuzzer --mode tools --protocol http --endpoint http://localhost:8000/mcp/ \
  --auth-config examples/auth_config.json

# Load settings from YAML
mcp-fuzzer --config config.yaml

Example Servers

This repository bundles:

For other stdio usage, point the fuzzer at your own server:

mcp-fuzzer --mode tools --protocol stdio --endpoint "python my_server.py" \
  --enable-safety-system --fs-root /tmp/mcp-safe

More runnable example flows are documented in examples/README.md.

Runtime monitoring (optional)

Pair the fuzzer with mcpfz-probe, a runtime sidecar that watches what a tool call actually does at the kernel level (process exec, network connect/send, file open/delete, chmod, ptrace) and reports anything suspicious as findings attributed to the exact tool call.

Fetch the released Linux binary from mcpfz-probe and point the fuzzer at it:

# Latest eBPF-enabled Linux build (built and published by mcpfz-probe CI)
curl -L -o mcpfz-probe \
  https://github.com/Agent-Hellboy/mcpfz-probe/releases/latest/download/mcpfz-probe-x86_64-linux-ebpf
chmod +x mcpfz-probe

pip install "mcp-fuzzer[mcpfz-probe]"   # pulls in the Python monitor package

export MCP_FUZZER_RUNTIME_PROBE=1
export MCPFZ_PROBE_BIN="$PWD/mcpfz-probe"
export MCPFZ_PROBE_BACKEND=ebpf          # Linux + root/CAP_BPF; use "fake" elsewhere
sudo -E mcp-fuzzer --mode tools --protocol stdio \
  --endpoint "python my_server.py" --runs 3 --max-concurrency 1

Runtime findings (runtime.exec, runtime.net_connect, runtime.sensitive_read, runtime.fs_write, runtime.fs_delete, runtime.fs_chmod, runtime.ptrace) are merged into the normal report. The hooks are no-ops unless MCP_FUZZER_RUNTIME_PROBE is set, so default behavior is unchanged. See the mcpfz-probe README for the backend/build details.

Documentation

Keep the README for the basics. Use the docs for everything else:

License

MIT. See LICENSE.