An approval request asks whether one proposed tool call may run. It describes the tool and the arguments that will be passed to it.
The request does not execute the tool. It gives the provider enough information to decide whether execution is permitted.
Request
The adapter submits a CreateApprovalRequest, defined in the OpenAPI schema.
It names the tool, supplies its arguments and requests a window in which to decide.
It can also include the agent's reasoning and context observed by the adapter.
For example:
{
"tool": "issue_refund",
"server": "stripe",
"arguments": {
"payment_id": "payment_123",
"amount": 4900,
"currency": "GBP"
},
"timeout": "30m",
"agent_reasoning": "The customer was charged twice.",
"context": {
"session_id": "session_456"
}
}The tool and arguments must come from the intercepted call. They must not be replaced with the agent's description of what it intends to do.
The provider may use agent_reasoning to help a reviewer understand the request.
However, it is a claim from the agent and must be presented as such.
Agents should keep it to one or two sentences on why they make the call.
It should not repeat the arguments.
context is information collected by the adapter.
Tool Identity
tool is the name under which the tool is defined.
For a tool served over MCP, that is the name the server advertises in tools/list.
For a tool built into the runtime, it is the runtime's own name for it.
server is the alias of the MCP server that defines the tool, as the runtime configured it.
It is present only for tools served over MCP.
It is a label observed by the adapter and does not establish the server's identity.
Runtimes commonly join the two into one name when presenting tools to the model, such as mcp__stripe__create_refund.
The adapter must split that spelling into tool and server before submitting the request.
It must not submit the joined name.
The provider must record and return both fields unchanged.
A rule that matches on tool therefore sees the same name for the same tool whichever runtime intercepted the call.
Request Resource
The provider returns an ApprovalRequest, defined in the OpenAPI schema.
It contains the submitted call, a provider-assigned ID, the current state and the approval deadline.
Once the request is terminal, it also contains the decision.
The request ID identifies the same request throughout its lifetime.
States
A request has one of five states:
| State | Meaning | May the call run? |
|---|---|---|
pending | The provider has not recorded an outcome. | No. |
approved | The provider permits the call. | Once, after consuming the approval before decision.expires_at. |
denied | The provider refused the call. | No. |
expired | The approval window ended without a decision. | No. |
cancelled | The request was withdrawn or its instance was deleted. | No. |
Loading diagram…
Diagram source
stateDiagram-v2
[*] --> pending
pending --> approved
pending --> denied
pending --> expired
pending --> cancelled
approved --> [*]
denied --> [*]
expired --> [*]
cancelled --> [*]The final four states are terminal. A terminal request must not change state. Each request receives exactly one terminal outcome.
A provider may decide immediately when the request is created.
The adapter may therefore receive a terminal request without ever observing pending.
Decision
Once a request is terminal, its decision contains the outcome.
The Decision object is defined in the OpenAPI schema.
For example:
{
"status": "approved",
"note": "The duplicate charge has been confirmed.",
"decided_at": "2026-09-16T12:02:00Z",
"expires_at": "2026-09-16T12:07:00Z"
}The decision's status must match the request's status.
An approved decision must include expires_at.
The provider sets this to the time at which permission to start the call ends.
It must be later than decided_at.
The validity period is up to the provider.
Other outcomes do not include expires_at.
A note gives the agent additional information. It does not change the approved arguments or grant permission for other calls.
Deadlines and Cancellation
The adapter requests an approval window through timeout.
The provider sets deadline_at when it accepts the request.
It may shorten the requested window to meet its own limits, but must not lengthen it.
If the request is still pending at its deadline, the provider records expired.
It must not approve a pending request after that deadline.
Every provider must support cancellation. The requesting instance can withdraw a pending request when execution is abandoned. Deleting an instance also cancels all of its pending requests. If a decision, cancellation and expiry race, the provider must record only one terminal outcome.
Execution
An approval applies to exactly one submitted tool, arguments and execution attempt. If the proposed call changes, the adapter must request approval again.
Consumption
Before an approved call starts, the adapter must consume the approval. Consuming records with the provider that this approval is being used. It succeeds once per request, so receiving the same approval twice cannot cause the tool to run twice. This holds across retries, restarts and replicas: a second attempt, by any process, is refused.
The adapter must not start the call unless consumption succeeds.
A consumption that cannot be confirmed means the call does not run.
Once an approval is consumed, the request includes consumption.consumed_at.
Consumption gives at-most-once execution. If the adapter consumes an approval and then fails before the call completes, that approval cannot be used again. A new attempt requires a new approval request and idempotency key.
The deadline limits how long the provider may take to decide.
decision.expires_at limits how long the adapter has to start an approved call.
The adapter must check this time immediately before consuming the approval.
The provider must refuse consumption at or after expires_at.
A call that has already started may finish after this time.
The recorded request remains approved after its permission expires.
The expired request state means that no decision was reached before deadline_at.
Reading the decision again or replaying the original request does not extend expires_at.
If a new execution attempt is needed after expiry, it requires a new approval request and idempotency key.
Outcome
After the call finishes, the adapter may report whether it succeeded or failed.
The report appears in consumption.outcome and is optional.
It informs reviewers and audit records only.
It never changes the request's status or decision.