Same MCP version. Different tool description. Who signed it?
On Monday, a test MCP server calls its tool search and describes it as “Search the docs.” On Tuesday, it says “Search the private docs.” The server still reports version 1.4.2. The tool name and input schema are identical. Would your client ask for approval again?
An MCP client discovers tool descriptions and argument schemas through tools/list, and a host can pass that metadata to its model. Whoever controls those strings can influence what the model thinks a tool does before a call ever happens. Invariant Labs demonstrated this with tool poisoning. A November 2026 preprint by Farooq Shaikh and Biagio Montaruli tested poisoning across agent frameworks and checked whether attacks led to actual tool execution.
MCP tools specification · Invariant Labs disclosure · Cross-framework study, v2
HISTOR already had a way to record what it observed a server advertising. The new code handles the publisher’s side of the story. It lets a release workflow sign the tool manifest and gives a client a way to compare the signed revision with the definitions it just received. The format follows a proposed test in the OWASP MCP Security Testing Guide.
Draft MCPSTG-SUPP-06
One sentence, two new hashes
The publisher takes a snapshot of tools/list, following the pagination cursor until the list is complete. The seven fields eligible for HISTOR’s revision format are name, title, description, inputSchema, outputSchema, annotations, and execution. It uses tool names as object keys and rejects duplicates so one entry can’t silently overwrite another.
Each entry gets a SHA-256 digest. The full name-keyed object gets one too. Both hash the bytes of canonical JSON, so a reordered tool list or JSON object produces the same digest. The package version travels in the signed statement as context. The manifest digest depends on the tool definitions.
RFC 8785
One synthetic tool, one description edit. The diagram shows the first eight hexadecimal characters of each SHA-256 digest.
Run the one-line edit
Use the pinned HISTOR source checkout with its project dependencies installed. From that checkout, save the block as compare.py and run python compare.py in your project environment. The example was checked with Python 3.12.13 and uses two synthetic tool lists.
HISTOR source at ba957e4
from histor.revision import build_statement, digests
tool = {
"name": "search",
"description": "Search the docs.",
"inputSchema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
}
changed = {**tool, "description": "Search the private docs."}
release = {
"server": "io.github.example/docs",
"identity": (
"https://github.com/example/docs/"
".github/workflows/sign.yml@refs/heads/main"
),
"server_version": "1.4.2",
}
before = build_statement(tools=[tool], **release)
after = build_statement(tools=[changed], previous=before, **release)
assert before["serverVersion"] == after["serverVersion"] == "1.4.2"
assert after["previousManifestDigest"] == before["manifestDigest"]
assert before["manifestDigest"] != after["manifestDigest"]
assert before["toolDigests"]["search"] != after["toolDigests"]["search"]
for snapshot in ([tool], [changed]):
manifest, per_tool = digests(snapshot)
print(per_tool["search"][7:15], manifest[7:15])
The first column is the tool digest prefix. The second is the manifest digest prefix.
- 896aa19c 4f97f46c
- 1d051183 cdf20eeb
What the workflow actually signs
CI signs a small statement containing the server name, revision number, manifest digest, per-tool digests, and a link to the previous manifest digest. histor revision build writes the statement as canonical bytes. The verifier reconstructs those same bytes from the envelope by removing signer.signature and transparencyLog. The envelope can be formatted for humans because the verifier canonicalizes the statement again.
The publisher’s GitHub Actions workflow uses OIDC to obtain a short-lived signing certificate from Sigstore’s Fulcio. For an io.github.<owner>/... name, the HISTOR verifier checks the GitHub Actions issuer and the certificate’s repository owner against that namespace. The CLI can also pin an exact workflow identity.
Rekor’s inclusion proof is a separate check on the signed entry. HISTOR verifies the proof from the Sigstore bundle and checks that its log ID and index agree with the copy in the envelope.
Sigstore signing overview · Rekor
The difference shows up when a signed revision is valid but a client receives different tools. Signature verification still succeeds. For a listed server with an ingested revision, a valid raw tools array in POST /api/v1/check lets HISTOR recompute the digest. In this case, signedRevision.match comes back as different. When the client sends only an MTL/1 digest, HISTOR omits that comparison. MTL/1 labels describe observer snapshots and use their own digest format.
Rekor records the publisher’s signature. HISTOR’s revision tree records its own acceptance of that revision. The client’s tool list is compared when it calls /check.
What the pilot covered
On November 11, the first publisher signature covered revision 1 of io.github.alexar76/histor at Rekor index 3803861704. To reproduce it, the local verifier checked the public bundle against the expected workflow identity and compared the signed digest with a fresh listing of the three tools from the released aimarket-histor 0.7.0 package. Both checks passed. Editing the version in the envelope broke signature verification.
Signed revision · Signing run · Rekor entry
The revision sync and signedRevision response are implemented in the checkout, with production rollout pending as of November 11. The focused local suite passed 13 cases. It exercised canonical bytes, revision chaining, and the comparison response on SQLite and PostgreSQL. The ingest fixture bypasses cryptography, which the public bundle check above exercises.
Even a matching manifest can come from a server whose implementation or tool responses have changed. A publisher can also sign a harmful description. The next time a tool changes under a familiar version number, the client has a concrete question it can check. Did this publisher sign the exact definitions I’m about to hand to the model?
AI assistance was used to draft and edit this article and to generate the cover illustration. The example and reported local results were checked against the implementation.
Comments
No comments yet. Start the discussion.