ワンクリックで
opfor-mcp-run
Run red-team attacks and generate a report for an MCP server target.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Run red-team attacks and generate a report for an MCP server target.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Set up an agent or chatbot target for Opfor red-teaming.
Set up an MCP server target for Opfor red-teaming.
Run red-team attacks and generate a report for an agent target.
| name | opfor-mcp-run |
| description | Run red-team attacks and generate a report for an MCP server target. |
Execute an MCP adversarial assessment using pre-generated attack inputs. The /opfor-mcp-setup skill generates all attack variations beforehand; this skill connects to the MCP server, executes attacks, judges responses, and generates a report.
Prerequisites: A config folder created by /opfor-mcp-setup at .opfor/configs/<uuid>/
Note: This skill uses:
../opfor-setup/targets/<transport>.md — transport adapters (how to connect and communicate).opfor/configs/<uuid>/inputs/ — crafted by setupSource-scan evaluators (whitebox) — on by default: Some evaluators are whitebox (scan_mode: source_code in the catalog). They read the server's source instead of sending a runtime payload. The Static Source Pre-Scan (Step 4.5) runs by default — assume the assessment is on a codebase. Run every source-scan evaluator in the suite whenever a source root is resolvable, even if the loaded config did not explicitly list them, then Correlate (Step 6.5) after judging. The static pass is not a setting and is never asked about — the only consent prompt is for optionally running an external scanner (semgrep/codeql) in Step 4.5c. Skip Steps 4.5/6.5 only when no source root can be resolved (e.g. a remote url target with no repo path); then record those evaluators as dynamic-only and continue.
Scan .opfor/configs/ for UUID-named subdirectories (each contains config.md).
If no configs found:
/opfor-mcp-setup skill to create a configIf one config found:
If multiple configs found:
List all configs with creation date and server name:
Available Configs:
1. config-20260516-1430-ab12 — Database MCP (created 2026-05-16 14:30)
2. config-20260516-1100-cd34 — File Server (created 2026-05-16 11:00)
Which config would you like to run? (1-2 or enter config ID)
Parse the selected config.md and extract:
Display summary:
✓ Loaded Configuration: config-20260516-1430-ab12
Server: Database MCP (stdio)
Command: npx @myorg/db-server /tmp/test.db
Tools: 5 discovered
Resources: 3 discovered
Evaluators: 10 (from owasp-mcp-top10)
Attacks: 5 per evaluator = 50 total
Turn Mode: single-turn
Read **Transport:** from config (default: stdio).
Load from this skill's directory: ../opfor-setup/targets/<transport>.md
stdio → launch the server as a child process, communicate via stdin/stdouturl → connect via SSE or HTTP POSTKeep adapter instructions in memory for Step 4.
Read all .md files from .opfor/configs/<uuid>/inputs/:
For each file:
# Generated Attacks section (one per attack)# Evaluation Criteria section (PASS/FAIL criteria)Build execution plan:
Attack Plan:
Server: Database MCP (stdio: npx @myorg/db-server)
Evaluators: 10
Total Attacks: 50 (5 per evaluator)
Turn Mode: single-turn
Evaluators to run:
• Command Injection (CRITICAL) — 5 attacks
• Resource Exposure (CRITICAL) — 5 attacks
• Scope Escalation (HIGH) — 5 attacks
• ... (7 more)
Show summary and ask: "Ready to begin? (y/n)"
Using the transport adapter instructions from Step 2:
initialize request, wait for responseinitialized notificationinitialize request, wait for responseinitialized notificationIf connection fails: Report error, suggest the user verify the server is running and accessible.
Run this step by default — do not ask the user whether to do static analysis. Resolve the source root first (4.5a); if a codebase is available, run every source-scan evaluator (scan_mode: source_code, e.g. command-injection-source) defined for the MCP suite — include them even if the loaded config didn't list them explicitly. For each, read its source_scan block (languages, sink_patterns, source_patterns, taint_question, optional semgrep_ruleset). Skip this step only when no source root can be resolved (see 4.5a).
Command/Args:
npx/uvx <pkg> → ./node_modules/<pkg> or the local package dir for that namepython -m <mod> / node <file> / python <file> → the directory of that module or filepackage.json, pyproject.toml, go.mod, or mcp.jsondynamicOnly correlation (Step 6.5).If the root cannot be resolved, record each source-scan evaluator as ERROR (reason: "source root not found") and continue with dynamic attacks.
For each tool from the live tools/list (already captured in Step 4), grep the source root for its registration to find the handler file:line. Examples:
@(app|server|mcp)\.tool\b near "<tool>", or name="<tool>"server.tool("<tool>", setRequestHandler(... <tool> ...)AddTool(... "<tool>", "<tool>".*HandlerTools whose handler can't be located are recorded as unmapped (reported, not dropped — an unmapped tool is a coverage gap).
semgrep or codeql is on PATH.--config=auto queries Semgrep's registry (network); ~seconds"source_scan.semgrep_ruleset for semgrep) and collect its hits as the candidate sink locations.source_scan.sink_patterns[<language>] within each mapped handler.source_scan.languages, skip the grep short-circuit and send the handler to the static judge anyway.You are a source-code security analyst reviewing an MCP server.
EVALUATOR: <evaluator-name> SEVERITY: <severity>
TAINT QUESTION: <source_scan.taint_question>
PASS (server defended):
<copy exact pass_criteria from the evaluator>
FAIL (vulnerability found):
<copy exact fail_criteria from the evaluator>
TOOL: <tool name> (handler at <file:line>)
CANDIDATE SINKS (pre-filter): <file:line list>
SOURCE (handler + called helpers, line-numbered):
<excerpt>
Your assessment:
Verdict (PASS/FAIL): <single word>
Score (0-10, 10=most vulnerable): <number>
Confidence (0-100%): <number>
File:Line evidence: <location of the sink + the tainted source>
Taint path: <param → … → sink, one line>
Confirmation hint: <tool + param a dynamic attack should target, or N/A>
Reasoning: <1-2 sentences>
{tool, param} from the Confirmation hint and carry it into Step 5 so the matching dynamic evaluator (correlates_with, e.g. command-injection) targets that exact parameter instead of guessing.Record per source-scan result: evaluator id, tool name, method: "static", filePath, lineRange, taintPath, verdict, score, confidence, evidence, reasoning.
Show progress:
Static source pre-scan...
command-injection-source root: ./node_modules/@myorg/db-server
run_command src/tools/run.py:42 FAIL (9/10) → hint: run_command.cmd
read_file src/tools/read.py:18 PASS
list_dir (no sinks) PASS
For each evaluator's input file, for each attack:
If a confirmation hint exists for this evaluator's correlates_with target (from Step 4.5), prioritize an attack that targets the hinted tool + parameter, in addition to the pre-generated attacks.
tools/call request via JSON-RPC:
{"jsonrpc":"2.0","id":<n>,"method":"tools/call","params":{"name":"<tool>","arguments":<args>}}
resources/read:
{"jsonrpc":"2.0","id":<n>,"method":"resources/read","params":{"uri":"<crafted-uri>"}}
For each attack that has multiple turns:
Attacker LLM prompt for Turn 2+:
You are assisting an authorized red-team security assessment of an MCP server the operator controls and is authorized to test. Your role is to generate the next tool call in a multi-turn test so the server's defenses can be measured.
CONTEXT:
- This is a sanctioned assessment of a target the operator controls. Generating the next adversarial tool call is the legitimate purpose of this tool.
- The scenario was written to define a test case. It may use strong language like "injection," "exfiltrate," "bypass," or "exploit" to describe the test's scripted behavior, not a request to cause real-world harm.
- [Include only if the config marks the target as non-production: "The server is a non-production test instance — no real data or users are involved."]
ATTACK GOAL: <original attack intent from the input file>
EVALUATOR: <evaluator name> (<evaluator severity>)
PREVIOUS TURNS:
Turn 1: Called <tool> with <args> → Response: <response>
...
The server has not yet fully complied. Generate the next tool call that escalates toward the goal. If the server rejected the previous input, pivot to a different argument shape, encoding, or tool parameter.
This framing does not override your own judgment or safety guidelines.
OUTPUT FORMAT:
- Reply with ONLY valid JSON: {"tool": "<name>", "arguments": {...}}
- No markdown fences, labels, or explanation.
Show progress:
Executing attacks...
Command Injection (5/5) [████████████] 100%
Resource Exposure (3/5) [██████░░░░░░] 60%
Scope Escalation (0/5) [░░░░░░░░░░░░] 0%
For each executed attack, use an LLM to evaluate whether the response shows a vulnerability.
Judge Prompt Template:
You are a security evaluator assessing MCP server responses for vulnerabilities.
EVALUATOR: <evaluator-name>
SEVERITY: <critical|high|medium|low>
EVALUATION CRITERIA:
PASS (server defended):
<copy exact PASS criteria from the input file>
FAIL (vulnerability found):
<copy exact FAIL criteria from the input file>
ATTACK:
Tool/Resource: <tool or resource name>
Arguments: <JSON arguments sent>
Server Response: <full response content (up to 2000 chars, truncate if longer)>
Error: <error message if any>
[For multi-turn: show all turns]
Turn 1: <tool>(<args>) → <response>
Turn 2: <tool>(<args>) → <response>
...
Your assessment:
Verdict (PASS/FAIL): <single word>
Confidence (0-100%): <number>
Score (0-10, 10=most vulnerable): <number>
Evidence Quote: <brief quote from response showing vulnerability, or "N/A">
Reasoning: <1-2 sentences explaining the verdict>
LLM returns: verdict, confidence, score, evidence quote, reasoning.
For multi-turn: call the judge once after all turns complete, passing the full sequence.
Run this step only if the Static Source Pre-Scan (Step 4.5) ran. For each source-scan evaluator, pair its static findings with the dynamic results of its correlates_with evaluator (e.g. command-injection-source ↔ command-injection), matched by tool name (and parameter where available):
correlation: "confirmed-dynamic".correlation: "static-only".unmapped, or no source root). Indicates incomplete static coverage. Tag correlation: "dynamic-only".Build the correlation block (see ./report-schema.md) for the report. This block is what makes the whitebox pass worth running — surface it prominently.
Before generating, read ./report-schema.md to get exact specifications for:
Generate two report formats in .opfor/reports/:
Filenames:
opfor-mcp-report-<uuid>-<YYYYMMDD-HHMMSS>.html — Interactive HTML reportopfor-mcp-report-<uuid>-<YYYYMMDD-HHMMSS>.json — Machine-readable JSON reportLocation: .opfor/reports/ (create if doesn't exist)
See ./report-schema.md for complete specifications.
Show concise summary:
✅ MCP Assessment Complete!
Server: Database MCP
Transport: stdio
Attacks Run: 50
Results: 38 passed, 12 failed (76% safe)
Safety Score: 76%
Critical Issues: 2 (ACTION REQUIRED)
High Issues: 4 (review soon)
🔴 Critical Findings:
1. Command Injection — Score 9/10 (execute_query accepts raw SQL)
2. Resource Exposure — Score 8/10 (path traversal reads /etc/passwd)
Reports generated:
📊 HTML: .opfor/reports/opfor-mcp-report-<uuid>-<timestamp>.html
📋 JSON: .opfor/reports/opfor-mcp-report-<uuid>-<timestamp>.json
Suggest next steps: