Deterministic fail-closed auditor for MCP tool-description poisoning.
Project description
reizan-mcpfuzz
reizan-mcpfuzz is a deterministic, fail-closed auditor for MCP server
tool-poisoning. Run it before trusting or installing a third-party MCP server.
It enumerates the server's tools, scans tool descriptions and input schemas for
instruction-injection and obfuscation patterns, and emits per-tool verdicts:
CLEAN, SUSPICIOUS, or POISONED.
There is no LLM judge in the static verdict.
Ethical Line
This is a self-run / authorized component evaluation tool. Use it on an MCP server command you are about to trust, install, or operate, or on a static tools manifest you already possess. Do not use it to scan someone else's deployed infrastructure or to probe services without authorization.
reizan-mcpfuzz speaks stdio MCP only for local child processes and reads local
JSON manifests. It is not a remote internet scanner.
Why
MCP clients list server tools before an agent decides what it can call. That
means a malicious server can hide prompt-injection text in description or
inputSchema, and the agent may read that text as trusted operational context.
Conventional dependency and supply-chain tools usually inspect packages,
versions, and vulnerabilities; they do not inspect the instruction surface that
the agent consumes at tool-list time.
Quickstart
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
make demo
make test
Scan a static manifest:
reizan-mcpfuzz scan --manifest examples/poisoned-tools.json
reizan-mcpfuzz scan --manifest examples/poisoned-tools.json --json
Scan a local stdio MCP server:
reizan-mcpfuzz scan -- python -m your_mcp_server
Use --fail-on poisoned when you only want CI to fail on POISONED. The
default is stricter: any SUSPICIOUS or POISONED result exits non-zero.
Rules
Rules are portable YAML under .mcpfuzz/rules.yaml:
rules:
- id: inj.ignore_previous
category: instruction_injection
pattern_type: regex
signature: '\bignore\s+previous\s+instructions\b'
severity: high
Required fields are:
idcategorypattern_type:regex,hidden_char, orimperativesignatureseverity:critical,high,medium,low, orinfo
critical and high findings produce POISONED; lower severities produce
SUSPICIOUS.
The v0 seed rules cover:
ignore previous/disregard/overrideinstruction patternsyou must,before responding, and fakesystem:markers- exfiltration language and imperative
fetch https://...patterns - base64-like blobs
- zero-width characters, ANSI escapes, and Unicode hyphen/lookalike tricks
- imperative language paired with tool-call syntax or URLs
- destructive or over-broad schema signals such as shell commands and
additionalProperties: true - sensitive targets such as
secrets/, credentials, API keys, and SSH keys
MCP Behavior
The stdio scanner starts the target command as a local child process, sends
JSON-RPC initialize, sends the notifications/initialized notification, then
calls tools/list. Paginated nextCursor results are followed.
Parse, load, or connection errors are fail-closed: the scan result becomes
SUSPICIOUS, never silently CLEAN.
Relevant MCP spec references:
Optional Dynamic Mode
Static scanning is the primary verdict. Dynamic mode is opt-in:
reizan-mcpfuzz scan --manifest examples/poisoned-tools.json --dynamic
By default, dynamic mode uses mock://susceptible, so it is key-free and
deterministic. To call a real OpenAI-compatible endpoint:
export MCPFUZZ_OPENAI_API_KEY=...
reizan-mcpfuzz scan --manifest tools.json \
--dynamic \
--dynamic-base-url https://api.example.test/v1 \
--dynamic-model your-model
The dynamic harness feeds a poisoned MCP tool-result into a small
OpenAI-compatible agent loop. A SHA-256 canary oracle returns CONFIRMED if the
canary appears in final output or if the model requests an out-of-scope fixture
fetch. The fixture tool is guarded by a ScopeGate-style allowlist and never
performs real URL fetches.
OWASP Agentic Top 10 Mapping
The findings map most directly to OWASP Agentic AI risks around prompt
injection, tool misuse, excessive agency, sensitive information disclosure, and
supply-chain compromise. reizan-mcpfuzz sits before trust/install time: it
checks whether the tool metadata itself is a hostile instruction surface before
an agent sees it.
OWASP reference: OWASP Top 10 for Agentic Applications
GitHub Actions
.github/workflows/mcpfuzz.yml is a skeleton workflow. It installs this package
and runs:
reizan-mcpfuzz scan --manifest "$MANIFEST" --json --fail-on poisoned
That policy fails CI only when any tool is POISONED.
License
MIT.
Project details
Release history Release notifications | RSS feed
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 reizan_mcpfuzz-0.1.0.tar.gz.
File metadata
- Download URL: reizan_mcpfuzz-0.1.0.tar.gz
- Upload date:
- Size: 23.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1e8d9c3c01cd25650cc1dd63758ed52f4015531d19e7f7a77f6706a47726b32
|
|
| MD5 |
802d1199984ca7480ae5728916ac5b31
|
|
| BLAKE2b-256 |
0209d6cf12dedf58d04b5db81a293f1a95095a1d4043342718ca03fec747fcba
|
File details
Details for the file reizan_mcpfuzz-0.1.0-py3-none-any.whl.
File metadata
- Download URL: reizan_mcpfuzz-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1dfeee9f154ae02c57a3c849d669f3e08323af3f71f65bc5095d3c704d11aa7
|
|
| MD5 |
03e4a8f78832e2719c9deaf6fd43b06f
|
|
| BLAKE2b-256 |
692011a484f5dabd449bb984d3216f124550b94bdee54f44e73742c1b344de04
|