Schema change: descriptor_digest needs a profile identifier, or a pin and an observation from two producers compare as contradicted

Aperta
#4 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
20/100
Tipo di issue
Funzionalità
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Ambito
security

Direzione di ricerca

Start with ARCHITECTURE.md sections 4 and 5, G-DESC-001 through G-DESC-003, and the descriptor evidence definitions in the v0.1 schema; schemas/evidence/v0.1/ is currently empty. Decide how descriptor digest profiles, unavailable digests, and capture conditions should be represented, then check the result against the pin comparison and claim status rules. Done means the schema and its trust rules resolve when digests are comparable and when they are not.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Problem

ARCHITECTURE.md gives descriptor_digest three jobs at once. It is a correlation join key in section 5, it is the trigger for a contradicted claim in section 4 ("invocation digest != pinned digest"), and it is the subject of G-DESC-001 through G-DESC-003. In all three it is a bare "sha256:..." string, and nothing in the repository states which bytes it covers. DT-3 fixes canonical serialization for the project's own records, not for the third-party tool descriptor being hashed.

That is fine while one recorder computes both sides. reference.pin is the case where it is not: its expected_digest comes from an out-of-band manifest, so by design the pin and the observation are produced by different code.

Public MCP tool-descriptor digests do not currently agree on what they cover, and the disagreements are not edge cases:

  • Field set. MTL/1 digests exactly name, description, inputSchema and outputSchema, dropping title, annotations and _meta. agentavow.mcp-tool-definition.v1 includes title and annotations in the same digest. An annotation-only change moves one and not the other.
  • Absent members. MTL/1 writes a missing or non-string description as the empty string. AgentAvow omits a missing member. Same descriptor, two digests.
  • Transformation inside a member. Over a month of observed changes, 1,270 modified tool entries changed only $.additionalProperties inside inputSchema. Any scope that keeps inputSchema whole sees them, and a fingerprint that rewrites that member does not.

So two honest implementations can produce different digests for a descriptor that never changed. Under section 4 as written, that is a contradicted claim, which is the strongest negative verdict the project has. EV-4 says the project must not state an attack is detectable unless a scenario test demonstrates it, and S3's pinned variant is exactly where this would land.

There is a second shape underneath it, and it is measured rather than hypothetical. Two independent readers digested the same registry on the same day about a minute apart. 315,605 of 318,389 tools agreed byte for byte, so 99.1 percent. Of the 444 servers that differed, 73 differed only in how they were read: network or none at launch, the reader's own home directory or a placeholder credential echoed into a description, outputSchema withheld from a client that did not send the protocol-version header, and only the first tools/list page read. Five more were left unexplained rather than attributed to capture. Both sides looked, they disagree, and the disagreement says nothing about the server. None of the four claim statuses has a cell for that. It is not contradicted, and it is not insufficient_evidence either, because the evidence is present on both sides.

This lands on a decision that is already open. In issue #1 the maintainer put it as the concrete first topic: recording a tool's name and version alone cannot establish which tool definition was in effect when an MCP server changes it. OD-1 then makes a bidirectional mapping with a parallel process-evidence project the thing that blocks schema v1.0. A mapping of descriptor evidence between two projects is not well defined until each side says what its digest covers.

Raising it now because schemas/evidence/v0.1/ is still empty until M1, so this is a question about what to write rather than a migration.

What the change would buy, which is the point of it:

  1. A pin and an observation compare only when both carry a profile and the two profiles are equal. Where they differ, the result is a gap, not contradicted. Suggested rule, which I am happy to file separately rather than bundle here: G-DESC-004, fires when a reference.pin and a tool.descriptor.observed for the same tool carry different profile values, protects the conclusion "the descriptor in use was the one that was pinned", severity high.
  2. not_digestible and not_observed stay apart in the record even where the gate does the same thing for both. A run carrying a hundred of the first has a different problem from one carrying a hundred of the second, and declared minus verified is not computable once they collapse. The optional revision field is there for a reason worth stating: not_digestible is only true relative to a profile at a point in time. MTL/1 refused a class of numbers under its revision 1.0 and digests them under 1.1, while the identifier in the descriptor stayed at 1, so a stored not_digestible could go out of date with nothing in the record saying so. Its revision 1.2 of 6 November now names the issuing revision on every label. A record that keeps only the profile name inherits that problem.
  3. For the capture case, the honest output is a gap rather than contradicted, and the records that support it need the capture conditions beside the digest: the negotiated protocol version, whether every tools/list page was read, and the launch conditions. A draft of such a block is published at histor.modelmarket.dev/datasets/interop-digests-2026-10-31/capture-block.md. I would rather see the capture conditions added to tool.descriptor.observed than see a fifth claim status, but either closes it.

Sources for the figures above, all public and CC BY unless noted:

  • The same gap on the wire, with the thread that produced the profile-identifier shape: GenAI-Security-Project/agent-control-standard#38
  • Registry measurement deposit, 44,172 tools across 2,043 servers: doi.org/10.5281/zenodo.22649163
  • The binding paper built on it, Bharti and Agnihotri: doi.org/10.5281/zenodo.22649164
  • HISTOR 20 October snapshot, 21,543 observed tool-list changes: doi.org/10.5281/zenodo.23512760
  • The two-reader interop comparison and the capture-block draft: histor.modelmarket.dev/datasets/interop-digests-2026-10-31/
  • MTL/1 profile, section 5.2 normalisation: github.com/alexar76/aicom/blob/main/awr/adoption/mcp-trust-label/PROFILE.md
  • AgentAvow digest vectors, Apache-2.0: github.com/AgentAvow/AgentAvow/tree/main/docs/standards/tool-manifest-digest-vectors-v1
Proposed change
// correlation.descriptor_digest becomes an object rather than a string
"correlation": {
  "descriptor_digest": {
    "profile": "mtl/1",            // required: names the field set, the absent-member rule,
                                   // and whether any member is transformed before hashing
    "profile_revision": "1.2",     // optional, where the profile versions its own rules
    "algorithm": "sha256",
    "value": "9f2b…"               // omitted when no_digest is present
  }
}

// and, where a declaration was seen but could not be reduced to a digest
"descriptor_digest": {
  "profile": "mtl/1",
  "no_digest": {
    "reason": "not_digestible",    // enum: not_observed | not_digestible
    "profile_code": "MTL-NUM-001"  // optional, the profile's own code where it gives one
  }
}
Trust and provenance impact

The profile string is a statement about how a digest was computed, so it has to inherit trust the same way everything else here does, from the channel rather than from the emitter.

For a record the recorder computes itself at a tap, the recorder sets the profile, because it owns the canonicalization. That stays observed and no emitter can touch it.

For reference.pin the profile is part of the pin's out-of-band source material and inherits the pin's trust, alongside pin_source. An agent-side component cannot assert it.

The change cannot create a new route to verified. Its only effect on claim status is to withhold a verdict where the two sides are not comparable, turning a contradicted into a gap. It never turns a gap into supported. If a self-reported profile on an asserted record were ever trusted to license a comparison, an emitter could claim a profile it did not use and a coincidental digest match would look verified, which is why the profile has to come from the same channel as the digest it describes and why a comparison requires both sides to be present rather than one.

Compatibility

Additive (existing fixtures stay valid)

Alternatives considered

Name one canonical form in the contract and require it. Cleanest for a single implementation and it gives up interoperability, which OD-1 is in the middle of deciding. It also makes this project's digests incomparable with the public records it would most want to read.

Leave descriptor_digest a string and document that pins must come from the same implementation. That is internally consistent and it removes the out-of-band manifest, which is the only thing in the trust model that can make a descriptor record verified rather than observed.

Add a fifth claim status for "incomparable". Possible, and heavier than it needs to be. A gap already means "a required element is missing", and a comparable digest on the other side is a required element.

Lingua principale
Python
Stelle
3
Fork
2
Merge medio
1m
PR unite (30g)
1

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di OWASP/Agentic-Evidence

Tutte le issue di OWASP/Agentic-Evidence

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.