Skip to main content

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

Project description

reizan-mcpfuzz

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:

  • 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.0.tar.gz (23.4 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.0-py3-none-any.whl (23.7 kB view details)

Uploaded Python 3

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

Hashes for reizan_mcpfuzz-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f1e8d9c3c01cd25650cc1dd63758ed52f4015531d19e7f7a77f6706a47726b32
MD5 802d1199984ca7480ae5728916ac5b31
BLAKE2b-256 0209d6cf12dedf58d04b5db81a293f1a95095a1d4043342718ca03fec747fcba

See more details on using hashes here.

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

Hashes for reizan_mcpfuzz-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c1dfeee9f154ae02c57a3c849d669f3e08323af3f71f65bc5095d3c704d11aa7
MD5 03e4a8f78832e2719c9deaf6fd43b06f
BLAKE2b-256 692011a484f5dabd449bb984d3216f124550b94bdee54f44e73742c1b344de04

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