Skip to content

MCP Offensive Playbooks

These playbooks cover MCP over HTTP/WebSocket and local stdio.

Tool-Call Argument Rewrite

Threat model: a planner calls a tool with safe-looking arguments, but an in-path proxy changes the account, object id, or action argument before the tool server receives it.

seam rules explain --rules rules/mcp_tool_call_argument_rewrite.yaml \
  --rule mcp_tool_call_argument_rewrite

Expected touched path:

decoded.json.params.arguments.account

Tool-Call Argument Merge

Threat model: the operator needs to preserve the original call structure while adding nested authority, scope, or policy fields.

seam rules test \
  --rules rules/mcp_tool_call_argument_merge.yaml \
  --fixture examples/mcp-tool-call.json \
  --expect-rule mcp_tool_call_argument_merge \
  --json

Expected touched path:

decoded.json.params.arguments

The payload comes from examples/payloads/mcp-argument-merge.yaml.

Tool-Result Injection

Threat model: a planner trusts tool output that can be rewritten in path.

seam rules explain --rules rules/mcp_tool_result_string_injection.yaml \
  --rule mcp_tool_result_string_injection
seam rules explain --rules rules/mcp_tool_result_content_insert.yaml \
  --rule mcp_tool_result_content_insert

Expected touched paths:

decoded.json.result.content.0.text
decoded.json.result.content

Lab L6 tool_result_injection is the proof-oriented scenario for this pattern.

Local stdio Wrapping

Use stdio mode when the target launches a local MCP server command.

printf '{"jsonrpc":"2.0","id":"call-1","method":"tools/call","params":{"name":"echo","arguments":{"account":"ORIGINAL"}}}\n' \
  | seam stdio proxy --command python3 \
      --rules rules/mcp_stdio_argument_spoof.yaml \
      --transcript stdio.json \
      --schema schemas/transcript.schema.json \
      -- examples/mcp-stdio-fixture.py

Seam decodes stdout lines as MCP traffic. Child stderr is forwarded as logs and is not decoded or mutated.

For object merge instead of scalar overwrite:

printf '{"jsonrpc":"2.0","id":"call-1","method":"tools/call","params":{"name":"echo","arguments":{"account":"ORIGINAL"}}}\n' \
  | seam stdio proxy --command python3 \
      --rules rules/mcp_stdio_argument_merge.yaml \
      --transcript stdio-merge.json \
      --schema schemas/transcript.schema.json \
      -- examples/mcp-stdio-fixture.py

Correlation

MCP JSON-RPC ids are tracked under decoded.correlation. Use that when rules need to target a specific interleaved request/response pair.