Approvals and sign-ins
How a program answers the approvals and the connector sign-ins of a turn: the handler, the request it gets, the answers, and what turn.answer returns.
Guide: Approvals.
| Name | What it is |
|---|---|
ApprovalHandler | A plain or async function of (ctx, request) that answers an approval: AllowApproval, DenyApproval, or KeepCallbackOpen() to keep it open. |
ApprovalRequest | A tool call that waits for an approval. |
ApprovalTool | The tool that an approval is for. |
AllowApproval | Allow this call once, or matching calls for the rest of the session. |
DenyApproval | Deny this call. |
KeepCallbackOpen | What a callback handler returns to keep the callback open, for someone to answer later. |
SignInRequest | One source that a sign-in asks the user to connect. |
SignInAnswer | The answer to an OpenSignIn: the IDs of the sources the user connected, and of those they refused to connect. |
Answered | The callback was open when read, and it was answered. |
AlreadySettled | An answer named a callback that is no longer open: it was answered, or it closed. |
NotFound | An answer named a callback that the session never opened, and that none of its subagents has open. |
ApprovalHandlerApprovalHandler
class ApprovalHandler(Protocol):
def __call__(
ctx: ApprovalCallbackContext,
request: ApprovalRequest,
/,
) -> AllowApproval | DenyApproval | KeepCallbackOpen | Awaitable[AllowApproval | DenyApproval | KeepCallbackOpen]A plain or async function of (ctx, request) that answers an approval: AllowApproval,
DenyApproval, or KeepCallbackOpen() to keep it open. Any other return, None
included, raises SdkError. Its parameters may have any names.
- It runs in your program, and only while
agent.run(),session.run()orturn.result()waits. Between those calls, an approval stays open.iter_events()never runs it. - A plain function runs on the event loop: do not block in it. An
async deffunction can await. - If it raises, or returns something else, the waiting call raises that error and the approval stays open. The turn keeps running.
- It also answers the approvals of the session's subagents, with
ctx.sessionset to the subagent's session. - Decide on
request.tool.id, the tool's stable ID, never on the name the model called. AllowApproval(scope="session")also allows the same call with the same input for the rest of the session.
ApprovalRequestApprovalRequest
class ApprovalRequest(
*,
tool: ApprovalTool,
hook: str | None = None,
title: str,
input: JsonValue = None,
)A tool call that waits for an approval. The IDs are in the handler's ctx.
A hook's approval cannot be allowed for the session. Answer it with AllowApproval(), once.
ApprovalToolApprovalTool
class ApprovalTool(
*,
id: str,
name: str,
kind: Literal['builtin', 'client', 'mcp', 'connector', 'plugin'] | UnknownValue,
description: str | None = None,
input_schema: dict[str, JsonValue] | None = None,
output_schema: dict[str, JsonValue] | None = None,
definition: PythonTool[..., Any] | ClientTool[..., Any] | ToolDefinition | None = None,
annotations: ToolAnnotations = ToolAnnotations(),
)The tool that an approval is for.
Decide on id, never on name: name changes with how the model called the tool. A hint
the tool does not declare in annotations takes MCP's default, which assumes the worst.
| Parameter | Type | Default | Description |
|---|---|---|---|
id | str | required | The tool's stable ID, the one permission rules use, such as "file_system.bash", "client.open_case", "billing.charge" or "github.create_issue". |
name | str | required | The name the model called, which changes with how it called the tool: deploy_service directly, tools.client.deploy_service from run_typescript. |
kind | Literal['builtin', 'client', 'mcp', 'connector', 'plugin'] | UnknownValue | required | Where the tool comes from: "builtin" for the agent's built-in tools, "client" for the agent's @agents.tool and client tools, "mcp" for an MCP server's, "connector" for a Mistral connector's, "plugin" for any other, or an UnknownValue for a kind added after your SDK version. |
description | str | None | None | The tool's description, as the model reads it; None for builtins. |
input_schema | dict[str, JsonValue] | None | None | The JSON Schema of the tool's input, as the model reads it; None for builtins. |
output_schema | dict[str, JsonValue] | None | None | The JSON Schema of the tool's output; None for builtins and for a tool that declares none. |
definition | PythonTool[..., Any] | ClientTool[..., Any] | ToolDefinition | None | None | The tool's definition. For a tool of the agent or of one of its subagents, the tool object the agent was given, such as the one @agents.tool returned; None on a session handle that does not hold the agent's tools. For an MCP server's or a connector's tool, an agents.ToolDefinition of the whole tool as the server listed it, such as its title, annotations, output_schema and meta, with the server's name as source, with or without the agent. None for the other tools. Every definition has display_title, the tool's name to show people. |
annotations | ToolAnnotations | ToolAnnotations() | The hints the tool declares, such as read_only_hint: those of annotations= on @agents.tool, agents.PythonTool and agents.client_tool, and those an MCP server or a connector declares for its tool. The approval carries them, so every approver reads the same ones, with or without the agent. A hint the tool does not declare takes MCP's default, which assumes the worst: a policy that allows only read-only tools allows a tool only if it declares read_only_hint=True. |
AllowApprovalAllowApproval
class AllowApproval(*, scope: Literal['once', 'session'] = 'once')Allow this call once, or matching calls for the rest of the session.
| Attribute | Type | Description |
|---|---|---|
decision | Literal['allow'] | Always "allow". |
DenyApprovalDenyApproval
class DenyApproval()Deny this call.
| Attribute | Type | Description |
|---|---|---|
decision | Literal['deny'] | Always "deny". |
KeepCallbackOpenKeepCallbackOpen
class KeepCallbackOpen()What a callback handler returns to keep the callback open, for someone to answer later.
Return agents.KeepCallbackOpen() from on_approval, on_client_tool or a client tool's
answer: nothing is sent, the callback stays open, and the waiting call returns Blocked on it,
unless it was given after= a Blocked that already lists the callback: that wait returns only
on a later change. Answer it later with turn.answer, an approval with AllowApproval or
DenyApproval and a client tool call with the tool's result, or answer it from another client.
It is a handler's return only, never an answer: turn.answer refuses it. None is not
this either: an approval handler that returns None raises agents.SdkError, and a client
tool that returns None sends the null result.
Sign-ins
SignInRequestSignInRequest
class SignInRequest(
*,
source: Literal['connector', 'mcp'] | UnknownValue,
id: str,
name: str,
mode: Literal['connect', 'reconnect'] | UnknownValue = 'connect',
)One source that a sign-in asks the user to connect.
| Parameter | Type | Default | Description |
|---|---|---|---|
source | Literal['connector', 'mcp'] | UnknownValue | required | "connector" for a Mistral connector, "mcp" for an MCP server, or an UnknownValue for a source added after your SDK version. |
id | str | required | The source's ID, the one SignInAnswer takes: a connector's is the name the agent gives it, as in agents.connector("linear"). |
name | str | required | The name to show the user: its display name when Mistral has one, else the ID. |
mode | Literal['connect', 'reconnect'] | UnknownValue | 'connect' | "connect" for a first sign-in, "reconnect" when the source was connected and its access has to be renewed, or an UnknownValue for a mode added after your SDK version. |
SignInAnswerSignInAnswer
class SignInAnswer(*, connected: Iterable[str] = (), denied: Iterable[str] = ())The answer to an OpenSignIn: the IDs of the sources the user connected, and of those they
refused to connect.
Name each source the sign-in asks for in one list: an answer that leaves one out raises
SdkError and sends nothing, and the sign-in stays open. The agent checks each source said
to be connected, and is told when one is still not connected.
Answer results
AnsweredAnswered
class Answered(
*,
session_id: str,
callback_id: str,
answer: AllowApproval | DenyApproval | None = None,
)The callback was open when read, and it was answered.
AlreadySettledAlreadySettled
class AlreadySettled(*, session_id: str, callback_id: str)An answer named a callback that is no longer open: it was answered, or it closed. Nothing changed.
Another client may have answered first, the callback's turn ended, or the session ended.
NotFoundNotFound
class NotFound(*, session_id: str, callback_id: str)An answer named a callback that the session never opened, and that none of its subagents has open. Nothing changed.
Once a subagent's callback is no longer open, its root says NotFound. The subagent's own
session says AlreadySettled.