mcp_kitchen_sink — deliberately-insecure reference MCP agent
a.k.a. "the reference app" — Mylonite's built-in proving ground. The reference app,
the mcp-kitchen-sink pip package, and the reference:vulnerable /
reference:guarded scan targets are all the same artifact under three
names. The canonical walkthrough is docs/quarry.md.
⚠️ This package is intentionally insecure research scaffolding. It exists as ground truth for Mylonite's differential-oracle validation engine (see
ROADMAP.mdPhases 0–2). Do not expose it to anything but loopback. Both server variants refuse to bind to non-loopback interfaces by default. Seemylonite/SECURITY.mdfor the project's dual-use policy.The reference app is a deliberately vulnerable in-process reference agent. It never binds to a network. Never point Mylonite at a system you don't own or operate (see SECURITY.md).
What this is
A small agentic application with four tools — read_note, write_note,
web_fetch, send_email — shipped in two variants:
server_vulnerable: Seeded weaknesses across three categories that Phase 1 of Mylonite's exploit-finding agent will discover:- Indirect prompt injection — tool results are inlined into the planner's context with no quarantine wrapper.
- Tool poisoning — tool descriptions carry text that the planner happily treats as instruction.
- Excessive agency —
web_fetchhas no allow-list;send_emailfires without confirmation.
server_guarded: Same tool surface, hardened. Untrusted content goes through an<untrusted>quarantine envelope; tool descriptions pass a character allowlist;web_fetchis restricted;send_emailrequires a separateconfirm_sendstep.
A thin LiteLLM-backed planner (planner.py) sits in front of either server
and is used as "the agent under attack" in the validation tests. Two planner
variants mirror the server variants so the differential oracle has a clean
vulnerable-vs-guarded matchup.
The four seeded weaknesses
The full catalogue (loci, summaries) is in
seeds/seeds.yaml; the attack patterns that exercise each
weakness live in src/mylonite/scan/seeds.py. The scenario-by-scenario
walkthrough is docs/quarry.md.
| W | Name | OWASP LLM | OWASP ASI | MITRE ATLAS | Guard (in server_guarded) |
|---|---|---|---|---|---|
| W1 | tool-description-instruction-smuggling | LLM01 | ASI02 | AML.T0051 | description allowlist + parenthetical refusal |
| W2 | indirect-injection-via-note-body | LLM01, LLM05 | ASI01, ASI06 | AML.T0051 | read_note wraps bodies in <untrusted> |
| W3 | unrestricted-web-fetch | LLM06 | ASI02, ASI05 | AML.T0049 | web_fetch hostname allowlist |
| W4 | unconfirmed-email-send | LLM06 | ASI02 | — | two-step send: send_email + confirm_send |
Try it
export ANTHROPIC_API_KEY="sk-ant-..." # pragma: allowlist secret (placeholder)
mylonite scan reference:vulnerable
mylonite scan reference:guarded
Runs Mylonite's real scan pipeline against both builds of the reference app and
finds the weaknesses above on the vulnerable build, none on the guarded one —
that vulnerable-vs-guarded gap is the differential oracle in action. See
docs/quarry.md for the full walkthrough and the
follow-on mylonite scan/generate/validate flows.
How to run
# from the repo root — two editable installs:
pip install -e ".[dev]"
pip install -e ./reference_targets/mcp_kitchen_sink
python -m mcp_kitchen_sink.server_vulnerable # vulnerable, loopback only
python -m mcp_kitchen_sink.server_guarded # hardened, loopback only
Both servers exit immediately if the configured bind address is not
loopback (127.0.0.1 / ::1).
Real MCP stdio server (custom-target on-ramp)
server_vulnerable.py/server_guarded.py above are in-process server
classes only — programmatic use from tests, or mylonite scan's built-in
reference:vulnerable/reference:guarded targets. To drive this same tool
surface through mylonite's generic --target-file custom-target flow
(scan/ablate/validate/gate) — the same code path a real third-party
MCP app goes through — use the real stdio-speaking wrappers instead:
pip install -e "./reference_targets/mcp_kitchen_sink[mcp]" # needs the mcp SDK
mcp-kitchen-sink-vulnerable # console script, or:
python -m mcp_kitchen_sink.stdio_vulnerable
mcp-kitchen-sink-guarded # / python -m mcp_kitchen_sink.stdio_guarded
Each speaks real MCP over stdin/stdout until the peer closes the pipe —
exactly what a target.yaml's command/args spawn. examples/target.yaml
at the repo root is a ready-to-use target file pointed at the vulnerable
variant:
mylonite scan --target-file examples/target.yaml --authorize kitchen-sink
See tests/integration/test_custom_target_offline.py for an offline
(scripted-LLM) test that spawns this real subprocess end-to-end.
Seeds
seeds/ contains a small bank of failure-mode descriptions used by the
forthcoming security-mutation-score check in Phase 2. Each seed pairs a
specific weakness in the vulnerable server with a guard in the guarded
server and is tagged with OWASP LLM / OWASP ASI / MITRE ATLAS IDs.
Tests
tests/ proves the basic differential ground truth:
- A canned indirect-injection payload against
server_vulnerablemakes the planner act on the injected instruction. - The same payload against
server_guardedis refused.
These two tests are the contract that Phase 2's validation pipeline mechanises.
License
Apache-2.0, same as the parent project. See LICENSE at the repo root.
Release files for mcp-kitchen-sink 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_kitchen_sink-0.2.0.tar.gz | 21.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_kitchen_sink-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.8 kB
Release files / mcp_kitchen_sink-0.2.0.tar.gz
| Download URL | mcp_kitchen_sink-0.2.0.tar.gz |
|---|---|
| Size | 21.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1f2f6d33c0b6e6ea6f902a0a2a06d76dd22499f58f6ed284266157e89570fbe8
|
|
BLAKE2b-256 checksum How to use checksums |
2beb1a0ff4f7e0287ae487d3a3bf0c560d045cc8eb7b6f546a5e127543eef17f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / mcp_kitchen_sink-0.2.0-py3-none-any.whl
| Download URL | mcp_kitchen_sink-0.2.0-py3-none-any.whl |
|---|---|
| Size | 19.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
90d1e84f697867e32bf3ceee84748377de61a3cb63dc4c07eff18f2758731aa7
|
|
BLAKE2b-256 checksum How to use checksums |
c5190cbcae86c9920d545b85a6041eb3bf435939294c107ed580cf401e351e23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log