Active, runtime adversarial testing for MCP servers: point it at your own server and find out what breaks.
Project description
MCP-Strike
Active, runtime adversarial testing for MCP servers: point it at your own server and find out what breaks.
Most existing MCP scanners read tool descriptions and configs statically. MCP-Strike connects to a live Model Context Protocol server, runs a battery of real attacks against the actual tool surface, observes what comes back, and (optionally) uses an LLM-as-judge plus an LLM-driven adaptive agent to score each finding.
The wedge: a usable, active, single-developer tool for the dev who builds their own MCP server and just wants a "point it at my server and tell me what breaks" answer. Enterprise vendors aim at security teams; academic benchmarks produce papers; this is for you.
Quickstart
pip install mcp-strike # or: uvx mcp-strike demo
mcp-strike demo
That's it. demo spins up a bundled deliberately-vulnerable MCP server, scans it, and prints a colorized report. No keys required (the LLM features auto-enable only if you set OPENAI_API_KEY).
What you get
A typical mcp-strike demo --no-judge --no-agent run:
╭────────────────────── Responsible use ──────────────────────╮
│ MCP-Strike is a defensive tool for testing MCP servers you │
│ own. Running it against systems you don't control may be │
│ illegal in your jurisdiction. See the "Responsible use" │
│ section of this README for the full note. │
╰─────────────────────────────────────────────────────────────╯
Scan summary: 29 check(s) ran. 5 SUCCESS, 2 UNCERTAIN, 22 FAILURE
┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳──────────────────────────────────────┓
┃ Verdict ┃ Stage ┃ Attack ┃ Tool ┃ Rationale ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇──────────────────────────────────────┩
│ SUCCESS │ metadata │ description_prompt_injection │ get_weather │ Description contains 2 known │
│ │ │ │ │ prompt-injection markers. │
│ SUCCESS │ metadata │ overreaching_parameters │ submit_feedback │ Tool requests 'user_password', │
│ │ │ │ │ 'api_key'. │
│ SUCCESS │ parameters │ path_traversal_probe │ read_file │ Tool returned /etc/passwd content │
│ │ │ │ │ for traversal payload. │
│ SUCCESS │ response │ data_exfiltration_probe │ fetch_status │ Response matched 3 exfiltration │
│ │ │ │ │ patterns. │
│ SUCCESS │ response │ response_injection_probe │ get_news │ Response contains 2 injection │
│ │ │ │ │ markers. │
│ UNCERTAIN │ response │ data_exfiltration_probe │ read_file │ Tool errored on benign input; │
│ │ │ │ │ cannot probe normal behavior. │
│ UNCERTAIN │ response │ response_injection_probe │ read_file │ Tool errored on benign input; │
│ │ │ │ │ cannot probe normal behavior. │
└───────────┴────────────┴──────────────────────────────┴─────────────────┴──────────────────────────────────────┘
FAILURE rows are hidden by default for readability; pass --show-all to see the clean checks too.
The sample above is from Linux/macOS. On Windows the demo's
read_filetool finds no host/etc/passwdto read, sopath_traversal_probereturnsUNCERTAINinstead ofSUCCESS— you'll see 4 SUCCESS / 3 UNCERTAIN. The other findings are platform-independent.
How it works
A scan runs in three layers, cheapest first:
- Deterministic static attacks scan the tool surface for known bad patterns. Zero cost, fast.
- LLM judge (optional) reviews each finding and confirms or overrides. Cheap (gpt-4o-mini by default), capped per run.
- Adaptive LLM agent (optional) probes each tool with multi-round reasoning, finding things the static patterns can't.
Five static attacks ship in v0.1, organized by the MCP pipeline stage they target:
| Attack | Stage | Active? | Catches |
|---|---|---|---|
description_prompt_injection |
metadata | passive | Tool descriptions carrying injection markers aimed at the calling LLM |
overreaching_parameters |
metadata | passive | Tools whose schema asks for password, api_key, ssn, etc. |
path_traversal_probe |
parameters | active | Tools that handle string parameters as filesystem paths (sends ../../etc/passwd) |
response_injection_probe |
response | active | Tools whose response contains injection content aimed at the calling LLM |
data_exfiltration_probe |
response | active | Tools whose response coaxes the LLM to POST/send data to a sink |
The adaptive agent is the differentiator: given the tool surface, an LLM proposes payloads, observes responses, and iterates up to 3 rounds per tool. It catches social-engineering and unusual injection patterns the static heuristics can't enumerate ahead of time.
Configure the LLM features (optional)
The judge and agent are off by default; they auto-enable as soon as OPENAI_API_KEY is available, either as a shell export or in a .env file at the repo root.
# Option A: shell export
export OPENAI_API_KEY=sk-...
# Option B: .env file (gitignored; auto-loaded by the CLI)
echo 'OPENAI_API_KEY=sk-...' > .env
mcp-strike demo # judge + agent both run
A precedence note: a shell-exported variable always wins over .env. If you want .env to take precedence, unset the shell var first.
All CLI flags
Run mcp-strike --help, mcp-strike scan --help, or mcp-strike demo --help for the full per-command listing. The most commonly used flags:
Global
| Flag | Effect |
|---|---|
--version / -V |
Print the installed version and exit. |
Scan shape (apply to scan and demo)
| Flag | Effect |
|---|---|
--only NAME |
Run only the named attack (repeatable). Recognizes the special name adaptive_agent. |
--call-timeout SECONDS |
Per-MCP-call timeout (default 30). A buggy or malicious server can't block the scan beyond this. |
--show-all |
Include FAILURE rows in the terminal report (hidden by default). |
--notice / --no-notice |
Print the responsible-use notice at startup (default on, terminal mode only). |
--json |
Emit JSON to stdout instead of a terminal table. Suppresses the notice. |
--output-file PATH |
Write the JSON report to a file. Implies --json. |
LLM judge
| Flag | Effect |
|---|---|
--judge / --no-judge |
Force the judge on/off. Default: auto, enabled iff OPENAI_API_KEY is set. |
--judge-model NAME |
Override the default judge model (gpt-4o-mini). |
--max-llm-calls N |
Cap on real judge LLM calls per run (default 20). |
Adaptive agent
| Flag | Effect |
|---|---|
--agent / --no-agent |
Force the agent on/off. Default: auto, enabled iff OPENAI_API_KEY is set. |
--agent-model NAME |
Override the default agent model (gpt-4o-mini). |
--agent-max-rounds N |
Per-tool round cap for the agent (default 3). |
--max-agent-calls N |
Cap on real agent LLM calls per run (default 50). |
Scan your own MCP server
# Stdio transport (the only one v0.1 supports; HTTP coming in a later release)
mcp-strike scan --command python --arg -m --arg your_mcp_server
mcp-strike scan --command /path/to/your-server --arg --port --arg 8080
--arg is repeatable; pass each argument separately so quoting works.
JSON output for CI
mcp-strike demo --json --no-notice --output-file scan.json
Stable schema, suitable for CI pipelines. Top-level shape:
{
"summary": {
"total": 29,
"success": 5,
"uncertain": 2,
"failure": 22,
"llm_calls_used": 7, // null when judge didn't run
"llm_calls_cap": 20,
"agent_calls_used": null, // null when agent didn't run
"agent_calls_cap": null
},
"results": [
{
"attack_name": "description_prompt_injection",
"stage": "metadata",
"target_tool": "get_weather",
"verdict": "success",
"rationale": "...",
"evidence": { "...": "..." },
"judge": {
"verdict": "success",
"rationale": "...",
"model": "gpt-4o-mini",
"ran": true
}
}
]
}
Exit code is 0 even when findings exist; CI consumers decide what verdict counts as a build failure.
Architecture at a glance
| Module | Role |
|---|---|
mcp_strike.client |
Stdio transport wrapper over the official mcp SDK |
mcp_strike.target |
Normalized Target adapter (what attacks consume) |
mcp_strike.attacks |
BaseAttack ABC + decorator registry + the 5 concrete attacks |
mcp_strike.judge |
LLM-as-judge layer (NullJudge no-op + OpenAIJudge) |
mcp_strike.agent |
Adaptive LLM-driven attacker |
mcp_strike.report |
Renderers: terminal (rich) + JSON |
mcp_strike.config |
pydantic config models + .env loader |
mcp_strike.cli |
typer entry point with scan / list-attacks / demo |
mcp_strike.demo_server |
Bundled vulnerable target with 6 planted vulnerabilities |
See ROADMAP.md for what's shipped and what's next.
Development
Requires Python 3.10+ and uv.
git clone https://github.com/LeoMartinezTAMUK/mcp-strike.git
cd mcp-strike
uv sync --extra dev
uv run ruff check . # lint
uv run mypy src/mcp_strike # type-check
uv run pytest -v --cov # tests + coverage report
uv run mcp-strike demo # eyeball the scan output
See CONTRIBUTING.md for project conventions, how to add a new attack, and how to plug in a different LLM provider.
Status & roadmap
Currently 0.1.0 / alpha. CI gates on ruff + mypy + pytest across Python 3.10 through 3.13; production source has ~96% line coverage. See ROADMAP.md for what's next.
Responsible use
MCP-Strike is a defensive tool for testing MCP servers you own or are explicitly authorized to test. It is not a weapon for attacking third-party infrastructure. Running it against systems you don't control may be illegal in your jurisdiction and is not a supported use case. The CLI prints a responsible-use notice at startup; you can suppress it for scripting with --no-notice once you've internalized it.
If you find a vulnerability in someone else's server while using this tool, follow responsible disclosure: contact the operator of that server privately before publishing details.
To report a security issue in MCP-Strike itself (not in a third-party server), see SECURITY.md.
Contact
Built and maintained by Leo Martinez III.
- Email: mtz3.leo@gmail.com
- LinkedIn: linkedin.com/in/leo-martinez-iii
- GitHub: @LeoMartinezTAMUK
For security issues in MCP-Strike itself, see SECURITY.md. For bug reports and feature requests, the issue tracker is the fastest path. For anything else, the email and LinkedIn above both work.
License
MIT. See LICENSE.
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 mcp_strike-0.1.1.tar.gz.
File metadata
- Download URL: mcp_strike-0.1.1.tar.gz
- Upload date:
- Size: 165.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67af175b003e1859f7b264e6a1de828dd9aa724f37b0d65545d13783c53923de
|
|
| MD5 |
f3100165c58d4e77a81fa956d982637f
|
|
| BLAKE2b-256 |
c3bc01179e8c44e7db36e9276d3ba8fb64bf5351bb5f6ae92a8bc4ce390241bc
|
File details
Details for the file mcp_strike-0.1.1-py3-none-any.whl.
File metadata
- Download URL: mcp_strike-0.1.1-py3-none-any.whl
- Upload date:
- Size: 58.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5b13b7ea52720940411393744aacf7ea99698dcb2615b1a388eeaac4b51f346
|
|
| MD5 |
3aa2414e5a5683287687baa6e2923617
|
|
| BLAKE2b-256 |
1c397c4fbf1a9d17eed2f097c1de1f3c6503143953d65543c96a8857c016918c
|