Skip to main content

Deterministic fail-closed auditor for MCP tool-description poisoning.

Project description

reizan-mcpfuzz

reizan-mcpfuzz

By David Mellafe Zuvicdmzs.dev

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:

  • id
  • category
  • pattern_type: regex, hidden_char, or imperative
  • signature
  • severity: critical, high, medium, low, or info

critical and high findings produce POISONED; lower severities produce SUSPICIOUS.

The v0 seed rules cover:

  • ignore previous / disregard / override instruction patterns
  • you must, before responding, and fake system: 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reizan_mcpfuzz-0.1.1.tar.gz (23.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reizan_mcpfuzz-0.1.1-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file reizan_mcpfuzz-0.1.1.tar.gz.

File metadata

  • Download URL: reizan_mcpfuzz-0.1.1.tar.gz
  • Upload date:
  • Size: 23.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for reizan_mcpfuzz-0.1.1.tar.gz
Algorithm Hash digest
SHA256 ecf8ecb9551b20a30fed9dfba0ef3bc512d5d0378b9999384d9e0235872d7a38
MD5 ae1ddf1886a3033deed5c83f67b76774
BLAKE2b-256 6d2289d5bb385ef4b803d28b6b3cf4a10c49d07cabe182af6bf18ff842b2417b

See more details on using hashes here.

File details

Details for the file reizan_mcpfuzz-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: reizan_mcpfuzz-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 23.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for reizan_mcpfuzz-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3bd547c1574de71b0510d0ba4058ec0f71d6960a4ed31a9e051b76df333893c8
MD5 3bd05638d81b7a972000f972b30ee8b6
BLAKE2b-256 59af4c4f751ccd7955973b27fc22ea8b98d84f573282cb2d54c4d1b2e48dcba3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page