Skip to content

Commit e2bfcbe

Browse files
committed
docs(mcp): fuzz + conformance guide, CLI reference, ADR-0015, roadmap v1.2
1 parent 2905466 commit e2bfcbe

4 files changed

Lines changed: 123 additions & 6 deletions

File tree

docs/ROADMAP.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,15 @@ tools through MCP get offline, byte-identical integration tests (ADR-0014).
9797
- [x] stdio transport: transparent recording proxy + replay serve-loop
9898
- [x] `volo mcp record | serve` CLI, `examples/mcp_calc_server.py`, byte-faithful e2e test
9999
- [x] Docs: README quickstart, docs-site guide, CLI reference
100-
- [ ] M11: MCP fuzz — scenario operators at the MCP boundary (`volo mcp fuzz`) + conformance
101-
report for MCP-server authors
100+
101+
## v1.2.0 — M11: MCP fuzz + conformance ✅
102+
Goal: adversarial testing at the MCP boundary — "what does my agent do when the server returns
103+
garbage?" — plus a regression gate for MCP-server authors.
104+
105+
- [x] `volo_mcp.fuzz`: scenario operators applied inside the `{"result": ...}` envelope;
106+
handshake/meta steps and recorded protocol errors stay byte-intact; seeded/reproducible
107+
- [x] `volo mcp fuzz` — one servable mutated recording per operator (resilience, robustness,
108+
security, order_sensitivity), `--serve` for a live hostile world, `--report` JSON
109+
- [x] `volo_mcp.conformance` + `volo mcp conformance` — replay recorded requests against the
110+
LIVE server, diff answers (errors included), exit 1 on behavioral change
111+
- [ ] M12: pytest plugin (`pytest-volo`) — reliability tests as unit tests
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# ADR 0015: MCP fuzz targets result envelopes only; conformance replays against the live server
2+
3+
- Status: accepted
4+
- Date: 2026-07-03
5+
6+
## Context
7+
8+
M11 brings adversarial testing to the MCP boundary (ADR-0014). Two design questions:
9+
(1) *what* may the fuzzer mutate in a recorded MCP session, and (2) what does "conformance"
10+
mean for an MCP-server author using Volo as a regression gate?
11+
12+
## Decision
13+
14+
**Fuzz (`volo_mcp.fuzz`)** reuses the generic `volo-scenarios` operators unchanged, wrapped by
15+
two MCP-specific rules:
16+
17+
1. **Fuzz targets are only real tool responses**`tool_call` steps with an `mcp.tool:` prefix
18+
whose response is a `{"result": <object>}` envelope. Handshake/meta steps
19+
(`mcp:initialize`, `mcp:tools/list`, …) and recorded *protocol errors* are byte-intact in
20+
every mutation, so a fuzzed session still boots and error behavior stays authentic.
21+
2. **Operators run inside the envelope.** The fuzzer extracts the target steps into a
22+
sub-recording, unwraps `{"result": X}``X`, applies the operator, re-wraps, and merges the
23+
steps back into their original positions. Operators therefore mutate the object the agent
24+
actually reads (e.g. `corrupt_field` flips `isError`; `prompt_injection` lands inside the
25+
content), and the mutated recording replays through `MCPReplayServer` with no special cases.
26+
27+
Default library: `drop_tool_result`, `corrupt_field`, `prompt_injection`, `reorder_steps`
28+
(failure classes: resilience, robustness, security, order_sensitivity). Excluded:
29+
`inject_latency` (latency metadata is never served over the wire) and `ambiguous_user_turn`
30+
(MCP recordings contain no model calls). Mutations are seeded → reproducible in CI.
31+
32+
**Conformance (`volo_mcp.conformance`)** treats a recording as a behavioral contract: every
33+
recorded request is rebuilt (`messages.request_message`, the inverse of `tool_key`) and sent to
34+
a freshly spawned live server; each reply is compared to the recorded envelope. Verdicts:
35+
`identical` / `different` / `no_reply`; anything non-identical fails (exit 1 in the CLI).
36+
Recorded protocol errors are part of the contract and must reproduce.
37+
38+
## Consequences
39+
40+
- Every fuzz output is itself a valid, servable recording — `volo mcp serve` and future
41+
reliability scoring work on hostile worlds with zero extra machinery.
42+
- Restricting targets to result envelopes means the fuzzer never breaks the transport layer —
43+
by design. Malformed-protocol fuzzing (bad JSON-RPC framing, wrong ids) is a separate future
44+
concern, closer to the transport tests than to scenario operators.
45+
- Conformance request reconstruction is lossy for `tools/call` params beyond
46+
`name`/`arguments` (deliberately dropped from the cache identity in ADR-0014). Servers keying
47+
behavior on exotic params need a fresh recording rather than conformance replay.
48+
- Conformance compares byte-equal envelopes; servers with legitimately nondeterministic fields
49+
(timestamps, ids) will report `different`. A normalization hook is future work if demanded.
50+
51+
## Alternatives considered
52+
53+
- **MCP-specific operator implementations** — rejected: duplicates the M2 taxonomy and forks
54+
the failure-class vocabulary the reliability engine already understands.
55+
- **Fuzzing everything, including handshake/meta and error envelopes** — rejected: trivially
56+
broken sessions (failed `initialize`) mask the interesting failures, and mutating a recorded
57+
error's shape produces worlds no real server can express.
58+
- **Conformance via the simulator (replay both sides offline)** — rejected: that tests Volo
59+
against itself; the author's question is whether the *live build* still honors the contract.

website/cli.mdx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,21 @@ return JSON-RPC error `-32042` instead of a hallucination. See [Test MCP servers
5454
uv run volo mcp serve rec.json
5555
```
5656

57+
## `volo mcp fuzz`
58+
Mutate recorded tool responses with the adversarial scenario operators (seeded, reproducible);
59+
write one servable mutated recording per operator, or serve one hostile world directly.
60+
```bash
61+
uv run volo mcp fuzz rec.json [--seed 0] [--out-dir DIR] [--report fuzz.json]
62+
uv run volo mcp fuzz rec.json --op prompt_injection --serve
63+
```
64+
65+
## `volo mcp conformance`
66+
Replay recorded requests against the LIVE server and diff the answers (recorded errors included).
67+
Exit 1 if behavior changed — a CI regression gate for MCP-server authors.
68+
```bash
69+
uv run volo mcp conformance rec.json [--report conf.json] -- <server command...>
70+
```
71+
5772
## `volo scenarios`
5873
List the default adversarial scenario operators and their failure classes.
5974

website/mcp.mdx

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,42 @@ A recorded `tools/list` response is automatically distilled into per-tool **sche
8888
but whose tool declares a response shape — can be synthesized under the same guarantee:
8989
**validate or flag, never hallucinate**.
9090

91-
## What's next
91+
## Fuzz the boundary: what does your agent do when the server misbehaves?
9292

93-
MCP **fuzzing** is the next milestone: Volo's adversarial scenario operators
94-
(corrupt fields, inject latency, prompt-inject tool outputs…) applied at the MCP boundary, plus a
95-
conformance report for MCP server authors. Track progress in the
93+
`volo mcp fuzz` mutates the recorded tool responses with Volo's adversarial scenario operators —
94+
dropped results, corrupted fields, embedded prompt injections, reordered answers — and writes one
95+
mutated recording per operator. Each one is a servable "hostile world":
96+
97+
```bash
98+
uv run volo mcp fuzz calc.json --report fuzz-report.json
99+
# mcp fuzz: drop_tool_result [resilience] 1 change(s) -> calc.drop_tool_result.seed0.json
100+
# mcp fuzz: corrupt_field [robustness] 1 change(s) -> calc.corrupt_field.seed1.json
101+
# mcp fuzz: prompt_injection [security] 1 change(s) -> calc.prompt_injection.seed2.json
102+
# mcp fuzz: reorder_steps [order_sensitivity] 1 change(s) -> calc.reorder_steps.seed3.json
103+
104+
# point your agent at one hostile world directly:
105+
uv run volo mcp fuzz calc.json --op prompt_injection --serve
106+
```
107+
108+
Mutations are seeded (same seed, same hostile world — reproducible in CI), they only ever touch
109+
*real tool results*: the `initialize` handshake, `tools/list`, and recorded protocol errors stay
110+
byte-intact so the session still boots and failure behavior stays authentic.
111+
112+
## For MCP server authors: conformance
113+
114+
A recording is a behavioral contract. `volo mcp conformance` replays every recorded request
115+
against your **live** server build and diffs the answers — recorded errors included:
116+
117+
```bash
118+
uv run volo mcp conformance calc.json --report conformance.json -- python my_server.py
119+
# mcp conformance: ok [002] mcp:tools/list identical
120+
# mcp conformance: !! [003] mcp.tool:add different
121+
# mcp conformance: 3 identical, 1 different, 0 no-reply -> FAIL (exit code 1)
122+
```
123+
124+
Run it in your server's CI to catch behavioral regressions before your users' agents do.
125+
126+
## Roadmap
127+
128+
Track what's next in the
96129
[roadmap](https://github.com/abhay-codes07/VOLO/blob/main/docs/ROADMAP.md).

0 commit comments

Comments
 (0)