spec(ifc-fides): address review; reframe action-metadata for SEP-2061 closure
ifc-fides (review by @JoannaaKL): - confidentiality is public/private only on the wire; drop the reader-list (logins aren't uniform across servers, are themselves access-restricted, and reader sets can be huge) - add Reader-set resolution section: two private markers aren't equal, the confidentiality join is the intersection and isn't computable from opaque wire markers, so hosts resolve to concrete reader sets at decision time - integrity join is total/wire-computable; confidentiality join is not - emitter MUST classify per resource, not per repository (public repos serve non-world-readable sub-resources), making github-mcp-server a real proof point action-metadata / docs: - SEP-2061 closed 2026-06-13 in favour of this extension; this draft is now the canonical home. Updated action-metadata.mdx, README, sep-disposition, related-work, and intent-comment status accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
716eb8d89f
commit
b799af5bea
7 changed files with 87 additions and 40 deletions
|
|
@ -39,7 +39,7 @@ See [docs/decisions.md](docs/decisions.md) for the decision record and
|
|||
| Identifier | Status | What it specifies | Reference implementation(s) |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| [`io.modelcontextprotocol/trust-annotations`](specification/draft/trust-annotations.mdx) | Draft skeleton | **Primary extension.** A small, scheme-agnostic client-facing data-classification vocabulary (`sensitive`, `untrusted`) on result `_meta`, plus an optional `evidenceRef` pointer slot that carries richer payloads out-of-band. | Python SDK: [`kapil8811/mcp-trust-annotations`](https://github.com/kapil8811/mcp-trust-annotations) (138-test suite, healthcare demo, LLM usability study). |
|
||||
| [`io.modelcontextprotocol/action-metadata`](specification/draft/action-metadata.mdx) | Draft skeleton | `inputMetadata` / `returnMetadata` / outcome classifiers (incl. `requires_review`) on `ToolAnnotations`, describing where inputs go, where outputs originate, and what real-world effects a tool can cause. | Carries forward [SEP-2061 (Action Security Metadata)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2061) by [@rreichel3](https://github.com/rreichel3); reference impl per that proposal (`read_drafts` / `list_inbox` / `send_email`). |
|
||||
| [`io.modelcontextprotocol/action-metadata`](specification/draft/action-metadata.mdx) | Draft skeleton | `inputMetadata` / `returnMetadata` / outcome classifiers (incl. `requires_review`) on `ToolAnnotations`, describing where inputs go, where outputs originate, and what real-world effects a tool can cause. | Originally [SEP-2061 (Action Security Metadata)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2061) by [@rreichel3](https://github.com/rreichel3) — closed 2026-06-13 in favour of this extension; worked example `read_drafts` / `list_inbox` / `send_email`. |
|
||||
| [`io.modelcontextprotocol/ifc-fides`](specification/draft/ifc-fides.mdx) | Draft skeleton | A **profile** of the `trust-annotations` `evidenceRef` slot: `type: "ifc.fides.v1"` carrying an integrity + confidentiality label for deterministic information-flow control, following the FIDES paper ([arXiv:2505.23643](https://arxiv.org/abs/2505.23643)). | Emitter candidate: [`github-mcp-server`](https://github.com/github/github-mcp-server) (does not emit IFC labels today — closing that gap is the proof point). |
|
||||
|
||||
### Why FIDES is a profile, not a top-level extension
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
# Intent comment (draft, pre-post review)
|
||||
# Intent comments (posted)
|
||||
|
||||
This is the comment we plan to post on
|
||||
[SEP-1913](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1913),
|
||||
with an abbreviated pointer version for
|
||||
[SEP-2061](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2061).
|
||||
Kept here so it can be reviewed and stays in sync with
|
||||
[sep-disposition.md](./sep-disposition.md).
|
||||
Both comments below have been **posted**. Kept here as the source of record,
|
||||
in sync with [sep-disposition.md](./sep-disposition.md).
|
||||
|
||||
- **SEP-1913** umbrella comment — [posted 2026-06-10](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1913#issuecomment-4675047154).
|
||||
- **SEP-2061** coordination note — [posted 2026-06-10](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2061#issuecomment-4675049171);
|
||||
@localden then **closed SEP-2061 on 2026-06-13** in favour of the
|
||||
`action-metadata` extension.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,12 @@ Tracked here rather than in the spec drafts, so the drafts stay non-temporal.
|
|||
## ifc-fides
|
||||
|
||||
- Inline `_meta.ifc` for low-friction adoption vs. always behind `evidenceRef`.
|
||||
- GitHub Enterprise `internal` repo visibility → public/private/reader-set
|
||||
mapping (audience is the whole org, broader than collaborators).
|
||||
- GitHub Enterprise `internal` repo visibility → `public`/`private` mapping
|
||||
(audience is the whole org, broader than collaborators; resolved host-side).
|
||||
- Reader-set resolution is host-side by design — confidentiality join across two
|
||||
`private` sources needs the intersection, which the opaque wire marker can't
|
||||
express. Is the 3-step host resolution enough, or do some hosts need a
|
||||
standard `evidenceRef.ref` shape to locate the originating system?
|
||||
|
||||
## Parked (SEP-1913 umbrella)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ annotation work. Several were surfaced in IG meetings (notably 2026-05-28).
|
|||
## SEPs
|
||||
|
||||
- [SEP-1913 — Trust and Sensitivity Annotations](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1913) — the umbrella proposal these extensions derive from.
|
||||
- [SEP-2061 — Action Security Metadata](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2061) — carried forward as `action-metadata`.
|
||||
- [SEP-2061 — Action Security Metadata](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2061) — closed 2026-06-13; carried forward as `action-metadata`.
|
||||
- [SEP-1862 — Tool Resolution / pre-flight checks](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1862) — core-protocol, composes with these extensions.
|
||||
- [SEP-2133 — Extensions](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/seps/2133-extensions.md) — the framework this repo incubates under.
|
||||
- [SEP-2127 — Server Cards](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2893) — precedent for the Standards→Extensions Track refactor.
|
||||
|
|
|
|||
|
|
@ -68,12 +68,13 @@ session-level propagation rules. See [open-questions.md](./open-questions.md).
|
|||
|
||||
### SEP-2061 — Action Security Metadata
|
||||
|
||||
**Proposed:** becomes the [`action-metadata`](../specification/draft/action-metadata.mdx)
|
||||
extension. SEP-2061 is by [@rreichel3](https://github.com/rreichel3), who is
|
||||
also an IG co-facilitator and SEP-1913 co-author, so this is a fold-in, not a
|
||||
collision. Disposition mirrors 1913 option (A): keep the thread as the field-
|
||||
semantics discussion, add a pointer comment linking it to the
|
||||
`action-metadata` extension, refactor to Extensions Track when ready.
|
||||
**Disposition:** **closed 2026-06-13** in favour of the
|
||||
[`action-metadata`](../specification/draft/action-metadata.mdx) extension.
|
||||
SEP-2061 is by [@rreichel3](https://github.com/rreichel3), who is also an IG
|
||||
co-facilitator and SEP-1913 co-author, so this was a fold-in, not a collision.
|
||||
[@localden](https://github.com/localden) closed the PR (no active sponsor) after
|
||||
agreeing the extension is the right home; the extension now carries the field
|
||||
semantics forward, with SEP-2061 preserved as the origin and credit.
|
||||
|
||||
### SEP-1862 — Tool Resolution (pre-flight checks)
|
||||
|
||||
|
|
@ -97,7 +98,7 @@ with it if it lands, but do not block on it.
|
|||
| SEP | Title | Proposed disposition | Extension home |
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| 1913 | Trust & Sensitivity Annotations | Umbrella thread; schema moves to extensions | `trust-annotations` (+ `ifc-fides`) |
|
||||
| 2061 | Action Security Metadata | Fold into extension | `action-metadata` |
|
||||
| 2061 | Action Security Metadata | **Closed 2026-06-13**; lives as extension | `action-metadata` |
|
||||
| 1862 | Tool Resolution (pre-flight) | Stays core / Standards Track | — (composes, no dependency) |
|
||||
| 1984 | Comprehensive Tool Annotations | IG discussion item | — |
|
||||
| 2417 | Model Preferences for Tools | IG discussion item | — |
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ title: Action Metadata
|
|||
> [SEP-2061: Action Security Metadata](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2061)
|
||||
> by [@rreichel3](https://github.com/rreichel3) into the IG's experimental repo,
|
||||
> per the May 28 2026 decision to pursue trust/privacy work as an extension
|
||||
> first. SEP-2061 remains the canonical discussion thread for the field
|
||||
> semantics.
|
||||
> first. SEP-2061 was [closed](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2061#issuecomment-4675049171)
|
||||
> on 2026-06-13 in favour of this extension as the home for the work; this draft
|
||||
> is now the canonical place to discuss the field semantics.
|
||||
|
||||
## Abstract
|
||||
|
||||
|
|
@ -81,9 +82,9 @@ Carried under the extension-namespaced key on `ToolAnnotations`:
|
|||
| `outcome` | Real-world effect class: `benign` / `consequential` / `irreversible`. |
|
||||
| `requiresReview` | The tool author signals that a host SHOULD obtain explicit human confirmation before invocation. |
|
||||
|
||||
> Exact enum value sets are inherited from SEP-2061 and are **not** re-litigated
|
||||
> here; this draft tracks that proposal. Where SEP-2061 evolves, this file
|
||||
> follows.
|
||||
> Exact enum value sets are inherited from SEP-2061 as the starting point and
|
||||
> are **not** re-litigated here. With SEP-2061 now closed, this draft is where
|
||||
> they evolve.
|
||||
|
||||
### `requiresReview` lives here, deliberately
|
||||
|
||||
|
|
@ -130,3 +131,4 @@ would anchor the draft in a real ecosystem.
|
|||
| Date | Change |
|
||||
| ---------- | ------------------------------------------------------------------- |
|
||||
| 2026-06-10 | Initial draft skeleton, carrying SEP-2061 into the experimental repo; absorbed `requiresReview` from the trust taxonomy. |
|
||||
| 2026-06-15 | SEP-2061 closed in favour of this extension; this draft is now the canonical home for the field semantics. |
|
||||
|
|
|
|||
|
|
@ -44,11 +44,12 @@ integrity (is this data trusted) as context accumulates across tool calls, and
|
|||
deny or prompt before a flow violates policy.
|
||||
|
||||
A public MCP server is the natural emitter. [`github-mcp-server`](https://github.com/github/github-mcp-server)
|
||||
returns repository data whose confidentiality is determined by repo visibility
|
||||
and collaborator sets — exactly the public/private signal above — but does
|
||||
**not** emit IFC labels today. Closing that emitter gap is the concrete proof
|
||||
point for this profile: a host-side consumer of the label shape already exists,
|
||||
so the missing half is a server willing to emit it.
|
||||
returns repository data whose confidentiality follows from repository visibility
|
||||
and collaborator sets — the same public/private signal — but does **not** emit
|
||||
IFC labels today. Closing that emitter gap is the concrete proof point for this
|
||||
profile: a host-side consumer of the label shape already exists, so the missing
|
||||
half is a server willing to emit it, classifying each resource it returns (see
|
||||
[per-resource classification](#reference-implementation)).
|
||||
|
||||
## Specification
|
||||
|
||||
|
|
@ -67,32 +68,62 @@ be inlined by deployments that accept the wire cost) has the shape:
|
|||
```jsonc
|
||||
{
|
||||
"integrity": "trusted", // "trusted" | "untrusted" (FIDES §4.1 two-level lattice)
|
||||
"confidentiality": "public" // "public" | "private" | ["login1","login2", …]
|
||||
"confidentiality": "public" // "public" | "private"
|
||||
}
|
||||
```
|
||||
|
||||
| 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. |
|
||||
| `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](#reader-set-resolution)). |
|
||||
|
||||
> **Confidentiality is `public` / `private` only — never a reader list on the
|
||||
> wire.** Emitting concrete reader identities (e.g. logins) is out of scope: user
|
||||
> identity is not uniform across servers using different auth methods, the
|
||||
> identities are themselves access-restricted data, and a single resource can
|
||||
> have hundreds of readers. The opaque marker keeps the wire shape stable and the
|
||||
> sensitive resolution host-side.
|
||||
|
||||
### Label semantics
|
||||
|
||||
- **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 reader set.
|
||||
`untrusted`, confidentiality narrows toward the smallest permitted reader set.
|
||||
The **integrity join is total and computable from the wire values alone**
|
||||
(`untrusted` dominates). The **confidentiality join is NOT computable from the
|
||||
opaque wire markers alone** — see [Reader-set resolution](#reader-set-resolution).
|
||||
- **Policy check before egress.** Before a write/egress tool call, the host
|
||||
checks whether the current context label may flow to the call's target. When
|
||||
a label is absent, the host falls back to its default (trusted-action)
|
||||
policy rather than assuming the worst — labels are an *additive* signal.
|
||||
- **Confidentiality resolution.** `"private"` is intentionally opaque on the
|
||||
wire; resolving it to a concrete reader set (e.g. via a collaborators lookup)
|
||||
is a host concern, so servers need not enumerate audiences inline.
|
||||
|
||||
> 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.
|
||||
|
||||
### 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.
|
||||
|
||||
Keeping resolution host-side is what lets the wire stay at `public` / `private`
|
||||
while still supporting precise multi-source decisions.
|
||||
|
||||
### Relationship to `trust-annotations`
|
||||
|
||||
`ifc-fides` never appears without a host `trust-annotations` annotation
|
||||
|
|
@ -106,17 +137,24 @@ the IFC label is the precise, host-checkable evidence behind them.
|
|||
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)
|
||||
is the candidate — it already knows repo visibility and collaborator sets,
|
||||
which are exactly the confidentiality inputs.
|
||||
is the candidate — it already knows repository visibility and collaborator
|
||||
sets, which are the confidentiality inputs. Repository visibility is only a
|
||||
*default* hint, not the whole story: a public repository can serve
|
||||
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 returned**, not per repository.
|
||||
That makes the emitter a non-trivial proof point rather than a one-line
|
||||
`repo.private` read.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Should the label be inlinable on `_meta.ifc` directly for low-friction
|
||||
adoption, or always behind `evidenceRef` for schema minimalism? (Lean:
|
||||
permit both; `evidenceRef` is canonical, inline is a convenience.)
|
||||
- How does GitHub Enterprise `internal` repo visibility map onto the
|
||||
public/private/reader-set confidentiality model? (Audience is the whole org,
|
||||
strictly broader than collaborators — likely falls back to default policy.)
|
||||
- How does GitHub Enterprise `internal` repository visibility map onto the
|
||||
`public` / `private` confidentiality model? (Audience is the whole org,
|
||||
strictly broader than collaborators — likely classified `private` and resolved
|
||||
host-side, or falls back to default policy.)
|
||||
- Registry coordination with other attestation/evidence profiles
|
||||
(e.g. SEP-2787) so `evidenceRef.type` values don't collide.
|
||||
|
||||
|
|
@ -125,3 +163,4 @@ the IFC label is the precise, host-checkable evidence behind them.
|
|||
| Date | Change |
|
||||
| ---------- | ------------------------------------------------------------ |
|
||||
| 2026-06-10 | Initial draft skeleton. Reframed from a top-level `ifc` extension to a `trust-annotations` `evidenceRef` profile (`ifc.fides.v1`). |
|
||||
| 2026-06-15 | Confidentiality limited to `public` / `private` on the wire (dropped reader-list); added Reader-set resolution section; emitter classifies per resource, not per repository. (Review: @JoannaaKL.) |
|
||||
|
|
|
|||
Loading…
Reference in a new issue