Authoring mcp-test Suites
mcp-test runs YAML-defined functional tests and injection probes against a
running MCP server over stdio, with CI-ready exit codes (0 pass, 1 findings,
2 config error). This skill covers the parts the README assumes.
Start functional, then arm security
- Handshake smoke:
listTools: true+toolsContain: [expected-tools]— catches startup config errors instantly. - One call per tool: empty
assert: {}asserts "completed" — the cheapest possible coverage. Then addcontains/jsonSchema/maxLatencyMsfor the tools that matter. - Security section last, on the tools that touch untrusted input (files, web, tickets, notes).
Probe philosophy (where FPs come from)
Probes send known injection payloads (instruction-override and credential-file families — the exact payload table ships in the mcp-test README and probes source) through tool arguments, then run leak detectors (private-key headers, token shapes, env-dump shapes, system-prompt markers) on responses. Two design facts matter when reading results:
- A tool merely echoing the payload is not a leak — payloads are stripped before detection, including slash-normalized echoes (Windows paths). If you see a leak, look for the detector evidence line: it names what was found.
suspiciousnever fails CI — it means compliance language + echo, worth a look, not a gate.
FP tuning workflow
A detector firing on your legitimate server:
- Read the evidence excerpt — which detector, which shape?
- Drop that probe for the tool (
probes: [ssh-key-exfil, env-exfiltration]) as an immediate unblock, then - Fix the underlying imprecision (usually: severity should follow data flow,
not vocabulary —
env | grep VARpresence probes are debugging idioms, not dumps), add a regression test with the exact false-positive input.
CI integration
- run: npx mcp-test -c mcp-test.yaml --format github
--format github emits ::error file=...,line=... annotations pointing at
the failing YAML entry; the exit code gates the build with no extra steps.
Note: .cmd server commands on Windows need command: cmd, args: [/c, ...]
(spawn is shell-less).
Schema-drift upgrade gate (companion)
Pin tool versions with mcp-test snapshot; before any upgrade run
mcp-test diff --snapshot old.json --new <candidate> — it spins both versions
over stdio and classifies breaking (removed tool, +required param, type
change) vs additive. Exit 1 on breaking changes gates the upgrade in CI.