Adding Tool Call Approvals in OpenResponses
Description
Agents require user approval for state-changing operations and reads of sensitive data. We propose adding tool call approvals as a subtype of a new generic elicitation item type, with options to approve once, approve for all sessions, approve for this session, or deny the request.
The OpenAI Responses API has types mcp_approval_request and mcp_approval_response, but as server side tool execution increases in popularity, a generic, non-MCP specific option will unlock additional capabilities for server-side tool execution. When/if MCP specific events are added to the OpenResponses spec, we should reuse these events for MCP approvals as well.
The elicitation Item Type
This proposal introduces a generic elicitation item type to the Responses API. The elicitation type uses an elicitation_type field as a discriminator to support different kinds of server-to-client interactions that require user input. Tool call approvals are the first subtype (elicitation_type: "tool_call_approval"), and this same pattern is reused by the OAuth Logins proposal (elicitation_type: "oauth_login").
All elicitation items share a common structure:
| Field |
Type |
Required |
Description |
id |
string |
Yes |
Unique identifier for this elicitation |
type |
string |
Yes |
Always "elicitation" |
elicitation_type |
string |
Yes |
Discriminator: "tool_call_approval", "oauth_login", or future types |
message |
string |
Yes |
Human-readable explanation of why the interaction is needed |
All elicitation responses share a common structure:
| Field |
Type |
Required |
Description |
id |
string |
Yes |
Unique identifier for this response |
type |
string |
Yes |
Always "elicitation_response" |
elicitation_id |
string |
Yes |
References the elicitation item being responded to |
action |
string |
Yes |
One of "accept", "decline", "cancel" |
reason |
string |
No |
Optional reason for the action |
Subtype-specific fields are added alongside these common fields. This design is forward-compatible with the MCP Elicitation specification:
- The
action field uses the same vocabulary as MCP elicitation responses (accept, decline, cancel).
- The
message field matches MCP's requirement for a human-readable explanation.
- Future MCP elicitation subtypes (e.g.
elicitation_type: "form" for MCP form mode, elicitation_type: "url" for MCP URL mode) can be added without breaking changes.
Example
When adding a comment to a GitHub PR, an agent will ask for permission before adding.
Requirements
- Applicable for all tool calls (including MCP tools if compatibility is added in the future)
- For the response fields of a tool call approval
elicitation_response:
action determines the user's choice:
accept: approve the tool call (requires a scope)
decline: deny this singular call to the specified tool
cancel: user dismissed the prompt without making a choice
scope (required when action is accept) determines the duration of the approval:
once: approve this singular call to the specified tool
session: for followup messages within the same session (i.e. one multi-turn chat thread), all calls to the specified tool are approved
always: for all invocations of the agent loop in the future from this user, all calls to the specified tool are approved
- Server-side agent loop requirements:
- When attempting any tool calls that require permission, scan the list of input
Items for any accept + always or accept + session approvals. Also check if this user has ever selected always in previous conversations with this agent for this tool. Providers will need to maintain this mapping (user <> tool approval status).
- If this tool has been pre-approved, proceed in the agent loop.
- If the tool has not been pre-approved, break out of the agent loop and yield control back to the client.
- If a tool call has been approved, run the server-side tool and continue the agent loop.
- If a tool call has been declined, pass the decline reason (if available) to the LLM to decide the next step.
- If a tool call has been cancelled, treat it as an implicit decline with no reason.
- For requests with parallel tool calls, any requests lacking an explicit response are implicitly declined.
- Client-side requirements:
- Present the user with options for approval. Clearly explain the implication behind each option of this approval request.
- Send the payload with the
action and scope fields filled out.
- Elicitation items and responses should be kept in client history in a multi-turn conversation.
- For responses with multiple elicitation items, clients should not send a request until all items have been responded to OR the user has explicitly submitted the request.
Open Questions
- Can we change
function_call_output to tool_call_output? Per the Responses API docs, Functions are meant to be client-side tools, but we hope to allow provider-specific tools to be outputted as well. This would influence the naming of the elicitation_type field on the tool_call_approval elicitation.
Sample Spec
TODO link to PR with spec in code
Tool Call Approval Elicitation Fields
In addition to the common elicitation fields, tool_call_approval adds:
| Field |
Type |
Required |
Description |
name |
string |
Yes |
Name of the tool being called |
arguments |
string |
Yes |
JSON-encoded arguments of the tool call |
In addition to the common elicitation response fields, the response adds:
| Field |
Type |
Required |
Description |
scope |
string |
When action is accept |
One of "once", "session", "always" |
Streaming Response
// response.created
// response.in_progress
{
"type": "response.output_item.added",
"item": {
"id": "elicit_123",
"type": "elicitation",
"elicitation_type": "tool_call_approval",
"message": "The agent wants to add a comment to PR #123",
"name": "add_pr_comment",
"arguments": "{\"pr_number\": 123, \"body\": \"Looks good!\"}"
},
"output_index": 0,
"sequence_number": 2
}
{
"type": "response.output_item.done",
"item": {
"id": "elicit_123",
"type": "elicitation",
"elicitation_type": "tool_call_approval",
"message": "The agent wants to add a comment to PR #123",
"name": "add_pr_comment",
"arguments": "{\"pr_number\": 123, \"body\": \"Looks good!\"}"
},
"output_index": 0,
"sequence_number": 3
}
// response.completed
Non-Streaming Response
{
"id": "resp_0a31882ebb2d3b550069cc629e73",
"object": "response",
"output": [
{
"id": "elicit_123",
"type": "elicitation",
"elicitation_type": "tool_call_approval",
"message": "The agent wants to add a comment to PR #123",
"name": "add_pr_comment",
"arguments": "{\"pr_number\": 123, \"body\": \"Looks good!\"}"
}
]
}
Client Follow-Up Request
{
"input": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "<user input>"
}
]
},
{
"id": "elicit_123",
"type": "elicitation",
"elicitation_type": "tool_call_approval",
"message": "The agent wants to add a comment to PR #123",
"name": "add_pr_comment",
"arguments": "{\"pr_number\": 123, \"body\": \"Looks good!\"}"
},
{
"id": "elicit_resp_123",
"type": "elicitation_response",
"elicitation_id": "elicit_123",
"action": "accept",
"scope": "once",
"reason": "optional reason for approving or declining"
}
]
}
Decline Example
{
"id": "elicit_resp_123",
"type": "elicitation_response",
"elicitation_id": "elicit_123",
"action": "decline",
"reason": "I don't want to comment on this PR"
}
Cancel Example
{
"id": "elicit_resp_123",
"type": "elicitation_response",
"elicitation_id": "elicit_123",
"action": "cancel"
}
Adding Tool Call Approvals in OpenResponses
Description
Agents require user approval for state-changing operations and reads of sensitive data. We propose adding tool call approvals as a subtype of a new generic
elicitationitem type, with options to approve once, approve for all sessions, approve for this session, or deny the request.The OpenAI Responses API has types
mcp_approval_requestandmcp_approval_response, but as server side tool execution increases in popularity, a generic, non-MCP specific option will unlock additional capabilities for server-side tool execution. When/if MCP specific events are added to the OpenResponses spec, we should reuse these events for MCP approvals as well.The
elicitationItem TypeThis proposal introduces a generic
elicitationitem type to the Responses API. Theelicitationtype uses anelicitation_typefield as a discriminator to support different kinds of server-to-client interactions that require user input. Tool call approvals are the first subtype (elicitation_type: "tool_call_approval"), and this same pattern is reused by the OAuth Logins proposal (elicitation_type: "oauth_login").All elicitation items share a common structure:
idtype"elicitation"elicitation_type"tool_call_approval","oauth_login", or future typesmessageAll elicitation responses share a common structure:
idtype"elicitation_response"elicitation_idaction"accept","decline","cancel"reasonSubtype-specific fields are added alongside these common fields. This design is forward-compatible with the MCP Elicitation specification:
actionfield uses the same vocabulary as MCP elicitation responses (accept,decline,cancel).messagefield matches MCP's requirement for a human-readable explanation.elicitation_type: "form"for MCP form mode,elicitation_type: "url"for MCP URL mode) can be added without breaking changes.Example
When adding a comment to a GitHub PR, an agent will ask for permission before adding.
Requirements
elicitation_response:actiondetermines the user's choice:accept: approve the tool call (requires ascope)decline: deny this singular call to the specified toolcancel: user dismissed the prompt without making a choicescope(required whenactionisaccept) determines the duration of the approval:once: approve this singular call to the specified toolsession: for followup messages within the same session (i.e. one multi-turn chat thread), all calls to the specified tool are approvedalways: for all invocations of the agent loop in the future from this user, all calls to the specified tool are approvedItemsfor anyaccept+alwaysoraccept+sessionapprovals. Also check if this user has ever selectedalwaysin previous conversations with this agent for this tool. Providers will need to maintain this mapping (user <> tool approval status).actionandscopefields filled out.Open Questions
function_call_outputtotool_call_output? Per the Responses API docs, Functions are meant to be client-side tools, but we hope to allow provider-specific tools to be outputted as well. This would influence the naming of theelicitation_typefield on thetool_call_approvalelicitation.Sample Spec
TODO link to PR with spec in code
Tool Call Approval Elicitation Fields
In addition to the common elicitation fields,
tool_call_approvaladds:nameargumentsIn addition to the common elicitation response fields, the response adds:
scopeactionisaccept"once","session","always"Streaming Response
Non-Streaming Response
{ "id": "resp_0a31882ebb2d3b550069cc629e73", "object": "response", "output": [ { "id": "elicit_123", "type": "elicitation", "elicitation_type": "tool_call_approval", "message": "The agent wants to add a comment to PR #123", "name": "add_pr_comment", "arguments": "{\"pr_number\": 123, \"body\": \"Looks good!\"}" } ] }Client Follow-Up Request
{ "input": [ { "role": "user", "content": [ { "type": "input_text", "text": "<user input>" } ] }, { "id": "elicit_123", "type": "elicitation", "elicitation_type": "tool_call_approval", "message": "The agent wants to add a comment to PR #123", "name": "add_pr_comment", "arguments": "{\"pr_number\": 123, \"body\": \"Looks good!\"}" }, { "id": "elicit_resp_123", "type": "elicitation_response", "elicitation_id": "elicit_123", "action": "accept", "scope": "once", "reason": "optional reason for approving or declining" } ] }Decline Example
{ "id": "elicit_resp_123", "type": "elicitation_response", "elicitation_id": "elicit_123", "action": "decline", "reason": "I don't want to comment on this PR" }Cancel Example
{ "id": "elicit_resp_123", "type": "elicitation_response", "elicitation_id": "elicit_123", "action": "cancel" }