SEP-1913: Trust and Sensitivity Annotations - #1913
Conversation
| 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.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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, @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?
He's going to get Nick Cooper to take a look also. |
|
@localden @nickcoai I merged @rreichel3's PR so now have co-author. |
936c53b to
d255f08
Compare
d255f08 to
271fcba
Compare
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
271fcba to
f46d45e
Compare
| - **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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
are enterpise setup allowing untrustedPublic? There must have been a check at the egress controller , whatever the company is using.
|
This comment was marked as spam.
This comment was marked as spam.
| - **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. |
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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(ornone? can't make up my mind if this should be the default or if something likepersonalshould be)personalgeneralconfidentialhighly_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.
There was a problem hiding this comment.
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. 👍
|
@krubenok thanks so much for the feedback looking to start incorporating it and do more practical examples shortly, so this is perfect timing. |
Maintainer Activity CheckHi @localden! You're assigned to this SEP but there hasn't been any activity from you in 18 days. Please provide an update on:
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. |
|
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. |
This comment was marked as spam.
This comment was marked as spam.
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? |
Maintainer Activity CheckHi @localden! You're assigned to this SEP but there hasn't been any activity from you in 14 days. Please provide an update on:
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. |
|
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 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 For multi-tool-call workflow trust propagation (@Ratnaditya-J, @marras0914): a hash-chained audit log with stable Concretely: Vaara (https://github.com/vaaraio/vaara, Apache 2.0) has been emitting OVERT envelopes per MCP 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. |
|
@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. |
|
@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 ( 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 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. |
This comment was marked as spam.
This comment was marked as spam.
|
@Rul1an that's the right trim, and your four fields are already the durable shape. As an optional member on the annotation: Only This also folds @pshkv's @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 So 1913's scope decision comes out clean: reserve the optional 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. |
Maintainer Activity CheckHi @localden! You're assigned to this SEP but there hasn't been any activity from you in 14 days. Please provide an update on:
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. |
|
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 Two things pushed us here:
So the plan is to split this proposal into a few small, independently-shippable extensions, each with its own It's now scaffolded as two extensions plus a
Data-labelling schemes (the Deliberately removed: This follows the same Standards-Track → Extensions-Track refactor pattern as SEP-2127 (#2893). This PR is now the |
This comment was marked as spam.
This comment was marked as spam.
|
Good call on the Extensions-Track split, and glad The adoption evidence the IG is after is exactly where a second, unrelated implementation earns its keep. If I can bring the Vaara side. The SEP-2828 reference implementation already emits content-addressed records where 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 |
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
|
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 Spring AI Playground (incubating in the Spring AI Community) already turns tool It reads the existing On the evidence layer, the thread's emphasis on keeping the taxonomy, the policy Happy to share more if useful: how the mapping, the two enforcement layers, and Implementation references:
|
Maintainer Activity CheckHi @localden! You're assigned to this SEP but there hasn't been any activity from you in 18 days. Please provide an update on:
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. |
Maintainer Activity CheckHi @localden! You're assigned to this SEP but there hasn't been any activity from you in 20 days. Please provide an update on:
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. |
Maintainer Activity CheckHi @localden! You're assigned to this SEP but there hasn't been any activity from you in 14 days. Please provide an update on:
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: 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:
Key Features
Annotations
sensitiveHint: Granular sensitivity levels (low,medium,high)privateHint: Marks internal/private dataopenWorldHint: Indicates untrusted/external data sourcesmaliciousActivityHint: Signals detected suspicious patternsattribution: Provenance tracking for audit trailsPropagation Rules
Integration Points
trustedHintTool Annotation #1487, SEP-1560: Addition of secretHint Tool Annotation #1560, SEP-1561: Addition of unsafeOutputHint Tool Annotation #1561)Related Work
Open Questions
Closes #711
/cc @dend (sponsor)