Skip to content

Preventing tool poisoning: save signatures of possible tool calls #82

Open

Description

@tadasant

One potential benefit of a centralized registry is that we could have server.json submitters list out all the possible tools their server may ever invoke, fingerprint them, and store those fingerpoints for MCP client consumption.

A third party vendor could scan and approve these fingerprints as devoid of security risks, like tool poisoning attacks.

MCP clients could then use the fingerprints to avoid tool poisoning attacks that get surfaced due to hidden dynamic tool calls or supply chain attacks.

Activity

  1. added
    not go-live blockerThis issue has been reviewed and determined to not be a blocker to go-live
    on May 27, 2025
  2. connor4312 commented on May 27, 2025

    @connor4312
    Contributor

    I had a request on this for VS Code. I didn't dive deep into it but there are a couple scenarios that complicate this.

    1. Servers that provide a dynamic tools
    2. Tools that localized (Adds i18n support modelcontextprotocol#115)
    3. Tools that may change depending on the model (Feature Request: Model-Aware Content Adaptation for MCP modelcontextprotocol#469)

    Just letting clients do this may make more sense because the client will know at least (2) and (3) or know when they change. But the ecosystem tooling here is still quite immature.

  3. tadasant commented on May 27, 2025

    @tadasant
    MemberAuthor

    If we focus on the notion of possible tools (i.e. any tool that could ever surface, beyond just at-initialization), those three problems go away, right?

  4. connor4312 commented on May 27, 2025

    @connor4312
    Contributor

    Yes. It's a bit of a pain because, given both (2) and (3) happen it's a NxM multiplier to the number of tools, but it's possible.

  5. connor4312 commented on May 27, 2025

    @connor4312
    Contributor

    (though practically only a relatively small number of servers will get localized into a large number of languages)

  6. connor4312 commented on May 30, 2025

    @connor4312
    Contributor

    I think tool representation overall, either in the registry spec or MCP metadata, would be useful. Currently clients need to start servers in order to know what tools, prompts, etc. are available. Because MCP servers can now do nice authentication, this can lead to a bunch of login prompts for the user when they first start interacting with their language model. If servers optionally (there might always be servers with nondeterministic tools) published their starting set of tools/prompts, then we could avoid this.

  7. jonathanhefner commented on May 30, 2025

    @jonathanhefner
    Member

    I think tool representation overall, either in the registry spec or MCP metadata, would be useful. Currently clients need to start servers in order to know what tools, prompts, etc. are available.

    Agreed, in particular for tool representation in MCP metadata. Unrelated to tool poisoning, but it might allow clients to statically index tools for client-side tool search.

  8. goncalossilva commented on Jul 25, 2025

    @goncalossilva

    Hi everyone, I'm glad that @tadasant raised this.

    We are facing a challenge that would also be solved by having easy access to the tools provided by each MCP server. In our case, we want to avoid forcing a user to authenticate to a service before we can even confirm it has the tools to solve their problem (via tools/list).

    Could the server schema within the registry API be augmented to include tools? This would allow a server operator to choose to list their tool schemas publicly as part of the discovery process.

    There are several benefits to this, from improved user experience for cases like ours, to more accurate MCP server selection in case there is more than one for a particular service, not to mention improved transparency within the ecosystem.

    A nuanced “possible tools” angle makes sense considering dynamic cases as @connor4312 pointed out. However, while we want to support all cases, we do want to optimize for the common case. And the majority of MCP servers does have a static list of tools.

    Happy to contribute further details on our use case or to take part in the discussion on the details.

  9. tadasant commented on Jul 26, 2025

    @tadasant
    MemberAuthor

    Thanks @goncalossilva ! I think it'd be reasonable for someone to take on adding a field for statically defined tools.

    From a guidance perspective, we could say SHOULD enumerate all possible tools.

    Steps to contribute:

    1. Update https://github.com/modelcontextprotocol/registry/blob/4f5e85b51073bc878949850a44bd66d42e2d59fa/docs/server-json/schema.json
    2. Update https://github.com/modelcontextprotocol/registry/blob/main/docs/server-json/registry-schema.json
    3. Update https://github.com/modelcontextprotocol/registry/blob/main/docs/server-registry-api/openapi.yaml
    4. Include examples in the adjacent files, update any code this impacts

    For the first update, would expect to see a thoughtful canvassing of any other precedent out there for this feature. For example, Anthropic's DXT format is probably good precedent for this / maybe we should align with their shape for tools. And the team there would probably have an opinion on this as well cc @felixrieseberg

  10. rdimitrov commented on Jul 29, 2025

    @rdimitrov
    Member

    Would we rely on the author of the MCP server to populate the list of tools? If so should there be a way to prove/validate these upon publishing? What I mean is this raises a concern that bad actors can populate this list intentionally in a wrongful way which may cause malicious results further down. I'm supportive of having the tool's list in advance, just raising that it would be nice if there's a way to guarantee its correctness upon publishing and/or consuming.

  11. tadasant commented on Jul 29, 2025

    @tadasant
    MemberAuthor

    I don't think we can pursue running servers and introspecting them - there is too much of a rabbit hole there. What do you do for remote servers? What do you do for auth gates? What do you do for dynamic lists that won't be present on initial startup? Not to mention the quagmire that is trying to get them to run at all in a unified way across all the possible package registries.

    We should think through what exposure there might be and give guidance on "how consumers should use this data" accordingly. At the end of the day, I only expect consumers to use this data as a kind of search/filtering/documentation mechanism; it shouldn't be "run" in any way, so not even prompt injection should be a concern here (perhaps we should give guidance to not pull this data into inference for any reason).

    @goncalossilva can you share more about the nature of your intended use case to help guide this level of trust factor?

  12. goncalossilva commented on Jul 30, 2025

    @goncalossilva

    Steps to contribute

    Thanks for these! I'm happy to push these forward soon.

    can you share more about the nature of your intended use case to help guide this level of trust factor?

    We're exploring using LLMs to convert prompts into workflows with minimal manual work from the user. The upcoming MCP registry is a great start to help us discover available MCP servers we might want to use, but that's generally not enough. We also need to know which capabilities are available in each MCP server.

    We could do this back and forth of first identifying relevant MCP servers, then handling authentication, and only then listing tools and preparing the workflow. And for now, we plan to do exactly that. But we're concerned about the use case where it's only after all of that, including effort from the user themself, that we realize we don't have the right tools to carry out the job. Determining that before the user puts in any effort would be much better UX.

    Does this help?


    For what it's worth, I agree that having to prove the list of tools is complex and I wouldn't consider it for a first version. Trust is paramount, but this documentation would be authored by the MCP server authors themselves. Potentially even automated via the SDK, reducing the risk of even unintentional errors.

  13. tadasant commented on Jul 30, 2025

    @tadasant
    MemberAuthor

    Does this help?

    That makes sense! And aligned with what I was expecting / I stand by my earlier thoughts.

    Potentially even automated via the SDK, reducing the risk of even unintentional errors.

    Curious what exactly you mean by this?


    Thank you for jumping in on this!

  14. goncalossilva commented on Jul 31, 2025

    @goncalossilva

    Potentially even automated via the SDK, reducing the risk of even unintentional errors.

    Curious what exactly you mean by this?

    This is something I still have to explore, but seeing how users of MCP's SDKs annotate their tools (e.g., @mcp.prompt(title="Do something") in Python) it may be possible to automate creating the “manifest” of tools for the registry, so there is less manual work involved, and consequently, it becomes less error-prone.

    Thank you for jumping in on this!

    My pleasure! Let's see where this goes. I'm off next week but planning to take some of the steps above after I'm back.

  15. 3 remaining items

  16. razashariff commented on Mar 28, 2026

    @razashariff

    Pre-registration security scanning would go a long way here. Every server submitted to the registry could be scanned for OWASP MCP Top 10 issues before listing.

    We built cybersecify for exactly this — 8 security tools that can scan any MCP server for auth bypass, command injection, SSRF, tool poisoning, rug pulls, unsigned messages, replay attacks, and rate limiting.

    A registry integration could work like:

    1. Server submitted for listing
    2. Automated scan_server runs the OWASP MCP Top 10 checks
    3. assess_risk scores every exposed tool
    4. Results displayed as a security badge on the listing

    We also have DVMCP as a test target — deliberately vulnerable, 10 intentional issues, useful for validating any scanning pipeline.

    npx cybersecify — zero dependencies, works today.

  17. DSHCorrectover commented on Aug 23, 2026

    @DSHCorrectover
  18. qy6on commented on Sep 17, 2026

    @qy6on

    Registry fingerprints and SEP-1649 answer "what did this server declare when it was published".
    There's a second half that a registry structurally cannot cover: what the server served you, in
    this session.

    A server can pass pre-registration scanning and still hand a specific client a different tool
    definition at tools/list time — different description, different schema, different annotations.
    The registry never sees that exchange, so a publication-time fingerprint can't witness it. That is
    also the shape of the rug pull: the definition that gets approved and the definition that is later
    served are not the same object.

    The client-side half is cheap and needs no registry cooperation: digest each tool's name,
    description, input schema and annotations at listing time, keep a manifest digest of the set, and
    compare on every subsequent listing. A changed definition is then detectable in-session, and if the
    digests go into a hash-linked record, provable afterwards. The two compose — a registry fingerprint
    gives you an expected value, and the client-side digest tells you whether what arrived matches it.

    On @DSHCorrectover's nested field injection: computing the digest over an extracted, fixed field
    set
    rather than over the raw JSON-RPC response closes that class by construction. Fields injected
    at any nesting depth are simply not inputs to the digest, so they cannot alter or forge it. It costs
    you detection of the injected fields themselves, which is a separate check, but the evidence value
    of the digest survives a compromised upstream.

    I've implemented the client-side half in infy (Apache-2.0) as a
    wrapper around mcp.ClientSession, tested end-to-end against a live server including a real
    mid-session description rewrite. Happy to write it up as a client-side recommendation here if that's
    useful to the SEP-1649 discussion.

    One related thing worth stating in this thread regardless: ToolAnnotations are unverified server
    claims, and the SDK says so — "clients should never make tool use decisions based on ToolAnnotations
    received from untrusted servers."
    If annotations get carried into registry manifests for
    discovery-without-connecting, that caveat needs to travel with them, or a readOnlyHint becomes a
    trust signal purely by virtue of being in a registry.

  19. gostanos commented on Sep 27, 2026

    @gostanos

    Building on the last comment: there's a third thing between those two, which is what each release of a server declared, over time.

    I run smallprint.dev, which reads the published tool definitions of the servers in this registry (plus npm and PyPI) every night and keeps a hash per version over every tool's name, description and input schema. That way you can see when a description changed between releases, and diff it.

    On the client side, npx smallprint check --locked compares what's installed on your machine against a lock you took earlier, and fails when a tool changed.

    It doesn't cover the per-session case qy6on describes, a server that serves one client something different at tools/list. That needs a check in the client itself.

    The per-version digests are public, one server at a time, for example https://smallprint.dev/api/receipt/npm/@ai-sdk/mcp/2.0.49

  20. rdimitrov commented on Oct 23, 2026

    @rdimitrov
    Member

    Consolidating two related tool-surface proposals into this tracker:

    Keep the distinction between publisher declarations and third-party observations. The design still needs canonicalization rules, treatment of annotations and dynamic/auth-dependent tools, and client verification semantics; closing the parallel proposal threads does not adopt a format or imply that this has been implemented. Their source links and evidence remain in the linked issues.

  21. gostanos commented on Oct 24, 2026

    @gostanos

    @rdimitrov

    The design still needs canonicalization rules, treatment of annotations and dynamic/auth-dependent tools, and client verification semantics

    Here's how those came out in practice for smallprint.dev, as one data point.

    Canonical form: JSON of [{name, description, inputSchema}] sorted by tool name, with every object key sorted recursively. Prose (the name, the description, and any description or title inside the schema) gets runs of whitespace collapsed to one space and trimmed. Every other string (enum, const, pattern, default) stays byte for byte since whitespace can mean something there, and required arrays get sorted because their order doesn't. Prose over 20,000 characters goes in as its opening plus the length and SHA-256 of the whole, so the digest still covers every character. The digest is SHA-256 of that JSON.

    Annotations, title and outputSchema aren't in that digest. Whether they belong in a shared format is one of the calls this thread would have to make.

    Dynamic and auth-gated tools: an observer like mine only sees what a server lists without credentials. It reads the published package, and where it can't, it starts the package in a container with no network and no keys, so anything that only shows up after sign-in isn't in the digest. The record keeps how each version was read, and I think an observation format should carry that too.

    Verification: each version's line goes into a daily row, the rows are hash-chained and signed with Ed25519, and the public key is published. The receipt for a version gives the digest, when it was recorded and the row that holds it, and a short public script recomputes everything from the API. That's the observation time and observer identity #1681 asks for, and a changed or deleted observation would show up as a broken chain.

    Example, the one from my last comment: https://smallprint.dev/api/receipt/npm/@ai-sdk/mcp/2.0.49 (the script is https://github.com/gostanos/smallprint-action/blob/main/tools/verify-chain.mjs)

  22. Paraphern commented on Oct 24, 2026

    @Paraphern

    Following the consolidation from #1720 — contributing working answers to the four open questions, from a spec that's implemented and running (PIN-SPEC, plus a live cross-implementation check below).

    Canonicalization: RFC 8785 (JCS) profile — recursive key sort, compact serialization, non-ASCII as raw UTF-8, numbers per JCS §3.2.2.3. The one rule worth naming explicitly: both sides must state their float serialization, because it's unexercised until a schema carries a non-integer number — in a live cross-implementation check (our surfaceHash vs an external observatory's manifest_hash over the same public endpoint's capture, rugsnare#6) the digests agreed byte-for-byte, with the float case explicitly called out as the open edge rather than assumed compatible.

    Annotations: store, never hash, compare spec-default-aware — an absent hint equals the spec default, so appearance and disappearance both count as changes. We grade a flipped behavioral hint (readOnlyHint etc.) as its own finding class, separate from schema drift; that separation has held up in practice.

    Verification semantics: publisher-attested fingerprints want receipts, not just fields — Ed25519-signed pins, so "the publisher approved this surface" is attributable and tamper-evident in the registry entry itself.

    Dynamic/auth-dependent tools: a surface captured under one auth context is a different surface — the fingerprint should name its capture context (unauthenticated prober vs authenticated client), which is precisely the publisher-attested vs externally-observed distinction this thread preserved from #1720. Two hashes on one entry, each labeled with its vantage, joins cleanly later.

  23. emmanuelorta commented on Nov 7, 2026

    @emmanuelorta

    On the open question of canonicalization and annotations (rdimitrov's consolidation note), here is a cross-check of three public observers on the same servers, the same day.

    Method. On 2026-11-07 between 17:16 and 17:18 UTC, I read tools/list live once from 30 registry remotes (479 tools, all pages, chosen at random from servers that HISTOR, heldfast and CrawlCheck all record), plus CrawlCheck's own server. To those bytes I applied each project's documented digest:

    • HISTOR MTL/1: {name, description, inputSchema, outputSchema?}, one RFC 8785 array sorted by name in UTF-16 code-unit order, SRI.
    • heldfast: per tool only, over {name, title, description, input_schema, annotations, output_schema?, icons?} with ""/{} defaults, RFC 8785, hex.
    • CrawlCheck lockfile v1 (I work on it): {name, description, inputSchema, annotations}, nulls for absent fields; the list hash is the sorted per-tool hashes joined by \n.

    Result.

    • 30/30: MTL/1 over my bytes equals HISTOR's published toolSetDigest.
    • 30/30: heldfast's per-tool digest over my bytes equals every tool in its feed state/ file (479/479 tools).
    • 30/30: CrawlCheck's live POST /api/v1/mcp/lock equals its spec.
    • 0/30: any digest can be compared across projects.

    So the observations agree. Only the field scope and the list-hash construction differ. Of the 30 servers, 18 send title/annotations, 4 send outputSchema, 8 send execution and 5 send _meta. Any of these moves one project's digest and not another's.

    A sample (reproduce from https://histor.modelmarket.dev/api/v1/servers?q=<name> and rufat325/heldfast-feed/state/remote__<name>.json):

    server tools HISTOR toolSetDigest MTL/1 of live bytes heldfast tools equal CrawlCheck lk v1
    io.github.emmanuelorta/crawlcheck 27 sha256-AxcAxfuTrbxxTMqG80WqtRmiGfO4kaoqWJDBCooXSDg= same 27/27 033775e1e9b87637…
    com.focxle/afos 58 sha256-V2su5xHuYY2wBDtyEcMHOM0DKKLyydTE37u7lt81O5A= same 58/58 846dcfeea10816ba…
    io.github.eorscope/eorscope-mcp 14 (sends outputSchema, annotations, _meta) sha256-2KGyt6Dq9U9Sv28QPOUa64x0tR5Hi4vjhN1jbW/XEa8= same 14/14 305f725ab2e3fc51…

    What would make these comparable without anyone re-hashing their history:

    1. RFC 8785, SHA-256. Numbers as ECMAScript writes them; keys in UTF-16 code-unit order. A missing name, a number with no double, or a partially paginated list yields no digest (null plus a reason).
    2. Two digests per tool.
      • contract = {name, description, inputSchema, outputSchema?}.
      • extras = {title?, annotations?, icons?}.
      • _meta is in neither.
      • The list-level contract_set is exactly MTL/1's tool-set digest. It already matched on 30/30, so adopting it moves no published value.
      • extras_set is the same construction over [{name, extras}].
    3. Material change classes, so an annotation edit is neither hidden nor reported as a rug pull:
      • removed, added, description rewritten, schema widened;
      • hint escalated: readOnlyHint true→false/absent, destructiveHint or openWorldHint →true, absent hints compared as spec defaults;
      • cosmetic: title or icons only.
    4. Version expectation. A contract_set change or a hint escalation SHOULD come with a new serverInfo.version; observers record version_unchanged: true when it does not. (HISTOR's record of my own server shows seven tool-set changes since 10-10, each at an unchanged serverInfo.version. The version was bumped to 1.2.0 only afterwards. That is how I found this.)
    5. Change event. A JCS-signed object with registry name, endpoint, versions before and after, both digests before and after, per-tool classes, and observer id plus vantage (unauthenticated vs publisher, per Proposal: optional contract-stability metadata in server manifests (PIN-SPEC 3.2 surfaceHash) #1720). Observers that agree on after.contract_set corroborate one another without sharing code.

    CrawlCheck now publishes this alongside its old hash, without changing the old one: jcs_v1 in lockfiles and in /api/v1/mcp/changes, documented with test vectors at https://crawlcheck.io/spec/mcp-lockfile#jcs-v1. Its value for the server above is sha256-AxcAxfuTrbxxTMqG80WqtRmiGfO4kaoqWJDBCooXSDg=, the same as HISTOR's. The 30-server table and the script are available if anyone wants to re-run them.

  24. emmanuelorta commented on Nov 7, 2026

    @emmanuelorta

    Correction to point 2 above, on extras. HISTOR already publishes toolExtrasDigest, and its construction differs from what I wrote. It covers every tool member except name, description, inputSchema and outputSchema (so title, annotations, icons, execution, _meta and vendor fields), as {"tools": {<name>: <extras>}}. CrawlCheck's jcs_v1.extras_sha256 now uses that scope with RFC 8785 and equals HISTOR's published toolExtrasDigest on the same 30 servers: 30/30, including the 10 with no extras. For io.github.emmanuelorta/crawlcheck both are sha256-G44DGgW7+rp9YjMAaEG80Tm5RFoke8eYkrE6nGk69y8=.

    The two encodings can still differ in three narrow cases: astral-plane characters in tool names (sort order), integral floats such as 1.0, and integers beyond 2^53. Worth settling if this becomes the shared definition. Spec and test vector: https://crawlcheck.io/spec/mcp-lockfile#jcs-v1

  25. Paraphern commented on Nov 7, 2026

    @Paraphern

    Following up on our earlier canonicalization answer: the pin-then-verify loop is now complete end-to-end. As of rugsnare 1.6.0 (on npm today), the Ed25519-signed pin store is checked before the server starts (tampered baseline = boot-time failure), and in enforce mode, individual tools whose contracts drifted are blocked at call time — not just flagged after the fact.

    Field data unchanged: 218 silent changes in the npm ecosystem, 140 across reference servers. A fingerprints field in the registry spec would give clients a pre-approval baseline to diff against; we'd adopt it on day one.

  26. alexar76 commented on Nov 13, 2026

    @alexar76

    On the first two items of the consolidation note, canonicalization rules and annotations: here are the rules written
    down, with vectors, and what happened when existing code was run against them.

    @emmanuelorta's cross-check above showed that the contract digest of HISTOR and of CrawlCheck's jcs_v1 already agree
    on 30 of 30 servers, and named three narrow cases where two encodings can still differ: astral-plane characters in
    names, integral floats such as 1.0, and integers beyond 2^53. I wrote that construction down as a short
    specification with no project's name in the rules, and vectors for exactly those cases; @emmanuelorta has since joined
    as co-editor: https://github.com/alexar76/mcp-tool-set-digest
    (mcp-tool-set-digest/1; text CC BY 4.0, code and vectors Apache-2.0 OR MIT).

    What is in it:

    • Two digests, as proposed above. contract covers {name, description, inputSchema, outputSchema?} of every tool,
      as one RFC 8785 array sorted by name in UTF-16 code-unit order. extras covers every other member, as
      {"tools": {name: extras}}. A set in which no tool has extras has no extras digest.
    • 73 vectors, each with the canonical text that is hashed: orderings (Z_tool before a_tool; U+1F600 before U+E000),
      number forms (1.0, 1E2, -0.0, 1e21, an integer beyond 2^53, 1e400), every JSON escape, NFC and NFD, lone
      surrogates, two tools with one name, wrongly typed members, readOnlyHint flips, and which refusal code wins when
      several apply.
    • Three reference implementations, one file each with no dependencies (Python, JavaScript, Go). They agree on all 73.

    What running existing code against the vectors showed:

    • Contract digest: HISTOR's code and WARDEN's code, both written before this text, agree on 73 of 73.
    • Extras digest: HISTOR's code agrees on 57 and differs on 16. Each of the 16 is one of the three cases named above, or
      one of the storage limits of the deployed form (a 16 KiB cap per tool, U+FFFD for lone surrogates, an entry without a
      name passed over, of two tools with one name the last kept). The list is in the repository
      (scripts/cross_check_histor.py). The serialiser cases are defects on HISTOR's side; its code can now write the
      RFC 8785 form, and its public log still uses the old one.
    • CrawlCheck's SDKs now implement the text as its own function, apart from the bounded jcs_v1 that signed lockfiles
      carry. At commit 7568d46 they give every value of the 70 vectors that predate the three on the order of refusal
      codes.
    • The five values printed in CrawlCheck's specification (empty list, tools_sha256, extras_sha256, per-tool
      contract and extras of its one-tool example) reproduce.

    Settled between the two editors: the storage limits describe how a log stores extras, not the digest, and a value
    computed under them is labelled as such; two tools with one name that both carry extras give no extras digest, and the
    shared name is reported. Still open: whether instructions and serverInfo need a digest of their own, and under which
    field a publisher or the Registry would state the digest it expects. The last one is this tracker's question, and the
    text does not try to answer it.

    On client verification, one data point: the same comparison now runs as a validator for the interceptor extension
    (the response to tools/list), in WARDEN's source tree. It takes the expected digests in config and answers which
    tools were added, removed or changed, and whether a change is in the contract or only in extras.

    If the vector file is useful here, I am glad to move it wherever the Registry WG prefers, or to add cases.

    Disclosure: I maintain HISTOR and WARDEN. This comment was drafted with AI assistance and checked by me.

  27. emmanuelorta commented on Nov 13, 2026

    @emmanuelorta

    Confirming from the CrawlCheck side, with one update to the numbers above.

    The 70-vector figure was for commit 7568d46, before the three vectors on the order of refusal codes existed. We changed our code to follow section 10 (every entry is checked before any value is written) instead of asking for the text to allow either code. Results against the vectors at aca5812:

    Implementation Version Result
    JavaScript SDK @crawlcheck/sdk 1.15.1 (npm) 73 of 73
    Python SDK crawlcheck 1.16.1 (PyPI) 73 of 73
    Lockfile endpoint (POST /api/v1/mcp/lock) deployed 13 Nov 2026 73 of 73

    The SDK runs used the packages as installed from the registries. The endpoint figure is its digest function run over the vector file, not 73 live requests. Source: https://github.com/emmanuelorta/crawlcheck-core at 31771a9.

    Signed lockfiles now carry tool_set_digest (this text) beside the older jcs_v1. jcs_v1 stays for existing verifiers and is described as a bounded form, as section 8.1 asks: https://crawlcheck.io/spec/mcp-lockfile

    Disclosure: I maintain CrawlCheck and co-edit the text. This comment was drafted with AI assistance.

  28. alexar76 commented on Nov 14, 2026

    @alexar76

    A follow-up to the two comments above. Since 2026-11-13 20:03 UTC, HISTOR's public log writes the extras digest as section 8 of mcp-tool-set-digest/1 defines it (RFC 8785); until then it used sorted-key JSON. Of about 186,000 stored extras bodies, 54 got a different digest and 3,334 stored references were rewritten, with no change recorded for any of them. Contract digests were already RFC 8785 and did not move.

    A cross-check the same evening on the 30 servers in CrawlCheck's shared list, with CrawlCheck reading each server itself and HISTOR's latest observation (the same day for 28 of them, 10 November for one): contract digests agree on 29 of 29, extras digests on 29 of 29 (20 servers carry extras, 9 none on either side); one server could not be read. On these servers the old encoding gave the same values, so the move matters only where the two encodings differ (54 bodies in HISTOR's log); what changed is that both observers now compute the pair by one written rule, annotations included in the second digest.

    Disclosure: I maintain HISTOR. This comment was drafted with AI assistance and checked by me.

  29. lanbass869-cell commented on Nov 14, 2026

    @lanbass869-cell

    A third piece worth covering here: each per-version record could be signed and bound to the artifact digest, so a client can verify it offline instead of trusting the recorder.

  30. lanbass869-cell commented on Nov 14, 2026

    @lanbass869-cell

    To add to that: the per-version digests smallprint keeps are genuinely useful, but a client still has to trust smallprint's service and database. The same is true of any registry-side fingerprint.

    What composes with both is making the record verifiable without trusting the recorder: sign it (DSSE/Ed25519, in-toto Statement v1 payload) and bind it to the artifact digest, so anyone can check it offline with the public key. And on re-scan, supersede rather than overwrite, so a verdict flip stays auditable instead of disappearing.

    I built and ran that end to end: 20 PyPI MCP servers, each record bound to the exact sdist sha256 it was scanned from, signed, and published with the public key beside it. The records are under docs/scan/ in the lanbass869-cell/canari repository.

    Two caveats I want to be explicit about:

    • the scans in that sample were run by bandit, not by me; each record names the upstream tool and my code only records and signs it
    • this is artifact-level (bytes), not tool-surface-level, so it is complementary to SEP-1649 rather than a substitute
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

not go-live blockerThis issue has been reviewed and determined to not be a blocker to go-live

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions