Test your own app¶
The bundled reference app proves the machinery. The point of Mylonite is to run it against your AI agent. If your app exposes its tools over MCP (or any stdio MCP server), this is the end-to-end path: scaffold a target file, scan, and gate.
No MCP? If your agent is a plain HTTP endpoint (a prompt in, a reply out), use the
resttransport — describe the request shape in the target file and change nothing in your app.What gets tested. Mylonite drives your server's tools with its own planner — guardrails that live in your agent or host process, not the server, are never exercised. See Coverage by app shape for exactly which weakness classes are testable on your app's shape.
Authorisation. Mylonite finds and reproduces working exploits, so every non-reference target requires an explicit
--authorizeflag asserting you control the target. Its value is the target'sscope, or its family when it declares no scope; leave it off and Mylonite prints the value to pass. See Security. Never point it at an app you don't own.
1. Scaffold a target file¶
scan --scaffold launches your MCP server, lists its tools (no LLM call, no attack),
infers the likely weakness classes and the plant/retrieve tools, and writes a commented
target.yaml starter (no --authorize needed — nothing is attacked):
--scope is optional here (it labels the target and becomes the value --authorize must
match below — see the target.yaml reference); omit it and the
scaffolded target.yaml falls back to family: custom, so --authorize custom instead.
It suggests weakness_classes (W1/W2 baseline; W3 if it sees an egress tool; W4 if it
sees a consequential tool), pre-fills a seed_arm (how to plant poisoned content) and
an effect_probe (how to confirm damage), and lists your tools. Fill in the commented
sections — see the full target.yaml reference.
A minimal target file is just:
family: my-app
command: python
args: [my_server.py]
weakness_classes: [W2]
seed_arm: # how to plant untrusted content (required for W2)
tool: save_note
args_template: { body: "{payload}" }
Auto-wiring. For a W2 target, if you omit
seed_arm,scanwill infer it from the live tool surface when a no-id recall path exists (so the planted payload is guaranteed to be surfaced back) — printing what it inferred. Otherwise it blocks loudly rather than silently skipping the W2 attack patterns and reading as clean.
2. Scan it¶
This runs the single-shot engine. Findings land under
.mylonite/scans/. Use the
model roles to point the
planner at a representatively exploitable model.
3. Gate it (the full pipeline)¶
gate runs the whole pipeline — scan → generate → validate → (optionally) open a PR —
and only a kept test makes it through:
mylonite gate --target-file app.yaml --authorize my-app # writes the test; add --workflows for CI templates
mylonite gate --target-file app.yaml --authorize my-app --open-pr # also opens the PR via gh
Because you have no in-repo guarded build, the validator synthesizes one at the adapter boundary (the control-efficacy check) and proves the finding differentially by default — the emitted test gates on the control being load-bearing, with the boundary-proxy caveat stated on the label. See CI gating for the committed workflows and the PR anatomy.
4. Keep it honest over time¶
- Scoring each safeguard individually (load-bearing, security theater, redundant,
no-attack, or inconclusive) is
ablate, which is hidden and experimental — see docs/experimental.md. - When you change models — re-validate the committed test with the new
--planner-modelbefore you ship. See Re-validate on a new model.
Bundled targets¶
Three MCP servers ship as ready-to-scan targets (no target file needed) — useful for trying Mylonite against real servers:
| Target | Scope | Exposes |
|---|---|---|
mcp:filesystem:<abs/path> |
a sandbox dir | W1, W2, W4 (read/write files) |
mcp:fetch |
optional label | W3 (egress) |
mcp:github:<owner/repo> |
a repo | W1, W2, W4 (issues/comments) |
On Windows, use an existing, non-root directory and quote the path if it has spaces:
mcp:github needs a token: export GITHUB_PERSONAL_ACCESS_TOKEN (the exact variable
the real GitHub MCP server reads) before you scan — an unset value fails fast, naming it,
before any subprocess launches.
export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_...
mylonite scan mcp:github:myhandle/my-repo --authorize myhandle/my-repo
These bundled families are attack-only today (they scan but don't yet route through the differential oracle); routing them through the on-ramp so they can emit a gating test is planned. For a CI-gating test against a real app, use
--target-file.