███╗ ███╗ ██████╗██████╗ ██╗ █████╗ ██╗██╗
████╗ ████║██╔════╝██╔══██╗ ██║██╔══██╗██║██║
██╔████╔██║██║ ██████╔╝ ██║███████║██║██║
██║╚██╔╝██║██║ ██╔═══╝ ██ ██║██╔══██║██║██║
██║ ╚═╝ ██║╚██████╗██║ ╚█████╔╝██║ ██║██║███████╗
╚═╝ ╚═╝ ╚═════╝╚═╝ ╚════╝ ╚═╝ ╚═╝╚═╝╚══════╝
the flight recorder & firewall for MCP tool calls
Every tool call your agent makes — recorded, redacted, and (optionally) blocked. Local-first. One command. No SaaS.
Quickstart · Proof · How it works · Policy · Audit log · Threat model
Status: pre-1.0, building in the open. Shadow mode (observe + log) is the default and safe to try. Enforcement is opt-in. See the backlog.
The one-minute version
Your AI agent talks to MCP servers — GitHub, your filesystem, a database — through a stream of JSON-RPC tool calls. Today there is no portable record of which tool it called, with what arguments, or whether it should have been allowed to. Your provider logs show HTTP 200, not agent invoked delete_repo(name="prod").
mcpjail sits in the middle. It records every tool call to a tamper-evident audit log (hash-chained, signed, verifiable offline), redacts secrets before anything hits disk, and — if you turn it on — blocks calls that violate a policy you write in plain YAML.
# Wrap any MCP server. Zero code changes. Logs everything, blocks nothing (shadow mode).
uvx mcpjail wrap -- npx -y @modelcontextprotocol/server-filesystem /tmp
Not on PyPI yet. Until it's published,
uvx/pipx mcpjailwon't resolve — install from a clone (uv tool install .) and usemcpjail …. See Quickstart.
Proof
A coding agent, wrapped by mcpjail in enforce mode with a three-line policy that forbids repo deletion:
$ mcpjail log --since 5m --tool delete_repo --deny
2026-07-03T10:15:30Z github tools/call delete_repo
args: { "owner": "acme", "repo": "prod", "token": "[REDACTED:github_pat]" }
decision: DENY (rule: block_repo_deletion)
→ agent received a clean JSON-RPC error; the call never reached GitHub.
$ mcpjail verify
✓ 1,204 records, chain intact, 12 checkpoints, all signatures valid.
✓ audit-2026-07-03.jsonl has not been modified since it was written.
The destructive call was stopped, the token was never written to disk, and the log is provably unaltered. That's the whole product in one screen.
Quickstart (60 seconds)
# Run with no install (uv handles it):
uvx mcpjail wrap -- npx -y @modelcontextprotocol/server-everything
# Or install it (not yet on PyPI — install from a clone):
uv tool install . # or: pipx install .
# Point your agent at it automatically (backs up your config first):
mcpjail install claude # also: cursor | desktop | zcode
# Any other client: point at its MCP config directly.
mcpjail install --config /path/to/its/mcp.json
# ... use your agent normally ...
mcpjail uninstall claude # restores your original config exactly (byte-for-byte)
# See what happened, and prove the log is untampered:
mcpjail log --since 1h
mcpjail verify
Requires Python 3.11+. No Docker, no daemon, no account.
Commands
| Command | What it does |
|---|---|
mcpjail wrap -- <cmd> |
Run a local (stdio) MCP server under the jail |
mcpjail wrap-http --url <remote> --listen <host:port> |
Proxy a remote (HTTP) MCP server |
mcpjail install <claude|cursor|desktop|zcode> |
Route a client's MCP servers through mcpjail (--config PATH for any other client) |
mcpjail uninstall <client> |
Restore the client's config byte-for-byte from backup |
mcpjail log [--since 1h] [--tool T] [--deny] [--json] |
Query the audit log |
mcpjail verify |
Recompute the hash chain + checkpoint signatures |
mcpjail policy test <file> · policy explain … |
Validate a policy file · show what it would decide |
mcpjail version |
Version and environment info |
What it does
- Audit — every
tools/call,resources/read,tools/list, andinitialize, with redacted arguments, to append-only JSONL. - Redact — API keys, tokens, passwords, AWS keys, and private keys are replaced with
[REDACTED:<pattern>]before the record is hashed or written. Add your own patterns. - Tamper-evidence — a SHA-256 hash chain plus periodic Ed25519 signatures.
mcpjail verifyrecomputes the chain offline and names the first altered record if there is one. - Policy — first-match-wins YAML rules: allow/deny by server, tool, resource URI, or argument value. Default is shadow (log only); enforcement is one flag.
- Local & remote —
wrapproxies local stdio servers (opens no sockets);wrap-httpproxies remote HTTP servers. - Zero-friction adoption —
mcpjail install claude|cursor|desktop|zcoderewrites your agent's MCP config for you — top-level and project-scoped servers — anduninstallputs it back byte-for-byte.
How it works
your agent mcpjail (local) MCP server
(Claude Code, JSON-RPC ┌───────────────────────┐ JSON-RPC (github, fs,
Cursor, …) ───────────────▶│ frame → redact → policy│─────────────▶ database, …)
◀───────────────│ → audit │◀─────────────
└──────────┬─────────────┘
▼
~/.mcpjail/audit/*.jsonl (+ .sig checkpoints)
mcpjail is a protocol-agnostic JSON-RPC proxy. It matches on method names and forwards the exact bytes it received for every allowed message, so it stays correct across MCP spec revisions and never corrupts a payload. It does not reimplement MCP.
Local (stdio) vs remote (HTTP)
wrap handles local stdio servers and opens no sockets. For a remote (HTTP) server, point your client at a local listener and let mcpjail forward upstream:
mcpjail wrap-http --url https://remote.example.com/mcp --listen 127.0.0.1:8722
# then set the client's server URL to http://127.0.0.1:8722/
wrap-http audits and enforces on request bodies exactly like wrap; it is a separate, network-facing mode (the stdio path stays socket-free). It also correlates each call's outcome (ok/error) from the upstream response — JSON or SSE — without ever persisting the response body (ADR-004), and denies batch (JSON array) request bodies fail-closed rather than forwarding them ungoverned. Remaining gaps: install doesn't yet rewrite url-based entries to a local listener, and the GET server-push stream is unaudited — see docs/BACKLOG.md.
To see it observe an allowed call and block a denied one end-to-end (self-contained, no real remote needed): scripts/verify-http.sh.
Policy
version: 1
mode: enforce # start in `shadow` to observe first
rules:
- id: block_repo_deletion
match: { server: github, tool: "delete_repo" }
action: deny
reason: "Repo deletion is never allowed via an agent."
- id: block_secret_reads
match: { server: "*", resource_uri: "file://**/.env*" }
action: deny
- id: block_writes_outside_home
match:
tool: ["write_file", "edit_file"]
args: { path: "/etc/**" } # match on (redacted) argument values
action: deny
- id: flag_writes
match: { server: github, tool: ["create_issue", "create_pr"] }
action: allow
annotate: "write-operation"
Rules match first-match-wins on server, tool, resource_uri (globs; tool
may be a list), and args (each {name: pattern} must match the stringified,
redacted argument value).
mcpjail policy test mcpjail.yaml
mcpjail policy explain --server github --tool delete_repo # → "would DENY (rule: block_repo_deletion)"
# Enforce it when wrapping a server:
mcpjail wrap --policy mcpjail.yaml -- npx -y @modelcontextprotocol/server-github
The policy loaded by wrap defaults to ~/.mcpjail/policy.yaml (override with
--policy PATH); with no file present, mcpjail runs observe-only. A ready-to-copy
starting point lives at docs/policy.example.yaml.
The audit log
One JSON object per line, one file per day. Every record carries the hash of the previous one, so any edit, insertion, or deletion breaks the chain:
{"schema_version":1,"seq":42,"ts":"2026-07-03T10:15:30.123Z","method":"tools/call",
"server_label":"github","tool":"delete_repo",
"args_redacted":{"owner":"acme","repo":"prod","token":"[REDACTED:github_pat]"},
"decision":"deny","matched_rule":"block_repo_deletion",
"prev_hash":"sha256:…","record_hash":"sha256:…"}
EU AI Act note
For high-risk AI systems, Article 12 requires automatic, tamper-resistant logging of system operation. mcpjail's audit log is designed to be usable as part of that evidence for the agent's tool use. It is a building block, not legal advice or a complete compliance solution.
What the audit log does — and doesn't — prove
Being honest about the guarantees, because a trust tool that overclaims is worse than useless:
- It proves the log wasn't altered after the fact by anyone who doesn't hold the signing key, and that it was produced by this installation's key.
- It does not prove anything against an attacker who has already compromised the machine and stolen the signing key — they could rewrite and re-sign. Local-first tamper-evidence has this inherent limit; optional external anchoring may come later.
- It records tool calls, not intent. mcpjail does not detect prompt injection or judge whether a call was "meant" maliciously. It records and, if you ask, enforces your rules.
Protect ~/.mcpjail/key.ed25519 (created 0600) like any private key.
Comparison
| Scope | Deploy | Local-first | Tamper-evident log | Audit-first | |
|---|---|---|---|---|---|
| mcpjail | Any MCP server's tool calls (stdio + HTTP) | clone + uv tool install ., or config-rewrite |
Yes | Yes | Yes |
| Enterprise MCP gateways | Full gateway (routing, auth, rate-limit, …) | Server / Kubernetes | Sometimes | Varies | No (audit is one feature) |
| Pre-install scanners | Static scan of a server before use | CLI | Yes | N/A | No (not runtime) |
mcpjail's bet is to be the small, single-purpose, runtime audit + policy layer, not another heavyweight gateway.
Design docs
- SPEC.md — governed methods, audit schema, redaction, tamper-evidence, policy, CLI, success criteria
- ARCHITECTURE.md — components and data flow (stdio + HTTP)
- docs/policy.example.yaml — a ready-to-copy policy
- ADR-004 — why the log records outcome metadata, never response bodies
- BACKLOG.md — what's next (stdio-path outcome correlation,
installintegration for remote servers, GET-stream auditing)
Contributing
uv sync
uv run pytest
uv run ruff check .
Issues and PRs welcome. No telemetry lives in this project and none ever will.
License
Metadata
Release files for mcpjail 0.1.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 | Provenance | Actions |
|---|---|---|---|---|
| mcpjail-0.1.0.tar.gz | 99.5 kB | Without provenance | Details for mcpjail-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| mcpjail-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for mcpjail-0.1.0-py3-none-any.whl |
Total release size: 137.9 kB
Release files / mcpjail-0.1.0.tar.gz
| Download URL | mcpjail-0.1.0.tar.gz |
|---|---|
| Size | 99.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8cbc72820c0e4c84480f8de47f2a86b1ba9557aa61e070bf25c45124ad791f96
|
|
BLAKE2b-256 checksum How to use checksums |
d69ba399edab260297443842a40688b9efa6296fbbe31a1624aa0279877dffc5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Release files / mcpjail-0.1.0-py3-none-any.whl
| Download URL | mcpjail-0.1.0-py3-none-any.whl |
|---|---|
| Size | 38.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d05e61bb4a225b2a5e51cd6b1f13ed91904f97f46d9f1f0225dc3b84aad68e6b
|
|
BLAKE2b-256 checksum How to use checksums |
d0fca13b4a83666a530ebabdc7caf5a988cd0ac31550d25ee81448fb86803f77
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Security for mcpjail 0.1.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.