Scaffold trust/privacy extensions from the SEP-1913 carve#2
Scaffold trust/privacy extensions from the SEP-1913 carve#2SamMorrowDrums wants to merge 4 commits into
Conversation
Carve the schema-bearing parts of SEP-1913 into three independent experimental extensions, per @localden's "narrower first cut" ask and the Tool Annotations IG's 2026-05-28 extension-first decision: - io.modelcontextprotocol/trust-annotations (headline): narrow data-classification taxonomy (sensitive, untrusted) + open-ended evidenceRef pointer. - io.modelcontextprotocol/action-metadata: carries forward SEP-2061 (inputMetadata/returnMetadata/outcomes + requiresReview). - io.modelcontextprotocol/ifc-fides: FIDES information-flow control as a profile of evidenceRef, not a wire root. Adds repo meta (README, MAINTAINERS, CONTRIBUTING, LICENSE) and docs (sep-disposition, intent-comment, decisions, open-questions, trust-model, related-work). FIDES demoted to a profile; DataClass and requiresReview rehomed; maliciousActivityHint and propagation parked on the SEP-1913 umbrella. Citations index on public sources only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace 'carve'/'headline' throughout; reframe the front-facing docs (README, spec drafts, related-work) to describe what the repo contains and link to it rather than narrate the process. Process framing stays in the decision log and the intent-comment/sep-disposition planning docs. Live SEP-1913/SEP-2061 comments updated to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Official extension specs (ext-auth) use only `title:` in frontmatter plus an <Info> protocol-revision marker. Remove the invented extension_identifier / status / maintainers / related / profile_of fields and restate the identifier and profile relationship in the body prose where they belong. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
This carve is the right shape, and the One small editorial point on With only CBOR shown, implementers may treat it as the default rather than one valid envelope choice, which would quietly narrow the extension point the field is meant to keep open. On the open questions, keeping Happy to help exercise the shape with worked emitter/consumer cases for |
| ```jsonc | ||
| { | ||
| "integrity": "trusted", // "trusted" | "untrusted" (FIDES §4.1 two-level lattice) | ||
| "confidentiality": "public" // "public" | "private" | ["login1","login2", …] |
There was a problem hiding this comment.
We can't emit user logins as part of confidentiality for a few reasons:
- user identity is not uniform across mcp servers that use different auth methods
- user identity is itself an access-restricted data
- in real systems there can be hundreds of users that can access given resource and returning them will cause
I'd propose limiting it topublicandprivate
| | Field | Meaning | | ||
| | :--- | :--- | | ||
| | `integrity` | Two-level integrity lattice (`trusted` ⊑ `untrusted`): trusted data may flow to untrusted sinks, not vice versa. | | ||
| | `confidentiality` | `"public"` = world-readable; `"private"` = an opaque marker the host resolves to a reader set (e.g. repo collaborators); an explicit `string[]` = pre-resolved reader logins. Fewer readers = more confidential = higher in the lattice. | |
There was a problem hiding this comment.
| | `confidentiality` | `"public"` = world-readable; `"private"` = an opaque marker the host resolves to a reader set (e.g. repo collaborators); an explicit `string[]` = pre-resolved reader logins. Fewer readers = more confidential = higher in the lattice. | | |
| | `confidentiality` | `"public"` = world-readable; `"private"` = an opaque marker meaning "restricted to some reader set". The concrete reader set is resolved host-side at policy-decision time (see Reader-set resolution). | |
|
|
||
| ### Label semantics | ||
|
|
||
| - **Join on accumulation.** As a session ingests labeled results, the context |
There was a problem hiding this comment.
I'd update the bullets to
- Join on accumulation. As a session ingests labeled results, the context
- label is the join of what it has seen: integrity degrades toward untrusted,
- confidentiality narrows toward the smallest permitted reader set. Integrity
- join is total and computable from the wire values alone (untrusted
- dominates). **Confidentiality join is NOT computable from the opaque wire
- markers alone** — see Reader-set resolution.
| > The normative integrity/confidentiality lattice definitions follow the FIDES | ||
| > paper, §4.1 and §4.3. This draft references the model rather than restating | ||
| > the proofs. | ||
|
|
There was a problem hiding this comment.
Can we add a separate section to descrive reader set resolution?
Reader-set resolution
"private" is intentionally opaque on the wire. Two distinct "private"
markers (e.g. file contents from two different private repositories) are **not equal** and their confidentiality join is not the same "private" token: data
derived from both may flow only to principals who can read both sources — the
intersection of their reader sets. The opaque marker cannot express this
intersection, so a host that needs to make a precise cross-source flow decision
MUST resolve each "private" marker to a concrete reader set before joining.
Resolution is a host-side concern, performed at policy-decision time:
1. The host maps each contributing "private" label back to its source
(e.g. via the evidenceRef.ref locator, or its own record of which tool
result carried the label).
2. The host queries the originating system for the current reader set
(e.g. a repository collaborators lookup) using its own credentials.
3. The host computes the flow decision over the resolved sets (intersection for
a join of multiple private sources) and then discards them.
|
+1 to this direction — it's the precision that was missing behind keeping One case worth adding to the section: when resolution isn't available — Net, the wire stays small and free of user identities while hosts still have enough to make a precise flow decision when they hold source provenance plus host-side credentials — the same small-marker-on-the-wire, richer-state-host-side split as evidenceRef. |
| confidentiality}` label, maintains a context label across tool results, and | ||
| applies a flow policy before egress operations already exists in practice. | ||
| (Linked once a public reference is available.) | ||
| - **Emitter (gap / proof point):** [`github-mcp-server`](https://github.com/github/github-mcp-server) |
There was a problem hiding this comment.
Note that repository visibility is not the whole story - a public repository can contain sub-resources that are not world-readable (draft security advisories, draft releases, the collaborator roster itself, authenticated-user fields), so a correct emitter MUST classify per resource, not per repository. This makes the emitter side a non-trivial proof point rather than a one-line repo.private read.
|
+1 repository visibility is a default hint, not a classification boundary, so the emitter has to classify per resource returned (a public repo still serves things that aren't world-readable: draft advisories, the collaborator roster, authenticated-user fields), not per repo. It divides cleanly: the label rides the actual resource (emitter-side), the host resolves the concrete reader set later only when it needs a precise decision (the reader-set-resolution section), and unknown or mixed provenance classifies |
What this is
The first scaffold for incubating the SEP-1913 (Trust & Sensitivity Annotations) work as small, independently-shippable experimental extensions, following the Extensions Track (SEP-2133) and the Tool Annotations IG's 2026-05-28 decision to pursue this extension-first.
SEP-1913 stalled because it bundled four concerns into one schema change with a taxonomy that's hard to remove once shipped (@localden: "If the taxonomy turns out to be wrong, I worry that we can't remove it… Can we do a potential narrower first cut?"). This repo is that narrower first cut.
The carve
io.modelcontextprotocol/trust-annotationssensitive,untrusted) + open-endedevidenceRefpointer.io.modelcontextprotocol/action-metadatainputMetadata/returnMetadata/outcomes +requiresReview.io.modelcontextprotocol/ifc-fidesevidenceRef, not a wire root.Key decisions
ifc/root would silently foreclose them. It lives astype: "ifc.fides.v1"inside the openevidenceRefslot.evidenceRef.typestays an open string + non-binding registry, so future schemes fit without a spec change.requiresReview→ action-metadata (workflow signal, not a data property).data-class.v1profile, off the wire.maliciousActivityHint(structural objections) and session propagation.See
docs/sep-disposition.mdfor the per-SEP plan and the SEP-2127 migration precedent, anddocs/decisions.mdfor the rationale log.Follow-ups (not in this PR)
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com