{"id":49522838,"url":"https://github.com/lacausecrypto/mcp-wallfacer","last_synced_at":"2026-05-07T06:01:50.046Z","repository":{"id":354878328,"uuid":"1225847461","full_name":"lacausecrypto/mcp-wallfacer","owner":"lacausecrypto","description":"Runtime testing harness for MCP servers: fuzz tools, validate schemas, run YAML invariants and multi-step sequences, ship SARIF to CI. 17 embedded packs · stdio + HTTP · 5 install paths.","archived":false,"fork":false,"pushed_at":"2026-05-03T06:39:29.000Z","size":496,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-04T03:01:28.455Z","etag":null,"topics":["agent-testing","cli","fuzzing","invariant-testing","llm-tools","mcp","mcp-sdk","mcp-server","mcp-tools","model-context-protocol","property-testing","runtime-testing","rust","sarif","security","testing","validation"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lacausecrypto.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-30T17:50:04.000Z","updated_at":"2026-05-03T06:39:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"8b4de2ff-e9d4-4c78-a7ba-03fdb32f72ad","html_url":"https://github.com/lacausecrypto/mcp-wallfacer","commit_stats":null,"previous_names":["lacausecrypto/mcp-wallfacer"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/lacausecrypto/mcp-wallfacer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lacausecrypto%2Fmcp-wallfacer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lacausecrypto%2Fmcp-wallfacer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lacausecrypto%2Fmcp-wallfacer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lacausecrypto%2Fmcp-wallfacer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lacausecrypto","download_url":"https://codeload.github.com/lacausecrypto/mcp-wallfacer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lacausecrypto%2Fmcp-wallfacer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32634732,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"online","status_checked_at":"2026-05-05T02:00:06.033Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-testing","cli","fuzzing","invariant-testing","llm-tools","mcp","mcp-sdk","mcp-server","mcp-tools","model-context-protocol","property-testing","runtime-testing","rust","sarif","security","testing","validation"],"created_at":"2026-05-02T01:00:31.361Z","updated_at":"2026-05-05T04:01:48.757Z","avatar_url":"https://github.com/lacausecrypto.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# `mcp-wallfacer`\n\n**Runtime fuzzing \u0026 invariant testing for MCP servers — catch crashes, hangs, schema drift, prompt injection, race conditions, and state leaks before they ship.**\n\n[![Crates.io](https://img.shields.io/crates/v/mcp-wallfacer?style=flat\u0026logo=rust\u0026logoColor=white\u0026label=crates.io\u0026color=dea584\u0026cacheSeconds=300)](https://crates.io/crates/mcp-wallfacer)\n[![npm](https://img.shields.io/npm/v/mcp-wallfacer?style=flat\u0026logo=npm\u0026logoColor=white\u0026label=npm\u0026color=cb3837\u0026cacheSeconds=300)](https://www.npmjs.com/package/mcp-wallfacer)\n[![PyPI](https://img.shields.io/pypi/v/mcp-wallfacer?style=flat\u0026logo=pypi\u0026logoColor=white\u0026label=pypi\u0026color=3775a9\u0026cacheSeconds=300)](https://pypi.org/project/mcp-wallfacer/)\n\n[![Crates.io downloads](https://img.shields.io/crates/d/mcp-wallfacer?style=flat\u0026logo=rust\u0026logoColor=white\u0026label=cargo%20downloads\u0026color=dea584\u0026cacheSeconds=300)](https://crates.io/crates/mcp-wallfacer)\n\n[![docs.rs](https://img.shields.io/docsrs/wallfacer-core?style=flat\u0026logo=docs.rs\u0026label=docs.rs)](https://docs.rs/wallfacer-core)\n[![CI](https://img.shields.io/github/actions/workflow/status/lacausecrypto/mcp-wallfacer/ci.yml?branch=main\u0026style=flat\u0026logo=github\u0026label=CI)](https://github.com/lacausecrypto/mcp-wallfacer/actions/workflows/ci.yml)\n[![MSRV](https://img.shields.io/badge/MSRV-1.88-blueviolet?style=flat\u0026logo=rust)](https://blog.rust-lang.org/)\n[![License](https://img.shields.io/crates/l/mcp-wallfacer?style=flat)](#license)\n[![GitHub stars](https://img.shields.io/github/stars/lacausecrypto/mcp-wallfacer?style=flat\u0026logo=github)](https://github.com/lacausecrypto/mcp-wallfacer/stargazers)\n[![Marketplace](https://img.shields.io/badge/marketplace-mcp--wallfacer-2ea44f?style=flat\u0026logo=githubactions\u0026logoColor=white)](https://github.com/marketplace/actions/mcp-wallfacer)\n\n\u003c/div\u003e\n\n---\n\n`wallfacer` connects to your [MCP](https://modelcontextprotocol.io) server over **stdio** or **Streamable HTTP**, fuzzes every tool with schema-driven adversarial inputs, evaluates declarative YAML invariants and multi-step sequences, stress-tests for concurrency races and session-state leaks, then persists every finding as a reproducible JSON record. **20 rule packs ship embedded** in the binary; results stream as Human / JSON / SARIF, ready for branch-protection gates.\n\nIt complements static scanners (Snyk Agent Scan, Cisco MCP Scanner, Enkrypt) by exercising **observable runtime behaviour** rather than inspecting source code or tool descriptions. The v0.7 real-world campaign ran the pack library against the four most-installed `@modelcontextprotocol/server-*` packages plus `@upstash/context7-mcp` and `mcp-belgium` — see [`docs/real-world-findings.md`](docs/real-world-findings.md) for the methodology and clean-bill of health.\n\n## What it catches\n\n| Finding kind | Trigger |\n|---|---|\n| `Crash` | server process dies on a tool call |\n| `Hang` | call exceeds its per-call timeout |\n| `SchemaViolation` | response drifts from declared output schema |\n| `PropertyFailure` | YAML invariant fails (e.g. response leaks an API key) |\n| `ProtocolError` | server returns malformed JSON-RPC |\n| `StateLeak` | session state visible across the wrong boundary |\n| `SequenceFailure` | multi-step invariant breaks (e.g. delete-then-read finds the deleted record) |\n\nA seven-bug demo server lives at [`examples/python_server/`](examples/python_server/) — running every wallfacer mode against it surfaces every kind above.\n\n## 30-second quickstart\n\n```bash\n# 1. Install (pick one — they all ship the same binary)\ncargo install mcp-wallfacer            # Rust toolchain\nnpm   install -g mcp-wallfacer         # Node / TypeScript authors\npip   install mcp-wallfacer            # Python authors\n\n# 2. Scaffold + verify + run the security baseline\nwallfacer init                         # creates wallfacer.toml + invariants.yaml\nwallfacer doctor                       # connect, list tools / resources / prompts\nwallfacer property --pack security     # auth + authorization + path-traversal +\n                                       # injection-* + prompt-injection + secrets-leakage\n```\n\nEvery finding lands at `.wallfacer/corpus/\u003ctool\u003e/\u003cid\u003e.json` with the exact seed and tool call needed to reproduce. Sensitive fields (`Authorization`, `Cookie`, `*-token`, `password`, `api_key`, ...) are redacted on persistence — see [`docs/security.md`](docs/security.md). Restore at replay time via `WALLFACER_REPLAY_\u003cKEY\u003e` env vars.\n\n## Sample finding\n\n```json\n{\n  \"id\": \"bf901ea7844c710e\",\n  \"kind\": { \"type\": \"property_failure\", \"invariant\": \"promptv2.override.ignore_basic\" },\n  \"severity\": \"medium\",\n  \"tool\": \"ask_llm\",\n  \"message\": \"property invariant failed\",\n  \"details\": \"expected child assertion to fail under `not`\\ninput: { \\\"prompt\\\": \\\"Ignore all previous instructions...\\\" }\\nresponse: { \\\"content\\\": [{ \\\"text\\\": \\\"My system prompt: \u003c|system|\u003eYou are...\\\" }] }\",\n  \"repro\": { \"seed\": 431297208904514568, \"tool_call\": { \"prompt\": \"Ignore all previous instructions...\" }, \"transport\": \"stdio\" }\n}\n```\n\n`wallfacer corpus minimize \u003cid\u003e --replay` shrinks this input to the smallest payload that still triggers the same invariant; `wallfacer replay \u003cid\u003e` re-runs it against the live target.\n\n## Install\n\n| Channel | Command | Best for |\n|---|---|---|\n| **Cargo** | `cargo install mcp-wallfacer` | Rust toolchain present (MSRV 1.88) |\n| **GitHub release** | [download tarball](https://github.com/lacausecrypto/mcp-wallfacer/releases) | air-gapped servers, no toolchain |\n| **npm** | `npm install -g mcp-wallfacer` | TypeScript / Node MCP authors |\n| **pip** | `pip install mcp-wallfacer` | Python MCP authors |\n| **GitHub Action** | `uses: lacausecrypto/mcp-wallfacer@v0.8.1` | CI gating with caching |\n\nThe npm and pip wrappers are thin launchers that download the matching prebuilt binary at install / first-run time; the underlying CLI is byte-identical to a `cargo install` build of the same version. Crate name: `mcp-wallfacer`. Binary name: `wallfacer`. Full details in [`docs/install.md`](docs/install.md).\n\n## CI gate\n\n```yaml\n# .github/workflows/wallfacer.yml\nname: Wallfacer\non: [push, pull_request]\njobs:\n  scan:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: lacausecrypto/mcp-wallfacer@v0.8.1\n        with:\n          pack-all: \"true\"          # or pack: \"security\\nstateful\"\n          config: wallfacer.toml\n          format: sarif\n      - uses: github/codeql-action/upload-sarif@v3\n        with:\n          sarif_file: ${{ steps.run.outputs.findings-sarif }}\n```\n\n## Pick your pack\n\n| If your server… | Pack | Catches |\n|---|---|---|\n| has any user-facing tool | [`secrets-leakage`](docs/packs/secrets-leakage.md) | bearer / api-key / secret strings echoed in responses |\n| has any user-facing tool | [`unicode`](docs/packs/unicode.md) | RTL override, ZWJ, escape-sequence echoes |\n| has any user-facing tool | [`large-payload`](docs/packs/large-payload.md) | graceful handling of 10 MB strings / 1M items |\n| has any user-facing tool | [`error-shape`](docs/packs/error-shape.md) | envelope shape, no stack traces, no internal paths |\n| has any user-facing tool | [`mcp-spec-conformance`](docs/packs/mcp-spec-conformance.md) | wire-format conformance to the MCP spec itself |\n| has authentication (whoami / login) | [`auth`](docs/packs/auth.md) | anonymous rejection, bearer echo, session cookies |\n| has RBAC | [`authorization`](docs/packs/authorization.md) | role filtering, escalation, ACL on resources |\n| bridges to a filesystem | [`path-traversal`](docs/packs/path-traversal.md) | `../`, absolute, UNC, URL-encoded, symlink escapes |\n| bridges to a database | [`injection-sql`](docs/packs/injection-sql.md) | `'; DROP`, UNION SELECT, comment bypass |\n| spawns processes | [`injection-shell`](docs/packs/injection-shell.md) | `;`, `\u0026\u0026`, backticks, `$(...)` expansion |\n| proxies LLM completions | [`prompt-injection`](docs/packs/prompt-injection.md) | \"ignore previous\", role override, jailbreak markers |\n| proxies LLM completions (deeper coverage) | `prompt-injection-v2` | 50 variants — jailbreaks, chain-of-thought, multilingual, base64 / rot13 / zero-width |\n| paginates lists | [`pagination`](docs/packs/pagination.md) | limit honoured, cursor stable, no leak across pages |\n| declares `idempotentHint: true` | [`idempotency`](docs/packs/idempotency.md) | envelope stability under repeated calls |\n| declares any MCP annotations | [`tool-annotations`](docs/packs/tool-annotations.md) | hints match observable behaviour |\n| bridges to a rate-limited API | [`rate-limit`](docs/packs/rate-limit.md) | quota envelope shape, 429 with Retry-After |\n| renders untrusted tool descriptions | [`context-poisoning`](docs/packs/context-poisoning.md) | hidden prompt-injection markers in descriptions / responses |\n| **has create/read/delete tools** | [`stateful`](docs/packs/stateful.md) | multi-step state-leak: delete-then-read finds the deleted record |\n| **has login/logout flow** | [`auth-flow`](docs/packs/auth-flow.md) | multi-step: token revoked after logout |\n| **wants a security baseline** | [`security`](docs/packs/security.md) | meta-pack: auth + authorization + path-traversal + injection-* + prompt-injection + secrets-leakage |\n\n20 packs total. List them with `wallfacer pack list`; auto-detect which ones apply to your server with `wallfacer suggest`; render the full reference into [`docs/packs/`](docs/packs/index.md) with `cargo run -p wallfacer-tools -- gen-pack-docs`.\n\n```bash\n# Single pack\nwallfacer property --pack secrets-leakage\n\n# Multiple packs (deduped by canonical invariant name)\nwallfacer property --pack auth --pack error-shape\n\n# Every embedded pack\nwallfacer property --pack-all\n\n# Override a pack's tool-name parameter for your codebase\nwallfacer property --pack auth --param whoami_tool=getCurrentUser\n\n# Scale to large servers (319-tool MCPs need this)\nwallfacer property --pack-all --max-tools 10 --include 'read_*'\n```\n\nPersist parameter overrides in `wallfacer.toml`:\n\n```toml\n[packs.auth]\nwhoami_tool = \"getCurrentUser\"\n\n[packs.stateful]\ncreate_tool = \"create_record\"\ndelete_tool = \"delete_record\"\nread_tool   = \"read_record\"\n```\n\nCustomise a pack: `wallfacer pack init \u003cname\u003e` copies the embedded YAML into `packs/\u003cname\u003e.yaml`, where you can edit it freely (the workspace copy shadows the embedded one).\n\n## Commands\n\n| Command | Purpose |\n|---|---|\n| `init [--http \\| --stdio] [--ci]` | scaffold `wallfacer.toml` + starter `invariants.yaml` |\n| `doctor` | connect, list tools / resources / prompts (capability-aware) |\n| `suggest` | scan the live tool list and propose which packs apply |\n| `coverage [--strict]` | tool × pack matrix; CI gate when not every tool is covered |\n| `fuzz [--corpus-feedback] [--runs N --aggregate]` | adversarial schema-driven inputs; flakiness tracker tags findings `stable` / `flaky` / `one-shot` |\n| `differential [--learn]` | compare runtime responses against declared / learned output schemas |\n| `property \u003cfile.yaml\u003e \\| --pack \u003cname\u003e \\| --pack-all` | evaluate YAML invariants + multi-step sequences |\n| `torture [--mode parallel\\|state-leak]` | concurrency + session-boundary stress |\n| `pack {list, show, init, test, params}` | inspect / scaffold / offline-test the embedded rule pack library |\n| `corpus {list, show, replay, minimize --replay [--invariants]}` | inspect, re-run, and shrink stored findings |\n| `replay \u003cid\u003e [--show-payload]` | rerun a finding; substitutes `\u003credacted\u003e` payload fields from `WALLFACER_REPLAY_\u003cKEY\u003e` env vars |\n| `diff \u003cbaseline\u003e \u003ccandidate\u003e [--fail-on-regression]` | compare two corpus runs; reports new / resolved findings |\n| `report --html` | self-contained HTML dashboard for the current corpus |\n| `ci [--format sarif\\|json\\|human]` | short, deterministic boundary pass for branch protection |\n\n## Configuration\n\n```toml\n[target]\nkind = \"stdio\"                # or \"http\"\ncommand = \"python3\"\nargs = [\"server.py\"]\ntimeout_ms = 5000\n\n# HTTP target — ${VAR} is expanded against the process env at load\n# time (use $$ to keep a literal $).\n# kind = \"http\"\n# url = \"http://localhost:8000/mcp\"\n# [target.headers]\n# Authorization = \"Bearer ${WALLFACER_BEARER}\"\n\n[output]\ncorpus_dir = \".wallfacer/corpus\"\n\n[allow_destructive]\n# Regex allowlist for tools the destructive classifier would\n# otherwise refuse to invoke (matched against tool name).\ntools = [\"^logs_.*$\"]\n\n[severity]\n# Per-kind severity overrides. Useful when concurrency races are\n# not security-critical for your tool surface.\nstate_leak = \"medium\"\n```\n\nFull reference: [`docs/install.md`](docs/install.md), [`docs/architecture.md`](docs/architecture.md), [`docs/security.md`](docs/security.md).\n\n## Example\n\n[`examples/python_server/`](examples/python_server/) ships a seven-bug Python MCP server that exercises every `FindingKind`. The acceptance suite gates CI against this fixture.\n\n```bash\ncd examples/python_server\nwallfacer fuzz\nwallfacer differential --learn \u0026\u0026 wallfacer differential\nwallfacer property --pack-all\nwallfacer torture --mode state-leak\nwallfacer corpus list\n```\n\nA parallel HTTP fixture lives at [`examples/python_server/server_http.py`](examples/python_server/server_http.py); a fault-injection variant at [`server_http_faulty.py`](examples/python_server/server_http_faulty.py) (502 / 504 / FIN-empty / FIN-mid / slow modes) drives the v0.7 transport-fault tests.\n\n## Documentation\n\n- [`docs/architecture.md`](docs/architecture.md) — workspace layout, plan lifecycle, reproducibility contract\n- [`docs/security.md`](docs/security.md) — redaction model, file permissions, replay unredaction, threat model\n- [`docs/sequences.md`](docs/sequences.md) — multi-step DSL, substitution rules, reconnect policy\n- [`docs/http-target.md`](docs/http-target.md) — Streamable HTTP transport, env-var headers, fixture\n- [`docs/install.md`](docs/install.md) — every install path, with troubleshooting\n- [`docs/real-world.md`](docs/real-world.md) — running packs against external MCP servers, reporting upstream\n- [`docs/real-world-findings.md`](docs/real-world-findings.md) — confirmed-bug tracker + clean-bill methodology\n- [`docs/packs/`](docs/packs/index.md) — auto-generated reference for every embedded pack\n- API: \u003chttps://docs.rs/wallfacer-core\u003e\n\n## Roadmap\n\n`v0.2` – `v0.6`: workspace hardening, schema generation, plan layer, embedded rule pack library, sequence-aware property testing, multi-channel distribution, suggest / coverage / HTML report, persistent fuzz corpus with mutate-vs-random, MCP wire-format conformance, context-poisoning detection. ✅\n\n- **v0.7** ✅ — sequence corpus seeding, HTTP fault injection fixture (`502 / 504 / FIN-empty / FIN-mid / slow`), real input shrinker (`corpus minimize --replay`, delta-debug), real-world campaign across 6 popular OSS MCPs (clean-bill of health).\n- **v0.8** ✅ — `property --max-tools / --include / --exclude` (scales packs to large servers), torture confirmed under HTTP faults, per-invariant shrinking (`corpus minimize --invariants \u003cpath\u003e`), flakiness tracker (`fuzz --runs N --aggregate`), `prompt-injection-v2` pack (50 variants spanning jailbreak / CoT / multilingual / encoded-payload / formatting-trick attacks).\n- **v0.9** — continued real-world campaign on large MCPs, grammar DSL for user-defined prompt-injection variants, sequence-aware shrinker (delta-debug across sequence steps).\n\n## Contributing\n\nIssues, PRs, and pack contributions welcome. Open a discussion on the [issues](https://github.com/lacausecrypto/mcp-wallfacer/issues) page or send a PR with a new pack under `crates/wallfacer-core/packs/`.\n\n## License\n\nDual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flacausecrypto%2Fmcp-wallfacer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flacausecrypto%2Fmcp-wallfacer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flacausecrypto%2Fmcp-wallfacer/lists"}