Skip to main content

Verfy CLI

Guard coding-agent tool calls, scan and lock MCP tools, and sign a verifiable receipt for every action. Plain Python (≥ 3.10), no required dependencies, MIT.

pip install verfy                  # Python ≥ 3.10, no dependencies
pip install "verfy[fast]"          # optional: faster Ed25519 via `cryptography`

Guard Claude Code

verfy init                      # ~/.verfy: signing key (0600), starter policy
verfy hook install              # shows the change to ~/.claude/settings.json
verfy hook install --write      # saves it (a backup is kept); --project for ./.claude/settings.json

Claude Code then runs verfy hook pre-tool-use before every tool call. Verfy answers deny or ask with the rule and reason, or stays silent so Claude Code's own permissions decide; it never answers allow itself. Any error becomes ask (or deny with "fail_mode": "deny"). The hook never touches the network.

Guard MCP servers (gateway)

claude mcp add filesystem -- verfy proxy --name filesystem -- npx -y @modelcontextprotocol/server-filesystem@2025.8.21 ~/project
verfy wrap ~/.cursor/mcp.json --write      # wrap every server in a config (backup kept)

The gateway scans each tools/list answer, hides poisoned or drifted tools, checks every tools/call, can ask the user through MCP elicitation ("on_ask": "elicit"), and writes a receipt for every call.

Scan

verfy scan --discover                 # static check of Claude Code, Cursor, Claude Desktop configs
verfy scan --discover --live          # also start each stdio server and scan its tools
verfy scan tools.json --fail-under C  # a saved tools/list answer, for CI

Exit codes: config scan 0 ok, 1 medium, 2 high or worse; tool scan 1 with --fail-under when a server grades worse.

Lock

verfy lock status                 # 0 ok · 1 new/removed · 2 drift
verfy lock diff filesystem        # what changed, invisible characters spelled out
verfy lock approve filesystem     # review, then accept (--yes after reviewing in CI)
verfy lock add tools.json --server remote-api
verfy lock verify                 # verfy.lock is well formed and untampered

A project-level ./verfy.lock is used when present, otherwise ~/.verfy/verfy.lock.

In CI

verfy lock check --update         # start the servers in .mcp.json, review every definition, write ./verfy.lock
verfy lock check                  # 0 ok · 1 new/removed · 2 drift · 3 a server did not answer

Commit verfy.lock; a pull request that changes a tool then fails until someone reviews the diff and runs --update. Inside GitHub Actions the command also writes annotations and a step summary. The repository root has an action.yml that installs this CLI and runs the check:

- uses: actions/setup-python@v7
  with: { python-version: "3.13" }
- uses: ./            # or <owner>/verfy@<ref> once the repository is public
  with: { config: .mcp.json, lock: verfy.lock }

lock check starts each stdio server command from the config, as your MCP client would: pin package versions and pass keys as CI secrets. Remote servers are skipped.

Check and verify

verfy check "git push --force"                       # 0 allow · 1 ask · 2 deny
verfy check --tool send_payment --args '{"amount": 500}'
verfy ledger show
verfy verify                                         # your own ledger
verfy verify --ledger receipts.jsonl --pub <key> --kid <chain> [--start-after <hash>]   # an evidence export

Verfy Team

verfy team enroll --url https://verfy.example.com --code atle_…   # code from Dashboard → Devices
verfy sync                     # upload new receipts (run from cron/CI, never in the hook)
verfy team pull                # team policy + team-approved locks
verfy team propose SERVER TOOL # ask admins to approve a changed tool
verfy team status

Only the public key is sent at enrollment. The device token is stored in ~/.verfy/team.json (0600). Receipts are uploaded exactly as signed; they contain only hashes of arguments unless you enabled receipts.include_args, in which case sync needs --allow-args.

Policy

JSON merged over src/verfy/data/default_policy.json. Precedence: --policy FILE, ./verfy.policy.json, ~/.verfy/team/policy.json, ~/.verfy/policy.json.

{
  "on_ask": "deny",
  "fail_mode": "ask",
  "tools": {"deny": ["github/delete_*"], "ask": ["merge_pull_request"]},
  "network": {"allow": ["github.com", "*.python.org"], "unknown": "ask"},
  "rules": [{"id": "no-prod-db", "pattern": "prod-db", "verdict": "deny", "message": "Production is off-limits."}],
  "lock": {"on_new": "ask"},
  "receipts": {"include_args": false}
}

Receipt format (verfy.receipt/1)

One JSON object per line in ~/.verfy/ledger.jsonl: v, id, seq, prev, issued_at, agent, session, action{server, tool, kind, args_sha256, args?}, decision{verdict, rules, policy_sha256, approved_by?}, outcome{status, result_sha256}, tool_sha256, kid, sig. sig is Ed25519 over the canonical JSON of all other fields; prev is the SHA-256 of the previous receipt. Writes are serialized with an OS file lock, so parallel sessions never fork the chain.

SCITT signed statements

verfy scitt export --out statements/ [--issuer URL]     # one COSE_Sign1 per receipt
verfy scitt verify statements/*.cose --pub signer.json  # COSE signature, CWT subject and the receipt's own signature

Each statement is a COSE_Sign1 (RFC 9052) with alg EdDSA, content type application/json, the key id and CWT claims (iss, sub = the receipt hash) in the protected header, and the canonical receipt as payload: the signed-statement envelope of the IETF SCITT architecture draft. Verfy does not run a transparency service or issue SCITT receipts.

Tests

cd verfy-cli && python -m unittest discover -s tests     # needs Node for the cross-engine tests

Limits

Rules are pattern matching, not a sandbox. Verfy sees only calls that pass through its hook or gateway. A receipt proves what Verfy decided and that the log was not edited, not what a tool did internally. Keep ~/.verfy/key.json private.

Metadata

Release files for verfy 0.2.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 verfy 0.2.0
File Size Uploaded Provenance
verfy-0.2.0.tar.gz 78.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for verfy 0.2.0
File Interpreter ABI Platform
verfy-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 149.2 kB

Release files / verfy-0.2.0.tar.gz

Download URL verfy-0.2.0.tar.gz
Size 78.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ceb0a2cd0fe437ed4845e756c3287592a3a72960cdcec3c9ab71c349ce4b4a0b
BLAKE2b-256 checksum
How to use checksums
104ac142e1c5c91b9cfbbacda34f9a893763cfb9b842c48f98105bf02cd1ecb9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / verfy-0.2.0-py3-none-any.whl

Download URL verfy-0.2.0-py3-none-any.whl
Size 71.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
90e35a73dcdb22eec704279fadbea859c00bca2222698b9df4ad9b34d123ce5d
BLAKE2b-256 checksum
How to use checksums
9cc6876227750fc23c4060bec8199894b31b3460bd5adb26526a7ec4a5b6c9c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Security for verfy 0.2.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.

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