Skip to content

SEP: Attested Tool-Server Admission (ATSA)#2809

Open
metereconsulting wants to merge 10 commits into
modelcontextprotocol:mainfrom
metereconsulting:feature/attested-tool-server-admission
Open

SEP: Attested Tool-Server Admission (ATSA)#2809
metereconsulting wants to merge 10 commits into
modelcontextprotocol:mainfrom
metereconsulting:feature/attested-tool-server-admission

Conversation

@metereconsulting

Copy link
Copy Markdown

Re-submission of #2777, which was closed and could not be reopened because the head fork had been deleted. Same commits (tip f27a859), same content; pushed from a freshly recreated fork. Apologies for the noise.

Summary

Adds a new Standards Track SEP: Attested Tool-Server Admission (ATSA).

New file: seps/0000-attested-tool-server-admission.md (I'll rename it to the PR number once assigned, per the SEP guidelines).

ATSA is 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 — an unextended host ignores the mechanism and behaves exactly as today.

Motivation (the gap in the current spec)

A host today takes a server's identity and its advertised tools/list on faith, so a prompt-injected model can drive a destructive tool on any server it connects to. This is exploitable in every deployment, and disqualifying + unauditable for regulated operators (NIST SP 800-53 AC-3/AC-4/AC-6, AU-2/AU-9). TLS answers "did I reach the named endpoint"; the authorization profile answers "may this user use this server"; nothing answers the third question — is this server one the host is authorized to use as a tool provider, and at what sensitivity?

Why this belongs in the spec, not a per-vendor layer

It can be built above MCP today — but additive ≠ interoperable. Bolted on by one vendor, attestation secures only that vendor's island. The payoff ("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.

Evidence (written from a production prototype, not theory)

  • Public reference implementation: https://github.com/enclawed/enclawed-ossextensions/mcp-attested (plus a Google Workspace bridge in extensions/mcp-google-workspace). Includes a JSON Schema, an error registry, and machine-checkable conformance vectors.
  • 48 hermetic tests covering every verification rule and the tool-authorization rule.
  • LLM-driven adversarial campaign: 27,025 unique tool-name evasions + 14,378 unique forged clearance assertions — all denied, zero leaked network writes.
  • Live end-to-end run against a real Google Workspace MCP endpoint: the allow-listed tool was admitted and dispatched; out-of-allow-list tools were denied before any network call.
  • Companion preprint (full threat model, methodology, results): doi:10.5281/zenodo.20349263.

Backward compatibility

None broken. The assertion lives at a well-known URI (RFC 8615); the tool allow-list is host-side state; no existing message changes shape. Unextended hosts and servers are unaffected — incremental rollout.

Design-principles alignment

The SEP's Rationale maps the proposal to all eight MCP design principles and addresses the two most likely objections head-on — Composability over specificity ("can't this just be an extension?") and Stability over velocity ("permanent cost"). It leads with Demonstration over deliberation: the mechanism is already in production. It is also written to satisfy the SEP-2484 conformance requirement out of the box — every MUST/SHOULD is already enumerated as a vector in the reference implementation.

Sponsor

Seeking a sponsor — this sits in the security / authorization scope, and was raised in #security-ig and #auth-ig for validation. Happy to walk through it at a Core Maintainer meeting.

AI-assistance disclosure (per CONTRIBUTING.md)

The author originated and directed this work and independently verified every factual claim, citation, and normative statement; an AI agent was used only as a mechanical drafting aid.

The original PR (modelcontextprotocol#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 modelcontextprotocol#2809, so the SEP file name, title,
table fields, and index entries are updated to match the new number.
No specification content changes.
@chopmob-cloud

This comment was marked as spam.

Added contributions from Christopher Hopley (Algovoi) and Maaz (Interlock).
forgot to update the header with the contributors line
@metereconsulting

Copy link
Copy Markdown
Author

@chopmob-cloud thank you for your contribution. I updated the SEP and the paper to reflect that.

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.
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.
@metereconsulting metereconsulting force-pushed the feature/attested-tool-server-admission branch from 7071122 to 9394cdb Compare June 1, 2026 06:53
@scottrhodes

Copy link
Copy Markdown

Strong framing in the new "Composition with Runtime Drift Monitoring" section — the who / what split is a clean way to scope the admission edge against everything downstream of it.

One axis that composition map doesn't yet name sits alongside the drift layer rather than under it: caller-side governance. ATSA answers which server the host admits; the drift layer answers whether the admitted server's surface changed; neither addresses which calling identity, under what declared purpose and scope, may invoke an admitted tool — the demand-side counterpart to your supply-side admission. Such a layer would compose the way your section describes: bind to the verified id/signerKeyId anchor, enforce before dispatch, on the opposite end of the connection. (I maintain a working reference implementation of one (notboatanchor/gif) — an Apache-2.0 MCP enforcement server, identity- and session-scoped, with an append-only audit trail, exercised against its own conformance scenarios — so this is grounded in a built implementation, not theory.)

That points at the one guarantee currently left undefined on both ends. Your Audit rule is a SHOULD for a "tamper-evident record," and the downstream-layer expectations leave audit to the monitor; the Interceptors SEP (#2624) likewise states "organizations MUST ensure audit trails are tamper-proof" without defining or checking it. So across admission, drift, and caller-governance, tamper-proof audit is named everywhere and specified nowhere — re-punted to deployment policy at each seam. Per SEP-2484 that's precisely the shape of thing that needs a vector + a traceability file to be more than a SHOULD.

Would you see reconciling the audit guarantee across these layers living inside ATSA's clearance/auth-profile reconciliation (already in your Open Questions), or as its own effort that the admission, drift, and governance layers each reference?

@metereconsulting

Copy link
Copy Markdown
Author

Strong framing in the new "Composition with Runtime Drift Monitoring" section — the who / what split is a clean way to scope the admission edge against everything downstream of it.

One axis that composition map doesn't yet name sits alongside the drift layer rather than under it: caller-side governance. ATSA answers which server the host admits; the drift layer answers whether the admitted server's surface changed; neither addresses which calling identity, under what declared purpose and scope, may invoke an admitted tool — the demand-side counterpart to your supply-side admission. Such a layer would compose the way your section describes: bind to the verified id/signerKeyId anchor, enforce before dispatch, on the opposite end of the connection. (I maintain a working reference implementation of one (notboatanchor/gif) — an Apache-2.0 MCP enforcement server, identity- and session-scoped, with an append-only audit trail, exercised against its own conformance scenarios — so this is grounded in a built implementation, not theory.)

That points at the one guarantee currently left undefined on both ends. Your Audit rule is a SHOULD for a "tamper-evident record," and the downstream-layer expectations leave audit to the monitor; the Interceptors SEP (#2624) likewise states "organizations MUST ensure audit trails are tamper-proof" without defining or checking it. So across admission, drift, and caller-governance, tamper-proof audit is named everywhere and specified nowhere — re-punted to deployment policy at each seam. Per SEP-2484 that's precisely the shape of thing that needs a vector + a traceability file to be more than a SHOULD.

Would you see reconciling the audit guarantee across these layers living inside ATSA's clearance/auth-profile reconciliation (already in your Open Questions), or as its own effort that the admission, drift, and governance layers each reference?

The audit-tamper-evident observation is the cross-cutting one. ATSA, drift, Interceptors #2624 each name it; none specify it. Three SHOULDs, zero vectors — won't clear Final under
SEP-2484.

The reconciliation belongs in its own SEP, not ATSA's Open Questions. Same composition discipline that kept drift out of ATSA's wire format: the shape of a tamper-evident record
(fields, canonicalization, hash-chained vs. Merkle, verifier algorithm) is orthogonal to which layer writes to it. Folding it into ATSA inherits an "audit primitive owned by
admission" — wrong factoring for something three layers consume.

Bootstrapping isn't a blocker. ATSA v1 ships with the current SHOULD plus a forward reference; each seam upgrades to MUST against the audit SEP's vector when it lands. RFC 9449 /
6749 is the pattern.

A built Apache-2.0 caller-governance server with conformance vectors is exactly the third consumer that makes the audit SEP worth landing standalone. Happy to cross-reference both
once SEP numbers are assigned.

@scottrhodes

Copy link
Copy Markdown

The audit-tamper-evident observation is the cross-cutting one. ATSA, drift, Interceptors #2624 each name it; none specify it. Three SHOULDs, zero vectors — won't clear Final under SEP-2484.

The reconciliation belongs in its own SEP, not ATSA's Open Questions. Same composition discipline that kept drift out of ATSA's wire format: the shape of a tamper-evident record (fields, canonicalization, hash-chained vs. Merkle, verifier algorithm) is orthogonal to which layer writes to it. Folding it into ATSA inherits an "audit primitive owned by admission" — wrong factoring for something three layers consume.

Bootstrapping isn't a blocker. ATSA v1 ships with the current SHOULD plus a forward reference; each seam upgrades to MUST against the audit SEP's vector when it lands. RFC 9449 / 6749 is the pattern.

A built Apache-2.0 caller-governance server with conformance vectors is exactly the third consumer that makes the audit SEP worth landing standalone. Happy to cross-reference both once SEP numbers are assigned.

I read the paper end to end — the canonicalization discipline (one canonical body, Definition 1, reused on the signing and verification paths) and the deny-by-default flavor gating are clean, and the bootstrapping path you describe is the right way through: ship the SHOULD with a forward reference now, upgrade to a MUST against the audit SEP's vector once it lands. Agreed too that the record shape is orthogonal to which layer writes it — ATSA's audit is admission-scoped (the mcp.connect.* / mcp.tool.deny decision events), the Interceptors thread (#2624) names the same tamper-proof-audit obligation at the per-operation layer, and gif's record is caller-scoped (per invocation). Three layers reaching for one guarantee from three angles is exactly the case for landing it once, standalone, that you're making.

On gif being a consumer — that's fair, though it may be able to contribute a bit more than consume. The append-only audit in notboatanchor/gif is already a concrete instance of the contract this SEP would define: a per-call record bound to a caller identity and a declared purpose, written append-only at the database-permission level (UPDATE/DELETE revoked, no row-level policy permits them — not append-only by convention). The dispatch fields — caller, session, event type, tool, outcome — are carried over a deterministic preimage that is SHA-256 hash-chained entry-to-entry; the declared-purpose field is recorded on the row but currently sits outside that chain, which is itself one of the field-set questions this SEP would settle. The record format and the chain construction are defined and exercised by the test suite. It's a working reference implementation tested against its own scenarios — not a production-deployment claim — but it does mean the decisions you flagged as orthogonal (field set, canonicalization, hash-chain vs. Merkle, verification procedure) already have one fully worked, test-backed answer to argue from, rather than a blank page.

One thing the two implementations seem to already agree on, which might help scope the SEP: the tamper-evidence itself isn't wire-observable, so neither side puts it in the wire-checkable conformance surface — ATSA keeps audit a SHOULD, and gif keeps the hash chain in the storage layer, outside its wire-conformance scenarios. That suggests the audit SEP's machine-checkable vectors realistically cover the observable record — its schema, the emission events, scope-gated read — while the integrity construction is specified and referenced but attested rather than gated. Worth pinning down early, since it shapes what "conforming" can even mean.

I'd be glad to put gif's record format on the table as a starting point and to do real drafting work on the contract itself, not just reference it from the side. Two things that would help me calibrate: which venue you see this homed in — one of the existing IGs (#security-ig / #auth-ig) or its own effort — and whether finding a sponsor is the gating step. And yes to cross-referencing once numbers are assigned.

@metereconsulting

metereconsulting commented Jun 2, 2026

Copy link
Copy Markdown
Author

The audit-tamper-evident observation is the cross-cutting one. ATSA, drift, Interceptors #2624 each name it; none specify it. Three SHOULDs, zero vectors — won't clear Final under SEP-2484.
The reconciliation belongs in its own SEP, not ATSA's Open Questions. Same composition discipline that kept drift out of ATSA's wire format: the shape of a tamper-evident record (fields, canonicalization, hash-chained vs. Merkle, verifier algorithm) is orthogonal to which layer writes to it. Folding it into ATSA inherits an "audit primitive owned by admission" — wrong factoring for something three layers consume.
Bootstrapping isn't a blocker. ATSA v1 ships with the current SHOULD plus a forward reference; each seam upgrades to MUST against the audit SEP's vector when it lands. RFC 9449 / 6749 is the pattern.
A built Apache-2.0 caller-governance server with conformance vectors is exactly the third consumer that makes the audit SEP worth landing standalone. Happy to cross-reference both once SEP numbers are assigned.

I read the paper end to end — the canonicalization discipline (one canonical body, Definition 1, reused on the signing and verification paths) and the deny-by-default flavor gating are clean, and the bootstrapping path you describe is the right way through: ship the SHOULD with a forward reference now, upgrade to a MUST against the audit SEP's vector once it lands. Agreed too that the record shape is orthogonal to which layer writes it — ATSA's audit is admission-scoped (the mcp.connect.* / mcp.tool.deny decision events), the Interceptors thread (#2624) names the same tamper-proof-audit obligation at the per-operation layer, and gif's record is caller-scoped (per invocation). Three layers reaching for one guarantee from three angles is exactly the case for landing it once, standalone, that you're making.

On gif being a consumer — that's fair, though it may be able to contribute a bit more than consume. The append-only audit in notboatanchor/gif is already a concrete instance of the contract this SEP would define: a per-call record bound to a caller identity and a declared purpose, written append-only at the database-permission level (UPDATE/DELETE revoked, no row-level policy permits them — not append-only by convention). The dispatch fields — caller, session, event type, tool, outcome — are carried over a deterministic preimage that is SHA-256 hash-chained entry-to-entry; the declared-purpose field is recorded on the row but currently sits outside that chain, which is itself one of the field-set questions this SEP would settle. The record format and the chain construction are defined and exercised by the test suite. It's a working reference implementation tested against its own scenarios — not a production-deployment claim — but it does mean the decisions you flagged as orthogonal (field set, canonicalization, hash-chain vs. Merkle, verification procedure) already have one fully worked, test-backed answer to argue from, rather than a blank page.

One thing the two implementations seem to already agree on, which might help scope the SEP: the tamper-evidence itself isn't wire-observable, so neither side puts it in the wire-checkable conformance surface — ATSA keeps audit a SHOULD, and gif keeps the hash chain in the storage layer, outside its wire-conformance scenarios. That suggests the audit SEP's machine-checkable vectors realistically cover the observable record — its schema, the emission events, scope-gated read — while the integrity construction is specified and referenced but attested rather than gated. Worth pinning down early, since it shapes what "conforming" can even mean.

I'd be glad to put gif's record format on the table as a starting point and to do real drafting work on the contract itself, not just reference it from the side. Two things that would help me calibrate: which venue you see this homed in — one of the existing IGs (#security-ig / #auth-ig) or its own effort — and whether finding a sponsor is the gating step. And yes to cross-referencing once numbers are assigned.

Hi Scott,

To answer your initial question directly: No, I will not be adding the explicit definition of the audit-tamper-evident observation to this ATSA PR.

Your comment perfectly articulates why: folding it in here inherits an "audit primitive owned by admission," which is the wrong factoring. Since ATSA, Interceptors (#2624), and gif all consume this exact same guarantee, it belongs in its own standalone SEP rather than burying it in ATSA's Open Questions.

I completely agree with the bootstrapping path you outlined. I will keep the current SHOULD in this PR along with a forward reference to the future audit SEP. That keeps the wire-conformance surface clean and ensures this PR clears SEP-2484 without getting stalled by orthogonal architectural dependencies.

To your points on moving the standalone effort forward:

Venue & Sponsorship: This belongs in #security-ig. We need to secure the sponsor for the standalone audit SEP so we aren't gated.

The Draft: I welcome your drafting help. Let’s use notboatanchor/gif's record format, canonicalization discipline, and SHA-256 hash-chain mechanics as the baseline draft so we start with a worked, test-backed reference implementation.

Conformance Boundary: Agree completely that the machine-checkable vectors should cover the observable record (schema, emission, scope-gated read) while the integrity construction is specified and attested.

I’ll update this PR with the forward reference text shortly. Let's move the deeper audit definition discussion to a new issue under #security-ig—tag me when you put the gif format on the table and we’ll cross-reference the numbers.

@localden localden added SEP draft SEP proposal with a sponsor. labels Jun 8, 2026
@localden localden added proposal SEP proposal without a sponsor. and removed draft SEP proposal with a sponsor. labels Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

proposal SEP proposal without a sponsor. SEP

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants