Skip to main content

legion-lock — pin MCP server tools in a lockfile, fail CI on tool drift

An MCP server can change a tool's description or input schema after you approved it, and a model reads those descriptions as instructions (tool drift, "rug pull"). legion-lock pins the approved tool list of each MCP server in .mcp-tools.lock.json. check fails as soon as a server changed, and names the change: tool added, removed, description changed, schema paths changed.

Fingerprints come from the public LEGION transparency log of MCP tool lists (https://tlog.legion-api.com/tlog/v1). Each one is signed (Ed25519), included in an append-only Merkle tree (RFC 9162) and checked against a root key fingerprint you pin. The lockfile keeps the proofs: verify re-checks them offline.

Version française

Pin in 3 commands

python3 legion_lock.py add io.guestgraph/mental-model   # pin the current state (verified proof) in .mcp-tools.lock.json
python3 legion_lock.py check                            # 0 = matches · 1 = DRIFT (per-tool diff) · 2 = invalid proof
python3 legion_lock.py verify                           # offline: re-check the proofs stored in the lockfile

The id is the one of the official MCP registry. To approve an earlier version: add ID --at-leaf N. To prove it against a specific signed head: --at-head S.

Requirements: Python 3.8+, no dependency (cryptography is used if installed, otherwise pure-Python Ed25519). Files: legion_lock.py, plus vendor/legion_tlog_verify.py and vendor/legion_verify_mirror.py, LEGION's two public verifiers.

Check one live server in one command: legion_verify_server.py

python3 legion_verify_server.py https://mcp.example.com/mcp --save proof.json   # live tools/list vs the signed history
python3 legion_verify_server.py --offline proof.json                            # re-check the saved proof, no network

Verdict: UNCHANGED (live list = latest signed version) · CHANGED (per-tool diff) · UNKNOWN (never signed by the log) · AUTH (server requires auth). It only sends initialize and tools/list, never calls a tool. No score.

Demo (real drift, public log)

demo/demo_drift.sh pins two servers to a state BEFORE a change they really made, then runs check: io.guestgraph/mental-model (tool diagram: description and 2 schema paths changed, leaf 16044 → 17659) and ai.daystruct.api/daystruct (tool plan_project_development added, leaf 121 → 14516). Expected: exit 1 with the drift named; offline verify = 0; hand-edited lockfile = 2. Transcript: demo/transcript_drift_*.txt. Adversarial tests (lying log, forged archive, rewritten history, forged lockfile): tests/test_adversarial.py, 15/15.

What check verifies, in order

  1. The log keyring starts from the pinned root fingerprint a7c6a35b….
  2. Your pin and the current state are both signed records, included in signed heads (RFC 9162).
  3. The current head extends the head of your pin (consistency proof). Otherwise: rewritten history, fork or rollback, exit 2.
  4. Is the current fingerprint the approved one? If not, check fetches the archived definitions of both versions, accepts them only if they hash back to the signed fingerprints, then names the drift field by field.

Limits (read them)

  • This is the signed observation of a single observer (LEGION). It does not show what the server serves to other clients.
  • Only name, description and inputSchema are fingerprinted. A change limited to annotations, outputSchema or title is not seen.
  • Delay: a change appears only after LEGION's next observation, then the next signed head (one to three signed cycles a day, measured 9–11 Oct 2026). check --strict also fails when a newer observation is not signed yet.
  • Servers that require authentication are not observed.
  • The log's Bitcoin anchors are pending (no ots verify on a full node). legion-lock does not rely on them.

Integrations (integrations/)

  • github/mcp-tools-lock.yml: on every pull request and daily, verify then check. The job fails on drift.
  • pre-commit/pre-commit.sh (raw git hook).
  • claude-code/settings.json + legion_lock_hook.sh: PreToolUse hook on mcp__.*. Claude Code blocks the MCP tool call if a pinned server drifted. Result cached 10 min.

Lockfile format: FORMAT.md. Read-only MCP server on the same log: legion-mcp. License: MIT.

Metadata

Release files for legion-lock 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for legion-lock 0.1.1
File Size Uploaded Provenance
legion_lock-0.1.1.tar.gz 43.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for legion-lock 0.1.1
File Interpreter ABI Platform
legion_lock-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 87.8 kB

Release files / legion_lock-0.1.1.tar.gz

Download URL legion_lock-0.1.1.tar.gz
Size 43.5 kB
Tags Source
SHA-256 checksum
How to use checksums
f41ede0c10161ba581c0b7f1a1698e2134c09f165bd9bc6062aa2cc5780f6059
BLAKE2b-256 checksum
How to use checksums
5c34754c9f6845e0608cc7edba6baacddd7e1d597d40720f77eba79c693df496
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release files / legion_lock-0.1.1-py3-none-any.whl

Download URL legion_lock-0.1.1-py3-none-any.whl
Size 44.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e51e7bf3be129e121c8df3e59378ab0e9cb684bf6ba1b511d25f13d45b8849a3
BLAKE2b-256 checksum
How to use checksums
c1f3b2c4ded93bb2d27e5008548e32f2be359fcf9941c94b2503b608f39cdc1e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Security for legion-lock 0.1.1

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.1 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