- Status: Draft
- Type: Standards Track
- Created: 2026-05-30
- Author(s): HyunJong Cho (@hangum)
- PR: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2817
- Related discussion: https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/2704
Imported from upstream. Proposed as modelcontextprotocol#2817 by @hangum on 2026-05-29, where it was still open on 2026-09-25. Accepted into omcp on its technical merit. The text is the author's; only the status, the sponsor field, and documentation links were changed on import.
Reviewed 2026-09-25 — not yet accepted. Held as a draft because no prototype a reviewer can run is cited. Acceptance needs a prototype a reviewer can run, with its setup and the data to run it; Final also needs conformance tests and the reference documentation chapter.
Abstract
This SEP proposes a small optional convention for carrying AI-generated invocation audit context in MCP request _meta.
When an AI host or client initiates an MCP request, the server often needs operational audit context that is not part of the tool-specific arguments:
- why the AI/client requested this operation,
- which model produced the invocation,
- what user intent caused the work, when safe to provide,
- and which requests belong to the same user turn.
Today, implementations that need this context must invent server-specific arguments such as user_query, invocation_reason, or transport-specific model metadata. That pollutes tool schemas, does not generalize across transports, and fragments audit logs across MCP servers.
This SEP defines one optional request _meta entry:
_meta["io.modelcontextprotocol/aiInvocation"]
The value is an object that may contain invocationReason, model, userIntent, and turnId.
All four fields are optional and client-asserted. Servers may ignore, redact, transform, or store them according to local policy, and MAY echo only turnId back in response _meta for correlation. These fields are not authorization evidence.
Motivation
AI-initiated MCP requests are different from ordinary application requests because the immediate caller may be an AI host acting on behalf of a user. For audit, incident review, and security monitoring, operators often need to answer:
- What user request caused this MCP request?
- Why did the AI choose this operation?
- Which model produced the invocation?
- Which other MCP requests were part of the same user turn?
In practice, server implementations have already started solving this locally. TadpoleDBHub, for example, records a server-specific audit context using custom request arguments and transport metadata:
user_query: original user request, shared across a user turninvocation_reason: per-request rationale generated by the AI/client- model attribution from client/transport metadata
- request/session correlation IDs to join AI audit, MCP service audit, SQL execution audit, and events
This works as an implementation-specific convention, but it has protocol-level drawbacks:
- It adds audit-only fields to tool-specific arguments.
- Each server invents different names and semantics.
- Transport-specific metadata does not generalize cleanly to all transports.
- Cross-server audit tooling cannot rely on a common location.
A standard _meta convention would let clients provide this audit context consistently without changing each tool schema.
Specification
Clients MAY include the following optional object in request params._meta for AI-initiated MCP requests:
_meta["io.modelcontextprotocol/aiInvocation"]?: {
invocationReason?: {
text: string;
};
model?: {
name: string;
};
userIntent?: {
text?: string;
redacted?: boolean;
};
turnId?: string;
}
Example:
{
"method": "tools/call",
"params": {
"name": "execute_query",
"arguments": {
"database_id": "123",
"query": "SELECT * FROM employees LIMIT 10"
},
"_meta": {
"io.modelcontextprotocol/aiInvocation": {
"invocationReason": {
"text": "The user asked to view employee rows, and the employees table was identified as the target table."
},
"model": {
"name": "example-model"
},
"userIntent": {
"text": "Show me 10 employees",
"redacted": false
},
"turnId": "opaque-client-generated-id"
}
}
}
}
Intermediaries that forward MCP requests SHOULD preserve _meta["io.modelcontextprotocol/aiInvocation"] unmodified unless they have a specific policy reason to remove or redact it.
Key naming and namespace
This SEP places all fields under a single _meta key in the MCP-reserved namespace:
io.modelcontextprotocol/aiInvocation
Under the current _meta key-name rules, any prefix whose second label is modelcontextprotocol or mcp is reserved for MCP use (for example io.modelcontextprotocol/, dev.mcp/); see PR #2758, which aligned the reserved-prefix rule in the schema with the specification docs. First-party MCP keys already use this form (e.g. io.modelcontextprotocol/clientInfo, io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/tasks).
Because this prefix is reserved for MCP, the key proposed here is not something a third party may self-assign. This SEP requests that, if accepted, MCP define io.modelcontextprotocol/aiInvocation as a protocol-level key. Until this SEP is accepted, conformant implementations that want to experiment SHOULD use a vendor-owned reverse-DNS prefix (for example com.example/aiInvocation).
A single namespaced envelope is used deliberately instead of bare keys such as _meta.invocationReason or _meta.turnId. Bare keys are curated by MCP and risk future collision; grouping the fields under one reserved key keeps them together and provides a single, clear extension point.
Historical context: PR #775 expanded _meta name reservation and the issue SEP-1788 ("Clarify _meta Property Reserved Keys") tracks reserved-key behavior. Note that the earlier reservation grammar (pre-#2758) would not have reserved io.modelcontextprotocol/; implementers should follow the current second-label rule.
invocationReason
invocationReason describes why the AI/client requested this specific MCP operation.
Semantics:
invocationReasonas a whole is optional; if present,textis REQUIRED — an emptyinvocationReasonobject carries no information.textis a concise human-readable explanation, ideally one sentence.invocationReasonis scoped to the emitted MCP request. If a client retries a logical operation, each emitted retry request SHOULD carry its own request-levelinvocationReason.invocationReasonis client/AI-asserted and may be inaccurate, incomplete, or hallucinated.invocationReasonMUST NOT be used as authorization evidence.
This SEP intentionally does not standardize invocation reason categories, retry attempt ordering, policy outcomes, approval lifecycle states, or server-side decision records.
model
model identifies the model that produced the invocation, when known.
Semantics:
- The
modelobject as a whole is optional; omit it entirely when the invoking model is unknown. Ifmodelis present,nameis REQUIRED — an emptymodelobject carries no information. nameis the model name as known by the client or host.- When available,
nameSHOULD use the provider's canonical model identifier rather than a display name. - In multi-model systems,
modelidentifies the model that produced this MCP request, not necessarily the orchestrator, router, or gateway. - This field is client-asserted and may be incomplete or spoofed.
- It should be used for audit attribution, debugging, and operations, not as a sole security control.
This SEP intentionally standardizes only model.name. Provider, gateway, proxy, or version attribution may be added by future SEPs or implementation-specific metadata if interoperability demand is demonstrated.
userIntent
userIntent describes the user-level intent that caused the work, when safe and appropriate to provide.
Semantics:
textMAY contain the user request or a short client-generated intent label.- Unlike
invocationReason.textandmodel.name,userIntent.textis intentionally optional: a client MAY signal that user intent existed but was withheld for privacy by sendingredacted: truewithouttext. redactedindicates thattextwas modified, shortened, or sanitized.- For privacy-sensitive deployments, clients MAY send a shortened or sanitized
textwithredacted: true, or omituserIntententirely. Carrying a hash or reference to a private transcript instead of raw text is left to deployment policy or a future SEP (see Open Questions); this SEP does not define a hash/reference field or format. userIntentis optional and may be omitted for privacy-sensitive deployments.- Empty or absent
userIntentis expected for some unattended or scheduled invocations and MUST NOT be treated as malformed audit data. userIntentmay contain sensitive or personal data and should be handled under the server operator's privacy, retention, and access-control policy.userIntentis client-asserted and MUST NOT be used as authorization evidence.
turnId
turnId correlates MCP requests that were caused by the same user turn.
A user turn means one user input or initiating event that may produce multiple MCP client-to-server requests.
Semantics:
turnIdis an opaque client-generated string.- Clients and servers MUST NOT parse semantics from the value.
- UUIDv4 or ULID are reasonable non-normative formats.
- The same
turnIdMAY be reused across multiple MCP requests caused by the same user turn. turnIdis not an idempotency key.turnIddoes not replace JSON-RPCid, which identifies an individual request/response message.turnIddoes not replaceprogressToken, which tracks progress for a specific long-running request.turnIddoes not imply ordering between requests and does not identify a stable tool-call attempt.- Servers MAY echo only
turnIdin response_meta["io.modelcontextprotocol/aiInvocation"]to help downstream correlation of asynchronous or streamed responses. - Servers SHOULD NOT echo
userIntent,invocationReason, ormodelin responses unless explicitly required by local policy, to avoid re-propagating potentially sensitive text on the response path. - Future SEPs may define additional correlation fields; this SEP standardizes only turn-level grouping.
Example with multiple requests in one turn:
{
"method": "tools/call",
"params": {
"name": "list_tables",
"arguments": {
"database_id": "123"
},
"_meta": {
"io.modelcontextprotocol/aiInvocation": {
"invocationReason": {
"text": "Need to inspect available tables before generating a query."
},
"model": {
"name": "example-model"
},
"userIntent": {
"text": "Show me 10 employees"
},
"turnId": "2f4f0c9e-8d72-4c66-9c6f-3f7f3e6a1c9f"
}
}
}
}
{
"method": "tools/call",
"params": {
"name": "execute_query",
"arguments": {
"database_id": "123",
"query": "SELECT * FROM employees LIMIT 10"
},
"_meta": {
"io.modelcontextprotocol/aiInvocation": {
"invocationReason": {
"text": "The employees table was identified and the user requested sample employee rows."
},
"model": {
"name": "example-model"
},
"userIntent": {
"text": "Show me 10 employees"
},
"turnId": "2f4f0c9e-8d72-4c66-9c6f-3f7f3e6a1c9f"
}
}
}
}
Specification and schema impact
If accepted, adoption requires declaring io.modelcontextprotocol/aiInvocation in the canonical schema (schema.ts, with schema.json regenerated). A decision is needed on whether the envelope is expressed as a typed object on request _meta (a named metadata type) or documented as a free-form reserved key; there is no separate key-registry file in the repository today.
The optional server-side response echo of turnId (see the turnId section) carries the same key in result _meta — _meta["io.modelcontextprotocol/aiInvocation"] with only turnId populated — and should be specified alongside the request-side key so that request and response uses of the key stay consistent.
Rationale
Why _meta?
MCP already uses _meta for protocol metadata that is not part of the application-level tool arguments. Audit context such as model attribution, invocation reason, user intent, and turn correlation is not tool-specific input. Placing it in _meta avoids polluting tool schemas.
Why one namespaced envelope?
A single reserved key avoids claiming multiple bare _meta keys and keeps related AI invocation audit fields together. It also gives future revisions one clear extension point without expanding the top-level _meta surface.
Why not tool arguments?
Tool arguments are part of each tool's semantic contract. Audit fields such as user_query or invocation_reason are useful locally but force every server to define its own convention. They also make audit metadata look like business input.
Why not JSON-RPC id?
JSON-RPC id identifies a single request/response exchange. turnId groups multiple MCP requests caused by the same user turn. The cardinality is different.
Why not progressToken?
progressToken is for progress reporting within a long-running request. turnId is for correlating multiple requests caused by one user turn.
Why not OpenTelemetry only?
OpenTelemetry is useful for backend service tracing, and SEP-414 documents trace context propagation in _meta. However, AI invocation audit has AI-specific concepts such as user intent, AI-generated invocation rationale, and model attribution. turnId is also a user-turn grouping key, not a distributed trace identifier. This SEP does not require or prohibit deployments from additionally carrying trace context through OTel keys or baggage.
Prior Art and Related Work
All proposals referenced below are open at the time of writing unless noted as merged.
SEP-2787: Tool call attestation (open)
SEP-2787 proposes signed tool-call attestation for MCP, binding invocation context (such as intent and tool/argument information) into a verifiable record. This SEP is narrower and intentionally non-cryptographic: it defines optional client-asserted input-audit metadata and does not define signatures, argument hashes, execution proofs, or server-authoritative decisions. The two are complementary rather than competing — a deployment that needs cryptographic assurance could carry signed attestations (SEP-2787) alongside this lightweight, unsigned input-audit context, where an unsigned invocationReason is the lightweight counterpart of an attested rationale.
SEP-414: OpenTelemetry trace context (merged)
SEP-414 documents using _meta to carry W3C Trace Context (traceparent, tracestate, baggage). It deliberately uses bare (unprefixed) keys as a narrowly-justified exception, because those names mirror an external wire-format standard. This SEP has no such external wire-format to match, so it uses the MCP-reserved namespaced key rather than bare keys; SEP-414 is therefore a useful contrast that explains when bare keys are and are not appropriate. SEP-414 also addresses distributed-trace propagation, which is complementary to, and distinct from, AI-invocation audit semantics.
_meta key reservation: SEP-1788 (open), PR #775 (merged), PR #2758 (merged)
These define and refine how _meta keys are reserved. This SEP follows the current rule established by PR #2758 by requesting a key under the MCP-reserved io.modelcontextprotocol/ namespace.
SEP-2643: Structured Authorization Denials (open)
SEP-2643 defines one server-side decision/denial path — a structured record of an authorization denial. This SEP is the complementary client-asserted input half ("why the AI asked"); it does not define decision or denial records and is designed to compose with server-side records such as SEP-2643.
SEP-2061: Action Security Metadata for MCP Tools (open)
SEP-2061 covers tool side-effect classification and action security metadata, which this SEP lists as a Non-Goal; that work belongs there.
SEP-2448 and SEP-2028: telemetry (open)
SEP-2448 (server execution telemetry) and SEP-2028 (_meta-to-HTTP-header forwarding for distributed tracing) address operational and trace telemetry through _meta. They are complementary to, and distinct from, client-asserted AI-invocation audit semantics.
Backward Compatibility
This proposal is backward compatible.
All fields are optional. Servers that do not understand this _meta entry may ignore it. Clients that do not provide it remain valid.
Existing server-specific conventions, such as tool arguments named user_query or invocation_reason, may continue to work. Implementations may accept both existing custom fields and the standardized _meta form during migration.
Security Implications
This proposal introduces audit metadata, not authorization metadata.
Security considerations:
invocationReasonmay be inaccurate, incomplete, or hallucinated.userIntentmay contain sensitive or personal data.modelis client-asserted and may be spoofed.turnIdis correlation metadata and MUST NOT be used as an idempotency key or authorization token.turnIdenables cross-request linkability; operators should treat it under the same privacy and retention policy as other audit identifiers.- Free-text fields may be persisted into audit stores and displayed in administrative tools.
Servers MUST NOT use invocationReason, userIntent, or model as authorization evidence. Authorization should rely on deterministic server-side context such as authenticated principal, scopes, tool metadata, policy, environment, approval state, and server-side checks.
Audit consumers that summarize or export this metadata should preserve the distinction between client-asserted context and server- or intermediary-witnessed facts. Derived reports or counters must not promote aiInvocation fields into authorization, execution, or outcome evidence.
Servers SHOULD apply local policy for logging, redaction, retention, and access control before persisting these fields.
Servers SHOULD neutralize log-forging vectors (for example CR/LF and other control characters) before persisting these free-text fields, and audit viewers SHOULD apply contextual output-encoding when rendering them to prevent stored-XSS.
Implementations SHOULD enforce per-field and total byte limits for this audit context to avoid excessive log growth or denial-of-service via large metadata.
Non-Goals
This SEP does not define:
- server-authoritative decision records (cf. SEP-2643, structured authorization denials),
- stable
toolCallIdsemantics, agentRunIdorsessionId,- retry or resumability semantics,
- response-echo semantics richer than the optional
turnIdecho, - approval lifecycle state machines,
- invocation reason taxonomy,
- tool side-effect taxonomy (see SEP-2061),
- execution proofs and tamper-evident audit bundles,
- SIEM schemas,
- authorization or policy decision mechanisms.
Those are valuable follow-up topics, but they should not block standardizing the minimum client-asserted input-audit context.
Reference Implementation
TadpoleDBHub has implemented a server-specific version of this audit context for DB/MCP execution audit.
Current implementation data point:
- the original user request is captured as
user_query, - per-call AI rationale is captured as
invocation_reason, - model attribution is captured from client/transport metadata,
- request/session IDs are used to join AI audit, MCP service audit, SQL execution audit, and events.
This validates the operational need, but also shows the drawback of server-specific argument injection. A protocol-level _meta convention would avoid polluting tool schemas and make cross-server audit tooling easier.
Additional implementation feedback from the discussion:
- agent-guard-style systems distinguish input audit from server-side decision records,
- multiple implementers independently identified a future need for stable tool-call identity and longer-lived agent/session correlation,
- independent implementers offered to review the SEP against live or prototype audit implementations.
Open Questions
- Should this convention apply to all client-to-server requests, or only AI-selected operations such as
tools/call? - What length guidance should be recommended for
invocationReason.textanduserIntent.text? - Should a future revision define a
userIntenthash/reference field, and if so with a self-describing format (for examplesha256:<hex>)? - Should the optional
turnIdresponse echo remainMAY, or be strengthened toSHOULDfor asynchronous/streamed transports?
Acknowledgments
This proposal grew out of MCP Discussion #2704. Thanks to the participants there for shaping the key framing — separating client-asserted input audit from server-authoritative decision records, the turnId user-turn correlation model, and the privacy guidance for userIntent.
AI Assistance Disclosure
This SEP draft 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.