SEP-2817: AI Invocation Audit Context in Request _meta#2817
Conversation
Standards Track, Draft. Defines one optional reserved _meta key (io.modelcontextprotocol/aiInvocation) carrying client-asserted input-audit context (invocationReason, model, userIntent, turnId) for AI-initiated MCP requests. Operationalizes discussion modelcontextprotocol#2704. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @hangum — this looks like the right SEP-0 boundary to me. From an implementer perspective, the valuable split is clear:
The latest clarifications around per-emitted-request I would keep the security boundary sharp: these fields can help explain how an invocation was produced, but they should not be treated as evidence that the invocation was authorized. That preserves a clean path for a follow-up decision-record SEP without overloading this first input-audit layer. |
|
Thanks @rpelevin — that matches the intended boundary. I agree that the most important part is keeping this layer as client-asserted input-audit metadata only. It can explain how an invocation was produced, but it must not be treated as authorization evidence. That should leave a clean path for a follow-up decision-record SEP to define the server-authoritative layer separately: stable tool-call identity, approval/policy outcome, and execution/decision semantics. |
|
Implementer note from the SEP-2787 side. We shipped a proxy that emits a signed execution receipt paired with 2787 attestation per The "explicitly not authorization evidence" line is the right call, and it's worth saying why. The other half is a server- or proxy-side signed record of what actually ran: the resolved tool, an arguments digest, a result digest, and the 2787 attestation, bound to the same turn. Neither half stands alone as evidence. Client-asserted context with no signed execution record is unverifiable. A signed execution record with no intent context is hard to interpret. Bound together they give an auditable trail from why the model made the call to what the server actually did, which is the gap your follow-up bullet on server-side decision records points at. Concretely, the If the receipt field layout we settled on is useful input for the stable-tool-call-identity follow-up, I can post it. |
|
+1 to @vaaraio’s framing. Using the If the SEP-2787 receipt field layout is ready to share, I think posting it here would be useful input for the follow-up stable-tool-call-identity / decision-record discussion. |
|
Thanks @vaaraio and @AgentGymLeader — this framing matches the intended boundary. I agree that Using If you can share the SEP-2787 receipt field layout, that would be useful prior art for the follow-up stable-tool-call-identity / decision-record SEP. |
|
As committed in #2704, here's how SEP-2817's The load-bearing point first: these fields enter the audit stage, never the policy stage. agent-guard's pipeline is Field mapping
agent-guard's existing Where the SEP-0 / follow-up boundary falls out naturally The signed outcome — Working sketch Input request {
"io.modelcontextprotocol/aiInvocation": {
"invocationReason": { "text": "User asked to view employee rows; employees table identified as target." },
"model": { "name": "example-model" },
"userIntent": { "text": "Show me 10 employees", "redacted": false },
"turnId": "opaque-client-generated-id"
}
}Resulting audit record ( {
"type": "tool_call",
"timestamp": "2026-05-30T00:00:00Z",
"request_id": "req-7f3a",
"session_id": "sess-abc",
"turn_id": "opaque-client-generated-id",
"agent_id": "coder-1",
"tool": "bash",
"payload_hash": "sha256:…",
"decision": "allow",
"policy_version": "0.2.0",
"matched_rule": "read_only_select",
"details": {
"aiInvocation": {
"invocationReason": { "text": "User asked to view employee rows; …" },
"model": { "name": "example-model" },
"userIntent": { "text": "Show me 10 employees", "redacted": false }
}
}
}
Happy to refine this against the SEP as the schema firms up, and to run the implementation pass once it's open for review. |
|
Thanks @XuebinMa — this is a very useful implementation mapping. The most important part is that The I will treat this as a cross-implementation reference for the PR discussion, without expanding SEP-2817’s scope. |
|
For another implementation data point, TadpoleDBHub / Tadpole AI CLI currently implements a similar audit shape, but with app-specific arguments and headers because MCP does not yet have a standard Current mapping:
For strict audit configurations, TadpoleDBHub can reject calls missing the user intent or per-call invocation reason, but these fields remain audit-completeness inputs. Authorization and approval decisions still rely on server-side user/service/DB policy and execution controls, not on client-asserted rationale. This is the implementation pressure behind SEP-2817: the context is operationally useful, but without a standard |
|
@hangum here's the layout. It's shipped in v0.42 ( Three blocks plus the signature, mirroring the SEP-2787 trust-surface layout so both envelopes verify with the same canonicalization (RFC 8785 JCS) and the same HS256/ES256/RS256 stack. A 2787 verifier needs no new crypto to check a receipt. {
"version": 0,
"alg": "ES256",
"backLink": {
"attestationDigest": "sha256:...", // over the full 2787 wire envelope, signature included
"attestationNonce": "..." // echo of issuerAsserted.nonce, for fast correlation
},
"receiptAsserted": {
"iss": "...", "sub": "...", "iat": "...",
"nonce": "...", "secretVersion": "...", "alg": "ES256"
},
"outcomeDerived": {
"status": "executed | refused | errored",
"completedAt": "...",
"resultCommitment": { } // optional; absent for a refused call
},
"signature": "..."
}The field that carries the follow-up SEP is No
@XuebinMa your Layout, vectors, and the offline verifier ( |
|
Thanks @vaaraio — this is very useful prior art for the follow-up decision-record / execution-receipt SEP. I agree with the split: SEP-2817 should keep That gives a clean composition: user turn context ( I will keep this out of SEP-2817’s normative scope, but treat the receipt layout and vectors as strong input for the follow-up server-authoritative execution/decision record work. |
|
Thanks, this is useful prior art for the follow-up layer. The part I would preserve most carefully is the split between the two joins:
That keeps SEP-2817’s boundary clean: client-asserted intent context remains input-audit metadata, while the server/proxy-side receipt proves what actually happened. I would be cautious about standardizing the full receipt envelope here, but the |
This comment was marked as spam.
This comment was marked as spam.
|
Two things worth separating in the composition, since they decide whether the follow-up SEP needs one new shape or two. An allow/deny decision and the execution outcome are the same server-authoritative record, not two stacked layers. A denied call is audit evidence, so the decision belongs as a field on the outcome rather than a slot above it. In the receipt that field is What you bind to matters more than where the decision sits. Binding a decision to On the SETTLED/REVERSED tail: settlement and reversal are transaction semantics, and most So the composition already on the table is the one I'd keep: |
|
Thanks both — this is useful follow-up material. My current read is that SEP-2817 should stay unchanged: For the follow-up decision/execution-record work, I agree the general MCP shape should stay small and domain-neutral: a server-authoritative record with a request backlink and an outcome status such as |
|
Agreed on the back-link — that's the right critique, and it's the gap. agent-guard's So the composition you've drawn is the one I'd build to as well: One implementer data point on that last part, since agent-guard already separates the two timestamps you mention. Its pre-execution decision is Keeping the core enum small and domain-neutral (as you and @hangum landed) is right; settlement lifecycles layer above. Happy to align |
|
@XuebinMa agreed, and REFER is the right thing to pin down before the enum freezes. I'd keep it off the outcome status. The cleaner cut is the two-record shape from earlier in the thread: a decision record written before the side effect, an outcome record written after, both bound to the same attestation by That keeps the normative outcome enum as small and domain-neutral as @hangum landed on, and gives the human-review lifecycle a home on the decision axis rather than the outcome axis. agent-guard's |
This comment was marked as spam.
This comment was marked as spam.
|
On the follow-up server-authoritative record work, two updates. Vaara v0.48.0 ships the external time anchor for the audit chain. The chain head gets a trusted timestamp (an RFC 3161 token by default, or an eIDAS qualified timestamp where one is required), so the head's existence is provable against a clock the runtime does not control, even after a signing-key compromise. That is the post-compromise backdating defense a server-authoritative execution record needs, and it verifies offline. I also drafted the follow-up signed-execution-record SEP this thread keeps pointing at: a decision record before the side effect, an outcome record after, paired by Draft: https://github.com/vaaraio/vaara/blob/main/docs/sep/sep-server-execution-record.md I can open it against the SEP process if that's useful. |
This comment was marked as spam.
This comment was marked as spam.
|
Thanks — this looks like useful material for a separate server-authoritative execution-record SEP. For this PR, I would keep SEP-2817 focused on client-asserted input-audit context only. If you open the signed execution record draft separately, I’d be happy to review it there so this PR can stay scoped. |
|
Opened it as #2828, kept to the server-authoritative half so SEP-2817 stays scoped to client-asserted input audit. It defines a decision record before the side effect (the allow/block/escalate verdict plus the risk basis) and an outcome record after (executed/refused/errored plus a result commitment), paired by Thanks for the offer to review there. |
|
I rebased/updated the branch against current The scope remains unchanged: SEP-2817 is limited to client-asserted input-audit context in request I would appreciate maintainer guidance on whether this is ready for sponsorship/review, or if the proposal should be adjusted before moving forward. |
This PR adds a new Standards Track SEP (Status: Draft, seeking a sponsor): AI Invocation Audit Context in Request
_meta.It standardizes one optional reserved
_metakey —io.modelcontextprotocol/aiInvocation— carrying optional, client-asserted input-audit context for AI-initiated MCP requests:invocationReason— why the AI/client made this callmodel— which model produced the invocationuserIntent— the user-level intent that caused the work (when safe to provide)turnId— groups MCP requests from the same user turn (servers MAY echo onlyturnIdin response_meta)All fields are optional and explicitly not authorization evidence. The proposal is deliberately minimal; server-side decision records, stable tool-call identity, agent/session correlation, and taxonomies are left to follow-up SEPs.
Operationalizes Discussion #2704: #2704
Prior art reconciled in the SEP: SEP-2787 (tool-call attestation), SEP-414 (OTel trace context), SEP-1788 / #775 / #2758 (
_metakey reservation), SEP-2643 (structured authorization denials), SEP-2061 (action security metadata), SEP-2448 / SEP-2028 (telemetry).AI assistance disclosure
This SEP was prepared with AI assistance for structure, wording, and review. The proposal direction, implementation experience, and final technical judgment were reviewed and edited by the author.