Skip to main content

mcp-fingerprint

Deterministic fingerprinting for MCP tool contracts.

If you expose tools through an MCP server, those tools have a contract: names, schemas, descriptions, and related metadata. mcp-fingerprint turns that contract into a stable SHA-256 fingerprint, can save a receipt-bound baseline, and can later tell you whether the contract changed — and if so, what changed as a structural diff.

Same input → same fingerprint. Differences are inspectable field-level changes, not opaque scores or AI judgments.

Why use it

Use MCP Fingerprint when you want to:

  • detect tool-contract drift between environments or releases
  • pin a known-good MCP tool surface as a local baseline
  • review exact structural changes (added/removed tools, schema edits, etc.)
  • keep the check deterministic and automatable in CI or scripts

Install

pip install 'mcp-fingerprint==0.2.0'

Requires Python >=3.12,<3.13.

This pulls salt-grain 0.1.0 for the small deterministic primitives (canonicalize, digest, structured diff, receipt bind/verify) and the official mcp Python SDK (>=1.28,<2) for stdio acquisition. You do not need to install those separately.

CLI (0.2.0)

Fingerprint a live stdio MCP server, save a project-local baseline, or check for tool-contract drift:

mcp-fingerprint fingerprint -- <command> [args...]
mcp-fingerprint save -- <command> [args...]
mcp-fingerprint check -- <command> [args...]

The -- separator before the server command is required. Transport is stdio only in 0.2.0 (no HTTP/SSE). There is no --strict-tools flag.

Useful flags:

Flag Meaning
--baseline PATH Explicit baseline file (overrides default path)
--baseline-dir DIR Default dir for baselines (default .mcp-fingerprint)
--json Emit the machine-readable result envelope
--timeout SECONDS Stdio acquisition timeout

Default baseline path:

.mcp-fingerprint/<sanitized-server-name>.json

Option A live projection

When acquiring tools from a live MCP server, 0.2.0 projects each Tool into the closed mcp.fingerprint.snapshot.v0.1 identity by omitting icons, _meta, and execution (and any other non-identity keys). The library path for caller-supplied snapshots still rejects icons / _meta / unknown tool keys as UNSUPPORTED_SNAPSHOT.

Exit codes

Code Meaning
0 Success (fingerprint / save) or check UNCHANGED
1 Operational / core failure
2 Check observed CHANGED

Example:

mcp-fingerprint check --json -- python -m my_mcp_server
echo $?   # 0, 1, or 2

Expected snapshot shape

Pass an MCP tool-contract snapshot shaped like:

snapshot = {
    "schema_version": "mcp.fingerprint.snapshot.v0.1",
    "server": {"name": "demo", "version": "1.0.0"},
    "tools": [
        {
            "name": "search",
            "description": "Search documents",  # optional
            "inputSchema": {
                "type": "object",
                "properties": {"q": {"type": "string"}},
            },
            # optional when present on the tool object:
            # "title": "...",
            # "outputSchema": {...},
            # "annotations": {...},
        }
    ],
}

Identity includes schema_version, server.name, server.version, each tool's name and inputSchema, plus optional description / title / outputSchema / annotations only when those keys are present. Missing optional keys are not synthesized. Tool order in the list does not matter (tools are sorted by name). Unsupported keys such as icons and _meta are rejected.

Quickstart: fingerprint → save → check

from pathlib import Path
from mcp_fingerprint import fingerprint, save, check

snapshot = {
    "schema_version": "mcp.fingerprint.snapshot.v0.1",
    "server": {"name": "demo", "version": "1.0.0"},
    "tools": [
        {
            "name": "search",
            "description": "Search documents",
            "inputSchema": {
                "type": "object",
                "properties": {"q": {"type": "string"}},
            },
        }
    ],
}

# 1) Fingerprint
fp = fingerprint(
    {
        "schema_version": "mcp.fingerprint.request.v0.1",
        "snapshot": snapshot,
    }
)
assert fp["ok"] is True
print(fp["fingerprint"])  # 64 lowercase hex chars; stable for this snapshot

# 2) Save a receipt-bound baseline (fails if the path already exists)
baseline_path = Path("baseline.json")
if baseline_path.exists():
    baseline_path.unlink()
saved = save(
    {
        "schema_version": "mcp.fingerprint.save.request.v0.1",
        "snapshot": snapshot,
        "baseline_path": str(baseline_path),
    }
)
assert saved["ok"] is True

# 3) Check unchanged
unchanged = check(
    {
        "schema_version": "mcp.fingerprint.check.request.v0.1",
        "snapshot": snapshot,
        "baseline_path": str(baseline_path),
    }
)
assert unchanged["ok"] is True
assert unchanged["status"] == "UNCHANGED"
assert unchanged["changes"] == []
print(unchanged["status"])

# 4) Change a tool contract and get CHANGED + structural diff
changed_snapshot = {
    "schema_version": "mcp.fingerprint.snapshot.v0.1",
    "server": {"name": "demo", "version": "1.0.0"},
    "tools": [
        {
            "name": "search",
            "description": "Search documents (updated)",
            "inputSchema": {
                "type": "object",
                "properties": {"q": {"type": "string"}},
            },
        }
    ],
}
changed = check(
    {
        "schema_version": "mcp.fingerprint.check.request.v0.1",
        "snapshot": changed_snapshot,
        "baseline_path": str(baseline_path),
    }
)
assert changed["ok"] is True
assert changed["status"] == "CHANGED"
assert changed["changes"]  # inspectable structured diff entries
print(changed["status"], len(changed["changes"]))
print(changed["changes"][0])

Expected behavior for that example:

  • fingerprint prints the same 64-character hex string every run for the same snapshot
  • first check prints UNCHANGED
  • second check prints CHANGED with a non-empty changes list describing the description edit

How it works

normalized MCP tool contract
    ->
canonical bytes
    ->
deterministic SHA-256 fingerprint
    ->
optional receipt-bound baseline
    ->
structural diff
  1. Normalize — sort tools by name; keep only supported identity fields; reject unsupported envelope keys.
  2. Canonicalize — encode the normalized snapshot as exact deterministic bytes.
  3. SHA-256 — digest those bytes to a lowercase hex fingerprint.
  4. Baseline (optional)save writes fingerprint + snapshot + receipt to a local JSON file; check verifies the receipt before comparing.
  5. Structural diff — when fingerprints differ, check returns field-level changes (or a typed limit failure if the diff budget is exceeded).

CHANGED vs UNCHANGED

Status Meaning
UNCHANGED Current fingerprint matches the verified baseline; changes is [].
CHANGED Fingerprint differs; changes is a structural diff of identity fields.

check fails with a typed error (not CHANGED / UNCHANGED) when the baseline is missing, invalid, fails receipt verification, or when limits are exceeded.

Determinism guarantees

  • Identical normalized snapshots → identical fingerprints.
  • Tool list order does not affect identity.
  • Missing optional tool keys are not filled in; empty string ≠ absent key.
  • Caller-supplied mappings are not mutated.
  • No AI / model judgment is involved.

Failure behavior / limits

Operations return structured envelopes with ok: true|false. Common failure codes:

Code Meaning
DUPLICATE_TOOL_NAME Two tools share a name
UNSUPPORTED_SNAPSHOT Rejected fields (e.g. icons, _meta)
BASELINE_ALREADY_EXISTS save path already exists
BASELINE_INVALID Baseline file is not a valid baseline
BASELINE_VERIFICATION_FAILED Receipt verification failed (e.g. tamper)
LIMIT_EXCEEDED Canonical payload or diff change budget exceeded

Oversized or non-conforming inputs fail closed with typed codes.

What this is not

MCP Fingerprint does not:

  • use AI judgment or semantic “similarity”
  • detect malware
  • authenticate publishers
  • sign code or establish trust anchors
  • intercept or filter runtime MCP traffic

It fingerprints and diffs the tool-contract snapshot you supply.

Python support

  • Python >=3.12,<3.13

License

Apache License 2.0. See LICENSE.

Release files for mcp-fingerprint 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 mcp-fingerprint 0.2.0
File Size Uploaded
mcp_fingerprint-0.2.0.tar.gz 31.0 kB Details

Built distribution (wheel)

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

Total release size: 58.7 kB

Release files / mcp_fingerprint-0.2.0.tar.gz

Download URL mcp_fingerprint-0.2.0.tar.gz
Size 31.0 kB
Tags Source
SHA-256 checksum
How to use checksums
e81a7956414cd7bf0c736523b4100633d0dcffdd88ddac6035aab3f50e9925de
BLAKE2b-256 checksum
How to use checksums
79da61d9226d3d5d7f09b18c180c69d17b253aecaff4c3b7a6ee023940e08ecf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.

Transparency log

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

Download URL mcp_fingerprint-0.2.0-py3-none-any.whl
Size 27.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3af907171856f9b1be7ca82971bbd215fd77e32afe87707cb3c377468f282760
BLAKE2b-256 checksum
How to use checksums
502ff1c93966a2d82ce5d30eb59c56e85ffe31f36c9d903bd7703da86c7db847
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.1

2 release files

This release

0.2.0 This release

2 release files

0.1.1

2 release files

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