From ba3f5fa095b48b9ed1fa421fe71eba66b01fc699 Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Fri, 22 May 2026 22:10:41 -0700 Subject: [PATCH 01/12] Add SEP-0000: Attested Tool-Server Admission (ATSA) --- seps/0000-attested-tool-server-admission.md | 323 ++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 seps/0000-attested-tool-server-admission.md diff --git a/seps/0000-attested-tool-server-admission.md b/seps/0000-attested-tool-server-admission.md new file mode 100644 index 000000000..5c4750ec5 --- /dev/null +++ b/seps/0000-attested-tool-server-admission.md @@ -0,0 +1,323 @@ +# SEP-0000: Attested Tool-Server Admission (ATSA) + +- **Status**: Draft +- **Type**: Standards Track +- **Created**: 2026-05-22 +- **Author(s)**: Alfredo Metere (@metereconsulting) <alfredo.metere@enclawed.com>, Enclawed LLC +- **Sponsor**: None (seeking) +- **PR**: (to be assigned — rename this file to `NNNN-attested-tool-server-admission.md` once the PR number is known) +- **Preprint**: doi:10.5281/zenodo.20349263 +- **Requires**: RFC 2119, RFC 8174, RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519) + +The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHOULD**, +**SHOULD NOT**, **MAY**, and **OPTIONAL** are to be interpreted as in RFC 2119 +and RFC 8174. + +## Abstract + +MCP standardizes how a host and a tool server exchange messages, but not *trust*: +a host reads a server's self-declared tool list and dispatches calls with no +notion of which servers it may use, at what sensitivity, or which of a server's +tools are in bounds. This SEP adds an **optional, purely additive** admission +layer: a server publishes a small, offline-signed *clearance assertion* at a +well-known URI; a host verifies it against a locally pinned trust root before any +tool dispatch; admitting a server is kept distinct from authorizing its tools via +a closed per-server tool allow-list; and every admission decision is auditable. +No existing MCP message changes shape, and an unextended host ignores the +mechanism and behaves exactly as today. + +## Motivation + +A host today takes a server's identity (beyond its TLS certificate) and its +advertised tool list on faith. A prompt-injected model can therefore drive a +destructive tool on any server it connects to. This is exploitable in **every** +deployment; for a single operator it is a risk they absorb, but for a regulated +operator it is disqualifying — and, lacking any admission record, unauditable. +NIST SP 800-53 expects least-privilege access enforcement (AC-3/AC-6), +information-flow enforcement between sensitivity levels (AC-4), and an audit +record of access decisions (AU-2/AU-9); none can be satisfied if the host has no +notion of which server is which, at what level, or which tools are in bounds. + +The existing specification provides no place to express any of this. TLS proves +the host reached the named endpoint; MCP's authorization profile proves the user +may use the server; neither answers the missing third question — *is this server +one the host is authorized to use as a tool provider, and at what sensitivity?* + +## Specification + +### Discovery + +A server **MAY** advertise an attestation two interoperable ways: + +1. **Well-known resource.** The server **SHOULD** serve a *Server Attestation + Document* (SAD) at `/.well-known/mcp-attestation` on the same origin as its + MCP endpoint, over TLS. +2. **In-band.** A server **MAY** return the SAD in the `initialize` result under + an experimental capability key. + +A conforming host **MUST** attempt (1), **MAY** attempt (2), and **MUST** treat +the absence of both as *unattested*, applying its configured posture (see +*Backward Compatibility*). + +### Server Attestation Document (SAD) + +A JSON object. **REQUIRED** fields: `v` (integer `1`); `id` (stable server +identity); `publisher`; `version`; `clearance` (a canonical name or alias of a +level in a named, totally ordered classification scheme); `capabilities` (string +array that **MUST** contain `"mcp-server"`); `signerKeyId` (identifies a key in +the host trust root); `signature` (base64 detached signature over the canonical +body). **OPTIONAL**, and part of the signed body when present: `netAllowedHosts` +(string array — when non-empty, binds the SAD to those origins) and +`verification` (string, e.g. `"tested"`). Verifiers **MUST** ignore unknown +fields and **MUST NOT** include them in the canonical body until a future `v` +registers them. (Formal JSON Schema: see the reference implementation.) + +### Canonicalization and signature + +The canonical body is the deterministic JSON serialization of every registered +field except `signature`, with object keys in sorted order, array members +sorted, and an absent `signerKeyId` serialized as `null`. Signer and verifier +**MUST** compute the signature over exactly these bytes. The reference profile +uses Ed25519; a profile **MAY** substitute another suite if signer and verifier +agree. + +### Host verification rules + +Before treating a connection as *admitted*, a host **MUST** evaluate the +following in order and **MUST** deny on the first failure: + +1. the SAD parses and its `capabilities` contain `"mcp-server"`; +2. `signerKeyId` and `signature` are present; +3. `signerKeyId` resolves to a key in the trust root; +4. that key's validity window (`notAfter`, if set) includes now; +5. the key is approved for the asserted `clearance`; +6. the `signature` verifies over the canonical body; +7. `clearance` dominates the host's required level (numeric rank comparison); +8. if `netAllowedHosts` is non-empty, the connected origin is a member. + +A host in a deny-by-default (high-assurance) posture **MUST** reject an +unattested or failing server; a permissive host **SHOULD** surface the failure +and **MUST NOT** silently treat it as success. + +### Tool authorization + +Admitting a server **MUST NOT** be read as authorizing all its tools. A host +**SHOULD** keep a per-server allow-list and **MUST** deny a `tools/call` for any +tool absent from it *before* any network dispatch, regardless of what +`tools/list` advertises. Per-tool `clearance` **MAY** be added in a future `v`. + +### Audit + +A conforming host **SHOULD** append a tamper-evident record for every admission +decision (allow / deny / warn) and every tool-authorization denial, carrying the +resolved `signerKeyId`/`clearance` or the denial reason. + +### Trust establishment + +How the trust root is provisioned, pinned, and sealed is **deployment policy and +out of scope of this wire format**. Profiles **MAY** anchor it in a pinned key +set, X.509 chains, SPIFFE SVIDs, or a sigstore-style transparency log; the +verification rules are independent of the choice. + +### Error signalling + +When a host rejects a server for an attestation reason and surfaces it in-band, +it **SHOULD** carry a machine-readable `data.reason`, one per verification rule: +`not_mcp_server` (1), `unsigned` (2), `signer_not_trusted` (3), `signer_expired` +(4), `signer_not_approved` (5), `bad_signature` (6), `below_required` (7), +`host_not_bound` (8), and `tool_not_admitted` (tool authorization). + +### Compatibility, versioning, negotiation + +`v` versions the document; a verifier **MUST** reject versions it does not +understand. The extension is purely additive: an unextended host never fetches +the SAD, an unextended server never sees the host-side allow-list, and a host +**MUST** interoperate with unattested servers under its posture — enabling +incremental rollout. + +## Rationale + +### Why a spec extension rather than a per-vendor layer + +The mechanism can be implemented above MCP today — but *additive* is not +*interoperable*. Bolted on by one vendor, attestation secures only that vendor's +island; a server's signed clearance document is meaningful only to hosts that +already agreed, out of band, on its shape and verification order. The payoff +("this server is attested" as a claim any host can check; servers publishing one +document instead of N vendor dialects; SDKs shipping the gate on by default) only +materializes once a single format is agreed, and only the spec owner can mint +that Schelling point. That the mechanism rides entirely above MCP is what makes +adoption cheap and low-risk, not a reason to defer it. + +### Design decisions + +- **Offline, detached signature.** Admission requires no online call to a third + party; the host verifies against a locally pinned trust root. This keeps + admission available and fast and avoids a runtime dependency on the signer's + infrastructure. +- **Well-known URI for discovery.** A host can evaluate admission *before* + committing to a session, and the mechanism rides the established RFC 8615 + pattern instead of inventing a new one. +- **Admission separated from tool authorization.** Admitting a server says + nothing about which of its tools are in bounds. The closed per-server + allow-list is enforced before any network dispatch, defeating a prompt-injected + model that requests a tool the server advertises but the host never approved. +- **Numeric clearance ranks in a named, totally ordered scheme.** Dominance is a + single comparison; naming the scheme lets different sensitivity vocabularies + (government, healthcare, finance) coexist and interoperate. +- **Canonicalization fixes the signed bytes.** Sorted keys, sorted arrays, absent + `signerKeyId` as `null`, `signature` excluded — so signer and verifier agree on + exactly what was signed regardless of JSON serializer. + +### Alternatives considered + +- **Leave it to each vendor / keep it an extension only.** Rejected: additive ≠ + interoperable (above). Extensions are the right place to *experiment*; this + pattern has finished experimenting (production + evaluation), and its remaining + value is gated on a single agreed format that only the spec can supply. +- **In-band capability only, no well-known document.** Rejected: discovery must be + possible *before* a connection is trusted, but `initialize` already implies a + session. The well-known fetch lets a host decide admission before it commits. +- **mTLS / client certificates.** Rejected: TLS answers "did I reach the named + endpoint," not "is this server cleared to operate at this sensitivity, and which + of its tools are in bounds." Orthogonal; ATSA composes with it. +- **OAuth-style bearer token.** Rejected: that is *user*-authorization ("may this + user use this server"), a different question from host admission ("may this host + use this server as a tool provider, at what level"). ATSA composes with MCP's + authorization profile. +- **Trusting `clearance` embedded in `tools/list`.** Rejected: the tool list is + unsigned and self-declared; trust cannot rest on it. The signed SAD is the + anchor; the allow-list is host-side state. +- **Online (OCSP-style) revocation as a v1 requirement.** Deferred: it adds an + availability dependency at admission time. v1 enforces per-signer `notAfter`; + online revocation is future work (see *Open questions*). + +### Related work + +- **Artifact attestation.** sigstore, The Update Framework (TUF), and in-toto + apply "verify a signed claim against a trusted root before use" to software + supply chains. ATSA applies the same discipline to MCP server admission. +- **Well-known discovery precedent.** OAuth 2.0 Authorization Server Metadata + (RFC 8414), `security.txt` (RFC 9116), and OpenID Connect Discovery all ride + RFC 8615 without forking the base protocols they extend. + +### Design-principles alignment + +This proposal is written against the MCP [design +principles](https://modelcontextprotocol.io/community/design-principles): + +- **Demonstration over deliberation.** The mechanism is in production in the open + `enclawed-oss` distribution, with a JSON Schema, conformance vectors, 48 + hermetic tests, an LLM-driven adversarial campaign (27,025 tool-name evasions + + 14,378 forged assertions, all denied), and a live Google Workspace end-to-end + run. The SEP is written from what the prototype taught, not from theory. +- **Standardization over innovation.** ATSA codifies a pattern already proven in a + shipping host/server pair; it invents no new primitive. The + signed-document-at-a-well-known-URI shape is the one OAuth metadata, + `security.txt`, and OpenID discovery already use. +- **Convergence over choice.** One signed-attestation format, one verification + ordering — so "this server is attested" becomes a claim *any* host can check and + a server publishes *once*, instead of N mutually unintelligible vendor dialects. + Only the spec owner can mint that single path. +- **Interoperability over optimization.** It degrades gracefully: capability + negotiation gates in-band discovery, an unextended host never fetches the SAD, + an unextended server never sees the allow-list, and a conforming host **MUST** + still interoperate with unattested servers under its configured posture. Nothing + requires every participant to be equally capable. +- **Composability over specificity** *(objection addressed).* A reviewer may ask + whether this can be built from existing primitives. It can — and is, today, + above MCP — which is exactly why adoption is cheap. But composability gives each + vendor an island, not interoperability: a server cannot publish one document + every host understands until the shape and verification order are agreed in one + place. The SEP adds exactly one document and one ordering — the minimum that + turns a per-vendor capability into an ecosystem one. +- **Stability over velocity** *(objection addressed).* Because the addition is + permanent, it is deliberately small: one well-known document, one optional + `initialize` capability, zero changes to any existing message. An unextended + implementation is byte-for-byte unaffected, so the permanent cost to client + implementers is bounded to those who opt in. +- **Capability over compensation.** ATSA does not compensate for a temporary model + weakness. A more capable model that is prompt-injected is *more* dangerous, not + less; the trust and authorization gap it closes is independent of model quality + and does not fade as models improve. +- **Pragmatism over purity.** Trust-root provisioning is left to deployment policy + rather than mandated, because operators' PKI realities differ. The wire format + is fixed; the anchoring is not. + +## Backward Compatibility + +None broken. The SAD lives at a well-known URI (RFC 8615); the tool allow-list is +host-side state; no existing MCP message changes shape. This is the additive +discipline that let OAuth, `security.txt`, and OpenID discovery ride the +well-known mechanism without forking the protocols they extended. A host with no +required level and an empty allow-list policy behaves exactly as a host does +today. + +## Security Implications + +The design resists confused-deputy tool invocation (tool authorization), server +impersonation and cross-origin replay (rules 3/6/8), level escalation (the level +is inside the signed body), and signer-key aging (rule 4). Out of scope and +delegated to other layers: content inspection of admitted connections, and the +behavior of a correctly-admitted, correctly-cleared server within its allowed +tools. The trust root is the root of all guarantees; its provisioning and sealing +are deployment policy (see *Trust establishment*), and a compromised signer key +is bounded by `notAfter` until online revocation (future work) lands. + +## Reference Implementation + +A production reference implementation ships in the open `enclawed-oss` +distribution — — at +`extensions/mcp-attested` (with a first-party Google Workspace bridge at +`extensions/mcp-google-workspace`). It includes a JSON Schema, an error registry, +and machine-checkable conformance vectors. + +### Prototype and evaluation + +Per the SEP process (explore → prototype → write the SEP from what the prototype +taught): the mechanism is implemented and in production, and is evaluated in the +companion preprint. Every stated guarantee is backed by an executable test driven +by an LLM-generated adversarial corpus: + +- **48 hermetic tests** (44 under `vitest`, 4 under `node:test`) exercise every + verification rule and the tool-authorization rule. +- A local-LLM (Ollama) **coverage campaign** generated **27,025 unique tool-name + evasions** (case/Unicode/whitespace/separator/path/near-miss tricks) — all + denied, zero leaked network writes — and **14,378 unique forged clearance + assertions** — all rejected. +- A **live end-to-end** run drove a real Google Workspace MCP endpoint through the + gate: the allow-listed tool was admitted and dispatched; out-of-allow-list tools + were denied before any network call. + +### Conformance (prerequisite for Final) + +Per [SEP-2484](https://modelcontextprotocol.io/seps/2484-conformance-tests-required-for-final-seps), +a Standards Track SEP with observable protocol behavior must, before reaching +`Final`, land a conformance scenario in the +[conformance repository](https://github.com/modelcontextprotocol/conformance) +plus a `sep-NNNN.yaml` traceability file mapping every MUST/MUST NOT and +SHOULD/SHOULD NOT in this *Specification* to a check ID. This SEP is written to +make that mechanical: each verification rule (1–8), the tool-authorization rule, +and the audit and versioning requirements are already enumerated as +machine-checkable conformance vectors in the reference implementation. The +traceability file and scenario will be supplied against the draft spec-version tag +once a sponsor is engaged and the SEP number is assigned. + +## Open questions / future work + +- Per-tool (vs. per-server) clearance. +- A short-lived `notBefore`/expiry on the assertion itself and an online + (OCSP-style) revocation channel for compromised signer keys, beyond the + per-signer `notAfter` already enforced. +- Reconciling the `clearance` field with MCP's evolving authorization profile and + capability-negotiation handshake. + +## References + +- Companion preprint: *Attested Tool-Server Admission: A Security Extension to the + Model Context Protocol* — archived at Zenodo, doi:10.5281/zenodo.20349263 + (arXiv ID forthcoming; PDF in this package). +- RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), + RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). +- Reference implementation: — + `extensions/mcp-attested`. From a0229bfb42039ed58232793b33d277daa722705a Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Fri, 22 May 2026 22:14:00 -0700 Subject: [PATCH 02/12] Rename SEP to assigned number 2777; set heading + PR link --- seps/2777-attested-tool-server-admission.md | 323 ++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 seps/2777-attested-tool-server-admission.md diff --git a/seps/2777-attested-tool-server-admission.md b/seps/2777-attested-tool-server-admission.md new file mode 100644 index 000000000..8e15c25b2 --- /dev/null +++ b/seps/2777-attested-tool-server-admission.md @@ -0,0 +1,323 @@ +# SEP-2777: Attested Tool-Server Admission (ATSA) + +- **Status**: Draft +- **Type**: Standards Track +- **Created**: 2026-05-22 +- **Author(s)**: Alfredo Metere (@metereconsulting) <alfredo.metere@enclawed.com>, Enclawed LLC +- **Sponsor**: None (seeking) +- **PR**: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2777 +- **Preprint**: doi:10.5281/zenodo.20349263 +- **Requires**: RFC 2119, RFC 8174, RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519) + +The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHOULD**, +**SHOULD NOT**, **MAY**, and **OPTIONAL** are to be interpreted as in RFC 2119 +and RFC 8174. + +## Abstract + +MCP standardizes how a host and a tool server exchange messages, but not *trust*: +a host reads a server's self-declared tool list and dispatches calls with no +notion of which servers it may use, at what sensitivity, or which of a server's +tools are in bounds. This SEP adds an **optional, purely additive** admission +layer: a server publishes a small, offline-signed *clearance assertion* at a +well-known URI; a host verifies it against a locally pinned trust root before any +tool dispatch; admitting a server is kept distinct from authorizing its tools via +a closed per-server tool allow-list; and every admission decision is auditable. +No existing MCP message changes shape, and an unextended host ignores the +mechanism and behaves exactly as today. + +## Motivation + +A host today takes a server's identity (beyond its TLS certificate) and its +advertised tool list on faith. A prompt-injected model can therefore drive a +destructive tool on any server it connects to. This is exploitable in **every** +deployment; for a single operator it is a risk they absorb, but for a regulated +operator it is disqualifying — and, lacking any admission record, unauditable. +NIST SP 800-53 expects least-privilege access enforcement (AC-3/AC-6), +information-flow enforcement between sensitivity levels (AC-4), and an audit +record of access decisions (AU-2/AU-9); none can be satisfied if the host has no +notion of which server is which, at what level, or which tools are in bounds. + +The existing specification provides no place to express any of this. TLS proves +the host reached the named endpoint; MCP's authorization profile proves the user +may use the server; neither answers the missing third question — *is this server +one the host is authorized to use as a tool provider, and at what sensitivity?* + +## Specification + +### Discovery + +A server **MAY** advertise an attestation two interoperable ways: + +1. **Well-known resource.** The server **SHOULD** serve a *Server Attestation + Document* (SAD) at `/.well-known/mcp-attestation` on the same origin as its + MCP endpoint, over TLS. +2. **In-band.** A server **MAY** return the SAD in the `initialize` result under + an experimental capability key. + +A conforming host **MUST** attempt (1), **MAY** attempt (2), and **MUST** treat +the absence of both as *unattested*, applying its configured posture (see +*Backward Compatibility*). + +### Server Attestation Document (SAD) + +A JSON object. **REQUIRED** fields: `v` (integer `1`); `id` (stable server +identity); `publisher`; `version`; `clearance` (a canonical name or alias of a +level in a named, totally ordered classification scheme); `capabilities` (string +array that **MUST** contain `"mcp-server"`); `signerKeyId` (identifies a key in +the host trust root); `signature` (base64 detached signature over the canonical +body). **OPTIONAL**, and part of the signed body when present: `netAllowedHosts` +(string array — when non-empty, binds the SAD to those origins) and +`verification` (string, e.g. `"tested"`). Verifiers **MUST** ignore unknown +fields and **MUST NOT** include them in the canonical body until a future `v` +registers them. (Formal JSON Schema: see the reference implementation.) + +### Canonicalization and signature + +The canonical body is the deterministic JSON serialization of every registered +field except `signature`, with object keys in sorted order, array members +sorted, and an absent `signerKeyId` serialized as `null`. Signer and verifier +**MUST** compute the signature over exactly these bytes. The reference profile +uses Ed25519; a profile **MAY** substitute another suite if signer and verifier +agree. + +### Host verification rules + +Before treating a connection as *admitted*, a host **MUST** evaluate the +following in order and **MUST** deny on the first failure: + +1. the SAD parses and its `capabilities` contain `"mcp-server"`; +2. `signerKeyId` and `signature` are present; +3. `signerKeyId` resolves to a key in the trust root; +4. that key's validity window (`notAfter`, if set) includes now; +5. the key is approved for the asserted `clearance`; +6. the `signature` verifies over the canonical body; +7. `clearance` dominates the host's required level (numeric rank comparison); +8. if `netAllowedHosts` is non-empty, the connected origin is a member. + +A host in a deny-by-default (high-assurance) posture **MUST** reject an +unattested or failing server; a permissive host **SHOULD** surface the failure +and **MUST NOT** silently treat it as success. + +### Tool authorization + +Admitting a server **MUST NOT** be read as authorizing all its tools. A host +**SHOULD** keep a per-server allow-list and **MUST** deny a `tools/call` for any +tool absent from it *before* any network dispatch, regardless of what +`tools/list` advertises. Per-tool `clearance` **MAY** be added in a future `v`. + +### Audit + +A conforming host **SHOULD** append a tamper-evident record for every admission +decision (allow / deny / warn) and every tool-authorization denial, carrying the +resolved `signerKeyId`/`clearance` or the denial reason. + +### Trust establishment + +How the trust root is provisioned, pinned, and sealed is **deployment policy and +out of scope of this wire format**. Profiles **MAY** anchor it in a pinned key +set, X.509 chains, SPIFFE SVIDs, or a sigstore-style transparency log; the +verification rules are independent of the choice. + +### Error signalling + +When a host rejects a server for an attestation reason and surfaces it in-band, +it **SHOULD** carry a machine-readable `data.reason`, one per verification rule: +`not_mcp_server` (1), `unsigned` (2), `signer_not_trusted` (3), `signer_expired` +(4), `signer_not_approved` (5), `bad_signature` (6), `below_required` (7), +`host_not_bound` (8), and `tool_not_admitted` (tool authorization). + +### Compatibility, versioning, negotiation + +`v` versions the document; a verifier **MUST** reject versions it does not +understand. The extension is purely additive: an unextended host never fetches +the SAD, an unextended server never sees the host-side allow-list, and a host +**MUST** interoperate with unattested servers under its posture — enabling +incremental rollout. + +## Rationale + +### Why a spec extension rather than a per-vendor layer + +The mechanism can be implemented above MCP today — but *additive* is not +*interoperable*. Bolted on by one vendor, attestation secures only that vendor's +island; a server's signed clearance document is meaningful only to hosts that +already agreed, out of band, on its shape and verification order. The payoff +("this server is attested" as a claim any host can check; servers publishing one +document instead of N vendor dialects; SDKs shipping the gate on by default) only +materializes once a single format is agreed, and only the spec owner can mint +that Schelling point. That the mechanism rides entirely above MCP is what makes +adoption cheap and low-risk, not a reason to defer it. + +### Design decisions + +- **Offline, detached signature.** Admission requires no online call to a third + party; the host verifies against a locally pinned trust root. This keeps + admission available and fast and avoids a runtime dependency on the signer's + infrastructure. +- **Well-known URI for discovery.** A host can evaluate admission *before* + committing to a session, and the mechanism rides the established RFC 8615 + pattern instead of inventing a new one. +- **Admission separated from tool authorization.** Admitting a server says + nothing about which of its tools are in bounds. The closed per-server + allow-list is enforced before any network dispatch, defeating a prompt-injected + model that requests a tool the server advertises but the host never approved. +- **Numeric clearance ranks in a named, totally ordered scheme.** Dominance is a + single comparison; naming the scheme lets different sensitivity vocabularies + (government, healthcare, finance) coexist and interoperate. +- **Canonicalization fixes the signed bytes.** Sorted keys, sorted arrays, absent + `signerKeyId` as `null`, `signature` excluded — so signer and verifier agree on + exactly what was signed regardless of JSON serializer. + +### Alternatives considered + +- **Leave it to each vendor / keep it an extension only.** Rejected: additive ≠ + interoperable (above). Extensions are the right place to *experiment*; this + pattern has finished experimenting (production + evaluation), and its remaining + value is gated on a single agreed format that only the spec can supply. +- **In-band capability only, no well-known document.** Rejected: discovery must be + possible *before* a connection is trusted, but `initialize` already implies a + session. The well-known fetch lets a host decide admission before it commits. +- **mTLS / client certificates.** Rejected: TLS answers "did I reach the named + endpoint," not "is this server cleared to operate at this sensitivity, and which + of its tools are in bounds." Orthogonal; ATSA composes with it. +- **OAuth-style bearer token.** Rejected: that is *user*-authorization ("may this + user use this server"), a different question from host admission ("may this host + use this server as a tool provider, at what level"). ATSA composes with MCP's + authorization profile. +- **Trusting `clearance` embedded in `tools/list`.** Rejected: the tool list is + unsigned and self-declared; trust cannot rest on it. The signed SAD is the + anchor; the allow-list is host-side state. +- **Online (OCSP-style) revocation as a v1 requirement.** Deferred: it adds an + availability dependency at admission time. v1 enforces per-signer `notAfter`; + online revocation is future work (see *Open questions*). + +### Related work + +- **Artifact attestation.** sigstore, The Update Framework (TUF), and in-toto + apply "verify a signed claim against a trusted root before use" to software + supply chains. ATSA applies the same discipline to MCP server admission. +- **Well-known discovery precedent.** OAuth 2.0 Authorization Server Metadata + (RFC 8414), `security.txt` (RFC 9116), and OpenID Connect Discovery all ride + RFC 8615 without forking the base protocols they extend. + +### Design-principles alignment + +This proposal is written against the MCP [design +principles](https://modelcontextprotocol.io/community/design-principles): + +- **Demonstration over deliberation.** The mechanism is in production in the open + `enclawed-oss` distribution, with a JSON Schema, conformance vectors, 48 + hermetic tests, an LLM-driven adversarial campaign (27,025 tool-name evasions + + 14,378 forged assertions, all denied), and a live Google Workspace end-to-end + run. The SEP is written from what the prototype taught, not from theory. +- **Standardization over innovation.** ATSA codifies a pattern already proven in a + shipping host/server pair; it invents no new primitive. The + signed-document-at-a-well-known-URI shape is the one OAuth metadata, + `security.txt`, and OpenID discovery already use. +- **Convergence over choice.** One signed-attestation format, one verification + ordering — so "this server is attested" becomes a claim *any* host can check and + a server publishes *once*, instead of N mutually unintelligible vendor dialects. + Only the spec owner can mint that single path. +- **Interoperability over optimization.** It degrades gracefully: capability + negotiation gates in-band discovery, an unextended host never fetches the SAD, + an unextended server never sees the allow-list, and a conforming host **MUST** + still interoperate with unattested servers under its configured posture. Nothing + requires every participant to be equally capable. +- **Composability over specificity** *(objection addressed).* A reviewer may ask + whether this can be built from existing primitives. It can — and is, today, + above MCP — which is exactly why adoption is cheap. But composability gives each + vendor an island, not interoperability: a server cannot publish one document + every host understands until the shape and verification order are agreed in one + place. The SEP adds exactly one document and one ordering — the minimum that + turns a per-vendor capability into an ecosystem one. +- **Stability over velocity** *(objection addressed).* Because the addition is + permanent, it is deliberately small: one well-known document, one optional + `initialize` capability, zero changes to any existing message. An unextended + implementation is byte-for-byte unaffected, so the permanent cost to client + implementers is bounded to those who opt in. +- **Capability over compensation.** ATSA does not compensate for a temporary model + weakness. A more capable model that is prompt-injected is *more* dangerous, not + less; the trust and authorization gap it closes is independent of model quality + and does not fade as models improve. +- **Pragmatism over purity.** Trust-root provisioning is left to deployment policy + rather than mandated, because operators' PKI realities differ. The wire format + is fixed; the anchoring is not. + +## Backward Compatibility + +None broken. The SAD lives at a well-known URI (RFC 8615); the tool allow-list is +host-side state; no existing MCP message changes shape. This is the additive +discipline that let OAuth, `security.txt`, and OpenID discovery ride the +well-known mechanism without forking the protocols they extended. A host with no +required level and an empty allow-list policy behaves exactly as a host does +today. + +## Security Implications + +The design resists confused-deputy tool invocation (tool authorization), server +impersonation and cross-origin replay (rules 3/6/8), level escalation (the level +is inside the signed body), and signer-key aging (rule 4). Out of scope and +delegated to other layers: content inspection of admitted connections, and the +behavior of a correctly-admitted, correctly-cleared server within its allowed +tools. The trust root is the root of all guarantees; its provisioning and sealing +are deployment policy (see *Trust establishment*), and a compromised signer key +is bounded by `notAfter` until online revocation (future work) lands. + +## Reference Implementation + +A production reference implementation ships in the open `enclawed-oss` +distribution — — at +`extensions/mcp-attested` (with a first-party Google Workspace bridge at +`extensions/mcp-google-workspace`). It includes a JSON Schema, an error registry, +and machine-checkable conformance vectors. + +### Prototype and evaluation + +Per the SEP process (explore → prototype → write the SEP from what the prototype +taught): the mechanism is implemented and in production, and is evaluated in the +companion preprint. Every stated guarantee is backed by an executable test driven +by an LLM-generated adversarial corpus: + +- **48 hermetic tests** (44 under `vitest`, 4 under `node:test`) exercise every + verification rule and the tool-authorization rule. +- A local-LLM (Ollama) **coverage campaign** generated **27,025 unique tool-name + evasions** (case/Unicode/whitespace/separator/path/near-miss tricks) — all + denied, zero leaked network writes — and **14,378 unique forged clearance + assertions** — all rejected. +- A **live end-to-end** run drove a real Google Workspace MCP endpoint through the + gate: the allow-listed tool was admitted and dispatched; out-of-allow-list tools + were denied before any network call. + +### Conformance (prerequisite for Final) + +Per [SEP-2484](https://modelcontextprotocol.io/seps/2484-conformance-tests-required-for-final-seps), +a Standards Track SEP with observable protocol behavior must, before reaching +`Final`, land a conformance scenario in the +[conformance repository](https://github.com/modelcontextprotocol/conformance) +plus a `sep-NNNN.yaml` traceability file mapping every MUST/MUST NOT and +SHOULD/SHOULD NOT in this *Specification* to a check ID. This SEP is written to +make that mechanical: each verification rule (1–8), the tool-authorization rule, +and the audit and versioning requirements are already enumerated as +machine-checkable conformance vectors in the reference implementation. The +traceability file and scenario will be supplied against the draft spec-version tag +once a sponsor is engaged and the SEP number is assigned. + +## Open questions / future work + +- Per-tool (vs. per-server) clearance. +- A short-lived `notBefore`/expiry on the assertion itself and an online + (OCSP-style) revocation channel for compromised signer keys, beyond the + per-signer `notAfter` already enforced. +- Reconciling the `clearance` field with MCP's evolving authorization profile and + capability-negotiation handshake. + +## References + +- Companion preprint: *Attested Tool-Server Admission: A Security Extension to the + Model Context Protocol* — archived at Zenodo, doi:10.5281/zenodo.20349263 + (arXiv ID forthcoming; PDF in this package). +- RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), + RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). +- Reference implementation: — + `extensions/mcp-attested`. From 9b1ef6ac7f5b728cc260be69b5e672a8009fb781 Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Fri, 22 May 2026 22:14:02 -0700 Subject: [PATCH 03/12] Remove 0000 placeholder (renamed to 2777) --- seps/0000-attested-tool-server-admission.md | 323 -------------------- 1 file changed, 323 deletions(-) delete mode 100644 seps/0000-attested-tool-server-admission.md diff --git a/seps/0000-attested-tool-server-admission.md b/seps/0000-attested-tool-server-admission.md deleted file mode 100644 index 5c4750ec5..000000000 --- a/seps/0000-attested-tool-server-admission.md +++ /dev/null @@ -1,323 +0,0 @@ -# SEP-0000: Attested Tool-Server Admission (ATSA) - -- **Status**: Draft -- **Type**: Standards Track -- **Created**: 2026-05-22 -- **Author(s)**: Alfredo Metere (@metereconsulting) <alfredo.metere@enclawed.com>, Enclawed LLC -- **Sponsor**: None (seeking) -- **PR**: (to be assigned — rename this file to `NNNN-attested-tool-server-admission.md` once the PR number is known) -- **Preprint**: doi:10.5281/zenodo.20349263 -- **Requires**: RFC 2119, RFC 8174, RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519) - -The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHOULD**, -**SHOULD NOT**, **MAY**, and **OPTIONAL** are to be interpreted as in RFC 2119 -and RFC 8174. - -## Abstract - -MCP standardizes how a host and a tool server exchange messages, but not *trust*: -a host reads a server's self-declared tool list and dispatches calls with no -notion of which servers it may use, at what sensitivity, or which of a server's -tools are in bounds. This SEP adds an **optional, purely additive** admission -layer: a server publishes a small, offline-signed *clearance assertion* at a -well-known URI; a host verifies it against a locally pinned trust root before any -tool dispatch; admitting a server is kept distinct from authorizing its tools via -a closed per-server tool allow-list; and every admission decision is auditable. -No existing MCP message changes shape, and an unextended host ignores the -mechanism and behaves exactly as today. - -## Motivation - -A host today takes a server's identity (beyond its TLS certificate) and its -advertised tool list on faith. A prompt-injected model can therefore drive a -destructive tool on any server it connects to. This is exploitable in **every** -deployment; for a single operator it is a risk they absorb, but for a regulated -operator it is disqualifying — and, lacking any admission record, unauditable. -NIST SP 800-53 expects least-privilege access enforcement (AC-3/AC-6), -information-flow enforcement between sensitivity levels (AC-4), and an audit -record of access decisions (AU-2/AU-9); none can be satisfied if the host has no -notion of which server is which, at what level, or which tools are in bounds. - -The existing specification provides no place to express any of this. TLS proves -the host reached the named endpoint; MCP's authorization profile proves the user -may use the server; neither answers the missing third question — *is this server -one the host is authorized to use as a tool provider, and at what sensitivity?* - -## Specification - -### Discovery - -A server **MAY** advertise an attestation two interoperable ways: - -1. **Well-known resource.** The server **SHOULD** serve a *Server Attestation - Document* (SAD) at `/.well-known/mcp-attestation` on the same origin as its - MCP endpoint, over TLS. -2. **In-band.** A server **MAY** return the SAD in the `initialize` result under - an experimental capability key. - -A conforming host **MUST** attempt (1), **MAY** attempt (2), and **MUST** treat -the absence of both as *unattested*, applying its configured posture (see -*Backward Compatibility*). - -### Server Attestation Document (SAD) - -A JSON object. **REQUIRED** fields: `v` (integer `1`); `id` (stable server -identity); `publisher`; `version`; `clearance` (a canonical name or alias of a -level in a named, totally ordered classification scheme); `capabilities` (string -array that **MUST** contain `"mcp-server"`); `signerKeyId` (identifies a key in -the host trust root); `signature` (base64 detached signature over the canonical -body). **OPTIONAL**, and part of the signed body when present: `netAllowedHosts` -(string array — when non-empty, binds the SAD to those origins) and -`verification` (string, e.g. `"tested"`). Verifiers **MUST** ignore unknown -fields and **MUST NOT** include them in the canonical body until a future `v` -registers them. (Formal JSON Schema: see the reference implementation.) - -### Canonicalization and signature - -The canonical body is the deterministic JSON serialization of every registered -field except `signature`, with object keys in sorted order, array members -sorted, and an absent `signerKeyId` serialized as `null`. Signer and verifier -**MUST** compute the signature over exactly these bytes. The reference profile -uses Ed25519; a profile **MAY** substitute another suite if signer and verifier -agree. - -### Host verification rules - -Before treating a connection as *admitted*, a host **MUST** evaluate the -following in order and **MUST** deny on the first failure: - -1. the SAD parses and its `capabilities` contain `"mcp-server"`; -2. `signerKeyId` and `signature` are present; -3. `signerKeyId` resolves to a key in the trust root; -4. that key's validity window (`notAfter`, if set) includes now; -5. the key is approved for the asserted `clearance`; -6. the `signature` verifies over the canonical body; -7. `clearance` dominates the host's required level (numeric rank comparison); -8. if `netAllowedHosts` is non-empty, the connected origin is a member. - -A host in a deny-by-default (high-assurance) posture **MUST** reject an -unattested or failing server; a permissive host **SHOULD** surface the failure -and **MUST NOT** silently treat it as success. - -### Tool authorization - -Admitting a server **MUST NOT** be read as authorizing all its tools. A host -**SHOULD** keep a per-server allow-list and **MUST** deny a `tools/call` for any -tool absent from it *before* any network dispatch, regardless of what -`tools/list` advertises. Per-tool `clearance` **MAY** be added in a future `v`. - -### Audit - -A conforming host **SHOULD** append a tamper-evident record for every admission -decision (allow / deny / warn) and every tool-authorization denial, carrying the -resolved `signerKeyId`/`clearance` or the denial reason. - -### Trust establishment - -How the trust root is provisioned, pinned, and sealed is **deployment policy and -out of scope of this wire format**. Profiles **MAY** anchor it in a pinned key -set, X.509 chains, SPIFFE SVIDs, or a sigstore-style transparency log; the -verification rules are independent of the choice. - -### Error signalling - -When a host rejects a server for an attestation reason and surfaces it in-band, -it **SHOULD** carry a machine-readable `data.reason`, one per verification rule: -`not_mcp_server` (1), `unsigned` (2), `signer_not_trusted` (3), `signer_expired` -(4), `signer_not_approved` (5), `bad_signature` (6), `below_required` (7), -`host_not_bound` (8), and `tool_not_admitted` (tool authorization). - -### Compatibility, versioning, negotiation - -`v` versions the document; a verifier **MUST** reject versions it does not -understand. The extension is purely additive: an unextended host never fetches -the SAD, an unextended server never sees the host-side allow-list, and a host -**MUST** interoperate with unattested servers under its posture — enabling -incremental rollout. - -## Rationale - -### Why a spec extension rather than a per-vendor layer - -The mechanism can be implemented above MCP today — but *additive* is not -*interoperable*. Bolted on by one vendor, attestation secures only that vendor's -island; a server's signed clearance document is meaningful only to hosts that -already agreed, out of band, on its shape and verification order. The payoff -("this server is attested" as a claim any host can check; servers publishing one -document instead of N vendor dialects; SDKs shipping the gate on by default) only -materializes once a single format is agreed, and only the spec owner can mint -that Schelling point. That the mechanism rides entirely above MCP is what makes -adoption cheap and low-risk, not a reason to defer it. - -### Design decisions - -- **Offline, detached signature.** Admission requires no online call to a third - party; the host verifies against a locally pinned trust root. This keeps - admission available and fast and avoids a runtime dependency on the signer's - infrastructure. -- **Well-known URI for discovery.** A host can evaluate admission *before* - committing to a session, and the mechanism rides the established RFC 8615 - pattern instead of inventing a new one. -- **Admission separated from tool authorization.** Admitting a server says - nothing about which of its tools are in bounds. The closed per-server - allow-list is enforced before any network dispatch, defeating a prompt-injected - model that requests a tool the server advertises but the host never approved. -- **Numeric clearance ranks in a named, totally ordered scheme.** Dominance is a - single comparison; naming the scheme lets different sensitivity vocabularies - (government, healthcare, finance) coexist and interoperate. -- **Canonicalization fixes the signed bytes.** Sorted keys, sorted arrays, absent - `signerKeyId` as `null`, `signature` excluded — so signer and verifier agree on - exactly what was signed regardless of JSON serializer. - -### Alternatives considered - -- **Leave it to each vendor / keep it an extension only.** Rejected: additive ≠ - interoperable (above). Extensions are the right place to *experiment*; this - pattern has finished experimenting (production + evaluation), and its remaining - value is gated on a single agreed format that only the spec can supply. -- **In-band capability only, no well-known document.** Rejected: discovery must be - possible *before* a connection is trusted, but `initialize` already implies a - session. The well-known fetch lets a host decide admission before it commits. -- **mTLS / client certificates.** Rejected: TLS answers "did I reach the named - endpoint," not "is this server cleared to operate at this sensitivity, and which - of its tools are in bounds." Orthogonal; ATSA composes with it. -- **OAuth-style bearer token.** Rejected: that is *user*-authorization ("may this - user use this server"), a different question from host admission ("may this host - use this server as a tool provider, at what level"). ATSA composes with MCP's - authorization profile. -- **Trusting `clearance` embedded in `tools/list`.** Rejected: the tool list is - unsigned and self-declared; trust cannot rest on it. The signed SAD is the - anchor; the allow-list is host-side state. -- **Online (OCSP-style) revocation as a v1 requirement.** Deferred: it adds an - availability dependency at admission time. v1 enforces per-signer `notAfter`; - online revocation is future work (see *Open questions*). - -### Related work - -- **Artifact attestation.** sigstore, The Update Framework (TUF), and in-toto - apply "verify a signed claim against a trusted root before use" to software - supply chains. ATSA applies the same discipline to MCP server admission. -- **Well-known discovery precedent.** OAuth 2.0 Authorization Server Metadata - (RFC 8414), `security.txt` (RFC 9116), and OpenID Connect Discovery all ride - RFC 8615 without forking the base protocols they extend. - -### Design-principles alignment - -This proposal is written against the MCP [design -principles](https://modelcontextprotocol.io/community/design-principles): - -- **Demonstration over deliberation.** The mechanism is in production in the open - `enclawed-oss` distribution, with a JSON Schema, conformance vectors, 48 - hermetic tests, an LLM-driven adversarial campaign (27,025 tool-name evasions + - 14,378 forged assertions, all denied), and a live Google Workspace end-to-end - run. The SEP is written from what the prototype taught, not from theory. -- **Standardization over innovation.** ATSA codifies a pattern already proven in a - shipping host/server pair; it invents no new primitive. The - signed-document-at-a-well-known-URI shape is the one OAuth metadata, - `security.txt`, and OpenID discovery already use. -- **Convergence over choice.** One signed-attestation format, one verification - ordering — so "this server is attested" becomes a claim *any* host can check and - a server publishes *once*, instead of N mutually unintelligible vendor dialects. - Only the spec owner can mint that single path. -- **Interoperability over optimization.** It degrades gracefully: capability - negotiation gates in-band discovery, an unextended host never fetches the SAD, - an unextended server never sees the allow-list, and a conforming host **MUST** - still interoperate with unattested servers under its configured posture. Nothing - requires every participant to be equally capable. -- **Composability over specificity** *(objection addressed).* A reviewer may ask - whether this can be built from existing primitives. It can — and is, today, - above MCP — which is exactly why adoption is cheap. But composability gives each - vendor an island, not interoperability: a server cannot publish one document - every host understands until the shape and verification order are agreed in one - place. The SEP adds exactly one document and one ordering — the minimum that - turns a per-vendor capability into an ecosystem one. -- **Stability over velocity** *(objection addressed).* Because the addition is - permanent, it is deliberately small: one well-known document, one optional - `initialize` capability, zero changes to any existing message. An unextended - implementation is byte-for-byte unaffected, so the permanent cost to client - implementers is bounded to those who opt in. -- **Capability over compensation.** ATSA does not compensate for a temporary model - weakness. A more capable model that is prompt-injected is *more* dangerous, not - less; the trust and authorization gap it closes is independent of model quality - and does not fade as models improve. -- **Pragmatism over purity.** Trust-root provisioning is left to deployment policy - rather than mandated, because operators' PKI realities differ. The wire format - is fixed; the anchoring is not. - -## Backward Compatibility - -None broken. The SAD lives at a well-known URI (RFC 8615); the tool allow-list is -host-side state; no existing MCP message changes shape. This is the additive -discipline that let OAuth, `security.txt`, and OpenID discovery ride the -well-known mechanism without forking the protocols they extended. A host with no -required level and an empty allow-list policy behaves exactly as a host does -today. - -## Security Implications - -The design resists confused-deputy tool invocation (tool authorization), server -impersonation and cross-origin replay (rules 3/6/8), level escalation (the level -is inside the signed body), and signer-key aging (rule 4). Out of scope and -delegated to other layers: content inspection of admitted connections, and the -behavior of a correctly-admitted, correctly-cleared server within its allowed -tools. The trust root is the root of all guarantees; its provisioning and sealing -are deployment policy (see *Trust establishment*), and a compromised signer key -is bounded by `notAfter` until online revocation (future work) lands. - -## Reference Implementation - -A production reference implementation ships in the open `enclawed-oss` -distribution — — at -`extensions/mcp-attested` (with a first-party Google Workspace bridge at -`extensions/mcp-google-workspace`). It includes a JSON Schema, an error registry, -and machine-checkable conformance vectors. - -### Prototype and evaluation - -Per the SEP process (explore → prototype → write the SEP from what the prototype -taught): the mechanism is implemented and in production, and is evaluated in the -companion preprint. Every stated guarantee is backed by an executable test driven -by an LLM-generated adversarial corpus: - -- **48 hermetic tests** (44 under `vitest`, 4 under `node:test`) exercise every - verification rule and the tool-authorization rule. -- A local-LLM (Ollama) **coverage campaign** generated **27,025 unique tool-name - evasions** (case/Unicode/whitespace/separator/path/near-miss tricks) — all - denied, zero leaked network writes — and **14,378 unique forged clearance - assertions** — all rejected. -- A **live end-to-end** run drove a real Google Workspace MCP endpoint through the - gate: the allow-listed tool was admitted and dispatched; out-of-allow-list tools - were denied before any network call. - -### Conformance (prerequisite for Final) - -Per [SEP-2484](https://modelcontextprotocol.io/seps/2484-conformance-tests-required-for-final-seps), -a Standards Track SEP with observable protocol behavior must, before reaching -`Final`, land a conformance scenario in the -[conformance repository](https://github.com/modelcontextprotocol/conformance) -plus a `sep-NNNN.yaml` traceability file mapping every MUST/MUST NOT and -SHOULD/SHOULD NOT in this *Specification* to a check ID. This SEP is written to -make that mechanical: each verification rule (1–8), the tool-authorization rule, -and the audit and versioning requirements are already enumerated as -machine-checkable conformance vectors in the reference implementation. The -traceability file and scenario will be supplied against the draft spec-version tag -once a sponsor is engaged and the SEP number is assigned. - -## Open questions / future work - -- Per-tool (vs. per-server) clearance. -- A short-lived `notBefore`/expiry on the assertion itself and an online - (OCSP-style) revocation channel for compromised signer keys, beyond the - per-signer `notAfter` already enforced. -- Reconciling the `clearance` field with MCP's evolving authorization profile and - capability-negotiation handshake. - -## References - -- Companion preprint: *Attested Tool-Server Admission: A Security Extension to the - Model Context Protocol* — archived at Zenodo, doi:10.5281/zenodo.20349263 - (arXiv ID forthcoming; PDF in this package). -- RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), - RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). -- Reference implementation: — - `extensions/mcp-attested`. From 11ce94591155bc23c4c0453498d4492867e8d5a3 Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Fri, 22 May 2026 22:26:24 -0700 Subject: [PATCH 04/12] Format SEP with Prettier; generate SEP docs (mdx, index, docs.json) --- docs/docs.json | 3 +- .../2777-attested-tool-server-admission.mdx | 336 ++++++++++++++++++ docs/seps/index.mdx | 3 +- seps/2777-attested-tool-server-admission.md | 52 +-- 4 files changed, 366 insertions(+), 28 deletions(-) create mode 100644 docs/seps/2777-attested-tool-server-admission.mdx diff --git a/docs/docs.json b/docs/docs.json index 281d10280..dbf8cde98 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -455,7 +455,8 @@ "seps/2106-json-schema-2020-12", "seps/2164-resource-not-found-error", "seps/2484-conformance-tests-required-for-final-seps", - "seps/2596-spec-feature-lifecycle-and-deprecation" + "seps/2596-spec-feature-lifecycle-and-deprecation", + "seps/2777-attested-tool-server-admission" ] } ] diff --git a/docs/seps/2777-attested-tool-server-admission.mdx b/docs/seps/2777-attested-tool-server-admission.mdx new file mode 100644 index 000000000..f5ce1e0ac --- /dev/null +++ b/docs/seps/2777-attested-tool-server-admission.mdx @@ -0,0 +1,336 @@ +--- +title: "SEP-2777: Attested Tool-Server Admission (ATSA)" +sidebarTitle: "SEP-2777: Attested Tool-Server Admission (ATSA)" +description: "Attested Tool-Server Admission (ATSA)" +--- + +
+ + Draft + + + Standards Track + +
+ +| Field | Value | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **SEP** | 2777 | +| **Title** | Attested Tool-Server Admission (ATSA) | +| **Status** | Draft | +| **Type** | Standards Track | +| **Created** | 2026-05-22 | +| **Author(s)** | Alfredo Metere ([@metereconsulting](https://github.com/metereconsulting)) <alfredo.metere[@enclawed](https://github.com/enclawed).com>, Enclawed LLC | +| **Sponsor** | None (seeking) | +| **PR** | [#2777](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2777) | + +--- + +## Abstract + +MCP standardizes how a host and a tool server exchange messages, but not _trust_: +a host reads a server's self-declared tool list and dispatches calls with no +notion of which servers it may use, at what sensitivity, or which of a server's +tools are in bounds. This SEP adds an **optional, purely additive** admission +layer: a server publishes a small, offline-signed _clearance assertion_ at a +well-known URI; a host verifies it against a locally pinned trust root before any +tool dispatch; admitting a server is kept distinct from authorizing its tools via +a closed per-server tool allow-list; and every admission decision is auditable. +No existing MCP message changes shape, and an unextended host ignores the +mechanism and behaves exactly as today. + +## Motivation + +A host today takes a server's identity (beyond its TLS certificate) and its +advertised tool list on faith. A prompt-injected model can therefore drive a +destructive tool on any server it connects to. This is exploitable in **every** +deployment; for a single operator it is a risk they absorb, but for a regulated +operator it is disqualifying — and, lacking any admission record, unauditable. +NIST SP 800-53 expects least-privilege access enforcement (AC-3/AC-6), +information-flow enforcement between sensitivity levels (AC-4), and an audit +record of access decisions (AU-2/AU-9); none can be satisfied if the host has no +notion of which server is which, at what level, or which tools are in bounds. + +The existing specification provides no place to express any of this. TLS proves +the host reached the named endpoint; MCP's authorization profile proves the user +may use the server; neither answers the missing third question — _is this server +one the host is authorized to use as a tool provider, and at what sensitivity?_ + +## Specification + +### Discovery + +A server **MAY** advertise an attestation two interoperable ways: + +1. **Well-known resource.** The server **SHOULD** serve a _Server Attestation + Document_ (SAD) at `/.well-known/mcp-attestation` on the same origin as its + MCP endpoint, over TLS. +2. **In-band.** A server **MAY** return the SAD in the `initialize` result under + an experimental capability key. + +A conforming host **MUST** attempt (1), **MAY** attempt (2), and **MUST** treat +the absence of both as _unattested_, applying its configured posture (see +_Backward Compatibility_). + +### Server Attestation Document (SAD) + +A JSON object. **REQUIRED** fields: `v` (integer `1`); `id` (stable server +identity); `publisher`; `version`; `clearance` (a canonical name or alias of a +level in a named, totally ordered classification scheme); `capabilities` (string +array that **MUST** contain `"mcp-server"`); `signerKeyId` (identifies a key in +the host trust root); `signature` (base64 detached signature over the canonical +body). **OPTIONAL**, and part of the signed body when present: `netAllowedHosts` +(string array — when non-empty, binds the SAD to those origins) and +`verification` (string, e.g. `"tested"`). Verifiers **MUST** ignore unknown +fields and **MUST NOT** include them in the canonical body until a future `v` +registers them. (Formal JSON Schema: see the reference implementation.) + +### Canonicalization and signature + +The canonical body is the deterministic JSON serialization of every registered +field except `signature`, with object keys in sorted order, array members +sorted, and an absent `signerKeyId` serialized as `null`. Signer and verifier +**MUST** compute the signature over exactly these bytes. The reference profile +uses Ed25519; a profile **MAY** substitute another suite if signer and verifier +agree. + +### Host verification rules + +Before treating a connection as _admitted_, a host **MUST** evaluate the +following in order and **MUST** deny on the first failure: + +1. the SAD parses and its `capabilities` contain `"mcp-server"`; +2. `signerKeyId` and `signature` are present; +3. `signerKeyId` resolves to a key in the trust root; +4. that key's validity window (`notAfter`, if set) includes now; +5. the key is approved for the asserted `clearance`; +6. the `signature` verifies over the canonical body; +7. `clearance` dominates the host's required level (numeric rank comparison); +8. if `netAllowedHosts` is non-empty, the connected origin is a member. + +A host in a deny-by-default (high-assurance) posture **MUST** reject an +unattested or failing server; a permissive host **SHOULD** surface the failure +and **MUST NOT** silently treat it as success. + +### Tool authorization + +Admitting a server **MUST NOT** be read as authorizing all its tools. A host +**SHOULD** keep a per-server allow-list and **MUST** deny a `tools/call` for any +tool absent from it _before_ any network dispatch, regardless of what +`tools/list` advertises. Per-tool `clearance` **MAY** be added in a future `v`. + +### Audit + +A conforming host **SHOULD** append a tamper-evident record for every admission +decision (allow / deny / warn) and every tool-authorization denial, carrying the +resolved `signerKeyId`/`clearance` or the denial reason. + +### Trust establishment + +How the trust root is provisioned, pinned, and sealed is **deployment policy and +out of scope of this wire format**. Profiles **MAY** anchor it in a pinned key +set, X.509 chains, SPIFFE SVIDs, or a sigstore-style transparency log; the +verification rules are independent of the choice. + +### Error signalling + +When a host rejects a server for an attestation reason and surfaces it in-band, +it **SHOULD** carry a machine-readable `data.reason`, one per verification rule: +`not_mcp_server` (1), `unsigned` (2), `signer_not_trusted` (3), `signer_expired` +(4), `signer_not_approved` (5), `bad_signature` (6), `below_required` (7), +`host_not_bound` (8), and `tool_not_admitted` (tool authorization). + +### Compatibility, versioning, negotiation + +`v` versions the document; a verifier **MUST** reject versions it does not +understand. The extension is purely additive: an unextended host never fetches +the SAD, an unextended server never sees the host-side allow-list, and a host +**MUST** interoperate with unattested servers under its posture — enabling +incremental rollout. + +## Rationale + +### Why a spec extension rather than a per-vendor layer + +The mechanism can be implemented above MCP today — but _additive_ is not +_interoperable_. Bolted on by one vendor, attestation secures only that vendor's +island; a server's signed clearance document is meaningful only to hosts that +already agreed, out of band, on its shape and verification order. The payoff +("this server is attested" as a claim any host can check; servers publishing one +document instead of N vendor dialects; SDKs shipping the gate on by default) only +materializes once a single format is agreed, and only the spec owner can mint +that Schelling point. That the mechanism rides entirely above MCP is what makes +adoption cheap and low-risk, not a reason to defer it. + +### Design decisions + +- **Offline, detached signature.** Admission requires no online call to a third + party; the host verifies against a locally pinned trust root. This keeps + admission available and fast and avoids a runtime dependency on the signer's + infrastructure. +- **Well-known URI for discovery.** A host can evaluate admission _before_ + committing to a session, and the mechanism rides the established RFC 8615 + pattern instead of inventing a new one. +- **Admission separated from tool authorization.** Admitting a server says + nothing about which of its tools are in bounds. The closed per-server + allow-list is enforced before any network dispatch, defeating a prompt-injected + model that requests a tool the server advertises but the host never approved. +- **Numeric clearance ranks in a named, totally ordered scheme.** Dominance is a + single comparison; naming the scheme lets different sensitivity vocabularies + (government, healthcare, finance) coexist and interoperate. +- **Canonicalization fixes the signed bytes.** Sorted keys, sorted arrays, absent + `signerKeyId` as `null`, `signature` excluded — so signer and verifier agree on + exactly what was signed regardless of JSON serializer. + +### Alternatives considered + +- **Leave it to each vendor / keep it an extension only.** Rejected: additive ≠ + interoperable (above). Extensions are the right place to _experiment_; this + pattern has finished experimenting (production + evaluation), and its remaining + value is gated on a single agreed format that only the spec can supply. +- **In-band capability only, no well-known document.** Rejected: discovery must be + possible _before_ a connection is trusted, but `initialize` already implies a + session. The well-known fetch lets a host decide admission before it commits. +- **mTLS / client certificates.** Rejected: TLS answers "did I reach the named + endpoint," not "is this server cleared to operate at this sensitivity, and which + of its tools are in bounds." Orthogonal; ATSA composes with it. +- **OAuth-style bearer token.** Rejected: that is _user_-authorization ("may this + user use this server"), a different question from host admission ("may this host + use this server as a tool provider, at what level"). ATSA composes with MCP's + authorization profile. +- **Trusting `clearance` embedded in `tools/list`.** Rejected: the tool list is + unsigned and self-declared; trust cannot rest on it. The signed SAD is the + anchor; the allow-list is host-side state. +- **Online (OCSP-style) revocation as a v1 requirement.** Deferred: it adds an + availability dependency at admission time. v1 enforces per-signer `notAfter`; + online revocation is future work (see _Open questions_). + +### Related work + +- **Artifact attestation.** sigstore, The Update Framework (TUF), and in-toto + apply "verify a signed claim against a trusted root before use" to software + supply chains. ATSA applies the same discipline to MCP server admission. +- **Well-known discovery precedent.** OAuth 2.0 Authorization Server Metadata + (RFC 8414), `security.txt` (RFC 9116), and OpenID Connect Discovery all ride + RFC 8615 without forking the base protocols they extend. + +### Design-principles alignment + +This proposal is written against the MCP [design +principles](https://modelcontextprotocol.io/community/design-principles): + +- **Demonstration over deliberation.** The mechanism is in production in the open + `enclawed-oss` distribution, with a JSON Schema, conformance vectors, 48 + hermetic tests, an LLM-driven adversarial campaign (27,025 tool-name evasions + + 14,378 forged assertions, all denied), and a live Google Workspace end-to-end + run. The SEP is written from what the prototype taught, not from theory. +- **Standardization over innovation.** ATSA codifies a pattern already proven in a + shipping host/server pair; it invents no new primitive. The + signed-document-at-a-well-known-URI shape is the one OAuth metadata, + `security.txt`, and OpenID discovery already use. +- **Convergence over choice.** One signed-attestation format, one verification + ordering — so "this server is attested" becomes a claim _any_ host can check and + a server publishes _once_, instead of N mutually unintelligible vendor dialects. + Only the spec owner can mint that single path. +- **Interoperability over optimization.** It degrades gracefully: capability + negotiation gates in-band discovery, an unextended host never fetches the SAD, + an unextended server never sees the allow-list, and a conforming host **MUST** + still interoperate with unattested servers under its configured posture. Nothing + requires every participant to be equally capable. +- **Composability over specificity** _(objection addressed)._ A reviewer may ask + whether this can be built from existing primitives. It can — and is, today, + above MCP — which is exactly why adoption is cheap. But composability gives each + vendor an island, not interoperability: a server cannot publish one document + every host understands until the shape and verification order are agreed in one + place. The SEP adds exactly one document and one ordering — the minimum that + turns a per-vendor capability into an ecosystem one. +- **Stability over velocity** _(objection addressed)._ Because the addition is + permanent, it is deliberately small: one well-known document, one optional + `initialize` capability, zero changes to any existing message. An unextended + implementation is byte-for-byte unaffected, so the permanent cost to client + implementers is bounded to those who opt in. +- **Capability over compensation.** ATSA does not compensate for a temporary model + weakness. A more capable model that is prompt-injected is _more_ dangerous, not + less; the trust and authorization gap it closes is independent of model quality + and does not fade as models improve. +- **Pragmatism over purity.** Trust-root provisioning is left to deployment policy + rather than mandated, because operators' PKI realities differ. The wire format + is fixed; the anchoring is not. + +## Backward Compatibility + +None broken. The SAD lives at a well-known URI (RFC 8615); the tool allow-list is +host-side state; no existing MCP message changes shape. This is the additive +discipline that let OAuth, `security.txt`, and OpenID discovery ride the +well-known mechanism without forking the protocols they extended. A host with no +required level and an empty allow-list policy behaves exactly as a host does +today. + +## Security Implications + +The design resists confused-deputy tool invocation (tool authorization), server +impersonation and cross-origin replay (rules 3/6/8), level escalation (the level +is inside the signed body), and signer-key aging (rule 4). Out of scope and +delegated to other layers: content inspection of admitted connections, and the +behavior of a correctly-admitted, correctly-cleared server within its allowed +tools. The trust root is the root of all guarantees; its provisioning and sealing +are deployment policy (see _Trust establishment_), and a compromised signer key +is bounded by `notAfter` until online revocation (future work) lands. + +## Reference Implementation + +A production reference implementation ships in the open `enclawed-oss` +distribution — — at +`extensions/mcp-attested` (with a first-party Google Workspace bridge at +`extensions/mcp-google-workspace`). It includes a JSON Schema, an error registry, +and machine-checkable conformance vectors. + +### Prototype and evaluation + +Per the SEP process (explore → prototype → write the SEP from what the prototype +taught): the mechanism is implemented and in production, and is evaluated in the +companion preprint. Every stated guarantee is backed by an executable test driven +by an LLM-generated adversarial corpus: + +- **48 hermetic tests** (44 under `vitest`, 4 under `node:test`) exercise every + verification rule and the tool-authorization rule. +- A local-LLM (Ollama) **coverage campaign** generated **27,025 unique tool-name + evasions** (case/Unicode/whitespace/separator/path/near-miss tricks) — all + denied, zero leaked network writes — and **14,378 unique forged clearance + assertions** — all rejected. +- A **live end-to-end** run drove a real Google Workspace MCP endpoint through the + gate: the allow-listed tool was admitted and dispatched; out-of-allow-list tools + were denied before any network call. + +### Conformance (prerequisite for Final) + +Per [SEP-2484](https://modelcontextprotocol.io/seps/2484-conformance-tests-required-for-final-seps), +a Standards Track SEP with observable protocol behavior must, before reaching +`Final`, land a conformance scenario in the +[conformance repository](https://github.com/modelcontextprotocol/conformance) +plus a `sep-NNNN.yaml` traceability file mapping every MUST/MUST NOT and +SHOULD/SHOULD NOT in this _Specification_ to a check ID. This SEP is written to +make that mechanical: each verification rule (1–8), the tool-authorization rule, +and the audit and versioning requirements are already enumerated as +machine-checkable conformance vectors in the reference implementation. The +traceability file and scenario will be supplied against the draft spec-version tag +once a sponsor is engaged and the SEP number is assigned. + +## Open questions / future work + +- Per-tool (vs. per-server) clearance. +- A short-lived `notBefore`/expiry on the assertion itself and an online + (OCSP-style) revocation channel for compromised signer keys, beyond the + per-signer `notAfter` already enforced. +- Reconciling the `clearance` field with MCP's evolving authorization profile and + capability-negotiation handshake. + +## References + +- Companion preprint: _Attested Tool-Server Admission: A Security Extension to the + Model Context Protocol_ — archived at Zenodo, doi:10.5281/zenodo.20349263 + (arXiv ID forthcoming; PDF in this package). +- RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), + RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). +- Reference implementation: — + `extensions/mcp-attested`. diff --git a/docs/seps/index.mdx b/docs/seps/index.mdx index ff09a3642..f2a6fe0cd 100644 --- a/docs/seps/index.mdx +++ b/docs/seps/index.mdx @@ -12,8 +12,8 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi ## Summary +- **Draft**: 5 - **Final**: 32 -- **Draft**: 4 - **Accepted**: 4 - **In-review**: 1 @@ -21,6 +21,7 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi | SEP | Title | Status | Type | Created | | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ---------------------------------------------------- | ---------------- | ---------- | +| [SEP-2777](/seps/2777-attested-tool-server-admission) | Attested Tool-Server Admission (ATSA) | Draft | Standards Track | 2026-05-22 | | [SEP-2663](/seps/2663-tasks-extension) | Tasks Extension | Final | Extensions Track | 2026-04-27 | | [SEP-2596](/seps/2596-spec-feature-lifecycle-and-deprecation) | Specification Feature Lifecycle and Deprecation Policy | Draft | Process | 2026-04-17 | | [SEP-2577](/seps/2577-deprecate-roots-sampling-and-logging) | Deprecate Roots, Sampling, and Logging | Final | Standards Track | 2026-04-14 | diff --git a/seps/2777-attested-tool-server-admission.md b/seps/2777-attested-tool-server-admission.md index 8e15c25b2..c8994f59d 100644 --- a/seps/2777-attested-tool-server-admission.md +++ b/seps/2777-attested-tool-server-admission.md @@ -15,11 +15,11 @@ and RFC 8174. ## Abstract -MCP standardizes how a host and a tool server exchange messages, but not *trust*: +MCP standardizes how a host and a tool server exchange messages, but not _trust_: a host reads a server's self-declared tool list and dispatches calls with no notion of which servers it may use, at what sensitivity, or which of a server's tools are in bounds. This SEP adds an **optional, purely additive** admission -layer: a server publishes a small, offline-signed *clearance assertion* at a +layer: a server publishes a small, offline-signed _clearance assertion_ at a well-known URI; a host verifies it against a locally pinned trust root before any tool dispatch; admitting a server is kept distinct from authorizing its tools via a closed per-server tool allow-list; and every admission decision is auditable. @@ -40,8 +40,8 @@ notion of which server is which, at what level, or which tools are in bounds. The existing specification provides no place to express any of this. TLS proves the host reached the named endpoint; MCP's authorization profile proves the user -may use the server; neither answers the missing third question — *is this server -one the host is authorized to use as a tool provider, and at what sensitivity?* +may use the server; neither answers the missing third question — _is this server +one the host is authorized to use as a tool provider, and at what sensitivity?_ ## Specification @@ -49,15 +49,15 @@ one the host is authorized to use as a tool provider, and at what sensitivity?* A server **MAY** advertise an attestation two interoperable ways: -1. **Well-known resource.** The server **SHOULD** serve a *Server Attestation - Document* (SAD) at `/.well-known/mcp-attestation` on the same origin as its +1. **Well-known resource.** The server **SHOULD** serve a _Server Attestation + Document_ (SAD) at `/.well-known/mcp-attestation` on the same origin as its MCP endpoint, over TLS. 2. **In-band.** A server **MAY** return the SAD in the `initialize` result under an experimental capability key. A conforming host **MUST** attempt (1), **MAY** attempt (2), and **MUST** treat -the absence of both as *unattested*, applying its configured posture (see -*Backward Compatibility*). +the absence of both as _unattested_, applying its configured posture (see +_Backward Compatibility_). ### Server Attestation Document (SAD) @@ -83,7 +83,7 @@ agree. ### Host verification rules -Before treating a connection as *admitted*, a host **MUST** evaluate the +Before treating a connection as _admitted_, a host **MUST** evaluate the following in order and **MUST** deny on the first failure: 1. the SAD parses and its `capabilities` contain `"mcp-server"`; @@ -103,7 +103,7 @@ and **MUST NOT** silently treat it as success. Admitting a server **MUST NOT** be read as authorizing all its tools. A host **SHOULD** keep a per-server allow-list and **MUST** deny a `tools/call` for any -tool absent from it *before* any network dispatch, regardless of what +tool absent from it _before_ any network dispatch, regardless of what `tools/list` advertises. Per-tool `clearance` **MAY** be added in a future `v`. ### Audit @@ -139,8 +139,8 @@ incremental rollout. ### Why a spec extension rather than a per-vendor layer -The mechanism can be implemented above MCP today — but *additive* is not -*interoperable*. Bolted on by one vendor, attestation secures only that vendor's +The mechanism can be implemented above MCP today — but _additive_ is not +_interoperable_. Bolted on by one vendor, attestation secures only that vendor's island; a server's signed clearance document is meaningful only to hosts that already agreed, out of band, on its shape and verification order. The payoff ("this server is attested" as a claim any host can check; servers publishing one @@ -155,7 +155,7 @@ adoption cheap and low-risk, not a reason to defer it. party; the host verifies against a locally pinned trust root. This keeps admission available and fast and avoids a runtime dependency on the signer's infrastructure. -- **Well-known URI for discovery.** A host can evaluate admission *before* +- **Well-known URI for discovery.** A host can evaluate admission _before_ committing to a session, and the mechanism rides the established RFC 8615 pattern instead of inventing a new one. - **Admission separated from tool authorization.** Admitting a server says @@ -172,16 +172,16 @@ adoption cheap and low-risk, not a reason to defer it. ### Alternatives considered - **Leave it to each vendor / keep it an extension only.** Rejected: additive ≠ - interoperable (above). Extensions are the right place to *experiment*; this + interoperable (above). Extensions are the right place to _experiment_; this pattern has finished experimenting (production + evaluation), and its remaining value is gated on a single agreed format that only the spec can supply. - **In-band capability only, no well-known document.** Rejected: discovery must be - possible *before* a connection is trusted, but `initialize` already implies a + possible _before_ a connection is trusted, but `initialize` already implies a session. The well-known fetch lets a host decide admission before it commits. - **mTLS / client certificates.** Rejected: TLS answers "did I reach the named endpoint," not "is this server cleared to operate at this sensitivity, and which of its tools are in bounds." Orthogonal; ATSA composes with it. -- **OAuth-style bearer token.** Rejected: that is *user*-authorization ("may this +- **OAuth-style bearer token.** Rejected: that is _user_-authorization ("may this user use this server"), a different question from host admission ("may this host use this server as a tool provider, at what level"). ATSA composes with MCP's authorization profile. @@ -190,7 +190,7 @@ adoption cheap and low-risk, not a reason to defer it. anchor; the allow-list is host-side state. - **Online (OCSP-style) revocation as a v1 requirement.** Deferred: it adds an availability dependency at admission time. v1 enforces per-signer `notAfter`; - online revocation is future work (see *Open questions*). + online revocation is future work (see _Open questions_). ### Related work @@ -216,28 +216,28 @@ principles](https://modelcontextprotocol.io/community/design-principles): signed-document-at-a-well-known-URI shape is the one OAuth metadata, `security.txt`, and OpenID discovery already use. - **Convergence over choice.** One signed-attestation format, one verification - ordering — so "this server is attested" becomes a claim *any* host can check and - a server publishes *once*, instead of N mutually unintelligible vendor dialects. + ordering — so "this server is attested" becomes a claim _any_ host can check and + a server publishes _once_, instead of N mutually unintelligible vendor dialects. Only the spec owner can mint that single path. - **Interoperability over optimization.** It degrades gracefully: capability negotiation gates in-band discovery, an unextended host never fetches the SAD, an unextended server never sees the allow-list, and a conforming host **MUST** still interoperate with unattested servers under its configured posture. Nothing requires every participant to be equally capable. -- **Composability over specificity** *(objection addressed).* A reviewer may ask +- **Composability over specificity** _(objection addressed)._ A reviewer may ask whether this can be built from existing primitives. It can — and is, today, above MCP — which is exactly why adoption is cheap. But composability gives each vendor an island, not interoperability: a server cannot publish one document every host understands until the shape and verification order are agreed in one place. The SEP adds exactly one document and one ordering — the minimum that turns a per-vendor capability into an ecosystem one. -- **Stability over velocity** *(objection addressed).* Because the addition is +- **Stability over velocity** _(objection addressed)._ Because the addition is permanent, it is deliberately small: one well-known document, one optional `initialize` capability, zero changes to any existing message. An unextended implementation is byte-for-byte unaffected, so the permanent cost to client implementers is bounded to those who opt in. - **Capability over compensation.** ATSA does not compensate for a temporary model - weakness. A more capable model that is prompt-injected is *more* dangerous, not + weakness. A more capable model that is prompt-injected is _more_ dangerous, not less; the trust and authorization gap it closes is independent of model quality and does not fade as models improve. - **Pragmatism over purity.** Trust-root provisioning is left to deployment policy @@ -261,7 +261,7 @@ is inside the signed body), and signer-key aging (rule 4). Out of scope and delegated to other layers: content inspection of admitted connections, and the behavior of a correctly-admitted, correctly-cleared server within its allowed tools. The trust root is the root of all guarantees; its provisioning and sealing -are deployment policy (see *Trust establishment*), and a compromised signer key +are deployment policy (see _Trust establishment_), and a compromised signer key is bounded by `notAfter` until online revocation (future work) lands. ## Reference Implementation @@ -296,7 +296,7 @@ a Standards Track SEP with observable protocol behavior must, before reaching `Final`, land a conformance scenario in the [conformance repository](https://github.com/modelcontextprotocol/conformance) plus a `sep-NNNN.yaml` traceability file mapping every MUST/MUST NOT and -SHOULD/SHOULD NOT in this *Specification* to a check ID. This SEP is written to +SHOULD/SHOULD NOT in this _Specification_ to a check ID. This SEP is written to make that mechanical: each verification rule (1–8), the tool-authorization rule, and the audit and versioning requirements are already enumerated as machine-checkable conformance vectors in the reference implementation. The @@ -314,8 +314,8 @@ once a sponsor is engaged and the SEP number is assigned. ## References -- Companion preprint: *Attested Tool-Server Admission: A Security Extension to the - Model Context Protocol* — archived at Zenodo, doi:10.5281/zenodo.20349263 +- Companion preprint: _Attested Tool-Server Admission: A Security Extension to the + Model Context Protocol_ — archived at Zenodo, doi:10.5281/zenodo.20349263 (arXiv ID forthcoming; PDF in this package). - RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). From f27a859e15f50d8cf6e05892a2fb9c3ddae618c5 Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Fri, 22 May 2026 22:32:10 -0700 Subject: [PATCH 05/12] Fix MDX: use markdown links instead of autolinks in the SEP --- docs/seps/2777-attested-tool-server-admission.mdx | 4 ++-- seps/2777-attested-tool-server-admission.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/seps/2777-attested-tool-server-admission.mdx b/docs/seps/2777-attested-tool-server-admission.mdx index f5ce1e0ac..93622968b 100644 --- a/docs/seps/2777-attested-tool-server-admission.mdx +++ b/docs/seps/2777-attested-tool-server-admission.mdx @@ -280,7 +280,7 @@ is bounded by `notAfter` until online revocation (future work) lands. ## Reference Implementation A production reference implementation ships in the open `enclawed-oss` -distribution — — at +distribution — [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — at `extensions/mcp-attested` (with a first-party Google Workspace bridge at `extensions/mcp-google-workspace`). It includes a JSON Schema, an error registry, and machine-checkable conformance vectors. @@ -332,5 +332,5 @@ once a sponsor is engaged and the SEP number is assigned. (arXiv ID forthcoming; PDF in this package). - RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). -- Reference implementation: — +- Reference implementation: [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — `extensions/mcp-attested`. diff --git a/seps/2777-attested-tool-server-admission.md b/seps/2777-attested-tool-server-admission.md index c8994f59d..2cef2f444 100644 --- a/seps/2777-attested-tool-server-admission.md +++ b/seps/2777-attested-tool-server-admission.md @@ -267,7 +267,7 @@ is bounded by `notAfter` until online revocation (future work) lands. ## Reference Implementation A production reference implementation ships in the open `enclawed-oss` -distribution — — at +distribution — [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — at `extensions/mcp-attested` (with a first-party Google Workspace bridge at `extensions/mcp-google-workspace`). It includes a JSON Schema, an error registry, and machine-checkable conformance vectors. @@ -319,5 +319,5 @@ once a sponsor is engaged and the SEP number is assigned. (arXiv ID forthcoming; PDF in this package). - RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). -- Reference implementation: — +- Reference implementation: [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — `extensions/mcp-attested`. From e103f1a2fb03f3fd49ae5dc5e9e19aef63455a6e Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Thu, 28 May 2026 08:54:35 -0700 Subject: [PATCH 06/12] Renumber SEP to assigned PR number 2809 The original PR (#2777) was closed by accident; GitHub refused to reopen it because the head fork had been deleted in the meantime. This branch is re-submitted as PR #2809, so the SEP file name, title, table fields, and index entries are updated to match the new number. No specification content changes. --- docs/docs.json | 2 +- ...ission.mdx => 2809-attested-tool-server-admission.mdx} | 8 ++++---- docs/seps/index.mdx | 2 +- ...dmission.md => 2809-attested-tool-server-admission.md} | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename docs/seps/{2777-attested-tool-server-admission.mdx => 2809-attested-tool-server-admission.mdx} (98%) rename seps/{2777-attested-tool-server-admission.md => 2809-attested-tool-server-admission.md} (99%) diff --git a/docs/docs.json b/docs/docs.json index dbf8cde98..963c88d8d 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -456,7 +456,7 @@ "seps/2164-resource-not-found-error", "seps/2484-conformance-tests-required-for-final-seps", "seps/2596-spec-feature-lifecycle-and-deprecation", - "seps/2777-attested-tool-server-admission" + "seps/2809-attested-tool-server-admission" ] } ] diff --git a/docs/seps/2777-attested-tool-server-admission.mdx b/docs/seps/2809-attested-tool-server-admission.mdx similarity index 98% rename from docs/seps/2777-attested-tool-server-admission.mdx rename to docs/seps/2809-attested-tool-server-admission.mdx index 93622968b..216e6a1e9 100644 --- a/docs/seps/2777-attested-tool-server-admission.mdx +++ b/docs/seps/2809-attested-tool-server-admission.mdx @@ -1,6 +1,6 @@ --- -title: "SEP-2777: Attested Tool-Server Admission (ATSA)" -sidebarTitle: "SEP-2777: Attested Tool-Server Admission (ATSA)" +title: "SEP-2809: Attested Tool-Server Admission (ATSA)" +sidebarTitle: "SEP-2809: Attested Tool-Server Admission (ATSA)" description: "Attested Tool-Server Admission (ATSA)" --- @@ -15,14 +15,14 @@ description: "Attested Tool-Server Admission (ATSA)" | Field | Value | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **SEP** | 2777 | +| **SEP** | 2809 | | **Title** | Attested Tool-Server Admission (ATSA) | | **Status** | Draft | | **Type** | Standards Track | | **Created** | 2026-05-22 | | **Author(s)** | Alfredo Metere ([@metereconsulting](https://github.com/metereconsulting)) <alfredo.metere[@enclawed](https://github.com/enclawed).com>, Enclawed LLC | | **Sponsor** | None (seeking) | -| **PR** | [#2777](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2777) | +| **PR** | [#2809](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2809) | --- diff --git a/docs/seps/index.mdx b/docs/seps/index.mdx index f2a6fe0cd..65f161d9e 100644 --- a/docs/seps/index.mdx +++ b/docs/seps/index.mdx @@ -21,7 +21,7 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi | SEP | Title | Status | Type | Created | | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ---------------------------------------------------- | ---------------- | ---------- | -| [SEP-2777](/seps/2777-attested-tool-server-admission) | Attested Tool-Server Admission (ATSA) | Draft | Standards Track | 2026-05-22 | +| [SEP-2809](/seps/2809-attested-tool-server-admission) | Attested Tool-Server Admission (ATSA) | Draft | Standards Track | 2026-05-22 | | [SEP-2663](/seps/2663-tasks-extension) | Tasks Extension | Final | Extensions Track | 2026-04-27 | | [SEP-2596](/seps/2596-spec-feature-lifecycle-and-deprecation) | Specification Feature Lifecycle and Deprecation Policy | Draft | Process | 2026-04-17 | | [SEP-2577](/seps/2577-deprecate-roots-sampling-and-logging) | Deprecate Roots, Sampling, and Logging | Final | Standards Track | 2026-04-14 | diff --git a/seps/2777-attested-tool-server-admission.md b/seps/2809-attested-tool-server-admission.md similarity index 99% rename from seps/2777-attested-tool-server-admission.md rename to seps/2809-attested-tool-server-admission.md index 2cef2f444..57cf4073c 100644 --- a/seps/2777-attested-tool-server-admission.md +++ b/seps/2809-attested-tool-server-admission.md @@ -1,11 +1,11 @@ -# SEP-2777: Attested Tool-Server Admission (ATSA) +# SEP-2809: Attested Tool-Server Admission (ATSA) - **Status**: Draft - **Type**: Standards Track - **Created**: 2026-05-22 - **Author(s)**: Alfredo Metere (@metereconsulting) <alfredo.metere@enclawed.com>, Enclawed LLC - **Sponsor**: None (seeking) -- **PR**: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2777 +- **PR**: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2809 - **Preprint**: doi:10.5281/zenodo.20349263 - **Requires**: RFC 2119, RFC 8174, RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519) From df4708ab85e92f2d92cce1031612b466dc41f4bb Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Sun, 31 May 2026 22:39:51 -0700 Subject: [PATCH 07/12] Update 2809-attested-tool-server-admission.md Added contributions from Christopher Hopley (Algovoi) and Maaz (Interlock). --- seps/2809-attested-tool-server-admission.md | 92 ++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/seps/2809-attested-tool-server-admission.md b/seps/2809-attested-tool-server-admission.md index 57cf4073c..b5d3ac172 100644 --- a/seps/2809-attested-tool-server-admission.md +++ b/seps/2809-attested-tool-server-admission.md @@ -43,6 +43,13 @@ the host reached the named endpoint; MCP's authorization profile proves the user may use the server; neither answers the missing third question — _is this server one the host is authorized to use as a tool provider, and at what sensitivity?_ +The critical failure modes of this trust gap are empirically demonstrable. Adversarial testing against unprotected production multi-server MCP deployments reveals three exploitable runtime behaviors that bypass standard post-invocation defenses: +1. **Tool-Name Shadowing (`/tool-name-shadow`):** An untrusted or injected server registers a tool name that collides with a co-loaded, legitimate tool surface, causing the host to route execution calls silently to the malicious server. +2. **Dynamic Schema Drift (`/tool-schema-drift`):** A malicious server advertises an entirely benign schema during the initial `tools/list` handshake to pass static filters, but mutates parameter constraints or semantic requirements before the subsequent `tools/call` invocation. +3. **Registry Poisoning (`/tool-registry-poison`):** Direct manipulation of the host's tool cache via downstream prompt injection or unvetted secondary discovery layers. + +Baseline evaluations show that relying on the model layer to detect these exploits creates a brittle, non-deterministic boundary. Under standard model profiles, permissive personas completely fail to identify active schema drift, while catching tool-name shadowing imposes a severe reasoning load that degrades rapidly under context-window exhaustion. ATSA moves the security boundary from probabilistic, post-invocation reasoning to deterministic, cryptographic admission-time gating—preventing the prompt-injected model from ever accessing or driving an unvetted tool surface. + ## Specification ### Discovery @@ -168,6 +175,14 @@ adoption cheap and low-risk, not a reason to defer it. - **Canonicalization fixes the signed bytes.** Sorted keys, sorted arrays, absent `signerKeyId` as `null`, `signature` excluded — so signer and verifier agree on exactly what was signed regardless of JSON serializer. +- **Admission separated from tool authorization.** Admitting a server says + nothing about which of its tools are in bounds. The closed per-server + allow-list is enforced before any network dispatch, defeating a prompt-injected + model that requests a tool the server advertises but the host never approved. + *Empirical validation:* Independent defender-side benchmarks confirm that + runtime reasoning fails to contain dynamic exploits like schema drift under + non-stringent model personas. Enforcing a strict host-side allow-list at the + admission layer isolates the model from unvetted execution vectors entirely. ### Alternatives considered @@ -192,6 +207,72 @@ adoption cheap and low-risk, not a reason to defer it. availability dependency at admission time. v1 enforces per-signer `notAfter`; online revocation is future work (see _Open questions_). +## Composition with Runtime Drift Monitoring + +### The Admission/Runtime Boundary + +ATSA establishes trust strictly at the point of admission: it verifies an +unforgeable server identity and evaluates a pinned signer key before any network +dispatch. This is a point-in-time guarantee that structurally answers whether a +host is authorized to communicate with a specific server at a designated sensitivity +level at the moment of connection. + +By design, the wire format does not address whether a tool's internal behavior +or exposed schema schema-drift occurs over the lifetime of an active session. An +admitted server may pass verification successfully but subsequently mutate its +tool surface: a tool declared as read-only during an initial handshake could later +advertise mutating side-effects, inject new sensitive data parameters (e.g., PII), +escalate network externalities, or trigger dynamic schema modifications between +polling intervals. Because the underlying server identity remains unchanged, the +cryptographic admission guarantee holds, but the capability surface has drifted. + +This operational boundary is where a downstream runtime drift-monitoring +layer composes with ATSA. + +### Expectations of the Downstream Drift Layer + +A downstream continuous-monitoring framework requires a stable, cryptographically +verified anchor to bind its behavioral baselines to. ATSA provides this structural +dependency via two primitives: + +1. **Verified Server Identity (`id`):** The unforgeable identity string that the +runtime monitor utilizes as the primary indexing key for its tool schema baseline. +Without this cryptographic constraint, a drift monitor can be spoofed into +baselining an adversarial substitute server. +2. **Pinned Signer Key (`signerKeyId`):** The cryptographic guarantee that the entity +producing tool definitions at execution time matches the entity that was admitted. +This ensures an attacker cannot bypass drift telemetry by impersonating an admitted +origin. + +The downstream monitor anchors each tool baseline to the unique tuple `(id, toolName)` +at the first observation post-admission, checking subsequent `tools/list` or `tools/call` +envelopes against that baseline. + +### Explicit Non-Requirements of the Wire Format + +To prevent protocol scope creep and maintain structural minimalism, the following +boundaries are enforced: + +* **State Isolation:** The wire format **MUST NOT** carry drift state, baseline + histories, or schema versioning metadata. Baseline persistence and delta-evaluations + are strictly implementationconcerns of the runtime monitor. +* **Telemetry Isolation:** The wire format is not responsible for detecting or + signaling structural mutations. It provides the authenticated identity context; + detecting change is + the monitor's responsibility. +* **Frequency Decoupling:** ATSA is **NOT REQUIRED** to execute full cryptographic + re-attestation on every individual message frame. Admission remains an edge gate; + continuous runtime verification lives in the downstream monitoring layer. + +### Systemic Synergy + +Admission control without drift monitoring risks trusting a modified capability +surface indefinitely. Conversely, drift monitoring without a verified admission +anchor operates in the dark—its baselines remain completely vulnerable to origin +spoofing. Composed, they provide a complete security posture: ATSA guarantees +*who*, while the drift layer guarantees that the *who* hasn't silently changed +*what*. + ### Related work - **Artifact attestation.** sigstore, The Update Framework (TUF), and in-toto @@ -284,7 +365,10 @@ by an LLM-generated adversarial corpus: - A local-LLM (Ollama) **coverage campaign** generated **27,025 unique tool-name evasions** (case/Unicode/whitespace/separator/path/near-miss tricks) — all denied, zero leaked network writes — and **14,378 unique forged clearance - assertions** — all rejected. + assertions** — all rejected. This attacker-side validation is complemented + by independent defender-side baselines (e.g., the *AlgoVoi Agent-Trust-Bench* + differential profiles across 29-tool surfaces), confirming that unprotected + deployments remain uniformly vulnerable to runtime routing and session exploits. - A **live end-to-end** run drove a real Google Workspace MCP endpoint through the gate: the allow-listed tool was admitted and dispatched; out-of-allow-list tools were denied before any network call. @@ -312,6 +396,12 @@ once a sponsor is engaged and the SEP number is assigned. - Reconciling the `clearance` field with MCP's evolving authorization profile and capability-negotiation handshake. +## Acknowledgements + +Special thanks to: +* **Maaz (@maaz-interlock)** from Interlock for his critical contributions to the architectural framing of the runtime boundary and for drafting the normative composition language for downstream continuous drift-monitoring frameworks. +* **Christopher Hopley and the AlgoVoi team** for deploying their production adversarial testing suites (`agent-trust-bench.algovoi.co.uk`) to empirically validate the threat taxonomy closed by ATSA, providing critical defender-side baseline data for the protocol's motivation section. + ## References - Companion preprint: _Attested Tool-Server Admission: A Security Extension to the From 1ecc422053dc936abfc37d1d69d94ad037608e64 Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Sun, 31 May 2026 22:43:53 -0700 Subject: [PATCH 08/12] Update 2809-attested-tool-server-admission.md forgot to update the header with the contributors line --- seps/2809-attested-tool-server-admission.md | 1 + 1 file changed, 1 insertion(+) diff --git a/seps/2809-attested-tool-server-admission.md b/seps/2809-attested-tool-server-admission.md index b5d3ac172..055734425 100644 --- a/seps/2809-attested-tool-server-admission.md +++ b/seps/2809-attested-tool-server-admission.md @@ -8,6 +8,7 @@ - **PR**: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2809 - **Preprint**: doi:10.5281/zenodo.20349263 - **Requires**: RFC 2119, RFC 8174, RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519) +- **Contributor(s)**: Maaz (@maaz-interlock), Interlock; Christopher Hopley (@chopmob), AlgoVoi The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHOULD**, **SHOULD NOT**, **MAY**, and **OPTIONAL** are to be interpreted as in RFC 2119 From ed043738193ee718d4ed23c388781376070c8f71 Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Sun, 31 May 2026 22:55:43 -0700 Subject: [PATCH 09/12] chore(seps/2809): prettier fmt + regenerate rendered .mdx CI was red on two checks: - Markdown Format Check (npm run check:docs:format): prettier --check flagged seps/2809-attested-tool-server-admission.md for code-style drift introduced by the last hand-edits. - Render SEPs (npm run check:seps): the rendered mdx under docs/seps/ was older than the source .md. Re-ran `npx prettier --write seps/2809-attested-tool-server-admission.md` and `npm run generate:seps`. Both checks now pass locally. --- .../2809-attested-tool-server-admission.mdx | 94 ++++++++++++++++++- seps/2809-attested-tool-server-admission.md | 94 ++++++++++--------- 2 files changed, 141 insertions(+), 47 deletions(-) diff --git a/docs/seps/2809-attested-tool-server-admission.mdx b/docs/seps/2809-attested-tool-server-admission.mdx index 216e6a1e9..a769ae59e 100644 --- a/docs/seps/2809-attested-tool-server-admission.mdx +++ b/docs/seps/2809-attested-tool-server-admission.mdx @@ -56,6 +56,14 @@ the host reached the named endpoint; MCP's authorization profile proves the user may use the server; neither answers the missing third question — _is this server one the host is authorized to use as a tool provider, and at what sensitivity?_ +The critical failure modes of this trust gap are empirically demonstrable. Adversarial testing against unprotected production multi-server MCP deployments reveals three exploitable runtime behaviors that bypass standard post-invocation defenses: + +1. **Tool-Name Shadowing (`/tool-name-shadow`):** An untrusted or injected server registers a tool name that collides with a co-loaded, legitimate tool surface, causing the host to route execution calls silently to the malicious server. +2. **Dynamic Schema Drift (`/tool-schema-drift`):** A malicious server advertises an entirely benign schema during the initial `tools/list` handshake to pass static filters, but mutates parameter constraints or semantic requirements before the subsequent `tools/call` invocation. +3. **Registry Poisoning (`/tool-registry-poison`):** Direct manipulation of the host's tool cache via downstream prompt injection or unvetted secondary discovery layers. + +Baseline evaluations show that relying on the model layer to detect these exploits creates a brittle, non-deterministic boundary. Under standard model profiles, permissive personas completely fail to identify active schema drift, while catching tool-name shadowing imposes a severe reasoning load that degrades rapidly under context-window exhaustion. ATSA moves the security boundary from probabilistic, post-invocation reasoning to deterministic, cryptographic admission-time gating—preventing the prompt-injected model from ever accessing or driving an unvetted tool surface. + ## Specification ### Discovery @@ -181,6 +189,14 @@ adoption cheap and low-risk, not a reason to defer it. - **Canonicalization fixes the signed bytes.** Sorted keys, sorted arrays, absent `signerKeyId` as `null`, `signature` excluded — so signer and verifier agree on exactly what was signed regardless of JSON serializer. +- **Admission separated from tool authorization.** Admitting a server says + nothing about which of its tools are in bounds. The closed per-server + allow-list is enforced before any network dispatch, defeating a prompt-injected + model that requests a tool the server advertises but the host never approved. + _Empirical validation:_ Independent defender-side benchmarks confirm that + runtime reasoning fails to contain dynamic exploits like schema drift under + non-stringent model personas. Enforcing a strict host-side allow-list at the + admission layer isolates the model from unvetted execution vectors entirely. ### Alternatives considered @@ -205,6 +221,72 @@ adoption cheap and low-risk, not a reason to defer it. availability dependency at admission time. v1 enforces per-signer `notAfter`; online revocation is future work (see _Open questions_). +## Composition with Runtime Drift Monitoring + +### The Admission/Runtime Boundary + +ATSA establishes trust strictly at the point of admission: it verifies an +unforgeable server identity and evaluates a pinned signer key before any network +dispatch. This is a point-in-time guarantee that structurally answers whether a +host is authorized to communicate with a specific server at a designated sensitivity +level at the moment of connection. + +By design, the wire format does not address whether a tool's internal behavior +or exposed schema schema-drift occurs over the lifetime of an active session. An +admitted server may pass verification successfully but subsequently mutate its +tool surface: a tool declared as read-only during an initial handshake could later +advertise mutating side-effects, inject new sensitive data parameters (e.g., PII), +escalate network externalities, or trigger dynamic schema modifications between +polling intervals. Because the underlying server identity remains unchanged, the +cryptographic admission guarantee holds, but the capability surface has drifted. + +This operational boundary is where a downstream runtime drift-monitoring +layer composes with ATSA. + +### Expectations of the Downstream Drift Layer + +A downstream continuous-monitoring framework requires a stable, cryptographically +verified anchor to bind its behavioral baselines to. ATSA provides this structural +dependency via two primitives: + +1. **Verified Server Identity (`id`):** The unforgeable identity string that the + runtime monitor utilizes as the primary indexing key for its tool schema baseline. + Without this cryptographic constraint, a drift monitor can be spoofed into + baselining an adversarial substitute server. +2. **Pinned Signer Key (`signerKeyId`):** The cryptographic guarantee that the entity + producing tool definitions at execution time matches the entity that was admitted. + This ensures an attacker cannot bypass drift telemetry by impersonating an admitted + origin. + +The downstream monitor anchors each tool baseline to the unique tuple `(id, toolName)` +at the first observation post-admission, checking subsequent `tools/list` or `tools/call` +envelopes against that baseline. + +### Explicit Non-Requirements of the Wire Format + +To prevent protocol scope creep and maintain structural minimalism, the following +boundaries are enforced: + +- **State Isolation:** The wire format **MUST NOT** carry drift state, baseline + histories, or schema versioning metadata. Baseline persistence and delta-evaluations + are strictly implementationconcerns of the runtime monitor. +- **Telemetry Isolation:** The wire format is not responsible for detecting or + signaling structural mutations. It provides the authenticated identity context; + detecting change is + the monitor's responsibility. +- **Frequency Decoupling:** ATSA is **NOT REQUIRED** to execute full cryptographic + re-attestation on every individual message frame. Admission remains an edge gate; + continuous runtime verification lives in the downstream monitoring layer. + +### Systemic Synergy + +Admission control without drift monitoring risks trusting a modified capability +surface indefinitely. Conversely, drift monitoring without a verified admission +anchor operates in the dark—its baselines remain completely vulnerable to origin +spoofing. Composed, they provide a complete security posture: ATSA guarantees +_who_, while the drift layer guarantees that the _who_ hasn't silently changed +_what_. + ### Related work - **Artifact attestation.** sigstore, The Update Framework (TUF), and in-toto @@ -297,7 +379,10 @@ by an LLM-generated adversarial corpus: - A local-LLM (Ollama) **coverage campaign** generated **27,025 unique tool-name evasions** (case/Unicode/whitespace/separator/path/near-miss tricks) — all denied, zero leaked network writes — and **14,378 unique forged clearance - assertions** — all rejected. + assertions** — all rejected. This attacker-side validation is complemented + by independent defender-side baselines (e.g., the _AlgoVoi Agent-Trust-Bench_ + differential profiles across 29-tool surfaces), confirming that unprotected + deployments remain uniformly vulnerable to runtime routing and session exploits. - A **live end-to-end** run drove a real Google Workspace MCP endpoint through the gate: the allow-listed tool was admitted and dispatched; out-of-allow-list tools were denied before any network call. @@ -325,6 +410,13 @@ once a sponsor is engaged and the SEP number is assigned. - Reconciling the `clearance` field with MCP's evolving authorization profile and capability-negotiation handshake. +## Acknowledgements + +Special thanks to: + +- **Maaz (@maaz-interlock)** from Interlock for his critical contributions to the architectural framing of the runtime boundary and for drafting the normative composition language for downstream continuous drift-monitoring frameworks. +- **Christopher Hopley and the AlgoVoi team** for deploying their production adversarial testing suites (`agent-trust-bench.algovoi.co.uk`) to empirically validate the threat taxonomy closed by ATSA, providing critical defender-side baseline data for the protocol's motivation section. + ## References - Companion preprint: _Attested Tool-Server Admission: A Security Extension to the diff --git a/seps/2809-attested-tool-server-admission.md b/seps/2809-attested-tool-server-admission.md index 055734425..f57e3c8d0 100644 --- a/seps/2809-attested-tool-server-admission.md +++ b/seps/2809-attested-tool-server-admission.md @@ -45,6 +45,7 @@ may use the server; neither answers the missing third question — _is this serv one the host is authorized to use as a tool provider, and at what sensitivity?_ The critical failure modes of this trust gap are empirically demonstrable. Adversarial testing against unprotected production multi-server MCP deployments reveals three exploitable runtime behaviors that bypass standard post-invocation defenses: + 1. **Tool-Name Shadowing (`/tool-name-shadow`):** An untrusted or injected server registers a tool name that collides with a co-loaded, legitimate tool surface, causing the host to route execution calls silently to the malicious server. 2. **Dynamic Schema Drift (`/tool-schema-drift`):** A malicious server advertises an entirely benign schema during the initial `tools/list` handshake to pass static filters, but mutates parameter constraints or semantic requirements before the subsequent `tools/call` invocation. 3. **Registry Poisoning (`/tool-registry-poison`):** Direct manipulation of the host's tool cache via downstream prompt injection or unvetted secondary discovery layers. @@ -180,9 +181,9 @@ adoption cheap and low-risk, not a reason to defer it. nothing about which of its tools are in bounds. The closed per-server allow-list is enforced before any network dispatch, defeating a prompt-injected model that requests a tool the server advertises but the host never approved. - *Empirical validation:* Independent defender-side benchmarks confirm that - runtime reasoning fails to contain dynamic exploits like schema drift under - non-stringent model personas. Enforcing a strict host-side allow-list at the + _Empirical validation:_ Independent defender-side benchmarks confirm that + runtime reasoning fails to contain dynamic exploits like schema drift under + non-stringent model personas. Enforcing a strict host-side allow-list at the admission layer isolates the model from unvetted execution vectors entirely. ### Alternatives considered @@ -212,67 +213,67 @@ adoption cheap and low-risk, not a reason to defer it. ### The Admission/Runtime Boundary -ATSA establishes trust strictly at the point of admission: it verifies an -unforgeable server identity and evaluates a pinned signer key before any network -dispatch. This is a point-in-time guarantee that structurally answers whether a +ATSA establishes trust strictly at the point of admission: it verifies an +unforgeable server identity and evaluates a pinned signer key before any network +dispatch. This is a point-in-time guarantee that structurally answers whether a host is authorized to communicate with a specific server at a designated sensitivity level at the moment of connection. By design, the wire format does not address whether a tool's internal behavior -or exposed schema schema-drift occurs over the lifetime of an active session. An -admitted server may pass verification successfully but subsequently mutate its -tool surface: a tool declared as read-only during an initial handshake could later -advertise mutating side-effects, inject new sensitive data parameters (e.g., PII), -escalate network externalities, or trigger dynamic schema modifications between -polling intervals. Because the underlying server identity remains unchanged, the -cryptographic admission guarantee holds, but the capability surface has drifted. - -This operational boundary is where a downstream runtime drift-monitoring +or exposed schema schema-drift occurs over the lifetime of an active session. An +admitted server may pass verification successfully but subsequently mutate its +tool surface: a tool declared as read-only during an initial handshake could later +advertise mutating side-effects, inject new sensitive data parameters (e.g., PII), +escalate network externalities, or trigger dynamic schema modifications between +polling intervals. Because the underlying server identity remains unchanged, the +cryptographic admission guarantee holds, but the capability surface has drifted. + +This operational boundary is where a downstream runtime drift-monitoring layer composes with ATSA. ### Expectations of the Downstream Drift Layer A downstream continuous-monitoring framework requires a stable, cryptographically -verified anchor to bind its behavioral baselines to. ATSA provides this structural +verified anchor to bind its behavioral baselines to. ATSA provides this structural dependency via two primitives: 1. **Verified Server Identity (`id`):** The unforgeable identity string that the -runtime monitor utilizes as the primary indexing key for its tool schema baseline. -Without this cryptographic constraint, a drift monitor can be spoofed into -baselining an adversarial substitute server. + runtime monitor utilizes as the primary indexing key for its tool schema baseline. + Without this cryptographic constraint, a drift monitor can be spoofed into + baselining an adversarial substitute server. 2. **Pinned Signer Key (`signerKeyId`):** The cryptographic guarantee that the entity -producing tool definitions at execution time matches the entity that was admitted. -This ensures an attacker cannot bypass drift telemetry by impersonating an admitted -origin. + producing tool definitions at execution time matches the entity that was admitted. + This ensures an attacker cannot bypass drift telemetry by impersonating an admitted + origin. -The downstream monitor anchors each tool baseline to the unique tuple `(id, toolName)` -at the first observation post-admission, checking subsequent `tools/list` or `tools/call` +The downstream monitor anchors each tool baseline to the unique tuple `(id, toolName)` +at the first observation post-admission, checking subsequent `tools/list` or `tools/call` envelopes against that baseline. ### Explicit Non-Requirements of the Wire Format -To prevent protocol scope creep and maintain structural minimalism, the following +To prevent protocol scope creep and maintain structural minimalism, the following boundaries are enforced: -* **State Isolation:** The wire format **MUST NOT** carry drift state, baseline - histories, or schema versioning metadata. Baseline persistence and delta-evaluations - are strictly implementationconcerns of the runtime monitor. -* **Telemetry Isolation:** The wire format is not responsible for detecting or - signaling structural mutations. It provides the authenticated identity context; - detecting change is - the monitor's responsibility. -* **Frequency Decoupling:** ATSA is **NOT REQUIRED** to execute full cryptographic - re-attestation on every individual message frame. Admission remains an edge gate; - continuous runtime verification lives in the downstream monitoring layer. +- **State Isolation:** The wire format **MUST NOT** carry drift state, baseline + histories, or schema versioning metadata. Baseline persistence and delta-evaluations + are strictly implementationconcerns of the runtime monitor. +- **Telemetry Isolation:** The wire format is not responsible for detecting or + signaling structural mutations. It provides the authenticated identity context; + detecting change is + the monitor's responsibility. +- **Frequency Decoupling:** ATSA is **NOT REQUIRED** to execute full cryptographic + re-attestation on every individual message frame. Admission remains an edge gate; + continuous runtime verification lives in the downstream monitoring layer. ### Systemic Synergy -Admission control without drift monitoring risks trusting a modified capability -surface indefinitely. Conversely, drift monitoring without a verified admission -anchor operates in the dark—its baselines remain completely vulnerable to origin -spoofing. Composed, they provide a complete security posture: ATSA guarantees -*who*, while the drift layer guarantees that the *who* hasn't silently changed -*what*. +Admission control without drift monitoring risks trusting a modified capability +surface indefinitely. Conversely, drift monitoring without a verified admission +anchor operates in the dark—its baselines remain completely vulnerable to origin +spoofing. Composed, they provide a complete security posture: ATSA guarantees +_who_, while the drift layer guarantees that the _who_ hasn't silently changed +_what_. ### Related work @@ -366,9 +367,9 @@ by an LLM-generated adversarial corpus: - A local-LLM (Ollama) **coverage campaign** generated **27,025 unique tool-name evasions** (case/Unicode/whitespace/separator/path/near-miss tricks) — all denied, zero leaked network writes — and **14,378 unique forged clearance - assertions** — all rejected. This attacker-side validation is complemented - by independent defender-side baselines (e.g., the *AlgoVoi Agent-Trust-Bench* - differential profiles across 29-tool surfaces), confirming that unprotected + assertions** — all rejected. This attacker-side validation is complemented + by independent defender-side baselines (e.g., the _AlgoVoi Agent-Trust-Bench_ + differential profiles across 29-tool surfaces), confirming that unprotected deployments remain uniformly vulnerable to runtime routing and session exploits. - A **live end-to-end** run drove a real Google Workspace MCP endpoint through the gate: the allow-listed tool was admitted and dispatched; out-of-allow-list tools @@ -400,8 +401,9 @@ once a sponsor is engaged and the SEP number is assigned. ## Acknowledgements Special thanks to: -* **Maaz (@maaz-interlock)** from Interlock for his critical contributions to the architectural framing of the runtime boundary and for drafting the normative composition language for downstream continuous drift-monitoring frameworks. -* **Christopher Hopley and the AlgoVoi team** for deploying their production adversarial testing suites (`agent-trust-bench.algovoi.co.uk`) to empirically validate the threat taxonomy closed by ATSA, providing critical defender-side baseline data for the protocol's motivation section. + +- **Maaz (@maaz-interlock)** from Interlock for his critical contributions to the architectural framing of the runtime boundary and for drafting the normative composition language for downstream continuous drift-monitoring frameworks. +- **Christopher Hopley and the AlgoVoi team** for deploying their production adversarial testing suites (`agent-trust-bench.algovoi.co.uk`) to empirically validate the threat taxonomy closed by ATSA, providing critical defender-side baseline data for the protocol's motivation section. ## References From 9394cdb3443c52c2545d9ae910af5afc0c122355 Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Sun, 31 May 2026 23:17:50 -0700 Subject: [PATCH 10/12] seps/2809: add arXiv link (arXiv:2605.24248) to preamble and references The companion preprint is now indexed on arXiv. The earlier "arXiv ID forthcoming" placeholder is replaced with the canonical identifier in both the SEP preamble (alongside the Zenodo archive DOI) and the References list. Rendered .mdx regenerated to match. --- docs/seps/2809-attested-tool-server-admission.mdx | 4 ++-- seps/2809-attested-tool-server-admission.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/seps/2809-attested-tool-server-admission.mdx b/docs/seps/2809-attested-tool-server-admission.mdx index a769ae59e..119d59f3e 100644 --- a/docs/seps/2809-attested-tool-server-admission.mdx +++ b/docs/seps/2809-attested-tool-server-admission.mdx @@ -420,8 +420,8 @@ Special thanks to: ## References - Companion preprint: _Attested Tool-Server Admission: A Security Extension to the - Model Context Protocol_ — archived at Zenodo, doi:10.5281/zenodo.20349263 - (arXiv ID forthcoming; PDF in this package). + Model Context Protocol_ — [arXiv:2605.24248](https://arxiv.org/abs/2605.24248); + archived record: doi:10.5281/zenodo.20349263. - RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). - Reference implementation: [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — diff --git a/seps/2809-attested-tool-server-admission.md b/seps/2809-attested-tool-server-admission.md index f57e3c8d0..3cd3e7170 100644 --- a/seps/2809-attested-tool-server-admission.md +++ b/seps/2809-attested-tool-server-admission.md @@ -6,7 +6,7 @@ - **Author(s)**: Alfredo Metere (@metereconsulting) <alfredo.metere@enclawed.com>, Enclawed LLC - **Sponsor**: None (seeking) - **PR**: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2809 -- **Preprint**: doi:10.5281/zenodo.20349263 +- **Preprint**: [arXiv:2605.24248](https://arxiv.org/abs/2605.24248); archived record: doi:10.5281/zenodo.20349263 - **Requires**: RFC 2119, RFC 8174, RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519) - **Contributor(s)**: Maaz (@maaz-interlock), Interlock; Christopher Hopley (@chopmob), AlgoVoi @@ -408,8 +408,8 @@ Special thanks to: ## References - Companion preprint: _Attested Tool-Server Admission: A Security Extension to the - Model Context Protocol_ — archived at Zenodo, doi:10.5281/zenodo.20349263 - (arXiv ID forthcoming; PDF in this package). + Model Context Protocol_ — [arXiv:2605.24248](https://arxiv.org/abs/2605.24248); + archived record: doi:10.5281/zenodo.20349263. - RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). - Reference implementation: [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — From 9bc29155a835318be61b7d7b65a2cf74fa0da99f Mon Sep 17 00:00:00 2001 From: "metereconsulting, Alfredo Metere" Date: Sun, 21 Jun 2026 13:10:53 -0700 Subject: [PATCH 11/12] fix(docs): repair truncated docs.json from merge; re-render SEP nav (incl. SEP-2809) The merge of main truncated docs/docs.json mid-array (invalid JSON), crashing render-seps (Unexpected end of JSON input) and leaving docs/seps/index.mdx stale (prettier format failure). Restore docs.json from main and re-run scripts/render-seps.ts to regenerate the SEPs nav and index, which now includes SEP-2809. --- docs/docs.json | 338 ++++++++++++++++++++++++++++++++++++++++---- docs/seps/index.mdx | 98 ++++++------- 2 files changed, 361 insertions(+), 75 deletions(-) diff --git a/docs/docs.json b/docs/docs.json index beb6003b3..5203b1576 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -100,7 +100,7 @@ ] }, { - "group": "Tasks", + "group": "MCP Tasks", "pages": [ "extensions/tasks/overview" ] @@ -321,32 +321,16 @@ "group": "Base Protocol", "pages": [ "specification/draft/basic/index", - "specification/draft/basic/versioning", + "specification/draft/basic/lifecycle", + "specification/draft/basic/transports", + "specification/draft/basic/authorization", { - "group": "Message Patterns", - "pages": [ - "specification/draft/basic/patterns/index", - "specification/draft/basic/patterns/mrtr", - "specification/draft/basic/patterns/subscriptions", - "specification/draft/basic/patterns/cancellation", - "specification/draft/basic/patterns/progress" - ] - }, - { - "group": "Transports", - "pages": [ - "specification/draft/basic/transports/index", - "specification/draft/basic/transports/stdio", - "specification/draft/basic/transports/streamable-http" - ] - }, - { - "group": "Authorization", + "group": "Utilities", "pages": [ - "specification/draft/basic/authorization/index", - "specification/draft/basic/authorization/authorization-server-discovery", - "specification/draft/basic/authorization/client-registration", - "specification/draft/basic/authorization/security-considerations" + "specification/draft/basic/utilities/cancellation", + "specification/draft/basic/utilities/progress", + "specification/draft/basic/utilities/subscriptions", + "specification/draft/basic/utilities/mrtr" ] } ] @@ -423,4 +407,306 @@ "seps/986-specify-format-for-tool-names", "seps/990-enable-enterprise-idp-policy-controls-during-mcp-o", "seps/991-enable-url-based-client-registration-using-oauth-c", - "seps/994-shared-communication-practicesguidelines", \ No newline at end of file + "seps/994-shared-communication-practicesguidelines", + "seps/1024-mcp-client-security-requirements-for-local-server-", + "seps/1034--support-default-values-for-all-primitive-types-in", + "seps/1036-url-mode-elicitation-for-secure-out-of-band-intera", + "seps/1046-support-oauth-client-credentials-flow-in-authoriza", + "seps/1302-formalize-working-groups-and-interest-groups-in-mc", + "seps/1303-input-validation-errors-as-tool-execution-errors", + "seps/1319-decouple-request-payload-from-rpc-methods-definiti", + "seps/1330-elicitation-enum-schema-improvements-and-standards", + "seps/1577--sampling-with-tools", + "seps/1613-establish-json-schema-2020-12-as-default-dialect-f", + "seps/1686-tasks", + "seps/1699-support-sse-polling-via-server-side-disconnect", + "seps/1730-sdks-tiering-system", + "seps/1850-pr-based-sep-workflow", + "seps/1865-mcp-apps-interactive-user-interfaces-for-mcp", + "seps/2085-governance-succession-and-amendment", + "seps/2106-json-schema-2020-12", + "seps/2133-extensions", + "seps/2148-contributor-ladder", + "seps/2149-working-group-charter-template", + "seps/2164-resource-not-found-error", + "seps/2207-oidc-refresh-token-guidance", + "seps/2243-http-standardization", + "seps/2260-Require-Server-requests-to-be-associated-with-Client-requests", + "seps/2322-MRTR", + "seps/2468-recommend-issuer-claim-for-auth", + "seps/2484-conformance-tests-required-for-final-seps", + "seps/2549-TTL-for-list-results", + "seps/2567-sessionless-mcp", + "seps/2575-stateless-mcp", + "seps/2577-deprecate-roots-sampling-and-logging", + "seps/2596-spec-feature-lifecycle-and-deprecation", + "seps/2663-tasks-extension" + ] + }, + { + "group": "Draft", + "pages": [ + "seps/2809-attested-tool-server-admission" + ] + } + ] + }, + { + "tab": "Community", + "pages": [ + { + "group": "Get Involved", + "pages": [ + "community/contributing", + "community/communication", + "community/working-interest-groups", + "community/charter-template" + ] + }, + { + "group": "Propose Changes", + "pages": [ + "community/design-principles", + "community/sep-guidelines" + ] + }, + { + "group": "Governance", + "pages": [ + "community/governance", + "community/contributor-ladder", + "community/feature-lifecycle", + "community/sdk-tiers", + "community/antitrust" + ] + }, + { + "group": "Working Group Charters", + "pages": [ + "community/file-uploads/charter", + "community/inspector-v2/charter", + "community/interceptors/charter", + "community/sdk/charter", + "community/server-card/charter", + "community/skills-over-mcp/charter", + "community/triggers-events/charter" + ] + }, + { + "group": "Interest Group Charters", + "pages": [ + "community/tool-annotations/charter" + ] + }, + { + "group": "Roadmap", + "pages": [ + "development/roadmap" + ] + } + ] + } + ] + }, + "logo": { + "light": "/logo/light.svg", + "dark": "/logo/dark.svg" + }, + "seo": { + "metatags": { + "og:image": "https://raw.githubusercontent.com/modelcontextprotocol/docs/2eb6171ddbfeefde349dc3b8d5e2b87414c26250/images/og-image.png" + }, + "indexing": "navigable" + }, + "footer": { + "socials": { + "github": "https://github.com/modelcontextprotocol" + } + }, + "redirects": [ + { + "source": "/clients", + "destination": "/docs/getting-started/intro" + }, + { + "source": "/specification/versioning", + "destination": "/docs/learn/versioning" + }, + { + "source": "/specification/latest", + "destination": "/specification/2025-11-25", + "permanent": false + }, + { + "source": "/specification/latest/:slug*", + "destination": "/specification/2025-11-25/:slug*", + "permanent": false + }, + { + "source": "/tutorials/building-a-client", + "destination": "/docs/develop/build-client" + }, + { + "source": "/tutorials/building-a-client-node", + "destination": "/docs/develop/build-client" + }, + { + "source": "/tutorials/building-mcp-with-llms", + "destination": "/docs/develop/build-with-agent-skills" + }, + { + "source": "/faqs", + "destination": "/docs/getting-started/intro" + }, + { + "source": "/docs/tutorials/use-local-mcp-server", + "destination": "/docs/develop/connect-local-servers" + }, + { + "source": "/quickstart", + "destination": "/docs/develop/build-server" + }, + { + "source": "/docs/concepts/architecture", + "destination": "/docs/learn/architecture" + }, + { + "source": "/docs/concepts/elicitation", + "destination": "/specification/2025-06-18/client/elicitation" + }, + { + "source": "/docs/concepts/prompts", + "destination": "/specification/2025-06-18/server/prompts" + }, + { + "source": "/docs/concepts/resources", + "destination": "/specification/2025-06-18/server/resources" + }, + { + "source": "/docs/concepts/roots", + "destination": "/specification/2025-06-18/client/roots" + }, + { + "source": "/docs/concepts/sampling", + "destination": "/specification/2025-06-18/client/sampling" + }, + { + "source": "/docs/concepts/tools", + "destination": "/specification/2025-06-18/server/tools" + }, + { + "source": "/docs/concepts/transports", + "destination": "/specification/2025-06-18/basic/transports" + }, + { + "source": "/legacy/tools/debugging", + "destination": "/docs/tools/debugging" + }, + { + "source": "/legacy/concepts/architecture", + "destination": "/specification/latest/architecture" + }, + { + "source": "/legacy/concepts/elicitation", + "destination": "/specification/latest/client/elicitation" + }, + { + "source": "/legacy/concepts/prompts", + "destination": "/specification/latest/server/prompts" + }, + { + "source": "/legacy/concepts/resources", + "destination": "/specification/latest/server/resources" + }, + { + "source": "/legacy/concepts/roots", + "destination": "/specification/latest/client/roots" + }, + { + "source": "/legacy/concepts/sampling", + "destination": "/specification/latest/client/sampling" + }, + { + "source": "/legacy/concepts/tools", + "destination": "/specification/latest/server/tools" + }, + { + "source": "/legacy/concepts/transports", + "destination": "/specification/latest/basic/transports" + }, + { + "source": "/legacy/tools/inspector", + "destination": "/docs/tools/inspector" + }, + { + "source": "/introduction", + "destination": "/docs/getting-started/intro" + }, + { + "source": "/docs/concepts", + "destination": "/docs/learn/architecture" + }, + { + "source": "/docs/tutorials/use-remote-mcp-server", + "destination": "/docs/develop/connect-remote-servers" + }, + { + "source": "/quickstart/user", + "destination": "/docs/develop/connect-local-servers" + }, + { + "source": "/quickstart/server", + "destination": "/docs/develop/build-server" + }, + { + "source": "/quickstart/client", + "destination": "/docs/develop/build-client" + }, + { + "source": "/development/contributing", + "destination": "/community/contributing" + }, + { + "source": "/specification/draft/basic/security_best_practices", + "destination": "/docs/tutorials/security/security_best_practices" + }, + { + "source": "/specification/2025-11-25/basic/security_best_practices", + "destination": "/docs/tutorials/security/security_best_practices" + }, + { + "source": "/specification/2025-06-18/basic/security_best_practices", + "destination": "/docs/tutorials/security/security_best_practices" + }, + { + "source": "/extensions", + "destination": "/extensions/overview" + }, + { + "source": "/docs/extensions/overview", + "destination": "/extensions/overview" + }, + { + "source": "/docs/extensions/apps", + "destination": "/extensions/apps/overview" + }, + { + "source": "/docs/extensions/tasks", + "destination": "/extensions/tasks/overview" + }, + { + "source": "/community/seps", + "destination": "/seps" + }, + { + "source": "/community/seps/:slug*", + "destination": "/seps/:slug*" + } + ], + "contextual": { + "options": [ + "copy", + "view" + ] + } +} diff --git a/docs/seps/index.mdx b/docs/seps/index.mdx index 625d8701b..82906a249 100644 --- a/docs/seps/index.mdx +++ b/docs/seps/index.mdx @@ -17,60 +17,60 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi ## All SEPs -| SEP | Title | Status | Type | Created | -| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ---------------------------------------------------- | ---------------- | ---------- | -| [SEP-2809](/seps/2809-attested-tool-server-admission) | Attested Tool-Server Admission (ATSA) | Draft | Standards Track | 2026-05-22 | -| [SEP-2663](/seps/2663-tasks-extension) | Tasks Extension | Final | Extensions Track | 2026-04-27 | -| [SEP-2596](/seps/2596-spec-feature-lifecycle-and-deprecation) | Specification Feature Lifecycle and Deprecation Policy | Final | Process | 2026-04-17 | -| [SEP-2577](/seps/2577-deprecate-roots-sampling-and-logging) | Deprecate Roots, Sampling, and Logging | Final | Standards Track | 2026-04-14 | -| [SEP-2575](/seps/2575-stateless-mcp) | Make MCP Stateless | Final | Standards Track | 2025-06-18 | -| [SEP-2567](/seps/2567-sessionless-mcp) | Sessionless MCP via Explicit State Handles | Final | Standards Track | 2026-03-11 | -| [SEP-2549](/seps/2549-TTL-for-list-results) | TTL for List Results | Final | Standards Track | 2026-04-09 | -| [SEP-2484](/seps/2484-conformance-tests-required-for-final-seps) | Require Conformance Tests for Standards Track SEPs to Reach Final Status | Final | Process | 2026-03-27 | -| [SEP-2468](/seps/2468-recommend-issuer-claim-for-auth) | Recommend Issuer (iss) Parameter in MCP Auth Responses | Final | Standards Track | 2026-03-25 | -| [SEP-2322](/seps/2322-MRTR) | Multi Round-Trip Requests | Final | Standards Track | 2026-02-03 | -| [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Final | Standards Track | 2026-02-16 | -| [SEP-2243](/seps/2243-http-standardization) | HTTP Header Standardization for Streamable HTTP Transport | Final | Standards Track | 2026-02-04 | -| [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Final | Standards Track | 2026-02-04 | -| [SEP-2164](/seps/2164-resource-not-found-error) | Standardize Resource Not Found Error Code | Final | Standards Track | 2026-01-28 | -| [SEP-2149](/seps/2149-working-group-charter-template) | MCP Group Governance and Charter Template | Final | Process | 2025-01-15 | -| [SEP-2148](/seps/2148-contributor-ladder) | MCP Contributor Ladder | Final | Process | 2026-01-15 | -| [SEP-2133](/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | -| [SEP-2106](/seps/2106-json-schema-2020-12) | Tools `inputSchema` & `outputSchema` Conform to JSON Schema 2020-12 | Final | Standards Track | 2026-01-06 | -| [SEP-2085](/seps/2085-governance-succession-and-amendment) | Governance Succession and Amendment Procedures | Final | Process | 2025-12-05 | -| [SEP-1865](/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp) | MCP Apps - Interactive User Interfaces for MCP | Final | Extensions Track | 2025-11-21 | -| [SEP-1850](/seps/1850-pr-based-sep-workflow) | PR-Based SEP Workflow | Final | Process | 2025-11-20 | -| [SEP-1730](/seps/1730-sdks-tiering-system) | SDKs Tiering System | Final | Standards Track | 2025-10-29 | -| [SEP-1699](/seps/1699-support-sse-polling-via-server-side-disconnect) | Support SSE polling via server-side disconnect | Final | Standards Track | 2025-10-22 | -| [SEP-1686](/seps/1686-tasks) | Tasks | Final | Standards Track | 2025-10-20 | -| [SEP-1613](/seps/1613-establish-json-schema-2020-12-as-default-dialect-f) | Establish JSON Schema 2020-12 as Default Dialect for MCP | Final | Standards Track | 2025-10-06 | -| [SEP-1577](/seps/1577--sampling-with-tools) | Sampling With Tools | Final | Standards Track | 2025-09-30 | -| [SEP-1330](/seps/1330-elicitation-enum-schema-improvements-and-standards) | Elicitation Enum Schema Improvements and Standards Compliance | Final | Standards Track | 2025-08-11 | -| [SEP-1319](/seps/1319-decouple-request-payload-from-rpc-methods-definiti) | Decouple Request Payload from RPC Methods Definition | Final | Standards Track | 2025-08-08 | -| [SEP-1303](/seps/1303-input-validation-errors-as-tool-execution-errors) | Input Validation Errors as Tool Execution Errors | Final | Standards Track | 2025-08-05 | -| [SEP-1302](/seps/1302-formalize-working-groups-and-interest-groups-in-mc) | Formalize Working Groups and Interest Groups in MCP Governance | Final | Standards Track | 2025-08-05 | -| [SEP-1046](/seps/1046-support-oauth-client-credentials-flow-in-authoriza) | Support OAuth client credentials flow in authorization | Final | Standards Track | 2025-07-23 | -| [SEP-1036](/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera) | URL Mode Elicitation for secure out-of-band interactions | Final | Standards Track | 2025-07-22 | -| [SEP-1034](/seps/1034--support-default-values-for-all-primitive-types-in) | Support default values for all primitive types in elicitation schemas | Final | Standards Track | 2025-07-22 | -| [SEP-1024](/seps/1024-mcp-client-security-requirements-for-local-server-) | MCP Client Security Requirements for Local Server Installation | Final | Standards Track | 2025-07-22 | -| [SEP-994](/seps/994-shared-communication-practicesguidelines) | Shared Communication Practices/Guidelines | Final | Process | 2025-07-17 | -| [SEP-991](/seps/991-enable-url-based-client-registration-using-oauth-c) | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | Final | Standards Track | 2025-07-07 | -| [SEP-990](/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o) | Enable enterprise IdP policy controls during MCP OAuth flows | Final | Standards Track | 2025-06-04 | -| [SEP-986](/seps/986-specify-format-for-tool-names) | Specify Format for Tool Names | Final | Standards Track | 2025-07-16 | -| [SEP-985](/seps/985-align-oauth-20-protected-resource-metadata-with-rf) | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | Final | Standards Track | 2025-07-16 | -| [SEP-973](/seps/973-expose-additional-metadata-for-implementations-res) | Expose additional metadata for Implementations, Resources, Tools and Prompts | Final | Standards Track | 2025-07-15 | -| [SEP-932](/seps/932-model-context-protocol-governance) | Model Context Protocol Governance | Final | Process | 2025-07-08 | -| [SEP-414](/seps/414-request-meta) | Document OpenTelemetry Trace Context Propagation Conventions | Final | Standards Track | 2025-04-25 | +| SEP | Title | Status | Type | Created | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------- | ---------------- | ---------- | +| [SEP-2809](/seps/2809-attested-tool-server-admission) | Attested Tool-Server Admission (ATSA) | Draft | Standards Track | 2026-05-22 | +| [SEP-2663](/seps/2663-tasks-extension) | Tasks Extension | Final | Extensions Track | 2026-04-27 | +| [SEP-2596](/seps/2596-spec-feature-lifecycle-and-deprecation) | Specification Feature Lifecycle and Deprecation Policy | Final | Process | 2026-04-17 | +| [SEP-2577](/seps/2577-deprecate-roots-sampling-and-logging) | Deprecate Roots, Sampling, and Logging | Final | Standards Track | 2026-04-14 | +| [SEP-2575](/seps/2575-stateless-mcp) | Make MCP Stateless | Final | Standards Track | 2025-06-18 | +| [SEP-2567](/seps/2567-sessionless-mcp) | Sessionless MCP via Explicit State Handles | Final | Standards Track | 2026-03-11 | +| [SEP-2549](/seps/2549-TTL-for-list-results) | TTL for List Results | Final | Standards Track | 2026-04-09 | +| [SEP-2484](/seps/2484-conformance-tests-required-for-final-seps) | Require Conformance Tests for Standards Track SEPs to Reach Final Status | Final | Process | 2026-03-27 | +| [SEP-2468](/seps/2468-recommend-issuer-claim-for-auth) | Recommend Issuer (iss) Parameter in MCP Auth Responses | Final | Standards Track | 2026-03-25 | +| [SEP-2322](/seps/2322-MRTR) | Multi Round-Trip Requests | Final | Standards Track | 2026-02-03 | +| [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Final | Standards Track | 2026-02-16 | +| [SEP-2243](/seps/2243-http-standardization) | HTTP Header Standardization for Streamable HTTP Transport | Final | Standards Track | 2026-02-04 | +| [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Final | Standards Track | 2026-02-04 | +| [SEP-2164](/seps/2164-resource-not-found-error) | Standardize Resource Not Found Error Code | Final | Standards Track | 2026-01-28 | +| [SEP-2149](/seps/2149-working-group-charter-template) | MCP Group Governance and Charter Template | Final | Process | 2025-01-15 | +| [SEP-2148](/seps/2148-contributor-ladder) | MCP Contributor Ladder | Final | Process | 2026-01-15 | +| [SEP-2133](/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | +| [SEP-2106](/seps/2106-json-schema-2020-12) | Tools `inputSchema` & `outputSchema` Conform to JSON Schema 2020-12 | Final | Standards Track | 2026-01-06 | +| [SEP-2085](/seps/2085-governance-succession-and-amendment) | Governance Succession and Amendment Procedures | Final | Process | 2025-12-05 | +| [SEP-1865](/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp) | MCP Apps - Interactive User Interfaces for MCP | Final | Extensions Track | 2025-11-21 | +| [SEP-1850](/seps/1850-pr-based-sep-workflow) | PR-Based SEP Workflow | Final | Process | 2025-11-20 | +| [SEP-1730](/seps/1730-sdks-tiering-system) | SDKs Tiering System | Final | Standards Track | 2025-10-29 | +| [SEP-1699](/seps/1699-support-sse-polling-via-server-side-disconnect) | Support SSE polling via server-side disconnect | Final | Standards Track | 2025-10-22 | +| [SEP-1686](/seps/1686-tasks) | Tasks | Final | Standards Track | 2025-10-20 | +| [SEP-1613](/seps/1613-establish-json-schema-2020-12-as-default-dialect-f) | Establish JSON Schema 2020-12 as Default Dialect for MCP | Final | Standards Track | 2025-10-06 | +| [SEP-1577](/seps/1577--sampling-with-tools) | Sampling With Tools | Final | Standards Track | 2025-09-30 | +| [SEP-1330](/seps/1330-elicitation-enum-schema-improvements-and-standards) | Elicitation Enum Schema Improvements and Standards Compliance | Final | Standards Track | 2025-08-11 | +| [SEP-1319](/seps/1319-decouple-request-payload-from-rpc-methods-definiti) | Decouple Request Payload from RPC Methods Definition | Final | Standards Track | 2025-08-08 | +| [SEP-1303](/seps/1303-input-validation-errors-as-tool-execution-errors) | Input Validation Errors as Tool Execution Errors | Final | Standards Track | 2025-08-05 | +| [SEP-1302](/seps/1302-formalize-working-groups-and-interest-groups-in-mc) | Formalize Working Groups and Interest Groups in MCP Governance | Final | Standards Track | 2025-08-05 | +| [SEP-1046](/seps/1046-support-oauth-client-credentials-flow-in-authoriza) | Support OAuth client credentials flow in authorization | Final | Standards Track | 2025-07-23 | +| [SEP-1036](/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera) | URL Mode Elicitation for secure out-of-band interactions | Final | Standards Track | 2025-07-22 | +| [SEP-1034](/seps/1034--support-default-values-for-all-primitive-types-in) | Support default values for all primitive types in elicitation schemas | Final | Standards Track | 2025-07-22 | +| [SEP-1024](/seps/1024-mcp-client-security-requirements-for-local-server-) | MCP Client Security Requirements for Local Server Installation | Final | Standards Track | 2025-07-22 | +| [SEP-994](/seps/994-shared-communication-practicesguidelines) | Shared Communication Practices/Guidelines | Final | Process | 2025-07-17 | +| [SEP-991](/seps/991-enable-url-based-client-registration-using-oauth-c) | Enable URL-based Client Registration using OAuth Client ID Metadata Documents | Final | Standards Track | 2025-07-07 | +| [SEP-990](/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o) | Enable enterprise IdP policy controls during MCP OAuth flows | Final | Standards Track | 2025-06-04 | +| [SEP-986](/seps/986-specify-format-for-tool-names) | Specify Format for Tool Names | Final | Standards Track | 2025-07-16 | +| [SEP-985](/seps/985-align-oauth-20-protected-resource-metadata-with-rf) | Align OAuth 2.0 Protected Resource Metadata with RFC 9728 | Final | Standards Track | 2025-07-16 | +| [SEP-973](/seps/973-expose-additional-metadata-for-implementations-res) | Expose additional metadata for Implementations, Resources, Tools and Prompts | Final | Standards Track | 2025-07-15 | +| [SEP-932](/seps/932-model-context-protocol-governance) | Model Context Protocol Governance | Final | Process | 2025-07-08 | +| [SEP-414](/seps/414-request-meta) | Document OpenTelemetry Trace Context Propagation Conventions | Final | Standards Track | 2025-04-25 | ## SEP Status Definitions | Status | Definition | | ----------------------------------------------------- | -------------------------------------------------------- | -| Draft | SEP proposal with a sponsor, undergoing informal review | +| Draft | SEP proposal with a sponsor, undergoing informal review | | In-Review | SEP proposal ready for formal review by Core Maintainers | -| Accepted | SEP accepted, awaiting reference implementation | -| Final | SEP finalized with reference implementation complete | -| Rejected | SEP rejected by Core Maintainers | -| Withdrawn | SEP withdrawn by the author | +| Accepted | SEP accepted, awaiting reference implementation | +| Final | SEP finalized with reference implementation complete | +| Rejected | SEP rejected by Core Maintainers | +| Withdrawn | SEP withdrawn by the author | | Superseded | SEP replaced by a newer SEP | | Dormant | SEP without a sponsor, closed after 6 months | From cad8ad4a41b4e368f102cca693e807e8e5f372a8 Mon Sep 17 00:00:00 2001 From: Alfredo Metere Date: Sat, 4 Jul 2026 18:42:18 -0700 Subject: [PATCH 12/12] SEP-2809: strengthen audit to MUST; register the admission-control profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Audit: SHOULD -> MUST append a tamper-evident (hash-chained, append-only) record for every admission decision and tool-authorization denial; define what "tamper-evident" means. - Add a SHOULD to conform those records to the SEP-3004 admission-control extension profile, and define that profile here (SEP-3004 §2.2 names the extension and defers its registration to this SEP): type id, all-string fields, event_type vocabulary, and outcome binding. - Reference Implementation: note enclawed already satisfies the MUST via its hash-chained audit log; SEP-3004 conformance is the remaining SHOULD. - Add SEP-3004 to References. --- docs/docs.json | 7 +- .../2809-attested-tool-server-admission.mdx | 81 ++++++++++++++++++- seps/2809-attested-tool-server-admission.md | 81 ++++++++++++++++++- 3 files changed, 162 insertions(+), 7 deletions(-) diff --git a/docs/docs.json b/docs/docs.json index 0400bef14..a065f7806 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -456,7 +456,12 @@ "seps/2575-stateless-mcp", "seps/2577-deprecate-roots-sampling-and-logging", "seps/2596-spec-feature-lifecycle-and-deprecation", - "seps/2663-tasks-extension", + "seps/2663-tasks-extension" + ] + }, + { + "group": "Draft", + "pages": [ "seps/2809-attested-tool-server-admission" ] } diff --git a/docs/seps/2809-attested-tool-server-admission.mdx b/docs/seps/2809-attested-tool-server-admission.mdx index 119d59f3e..8d9490917 100644 --- a/docs/seps/2809-attested-tool-server-admission.mdx +++ b/docs/seps/2809-attested-tool-server-admission.mdx @@ -129,9 +129,75 @@ tool absent from it _before_ any network dispatch, regardless of what ### Audit -A conforming host **SHOULD** append a tamper-evident record for every admission +A conforming host **MUST** append a tamper-evident record for every admission decision (allow / deny / warn) and every tool-authorization denial, carrying the -resolved `signerKeyId`/`clearance` or the denial reason. +resolved `signerKeyId`/`clearance` or the denial reason. _Tamper-evident_ means +the record is committed to an append-only construction in which each record is +cryptographically chained to its predecessor, such that removal, reordering, or +mutation of any record is detectable by chain verification. An unrecorded +admission decision is unauditable and therefore non-conforming. + +Such records **SHOULD** conform to the `admission-control` extension profile of +the Tamper-Evident Audit Record Contract +([SEP-3004](https://modelcontextprotocol.io/seps/3004-tamper-evident-audit-record-contract)), +whose canonical form, shared integrity chain, and `C-REC-1`…`C-REC-7` conformance +vectors give the tamper-evidence an interoperable, independently verifiable shape +across implementations. SEP-3004 §2.2 names this extension and defers its +registration to this SEP; the profile is defined immediately below. Conformance to +SEP-3004 is a **SHOULD**, not a **MUST**: a host **MAY** satisfy the requirement +above with any tamper-evident construction (for example, its own hash-chained +audit log) and adopt the SEP-3004 profile to gain cross-implementation audit +interoperability. + +### Audit record profile: `admission-control` + +This defines the `admission-control` extension named by SEP-3004 §2.2, which +cross-references this SEP for the definition. A host that conforms its admission +records to SEP-3004 (the **SHOULD** above) carries the admission decision under +this type id. Every value defined here is a JSON string, keeping the extension +body within SEP-3004's string-only canonical form (§2.3); enumerated fields are +closed string vocabularies. Keys are serialized in the every-level lexicographic +sort SEP-3004 §2.3 mandates. + +**Type id:** `admission-control` + +**Required fields** + +| Field | Type | Description | +| -------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `server_id` | string | Stable identifier of the tool server whose admission was evaluated (its discovery identity, §Discovery). | +| `clearance_decision` | string (enum) | The gate verdict: `admitted` (the clearance assertion verified and all verification rules passed), `denied` (a verification rule failed, the server is outside the trust root, or the requested tool is outside the allow-list), or `deferred` (admission requires an out-of-band decision, e.g. a first-use prompt). | + +**Optional fields** + +| Field | Type | Description | +| ---------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `clearance_level` | string | The resolved `clearance` sensitivity classification from the Server Attestation Document. | +| `trust_root_id` | string | Identifier of the trust root against which the assertion was verified (§Trust establishment). | +| `assertion_uri` | string | The well-known URI (RFC 8615) the clearance assertion was retrieved from (§Discovery). | +| `assertion_serial` | string | Version/serial of the clearance assertion, for correlating admission across rotations. | +| `requested_tool` | string | The specific tool the recorded event attempted to invoke. | +| `tool_allowlist_scope` | string | The tools admitted for this server, encoded as a single canonical string: the allow-listed tool names normalized and lexicographically sorted (per SEP-3004 §2.3 ordering), joined by a single U+0020 space. The empty string denotes deny-by-default. Tool identifiers contain no whitespace. | + +**`event_type` vocabulary** (§2.9) + +| `event_type` | Emitted when | +| ------------------- | ------------------------------------------------------------------------------------ | +| `tool_call` | A tool invocation is evaluated against the admission gate. | +| `server_discovery` | A server's clearance assertion is evaluated for the first time. | +| `admission_recheck` | An already-admitted server is re-verified (assertion rotation or trust-root update). | + +**Outcome binding** (SEP-3004 §2.1.1) + +The `clearance_decision` is decision _evidence_ that informs the record's base +`outcome`; the base `outcome` states the enforcement result. The binding is: + +- `clearance_decision: admitted` → `outcome: allowed` +- `clearance_decision: denied` → `outcome: denied` +- `clearance_decision: deferred` → `outcome: deferred` + +A record whose base `outcome` is `allowed` **MUST NOT** carry `clearance_decision: +denied`, and vice versa. ### Trust establishment @@ -365,7 +431,12 @@ A production reference implementation ships in the open `enclawed-oss` distribution — [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — at `extensions/mcp-attested` (with a first-party Google Workspace bridge at `extensions/mcp-google-workspace`). It includes a JSON Schema, an error registry, -and machine-checkable conformance vectors. +and machine-checkable conformance vectors. This SEP was reverse-derived from that +implementation; the mechanism existed and ran in production before the wire format +was written down. Every admission decision (allow / deny / warn) and every +tool-authorization denial is appended to a hash-chained, append-only audit log +with chain verification, satisfying the §Audit **MUST** with an ATSA-native record +shape (SEP-3004 profile conformance is the remaining **SHOULD**). ### Prototype and evaluation @@ -424,5 +495,9 @@ Special thanks to: archived record: doi:10.5281/zenodo.20349263. - RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). +- Tamper-Evident Audit Record Contract: + [SEP-3004](https://modelcontextprotocol.io/seps/3004-tamper-evident-audit-record-contract) + (in progress; PR #3004) — the record contract the `admission-control` profile + above conforms to. - Reference implementation: [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — `extensions/mcp-attested`. diff --git a/seps/2809-attested-tool-server-admission.md b/seps/2809-attested-tool-server-admission.md index 3cd3e7170..6a903e990 100644 --- a/seps/2809-attested-tool-server-admission.md +++ b/seps/2809-attested-tool-server-admission.md @@ -117,9 +117,75 @@ tool absent from it _before_ any network dispatch, regardless of what ### Audit -A conforming host **SHOULD** append a tamper-evident record for every admission +A conforming host **MUST** append a tamper-evident record for every admission decision (allow / deny / warn) and every tool-authorization denial, carrying the -resolved `signerKeyId`/`clearance` or the denial reason. +resolved `signerKeyId`/`clearance` or the denial reason. _Tamper-evident_ means +the record is committed to an append-only construction in which each record is +cryptographically chained to its predecessor, such that removal, reordering, or +mutation of any record is detectable by chain verification. An unrecorded +admission decision is unauditable and therefore non-conforming. + +Such records **SHOULD** conform to the `admission-control` extension profile of +the Tamper-Evident Audit Record Contract +([SEP-3004](https://modelcontextprotocol.io/seps/3004-tamper-evident-audit-record-contract)), +whose canonical form, shared integrity chain, and `C-REC-1`…`C-REC-7` conformance +vectors give the tamper-evidence an interoperable, independently verifiable shape +across implementations. SEP-3004 §2.2 names this extension and defers its +registration to this SEP; the profile is defined immediately below. Conformance to +SEP-3004 is a **SHOULD**, not a **MUST**: a host **MAY** satisfy the requirement +above with any tamper-evident construction (for example, its own hash-chained +audit log) and adopt the SEP-3004 profile to gain cross-implementation audit +interoperability. + +### Audit record profile: `admission-control` + +This defines the `admission-control` extension named by SEP-3004 §2.2, which +cross-references this SEP for the definition. A host that conforms its admission +records to SEP-3004 (the **SHOULD** above) carries the admission decision under +this type id. Every value defined here is a JSON string, keeping the extension +body within SEP-3004's string-only canonical form (§2.3); enumerated fields are +closed string vocabularies. Keys are serialized in the every-level lexicographic +sort SEP-3004 §2.3 mandates. + +**Type id:** `admission-control` + +**Required fields** + +| Field | Type | Description | +| -------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `server_id` | string | Stable identifier of the tool server whose admission was evaluated (its discovery identity, §Discovery). | +| `clearance_decision` | string (enum) | The gate verdict: `admitted` (the clearance assertion verified and all verification rules passed), `denied` (a verification rule failed, the server is outside the trust root, or the requested tool is outside the allow-list), or `deferred` (admission requires an out-of-band decision, e.g. a first-use prompt). | + +**Optional fields** + +| Field | Type | Description | +| ---------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `clearance_level` | string | The resolved `clearance` sensitivity classification from the Server Attestation Document. | +| `trust_root_id` | string | Identifier of the trust root against which the assertion was verified (§Trust establishment). | +| `assertion_uri` | string | The well-known URI (RFC 8615) the clearance assertion was retrieved from (§Discovery). | +| `assertion_serial` | string | Version/serial of the clearance assertion, for correlating admission across rotations. | +| `requested_tool` | string | The specific tool the recorded event attempted to invoke. | +| `tool_allowlist_scope` | string | The tools admitted for this server, encoded as a single canonical string: the allow-listed tool names normalized and lexicographically sorted (per SEP-3004 §2.3 ordering), joined by a single U+0020 space. The empty string denotes deny-by-default. Tool identifiers contain no whitespace. | + +**`event_type` vocabulary** (§2.9) + +| `event_type` | Emitted when | +| ------------------- | ------------------------------------------------------------------------------------ | +| `tool_call` | A tool invocation is evaluated against the admission gate. | +| `server_discovery` | A server's clearance assertion is evaluated for the first time. | +| `admission_recheck` | An already-admitted server is re-verified (assertion rotation or trust-root update). | + +**Outcome binding** (SEP-3004 §2.1.1) + +The `clearance_decision` is decision _evidence_ that informs the record's base +`outcome`; the base `outcome` states the enforcement result. The binding is: + +- `clearance_decision: admitted` → `outcome: allowed` +- `clearance_decision: denied` → `outcome: denied` +- `clearance_decision: deferred` → `outcome: deferred` + +A record whose base `outcome` is `allowed` **MUST NOT** carry `clearance_decision: +denied`, and vice versa. ### Trust establishment @@ -353,7 +419,12 @@ A production reference implementation ships in the open `enclawed-oss` distribution — [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — at `extensions/mcp-attested` (with a first-party Google Workspace bridge at `extensions/mcp-google-workspace`). It includes a JSON Schema, an error registry, -and machine-checkable conformance vectors. +and machine-checkable conformance vectors. This SEP was reverse-derived from that +implementation; the mechanism existed and ran in production before the wire format +was written down. Every admission decision (allow / deny / warn) and every +tool-authorization denial is appended to a hash-chained, append-only audit log +with chain verification, satisfying the §Audit **MUST** with an ATSA-native record +shape (SEP-3004 profile conformance is the remaining **SHOULD**). ### Prototype and evaluation @@ -412,5 +483,9 @@ Special thanks to: archived record: doi:10.5281/zenodo.20349263. - RFC 2119 / RFC 8174 (requirement keywords), RFC 8414 (OAuth AS Metadata), RFC 8615 (Well-Known URIs), RFC 8032 (Ed25519), RFC 9116 (`security.txt`). +- Tamper-Evident Audit Record Contract: + [SEP-3004](https://modelcontextprotocol.io/seps/3004-tamper-evident-audit-record-contract) + (in progress; PR #3004) — the record contract the `admission-control` profile + above conforms to. - Reference implementation: [github.com/enclawed/enclawed-oss](https://github.com/enclawed/enclawed-oss) — `extensions/mcp-attested`.