Skip to content

SEP-1913: Trust and Sensitivity Annotations - #1913

Open
SamMorrowDrums wants to merge 11 commits into
modelcontextprotocol:mainfrom
SamMorrowDrums:sep-trust-annotations
Open

SEP-1913: Trust and Sensitivity Annotations#1913
SamMorrowDrums wants to merge 11 commits into
modelcontextprotocol:mainfrom
SamMorrowDrums:sep-trust-annotations

Conversation

@SamMorrowDrums

Copy link
Copy Markdown
Contributor

SEP: Trust and Sensitivity Annotations

Summary

This SEP proposes trust and sensitivity annotations for MCP requests and responses, enabling clients and servers to track, propagate, and enforce trust boundaries on data as it flows through tool invocations.

Motivation

As MCP adoption grows, data flows across tool boundaries without standardized trust metadata. This creates security gaps:

  1. Indirect Prompt Injection: Data from untrusted sources enters context without markers
  2. Data Exfiltration: Sensitive information can be passed to external destinations without policy enforcement
  3. Cross-Organization Boundaries: No way to mark internal vs. external data

Key Features

Annotations

  • sensitiveHint: Granular sensitivity levels (low, medium, high)
  • privateHint: Marks internal/private data
  • openWorldHint: Indicates untrusted/external data sources
  • maliciousActivityHint: Signals detected suspicious patterns
  • attribution: Provenance tracking for audit trails

Propagation Rules

  • Sensitivity escalates (never decreases) within an agent session
  • Boolean hints use union semantics (once true, stays true)
  • Attribution accumulates across context boundaries

Integration Points

Related Work

Open Questions

  1. Label namespaces for organization-specific classifications
  2. Declassification mechanisms
  3. Cross-server annotation sharing

Closes #711

/cc @dend (sponsor)

@dsp-ant dsp-ant changed the title SEP: Trust and Sensitivity Annotations SEP-1913: Trust and Sensitivity Annotations Dec 3, 2025
@localden localden self-assigned this Dec 4, 2025
Note over Web MCP: Detects prompt injection<br/>in page content
Web MCP-->>Client: Result (maliciousActivityHint: true,<br/>openWorldHint: true)

Client->>User: ⚠️ Warning: Potential malicious content detected

This comment was marked as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Should we call them MCP Server (FILE) and MCP Server (HTTP)

Although, it is kind of implied hence nit.

User->>Client: "Summarize this webpage"
Client->>Web MCP: tools/call (fetch URL)

Note over Web MCP: Detects prompt injection<br/>in page content

@realArcherL realArcherL Dec 11, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also highlight that this is best opportunity for servers to apply any preventative measures against indirect prompt injection (ex: Spotlighting, Prompt Sandwich etc)?

For example: Server applies Spotlighting and marks the data along with additional instruction. reference

OR do we want clients to deal with it, since the real attack of prompt injection(s) begin with LLMs?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't read it fully, but it seems like this is just a notification mechanism, and this should be, maybe, a new field inside the schema for suggestion mitigation if the server wants to do it.

@localden localden added the draft SEP proposal with a sponsor. label Jan 21, 2026
@SamMorrowDrums

SamMorrowDrums commented Jan 22, 2026

Copy link
Copy Markdown
Contributor Author

@localden, @rreichel3 (Open AI) is seeking to co-author this SEP as they see significant value for MCP Apps, and want to ensure that it does what they need, especially with respect to consequences of tool calls (such as being irreversible), would you be happy to also take a look at Robert's PR?

  • Open AI are in a unique position to require adoption of certain spec features for inclusion in their app store, which I think would be a boost
  • I also think Robert's ideas are cool and showcase the potential for this proposal.

He's going to get Nick Cooper to take a look also.

@SamMorrowDrums

Copy link
Copy Markdown
Contributor Author

@localden @nickcoai I merged @rreichel3's PR so now have co-author.

@SamMorrowDrums
SamMorrowDrums marked this pull request as ready for review January 28, 2026 20:33
@SamMorrowDrums
SamMorrowDrums requested a review from a team as a code owner January 29, 2026 13:54
SamMorrowDrums and others added 11 commits January 30, 2026 16:01
Introduces trust and sensitivity annotations for MCP requests and responses,
enabling clients and servers to track, propagate, and enforce trust boundaries
on data as it flows through tool invocations.

Key features:
- Result annotations: sensitiveHint, privateHint, openWorldHint, maliciousActivityHint, attribution
- Request annotations for propagating trust context
- Propagation rules ensuring sensitivity markers persist across agent sessions
- Integration with Tool Resolution (modelcontextprotocol#1862) for pre-execution annotations
- Per-item annotations for mixed results (e.g., search results)
- Defense-in-depth approach complementing tool-level annotations

Closes modelcontextprotocol#711
… type

- Extend existing ToolAnnotations with trust fields (privateHint, sensitiveHint, etc.)
- Leverage existing openWorldHint with refined meaning per context
- Remove per-item annotations (response-level aggregation only)
- Remove _meta nesting - trust annotations live in flat annotations field
- Add Alternative 1 explaining why separate type was rejected
- Update Tool Resolution integration to use flat annotations
Co-authored-by: Sam Morrow <sammorrowdrums@github.com>
Co-authored-by: Sam Morrow <sammorrowdrums@github.com>
- Rename DRAFT-trust-annotations.md to 1913-trust-and-sensitivity-annotations.md
- Update header to match SEP-1850 template format (dash-prefixed list)
- Add full PR URL
- Move issue reference to note below header
- Regenerate SEP documentation for docs site
- **User consent** cannot be meaningfully enforced without knowing a tool's real-world impact.
- **Distrust by default** leads to confirmation fatigue and bad user experience.

Action security metadata provides a declarative contract that describes where inputs go, where outputs originate, and what outcomes the tool can cause. This complements trust annotations, which track data characteristics in transit.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action security metadata provides a declarative contract that describes where inputs go, where outputs originate, and what outcomes the tool can cause. This complements trust annotations, which track data characteristics in transit.

Just for my understanding. Suppose my mcp is hosted inside a cluster as a pod and it needs egress to my internal service or maybe external, why do I enforce the security rule for data flow inside code running in that pod(I mean at protocol level), shouldn't I do it at infra(egress) level?

where inputs go, where outputs originate

I mean, shouldn't it be controlled at the infra level, not the protocol level? Since LLM clients are not deterministic, shouldn't we enforce security rules deterministically?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annotations are handled by clients, not LLMs themselves, so deterministic policy enforcement is exactly the sort of thing this could enable.


Indicates the origin of returned data.

- **untrustedPublic** — Public but unverified sources.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are enterpise setup allowing untrustedPublic? There must have been a check at the egress controller , whatever the company is using.

@connor4312

connor4312 commented Feb 4, 2026

Copy link
Copy Markdown
Contributor
  • maliciousActivityHint I have some concerns about this:

    • This is returned in tools/resolve which happens, in theory, before the actual tool execution happens. If I have a fetch_webpage tool, a server won't know if the response is potentially malicious before actually doing the fetch. It could in theory pre-fetch and cache the result, but that requires statefulness and also breaks the notion that "Resolution requests should complete in milliseconds" from SEP-1862
    • As a client this is not maximally useful to present warnings to users. Tool results size is unbounded. In my vision of strong injection/malicious detection in VS Code, we would use a model and highlight portions of the tool result which were flagged as concerning for potential manual review. The boolean hint just says something is wrong, without letting me give any better UX to users.
    • Generally speaking from the view of a client, I'm not going to trust the implementation of malicious content detection of random MCP servers. We will, at some point, do something in-product for this in VS Code. That will be tested, benchmarked, and controlled by user preference and organization policy. I might use maliciousActivityHint as a hint to give more or less scrutiny to content an MCP server returns, but nothing more.
  • Same tools/resolve concern for other hints. I think these would better belong on the Annotations which are associated with each ContentBlock in the result. That would also let you naturally be able to give ranges to which given annotations apply (byte offsets or code points, depending on the content type)

  • InputMetadata/ReturnMetadata seem okay. I would not that unlike maliciousActivityHint, I would be able to trust these as a client. The server is an authorized entity of whatever service it's representing, e.g. emails, and so I'm okay using its categorization of sources/destinations/outcomes. I think these metadata are generally fine but I am not an expert in the regulartory/data classification area.

  • RequestAnnotations.attribution -- as a client I don't think I can represent this very well. It can both be too comprehensive and also incomplete:

    • I don't know which resources the model synthesized into a given tool call, so I would have to present every resource/annotation I encountered in the conversation, which does not seem useful.
    • I don't know every resource and data encountered in a conversation. E.g. a model can use a terminal tool my client doesn't specifically recognize and that could pull in data from any number of unknown sources. Or to give another example, a previous agent session may have generated a file as intermediate content derived from any number of sources, and a new session that pulls it in would see 'just a file.'

@pshkv

This comment was marked as spam.

@dsp-ant dsp-ant added the roadmap/security Roadmap (horizon): Security & Authorization label Apr 15, 2026
@mcp-virtual-tpm mcp-virtual-tpm Bot added this to the 2026-06-30-RC milestone Apr 17, 2026
- **Resolved declarations**: `tools/resolve` selects concrete annotations for specific arguments
- **Response declarations**: `CallToolResult` includes annotations describing actual returned data

In `tools/list`, annotation fields express **possible** values. For boolean hints, `true` means the hint may be set; `false` or omission means it will never be set. For enum-like fields (for example `inputMetadata.outcomes`), tools MAY declare a set of possible values using an array; `tools/resolve` and responses SHOULD return a single resolved value.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking the example of a sensitivity annotation above, I think declaring all of them when tools/list is called should work, but I can imagine some instances where that might change within a session - would you expect the server to then send a notifications/tools/list_changed?

@krubenok krubenok left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fantastic and would be a great addition. Excellent work Sam and team! (even though the examples hit a little too close to home 😅💌).

| "user"
| "pii"
| "financial"
| "credentials"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you consider something like privileged in the common set for legal contexts or would you expect that to be done under the regulatory scope?


`DataClass` keeps sensitivity simple for common cases while allowing regulated data to be scoped. The `regulated` form declares applicable regimes; it does not assert compliance.

`RegulatoryScope` accepts arbitrary strings. The following are suggested examples for common regimes: GDPR, CCPA, HIPAA, GLBA, PCI-DSS, FERPA, COPPA, SOX.

@krubenok krubenok Apr 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since enterprises often have their own internal data classifications that don't map to industry standards (but are often supersets of them), I think the arbitrary strings here are necessary and the only "escape hatch" to support that scenario as written, but that will massively balloon the complexity of the schema for clients to support.

Wondering if something like a key:value pair of the common classes of labels and an open string description would allow for broader adoption? Microsoft's defaults are fairly complex but I think taking pieces of it might be helpful. For example, I might have data classes:

  • public (or none? can't make up my mind if this should be the default or if something like personal should be)
  • personal
  • general
  • confidential
  • highly_confidental

Those classes could be paired with an optional data type that indicates things like financial, pii or the regulatory schems(s)/open strings that apply to it. So the net result would be an email containing my health records being classified as: confidential:hipaa

The initial set of classes hopefully get broad adoption, but the individual types may not need to get as specific consideration in clients since the type should be able to cover a generalized expectation of how the data is handled.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made it further to the comments about #711 very similar thoughts there. I'll leave this here for posterity and as a nudge in a direction. 👍

@SamMorrowDrums

Copy link
Copy Markdown
Contributor Author

@krubenok thanks so much for the feedback looking to start incorporating it and do more practical examples shortly, so this is perfect timing.

@localden localden removed this from the 2026-06-30-RC milestone Apr 22, 2026
@sep-automation-bot

Copy link
Copy Markdown

Maintainer Activity Check

Hi @localden!

You're assigned to this SEP but there hasn't been any activity from you in 18 days.

Please provide an update on:

  • Current status of your review/work
  • Any blockers or concerns
  • Expected timeline for next steps

If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


This is an automated message from the SEP lifecycle bot.

@marras0914

Copy link
Copy Markdown

The response-level direction in this SEP is the right layer for data provenance. A tool-level boolean was never going to carry that signal.

Trust is also a property of call sequences, not tools or responses. The policies that fire most often in our logs are sequence-shape rules, not annotations on any one tool or response.

Wrote the longer version of this on #1487 #1487 (comment). Curious whether the working group has scoped sequence-level annotations as a separate layer, or whether it could fold into the propagation rules being proposed here.

@armorer-labs

This comment was marked as spam.

@marras0914

Copy link
Copy Markdown

One implementation detail that has been useful for us: keep the annotation taxonomy separate from the runtime evidence that produced it.

For example, a client/gateway can propagate small stable annotations like sensitive, untrusted, irreversible, or requires_review, while the local enforcement layer keeps richer evidence out-of-band:

  • deterministic credential match
  • semantic prompt-injection / exfiltration score
  • policy context such as tool_name, destination, or irreversible action
  • local feedback override reason, with a stable scan id

That separation makes the MCP message schema smaller, but still gives operators something auditable when a decision is challenged. It also avoids turning the model/context window into the place where full security evidence has to live.

We have been testing this shape in Armorer Guard as a local Rust scanner: the output reason labels are intentionally machine-readable, and the new feedback loop records local allow/block/review exemplars without mutating model weights or uploading prompts. The interesting mapping for this SEP is probably not “standardize Armorer labels”, but “make sure annotations can point to a local policy decision/evidence record when the deployment has one.”

Implementation context, if useful: https://github.com/ArmorerLabs/Armorer-Guard

The annotation as pointer idea matches what a gateway ends up doing in practice. The decision record (allow/block/approve + callId) lives in the audit log. The MCP message just carries a lighter outcome. Useful to say this from the operator side, not just the scanner side.

What does that pointer look like in the schema? A URN? An opaque string? Does 1913 need to define the pointer format, or just reserve a field for it?

@sep-automation-bot

Copy link
Copy Markdown

Maintainer Activity Check

Hi @localden!

You're assigned to this SEP but there hasn't been any activity from you in 14 days.

Please provide an update on:

  • Current status of your review/work
  • Any blockers or concerns
  • Expected timeline for next steps

If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


This is an automated message from the SEP lifecycle bot.

@vaaraio

vaaraio commented May 26, 2026

Copy link
Copy Markdown

A few notes from the runtime-evidence side, in case useful for the SEP's direction.

@armorer-labs's separation of annotation taxonomy from the runtime evidence that produced it is the right architectural cut. The two layers answer different questions. The annotation says "this output should be treated as sensitive." The evidence layer says "here is the cryptographically-verifiable record of what produced that output." A client that trusts the annotation needs the evidence layer available to verify the chain when the annotation is challenged. SEP-1913 sits well at the taxonomy layer.

For the decentralized case @viftode4 raised: a proxy at the MCP boundary is one practical answer that does not require both sides to cosign. The proxy emits attestations per tools/call, resources/read, and prompts/get regardless of whether the upstream server implements anything. The annotations defined in this SEP then live alongside that evidence, with the proxy as a stable intermediary.

OVERT 1.0 (Glacis Technologies, https://overt.is, published 2026-03-25, royalty-free patent covenant for conformant implementations) is one standardised envelope shape that can carry the runtime evidence layer. Canonical CBOR per RFC 8949, Ed25519 signatures, HMAC commitments to request content so payloads stay local. Closed schema, offline-verifiable. The non_content_metadata field can carry SEP-1913 annotations as structural fields without expanding the envelope.

For multi-tool-call workflow trust propagation (@Ratnaditya-J, @marras0914): a hash-chained audit log with stable agent_id and action_id correlations supports the call-sequence-as-property model. The attestationChainRef field @pshkv proposed maps to the action_id emitted per interaction. The policyDecisionRef maps to the decision record (ALLOW/DENY/ESCALATE plus the conformal risk interval and the reason).

Concretely: Vaara (https://github.com/vaaraio/vaara, Apache 2.0) has been emitting OVERT envelopes per MCP tools/call / resources/read / prompts/get since v0.24.0 (2026-05-20) in its MCP proxy. Working examples with GitHub MCP, SAP MCP, and Goose at examples/. The proxy is transparent to both client and server, so SEP-1913 annotations could ride in non_content_metadata without touching the protocol or requiring upstream changes.

The taxonomy work in this SEP and a runtime-evidence layer underneath are complementary. Notes on the corner cases we hit (notification streams, ack closure, propagation across upstream errors) are available if useful for the SEP's draft.

@marras0914

Copy link
Copy Markdown

@vaaraio the taxonomy and evidence split matches what we see in production. The annotation has to be small and stable to ride the protocol. The decision record, with the call sequence that fired it, lives in the audit log.

One gap still open on 1913. Sequence shape policies produce one audit record, but the response that goes back to the client has no annotation surface that says "this was the second call in a flagged sequence." So the annotation describes the tool or content, the audit log describes the sequence, and the client never sees the sequence shape as part of the trust signal on the wire.

If sequence derived annotations are in scope for this SEP, the open question is what the response level annotation should reference, and whether the sequence shape itself needs to be part of the schema or stay in the audit log.

@vaaraio

vaaraio commented May 27, 2026

Copy link
Copy Markdown

@marras0914 the gap is real, and the on-wire shape is the open question from your exchange with @armorer-labs: what does the pointer look like in the schema.

The response-level annotation gets a verifiable reference slot. The sequence shape stays in the audit / transparency layer. The reference is what lets the client check the link without trusting the host.

For the pointer format, content-addressed beats opaque-string. The pattern the SEP-2787 reference implementation in Vaara (src/vaara/attestation) uses for per-call args binding is a three-form union: digest-only, content-addressed reference ({ref, digest, canonicalization}), or redacted projection (when the underlying data needs to stay local). The same union shape fits @pshkv's attestationChainRef and policyDecisionRef. Minimum form is {digest, canonicalization} so the client can re-hash if it has the data. Fuller form adds ref into the audit stream. Strongest form pairs the reference with a transparency-log inclusion proof against a published Merkle root, so the client can verify "this annotation derives from record N in a chain rooted at R" without calling back to the host.

For "this was the second call in a flagged sequence" specifically, the response-level annotation references the audit record by its chain hash. The audit layer is hash-chained, so position-in-sequence and predecessor reconstruction fall out of the chain itself. PROV export with a per-record wasDerivedFrom layer gives the same information in a portable form for downstream verifiers.

For the taxonomy question, sequence shape, frequency anomaly, and single-output sensitivity all share the same on-wire shape: an annotation with a reference into the audit layer. The trigger taxonomy can stay enumerable in 1913 or hive off to a sibling SEP, but the wire field does not need to vary by trigger type.

For 1913's scope, the SEP either commits to the reference slot and leaves audit-layer mechanics to a sibling SEP, or stays purely on the taxonomy side and says nothing about how the annotation is verifiable. The first lets clients act on sequence-derived annotations. The second is honest about 1913's current scope but punts the trust question.

@Rul1an

This comment was marked as spam.

@vaaraio

vaaraio commented May 29, 2026

Copy link
Copy Markdown

@Rul1an that's the right trim, and your four fields are already the durable shape. As an optional member on the annotation:

evidenceRef: {
  type:             // enumerable trigger class: "sequence" | "content" | "frequency" | "policyDecision" | ...
  digest:           // hash of the referenced record
  canonicalization: // e.g. "cbor/rfc8949", so a client can re-derive the digest itself
  schema:           // id + version of the record the reference resolves to
  ref?:             // optional locator into the deployment's audit/evidence stream
}

Only digest and canonicalization are required. A client that understands nothing else still gets a bounded, machine-readable signal that the annotation derives from a record of class type, and a client that happens to hold the data can re-hash and check it. Deployments with an evidence layer fill in ref. Inclusion proofs, receipt chains, and transparency-log roots stay out of 1913 entirely; they hang off whatever ref resolves to, in a sibling evidence SEP, exactly as you framed it.

This also folds @pshkv's attestationChainRef and policyDecisionRef into one slot: both become type values rather than two separate fields, so 1913 reserves one optional member instead of growing a field per evidence kind.

@marras0914 that answers your open question directly. The sequence shape stays in the audit layer, not the schema. The wire carries the reference plus the bounded type: "sequence" tag and nothing about the sequence's internal structure. "This was the second call in a flagged sequence" is something a client learns by resolving ref, not something the response annotation spells out. The annotation surface stays the same size whether the flagged sequence was two calls or two hundred, which is what keeps it small enough to ride the protocol.

So 1913's scope decision comes out clean: reserve the optional evidenceRef, enumerate type here, define the field shape, and say nothing about how the referenced record is produced or proven. Sequence-derived annotations get somewhere to point without 1913 taking on any audit-chain or receipt semantics.

One implementation note: the digest-only / content-addressed / redacted-projection union we run for per-call argument binding in the 2787 reference impl lands on exactly this shape, so a client that implements one gets the verifier for both annotation references and argument bindings if both ship. A convenience if it happens, not something 1913 needs to depend on.

@sep-automation-bot

Copy link
Copy Markdown

Maintainer Activity Check

Hi @localden!

You're assigned to this SEP but there hasn't been any activity from you in 14 days.

Please provide an update on:

  • Current status of your review/work
  • Any blockers or concerns
  • Expected timeline for next steps

If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


This is an automated message from the SEP lifecycle bot.

@SamMorrowDrums

SamMorrowDrums commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Intent: split this SEP and migrate to the Extensions Track

A note on direction for everyone following this thread. When SEP-1913 was first framed, the Extensions Track (SEP-2133) and the experimental-ext-* incubation process didn't exist in their current form. They now do, and they're a better fit for this work than a single Standards Track SEP.

Two things pushed us here:

  • @localden's review ask for a narrower first cut — the concern that a broad taxonomy with array-or-scalar polymorphism is hard to remove or change once it lands.
  • The Tool Annotations IG's May 28 decision to pursue trust/privacy as an experimental extension first, gather adoption evidence, then ask core maintainers to absorb anything.

So the plan is to split this proposal into a few small, independently-shippable extensions, each with its own io.modelcontextprotocol/… identifier, reference implementation, and path to an Extensions Track SEP. Incubation is in experimental-ext-tool-annotations.

It's now scaffolded as two extensions plus a schemes/ folder of interchangeable data-labelling approaches, shipped as a stacked set of PRs:

  • #2 — repo scaffolding + the trust-annotations extension (the base).
  • #3 — the action-metadata extension.
  • #4 — FIDES as a data-labelling scheme under schemes/.
Extension Scope
trust-annotations The narrow data-classification taxonomy (sensitive, untrusted) + an open-ended evidenceRef pointer for richer, out-of-band evidence.
action-metadata Tool I/O + outcome contract (folds in @rreichel3's SEP-2061).

Data-labelling schemes (the evidenceRef slot). Richer evidence models are not extensions and not a wire root. They live in schemes/ as interchangeable fillers of the trust-annotations evidenceRef slot, each selected by an evidenceRef.type value, so a deployment can adopt one, several, or none without changing the extension. FIDES information-flow control (arXiv:2505.23643) is the first worked scheme (ifc.fides.v1) — the public/private-repo confidentiality case, with github-mcp-server as an emitter example. The folder is built to hold the range of other models raised in review (coarse data classification, design-pattern controls, capability tokens, cosigning, sequence-shape, attestation).

Deliberately removed: maliciousActivityHint (the structural concerns raised here are unresolved) and session-level propagation rules.

This follows the same Standards-Track → Extensions-Track refactor pattern as SEP-2127 (#2893). This PR is now the trust-annotations base of the stack; the action-metadata extension and the schemes/ folder are stacked on it. Everything is still in the incubation phase, so naming, design, and the choice of what to put forward as an extension are all open for discussion in the IG.

@Rul1an

This comment was marked as spam.

@vaaraio

vaaraio commented Jun 11, 2026

Copy link
Copy Markdown

Good call on the Extensions-Track split, and glad evidenceRef survived the carve.

The adoption evidence the IG is after is exactly where a second, unrelated implementation earns its keep. If evidenceRef is genuinely envelope-agnostic, then two independent emitters should produce references a single consumer can resolve by recomputation, and one emitter's references should resolve in two independent consumers. That two-implementation interop check is what tells you the boundary held before core maintainers absorb anything.

I can bring the Vaara side. The SEP-2828 reference implementation already emits content-addressed records where canonicalization and schema are first-class and the digest is client-recomputable, so it lands on the evidenceRef shape without assuming an envelope. During incubation I can contribute emitter examples for the policy-decision and sequence reference types, plus an independent verifier that resolves a reference by recomputation alone, without trusting the producer.

Paired with the Assay consumer cases Rul1an mentioned, that exercises the field against real records from a different emitter and a different consumer, not just the spec. I can open this against experimental-ext-tool-annotations in whatever shape the IG prefers.

@Rul1an

This comment was marked as spam.

@Rul1an

This comment was marked as spam.

@JM-Lab

JM-Lab commented Jun 25, 2026

Copy link
Copy Markdown

Adoption evidence from a shipping consumer (Spring AI Playground, Apache-2.0)

Disclosure: this comes from a maintainer of the project below, not a neutral
observer. Since the IG is gathering adoption evidence, here is a consumer data
point, with a note on the evidence layer.

Spring AI Playground (incubating in the Spring AI Community) already turns tool
annotations into an enforced trust posture: a gate that can actually withhold a
tool from the model, not just advice.

It reads the existing ToolAnnotations (readOnlyHint / destructiveHint /
idempotentHint / openWorldHint), a side-effect scope, and a sends-user-data
flag, and resolves them to an ordered risk level via a monotonic max-merge,
computed at exposure-time rather than at runtime but with the same
never-silently-lowered invariant. That level surfaces as a per-tool risk chip and
drives two separate enforcement layers: a hard cap that blocks a tool from being
exposed at all above a threshold (deny by default, no approval path), plus an
opt-in HITL gate that pauses each call of an exposed tool for human approval. So
sensitive / untrusted would feed the same level these layers already key off.
One point this backs in the proposal's direction: a single boolean wasn't enough
to drive graduated responses (a chip, a hard exposure cap, or per-call approval).
The project keys off an ordered level, which is exactly why sensitive carrying
levels rather than a flag matches what works in practice.

On the evidence layer, the thread's emphasis on keeping the taxonomy, the policy
decision, and any proof artifact as separate concerns (rather than collapsing them
into one surface) matches the project's design. Three separate mechanisms: the
annotation → risk level is the signal, the client-side ledger is the evidence,
and a human re-review is the decision. The ledger is intentionally treated as
detection, not proof. It keeps a SHA-256 over each tool's declared interface
(name + description + input schema), re-checks on every recomposition, and
withholds a tool pending re-review on mismatch. A matching digest only shows the
declared interface is unchanged, not that backend behavior behind an identical
interface is, so a match never implies trust by itself; the re-review gate does.
Making a claim's scope explicit in evidenceRef's schema (interface bytes vs.
behavior) would help prevent consumers from over-reading digest verification.

Happy to share more if useful: how the mapping, the two enforcement layers, and
the ledger are wired.

Implementation references:

@sep-automation-bot

Copy link
Copy Markdown

Maintainer Activity Check

Hi @localden!

You're assigned to this SEP but there hasn't been any activity from you in 18 days.

Please provide an update on:

  • Current status of your review/work
  • Any blockers or concerns
  • Expected timeline for next steps

If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


This is an automated message from the SEP lifecycle bot.

@sep-automation-bot

Copy link
Copy Markdown

Maintainer Activity Check

Hi @localden!

You're assigned to this SEP but there hasn't been any activity from you in 20 days.

Please provide an update on:

  • Current status of your review/work
  • Any blockers or concerns
  • Expected timeline for next steps

If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


This is an automated message from the SEP lifecycle bot.

@sep-automation-bot

Copy link
Copy Markdown

Maintainer Activity Check

Hi @localden!

You're assigned to this SEP but there hasn't been any activity from you in 14 days.

Please provide an update on:

  • Current status of your review/work
  • Any blockers or concerns
  • Expected timeline for next steps

If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


This is an automated message from the SEP lifecycle bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

draft SEP proposal with a sponsor. roadmap/security Roadmap (horizon): Security & Authorization security SEP

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[SPEC] Annotations for MCP Requests and Responses (security/privacy)