Skip to main content
   ███╗   ███╗ ██████╗██████╗      ██╗ █████╗ ██╗██╗
   ████╗ ████║██╔════╝██╔══██╗     ██║██╔══██╗██║██║
   ██╔████╔██║██║     ██████╔╝     ██║███████║██║██║
   ██║╚██╔╝██║██║     ██╔═══╝ ██   ██║██╔══██║██║██║
   ██║ ╚═╝ ██║╚██████╗██║     ╚█████╔╝██║  ██║██║███████╗
   ╚═╝     ╚═╝ ╚═════╝╚═╝      ╚════╝ ╚═╝  ╚═╝╚═╝╚══════╝
        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 mcpjail won't resolve — install from a clone (uv tool install .) and use mcpjail …. 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, and initialize, 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 verify recomputes 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 — wrap proxies local stdio servers (opens no sockets); wrap-http proxies remote HTTP servers.
  • Zero-friction adoption — mcpjail install claude|cursor|desktop|zcode rewrites your agent's MCP config for you — top-level and project-scoped servers — and uninstall puts 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, install integration 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

Apache-2.0.

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)

Source distribution for mcpjail 0.1.0
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)

Table of built distributions (wheels) for mcpjail 0.1.0
File Interpreter ABI Platform Actions
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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page