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.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 legion-lock 0.1.0
File Size Uploaded Provenance
legion_lock-0.1.0.tar.gz 42.8 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 86.5 kB

Release files / legion_lock-0.1.0.tar.gz

Download URL legion_lock-0.1.0.tar.gz
Size 42.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c01754b2e3bc0f61aaa87b40ffc28471bced0d6cd94b54c83819698bca7877ed
BLAKE2b-256 checksum
How to use checksums
499c41e364f1174fc6b99c89159e7cbf61a1b6f34cb7651b82ead5a6d3edc69a
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.0-py3-none-any.whl

Download URL legion_lock-0.1.0-py3-none-any.whl
Size 43.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
41af746996a48c8a9e92323616b59e1b97d6374fb152a5c784635b7425d91ec8
BLAKE2b-256 checksum
How to use checksums
bc2673e02f872a5981bdc212bd9c56bde48c2d7d37e7f97933d2aa2605b9312f
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.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