ytkoka

bad-mcp

Community ytkoka
Updated

A small, extensible harness for building test MCP servers that reproduce common MCP attack patterns, so you can measure whether your detector catches them. Built on the official MCP Python SDK (mcp 2.x).

bad-mcp

A small, extensible harness for building test MCP servers that reproducecommon MCP attack patterns, so you can measure whether your detector catchesthem. Built on the official MCP Python SDK (mcp 2.x).

Serves over Streamable HTTP (point a detector at a URL — the realistic"unvetted remote server" case) or stdio, and can also emit an event logoffline with no client at all.

Authorized testing only. This is a detection fixture, not a workingattack. Run it in an isolated environment dedicated to evaluating your owntooling.

Safety model

Every scenario is deliberately inert:

  • Injection / poisoning strings are realistic (so your detector has realsignal to match), but the runtime behaviour is harmless: tools returncanned text.
  • The only side effect a "successful" attack produces is appending a line to alocal sink.log — a stand-in for exfiltration that touches nothing sensitiveand never leaves the machine.
  • All referenced secrets are canaries (~/.bad-mcp/canary_secret,CANARY-TOKEN-...). No real files are read; there is no network egress.

Attack scenarios

name pattern
benign_control clean tools — use it to measure false positives
rug_pull tool is benign at approval time, then its description/schema silently mutates and a tools/list_changed notification is sent
description_poisoning tool descriptions carry hidden instructions: imperative overrides, HTML-comment smuggling, invisible Unicode-Tag smuggling, fake "SYSTEM:" directives
tool_shadowing duplicate tool names in one manifest, plus a tool impersonating a trusted server's tool and redirecting it

Install

Requires Python 3.10+ (the mcp SDK's minimum).

python3.10 -m venv venv && . venv/bin/activate
pip install -e .

This installs the repo as the bad_mcp package (see pyproject.toml), whichis what makes python -m bad_mcp.cli below work.

Use

List scenarios:

python -m bad_mcp.cli list

Produce an event log offline (no MCP client needed — best for iterating on astatic detector):

python -m bad_mcp.cli simulate --out events.jsonl
python detector.py events.jsonl

Run a live remote server over Streamable HTTP and point your detector's"MCP server URL" input at it (this is the realistic setup — an unvetted remoteMCP server):

python -m bad_mcp.cli serve-http --port 8971 --out events.jsonl
# detector connects to:  http://127.0.0.1:8971/mcp

The MCP endpoint is mounted at /mcp by default (--path to change). Yourdetector connects like any MCP client and will observe the rug-pull changemid-session, the poisoned descriptions, and the shadowed/duplicate tools — whilethe harness records the ground truth to events.jsonl for scoring.

Remote access from another host is off by default (DNS-rebinding protection).To allow it in an isolated test network:

# bind publicly and permit a specific Host header
python -m bad_mcp.cli serve-http --host 0.0.0.0 --port 8971 \
  --allow-host lab-box:8971 --out events.jsonl

# or, for a throwaway isolated net, skip the Host check entirely
python -m bad_mcp.cli serve-http --host 0.0.0.0 \
  --insecure-disable-host-check --out events.jsonl

There is also a local stdio mode for driving with a stdio MCP client:

python -m bad_mcp.cli serve --scenarios rug_pull,tool_shadowing --out events.jsonl

Pick scenarios and the rug-pull trigger:

python -m bad_mcp.cli simulate \
  --scenarios benign_control,rug_pull --trigger after_call --out events.jsonl

Event log (JSONL)

One JSON object per line. Two categories share the stream:

  • evidence (ground_truth: false) — what a detector is allowed to see:
    • manifest_snapshot — the tool list served on each list_tools, withper-tool desc_sha / input_schema_sha (diff these across snapshots tocatch rug-pulls).
    • tool_call — a tool invocation with its arguments.
    • notification — e.g. tools/list_changed.
  • ground truth (ground_truth: true) — labels for scoring:rug_pull_activated, poisoned_tool_present, duplicate_tool_name,shadow_impersonation, exfil_simulated.

detector.py is a baseline detector: it raises findings from evidenceonly, then scores itself against the ground-truth labels. Replace it with yourown tool and reuse the scoring harness.

Note: tool_shadowing and rug_pull interact by design. Two tools sharing aname make a naive "hash changed since last snapshot" heuristic report aspurious rug-pull on that name — a useful reminder to key rug-pull detectionon stable tool identity, not name alone.

Extending

Add a subclass of Scenario in scenarios.py and register it inREGISTRY:

class MyScenario(Scenario):
    name = "my_attack"
    def tools(self, state):
        return [types.Tool(name="...", description="...", inputSchema={...})]
    async def on_call(self, name, arguments, ctx, state):
        return [types.TextContent(type="text", text="...")]

Emit a ground-truth label whenever your attack actually fires (seeself._label_once(...) and self.events.emit(..., ground_truth=True)) so itshows up in scoring.

MCP Server · Populars

MCP Server · New

    getArbor-dev

    Arbor

    Graph-native code intelligence that replaces embedding-based RAG with deterministic program understanding.

    Community getArbor-dev
    Q00

    ouroboros

    Agent OS: Stop prompting. Start specifying.

    Community Q00
    velane-sh

    velane

    Velane is agentic cloud for deploying your basic workflows, agents and sub-agents. 800+ OAuth integrations, sandboxed Bun and Python execution, and a full deployment pipeline managed via MCP

    Community velane-sh
    MCPBlender

    BlenderMCP

    🎨 Control Blender 3D with Claude AI — prompt-driven 3D modeling, materials & scene generation via MCP

    Community MCPBlender
    MCPBlender

    AbletonMCP

    🎵 Control Ableton Live with Claude AI — create tracks, arrange clips & compose music via MCP

    Community MCPBlender