> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qoder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK References

<div id="functions" />

## Functions

<div id="query" />

### `query()`

The SDK's main entry function. Creates an async iterator that streams messages in arrival order.

```python theme={null}
async def query(
    *,
    prompt: str | AsyncIterable[dict[str, Any]],
    options: QoderAgentOptions | None = None,
) -> AsyncIterator[Message]:
    ...
```

<div id="parameters" />

#### Parameters

| Parameter | Type                                      | Description                                                          |
| :-------- | :---------------------------------------- | :------------------------------------------------------------------- |
| `prompt`  | `str \| AsyncIterable[dict[str, Any]]`    | Pass a string for single-turn; pass an async iterable for multi-turn |
| `options` | [`QoderAgentOptions`](#qoderagentoptions) | Optional session configuration                                       |

<div id="return-value" />

<div id="returnvalue" />

#### Return Value

Returns `AsyncIterator[Message]`, consumed via `async for`.

<div id="qodersdkclient" />

### `QoderSDKClient`

Class-based multi-turn conversation API. Suitable for scenarios that need to keep session state across turns or dynamically switch the model or permission mode.

```python theme={null}
client = QoderSDKClient(options=options)
await client.connect("Initial prompt.")
```

| Method                                           | Description                                                               |
| :----------------------------------------------- | :------------------------------------------------------------------------ |
| `client.query(prompt)`                           | Send a new round of user input                                            |
| `client.receive_response()`                      | Consume messages until `ResultMessage`                                    |
| `client.receive_messages()`                      | Background async iterator that receives the entire session message stream |
| `client.connect(prompt)` / `client.disconnect()` | Manually manage the connection                                            |
| `client.interrupt()`                             | Interrupt the current generation or tool execution                        |
| `client.set_model(model)`                        | Switch the model at runtime                                               |
| `client.set_permission_mode(mode)`               | Switch the permission mode at runtime                                     |
| `client.stop_task(task_id)`                      | Stop a background task                                                    |
| `client.apply_flag_settings(settings)`           | Inject flag-level settings                                                |
| `client.supported_agents()`                      | List the agents currently available                                       |
| `client.get_mcp_status()`                        | Get the status of all MCP servers                                         |
| `client.set_mcp_servers(servers)`                | Replace the MCP server configuration                                      |
| `client.reconnect_mcp_server(name)`              | Reconnect a specific MCP server                                           |
| `client.toggle_mcp_server(name, enabled)`        | Enable or disable an MCP server                                           |
| `client.get_available_models()`                  | Get the list of available models                                          |

<div id="types" />

## Types

<div id="qoderagentoptions" />

### `QoderAgentOptions`

Configuration object for `query()` and `QoderSDKClient`. The Python SDK uses snake\_case field names.

| Field                                | Type                                                    | Default | Description                                                                                                                            |
| :----------------------------------- | :------------------------------------------------------ | :------ | :------------------------------------------------------------------------------------------------------------------------------------- |
| `auth`                               | `InternalAuthOptions \| dict \| None`                   | `None`  | Authentication configuration                                                                                                           |
| `on_auth_expired`                    | `Callable \| None`                                      | `None`  | Callback when authentication expires; triggered at most once per session                                                               |
| `tools`                              | `list[str] \| ToolsPreset \| None`                      | `None`  | Tool set. Pass a string array to restrict available tools; built-in tool names are listed in [Built-in Tool List](#built-in-tool-list) |
| `allowed_tools`                      | `list[str]`                                             | `[]`    | Tool allowlist; listed tools are pre-authorized                                                                                        |
| `disallowed_tools`                   | `list[str]`                                             | `[]`    | Tool blocklist; takes priority over `allowed_tools` and `permission_mode`                                                              |
| `can_use_tool`                       | [`CanUseTool`](#canusetool)                             | `None`  | Custom tool permission callback                                                                                                        |
| `permission_mode`                    | [`PermissionMode`](#permissionmode)                     | `None`  | Session permission mode                                                                                                                |
| `allow_dangerously_skip_permissions` | `bool`                                                  | `False` | Allow skipping permission checks; used together with `permission_mode="bypassPermissions"`                                             |
| `permission_prompt_tool_name`        | `str \| None`                                           | `None`  | MCP tool name for permission prompts; mutually exclusive with `can_use_tool`                                                           |
| `model`                              | `str \| None`                                           | `None`  | Model to use                                                                                                                           |
| `fallback_model`                     | `str \| None`                                           | `None`  | Fallback model when the main model fails                                                                                               |
| `resolve_model`                      | [`ModelPolicyProvider`](#modelpolicyprovider)           | `None`  | Dynamic model-selection callback. Passing it switches into dynamic-callback mode; see [Model Policy](/en/cli/sdk/python/model-policy)  |
| `resolve_model_timeout_ms`           | `int`                                                   | `500`   | Callback timeout (milliseconds); only effective when `resolve_model` is passed                                                         |
| `system_prompt`                      | `str \| SystemPromptPreset \| SystemPromptFile \| None` | `None`  | System prompt                                                                                                                          |
| `cwd`                                | `str \| Path \| None`                                   | `None`  | Working directory                                                                                                                      |
| `env`                                | `dict[str, str \| None]`                                | `{}`    | Environment variables passed to the CLI process                                                                                        |
| `cli_path`                           | `str \| Path \| None`                                   | `None`  | Path to the qodercli executable                                                                                                        |
| `session_id`                         | `str \| None`                                           | `None`  | Specify the session UUID                                                                                                               |
| `resume`                             | `str \| None`                                           | `None`  | Session ID to resume                                                                                                                   |
| `continue_conversation`              | `bool`                                                  | `False` | Continue the most recent session                                                                                                       |
| `fork_session`                       | `bool`                                                  | `False` | When used with `resume`, fork into a new session ID                                                                                    |
| `max_turns`                          | `int \| None`                                           | `None`  | Maximum conversation turns                                                                                                             |
| `include_partial_messages`           | `bool`                                                  | `False` | Include `StreamEvent` streaming fragments; see [Streaming Output](/en/cli/sdk/python/streaming-output)                                 |
| `mcp_servers`                        | `dict[str, McpServerConfig] \| str \| Path`             | `{}`    | MCP server configuration; see [MCP](/en/cli/sdk/python/mcp)                                                                            |
| `allowed_mcp_server_names`           | `list[str]`                                             | `[]`    | Restrict which MCP servers are available                                                                                               |
| `strict_mcp_config`                  | `bool`                                                  | `False` | Strict MCP validation                                                                                                                  |
| `hooks`                              | `dict[HookEvent, list[HookMatcher]] \| None`            | `None`  | Lifecycle hooks; see [Hooks](/en/cli/sdk/python/hooks)                                                                                 |
| `agents`                             | `dict[str, AgentDefinition] \| None`                    | `None`  | Programmatically defined subagents; see [Agents Reference](#qoderagentoptionsagents)                                                   |
| `agent`                              | `str \| None`                                           | `None`  | Agent name used by the main thread; see [Agents Reference](#qoderagentoptionsagent)                                                    |
| `settings`                           | `str \| Path \| Settings \| None`                       | `None`  | Inline settings object or settings file path                                                                                           |
| `setting_sources`                    | `list[SettingSource] \| None`                           | `None`  | Which filesystem settings to load                                                                                                      |
| `add_dirs`                           | `list[str \| Path]`                                     | `[]`    | Additional directories accessible to the AI                                                                                            |
| `extra_args`                         | `dict[str, str \| None]`                                | `{}`    | Additional arguments passed to the CLI                                                                                                 |
| `plugins`                            | `list[SdkPluginConfig]`                                 | `[]`    | Load local plugins; see [Plugins](/en/cli/sdk/python/plugins)                                                                          |
| `skills`                             | `list[str] \| Literal["all"] \| None`                   | `None`  | Enabled skills; pass `"all"` to enable all                                                                                             |
| `enable_file_checkpointing`          | `bool \| None`                                          | `None`  | Enable file checkpointing                                                                                                              |
| `thinking`                           | `ThinkingConfig \| None`                                | `None`  | Thinking configuration                                                                                                                 |

<div id="authentication" />

### Authentication

```python theme={null}
from qoder_agent_sdk import access_token, access_token_from_env, qodercli_auth
```

| Factory function                  | Description                                                              |
| :-------------------------------- | :----------------------------------------------------------------------- |
| `access_token(token)`             | Pass the PAT directly                                                    |
| `access_token_from_env()`         | Read from the default environment variable `QODER_PERSONAL_ACCESS_TOKEN` |
| `access_token_from_env("MY_PAT")` | Read from a specified environment variable                               |
| `qodercli_auth()`                 | Reuse the local `qodercli login` session                                 |

For convenience usage, see [SDK Authentication](/en/cli/sdk/python/authentication).

<div id="agents-reference" />

<div id="agentsreference" />

## Agents Reference

This page summarizes the stable configuration items related to SDK Agents. For getting started and usage scenarios, see [Subagent Usage Guide](/en/cli/sdk/python/agents).

<div id="sources-of-available-agents" />

<div id="sourcesofavailableagents" />

### Sources of Available Agents

The agents available in the current session may come from multiple sources:

| Source                | Description                                                       |
| --------------------- | ----------------------------------------------------------------- |
| SDK registration      | Registered for the current session via `QoderAgentOptions.agents` |
| CLI built-in          | Built-in Agents registered when qodercli starts                   |
| User / project config | Agent definitions in the user or project configuration directory  |
| Plugin                | Agents provided by loaded plugins                                 |

In the interactive CLI, you can run `/agents` to view the currently discovered Agents; on the command line, run `qodercli agents list`. In the Python SDK, after `QoderSDKClient` connects, call `client.supported_agents()` to get a summary of agents available in the current session.

<div id="qoderagentoptionsagents" />

<div id="qoderagentoptions-agents" />

### `QoderAgentOptions.agents`

**Type:** `dict[str, AgentDefinition] | None`

Registers custom Agents available in the current session. The dict key is the Agent name and the value is that Agent's definition.

> **The `Agent` tool is required**: Custom subagents require the main session to delegate through the built-in `Agent` tool, so `allowed_tools` must include the `Agent` tool.

```python theme={null}
from qoder_agent_sdk import AgentDefinition, QoderAgentOptions


options = QoderAgentOptions(
    allowed_tools=["Agent"],
    agents={
        "reviewer": AgentDefinition(
            description="Reviews code quality and reports actionable findings.",
            prompt="Review the requested code and report concrete issues.",
            tools=["Read", "Grep", "Glob"],
        ),
    },
)
```

After registration, the model can invoke these subagents through the built-in `Agent` tool. The main session must include `Agent` in its tool set to delegate work; `allowed_tools=["Agent"]` is the required pre-authorization form. If you use `tools` to narrow the main session's available tools, include `Agent` there as well.

<div id="qoderagentoptionsagent" />

<div id="qoderagentoptions-agent" />

### `QoderAgentOptions.agent`

**Type:** `str | None`

Specifies which Agent identity the main session runs as. The value can be a name registered in `agents`, or a built-in / plugin Agent name discovered by the current CLI.

```python theme={null}
options = QoderAgentOptions(
    agents={
        "planner": AgentDefinition(
            description="Plans work before implementation.",
            prompt="Break work into steps, risks, and validation checks.",
            tools=["Read", "Grep", "Glob"],
        ),
    },
    agent="planner",
)
```

When set, the main session uses that Agent's `prompt`, `model`, and tool restrictions. When omitted, the default main-session behavior is used.

<div id="agentdefinition" />

### `AgentDefinition`

Definition of a custom Agent. The Python SDK's `AgentDefinition` is a dataclass whose field names use the protocol-style camelCase. The fields below are the stable capabilities currently covered and verified by the SDK.

```python theme={null}
from dataclasses import dataclass
from typing import Any, Literal


@dataclass
class AgentDefinition:
    description: str
    prompt: str
    tools: list[str] | None = None
    disallowedTools: list[str] | None = None
    model: str | None = None
    skills: list[str] | None = None
    mcpServers: list[str | dict[str, Any]] | None = None
    initialPrompt: str | None = None
    maxTurns: int | None = None
    effort: Literal["low", "medium", "high", "max"] | None = None
    permissionMode: PermissionMode | None = None
```

| Field             | Type                                           | Required | Description                                                                                |
| ----------------- | ---------------------------------------------- | -------- | ------------------------------------------------------------------------------------------ |
| `description`     | `str`                                          | Yes      | Agent purpose description; the model uses it to decide when to invoke the Agent            |
| `prompt`          | `str`                                          | Yes      | Agent system prompt                                                                        |
| `tools`           | `list[str] \| None`                            | No       | Tool allowlist for this Agent                                                              |
| `disallowedTools` | `list[str] \| None`                            | No       | Tools excluded from this Agent's tool set                                                  |
| `model`           | `str \| None`                                  | No       | Model override; `"inherit"` means inherit the main session model                           |
| `mcpServers`      | `list[str \| dict[str, Any]] \| None`          | No       | MCP server specs available to this Agent                                                   |
| `skills`          | `list[str] \| None`                            | No       | Skill names preloaded into the Agent context                                               |
| `initialPrompt`   | `str \| None`                                  | No       | First user input automatically submitted when this Agent is used as the main session Agent |
| `maxTurns`        | `int \| None`                                  | No       | Maximum API turns for the Agent                                                            |
| `effort`          | `"low" \| "medium" \| "high" \| "max" \| None` | No       | Reasoning effort level                                                                     |
| `permissionMode`  | `PermissionMode \| None`                       | No       | Permission mode for tool execution inside this Agent                                       |

The Python SDK serializes `AgentDefinition` into the initialize request via `dataclasses.asdict()`; qodercli then parses it according to the current Agent schema.

<div id="description" />

#### `description`

Describes what tasks the Agent is suitable for. It affects whether the model chooses this Agent.

```python theme={null}
description="Runs project tests, analyzes failing output, and suggests fixes."
```

Prefer a clear triggering scenario. Avoid broad descriptions such as `Helpful assistant`.

<div id="prompt" />

#### `prompt`

The Agent's system prompt. Use it to define the role, constraints, and output format.

```python theme={null}
prompt="""You are a security reviewer.
Check for authentication bypass, authorization bugs, injection risks, and secret leaks.
Return findings sorted by severity."""
```

<div id="tools" />

#### `tools`

Tool allowlist for the Agent. When set, the Agent can only use the listed tools.

```python theme={null}
tools=["Read", "Grep", "Glob"]
```

When `tools` is omitted, the subagent default tool set is used. A subagent's tool set does not inherit trimming from the main session's `allowed_tools`.

<div id="disallowedtools" />

#### `disallowedTools`

Excludes specific tools from the Agent's tool set.

```python theme={null}
disallowedTools=["Bash", "Write"]
```

When `disallowedTools` is omitted, the subagent does not inherit trimming from the main session's `disallowed_tools`. Usually avoid setting both `tools` and `disallowedTools` unless you explicitly know the final tool set.

<div id="model" />

<div id="agentdefinition-model" />

#### `model`

Specifies the model for the Agent; when omitted, the session default model is used. At the Python type level it is `str | None`; the SDK does not restrict specific strings locally. Common model tiers include:

| Value           | Tier          | Description                                                         | Suitable for                                          | Credit cost |
| --------------- | ------------- | ------------------------------------------------------------------- | ----------------------------------------------------- | ----------- |
| `"auto"`        | Smart routing | Intelligently selects the best model, balancing capability and cost | Most daily development work; recommended default      | \~1.0x      |
| `"ultimate"`    | Ultimate      | Expert-level deep reasoning and thinking capability                 | Complex system design and difficult analysis          | \~1.6x      |
| `"performance"` | Performance   | Advanced reasoning and high-quality output                          | Core implementation, architecture design, refactoring | \~1.1x      |
| `"efficient"`   | Efficient     | Standard reasoning with good cost efficiency                        | Basic code generation, unit tests, daily Q\&A         | \~0.3x      |
| `"lite"`        | Lite          | Basic reasoning, free to use                                        | Quick validation, simple logic, quick questions       | 0x          |

Agents also support two special forms:

| Value         | Description                                                                |
| ------------- | -------------------------------------------------------------------------- |
| `"inherit"`   | Inherit the main session model; usually not echoed in `supported_agents()` |
| Full model ID | Directly specify a model ID supported by the current CLI / backend         |

<div id="mcpservers" />

<div id="agentdefinition-mcpservers" />

#### `mcpServers`

```python theme={null}
mcpServers: list[str | dict[str, Any]] | None
```

Limits or adds MCP servers available to this Agent. Each entry can be a session-level server name, or an inline server configuration mapping.

Reference a session-level MCP server:

```python theme={null}
options = QoderAgentOptions(
    mcp_servers={
        "orders": {
            "command": "python",
            "args": ["servers/orders.py"],
        },
    },
    allowed_tools=["Agent"],
    agents={
        "support": AgentDefinition(
            description="Answers support questions using order tools.",
            prompt="Use order tools when needed and return a concise answer.",
            mcpServers=["orders"],
            tools=["mcp__orders__lookup_order"],
        ),
    },
)
```

Configure a dedicated MCP server for a specific Agent:

```python theme={null}
AgentDefinition(
    description="Searches the internal knowledge base.",
    prompt="Search the knowledge base and cite relevant entries.",
    mcpServers=[
        {
            "kb": {
                "command": "python",
                "args": ["servers/kb.py"],
            },
        },
    ],
    tools=["mcp__kb__search"],
)
```

When you only want to expose a specific MCP tool, also configure `tools=["mcp__server__tool"]` to avoid exposing every tool of that server to the Agent.

<div id="skills" />

#### `skills`

A list of skill names preloaded into the Agent context. Plain skill names and plugin-qualified names are both supported.

```python theme={null}
skills=["review", "sdk-test-plugin:sdk-echo"]
```

For session-level skill behavior, see [Skills](/en/cli/sdk/python/skills).

<div id="initialprompt" />

#### `initialPrompt`

Automatically submitted as the first user input when this Agent becomes the main session Agent through `QoderAgentOptions.agent`.

```python theme={null}
initialPrompt="Start by scanning authentication and session management code."
```

This field only takes effect for the main session Agent. It is ignored when the Agent is invoked as a subagent through the `Agent` tool.

<div id="maxturns" />

<div id="agentdefinition-maxturns" />

#### `maxTurns`

Limits the Agent's maximum API turns. Use it to control cost, execution time, and loop risk.

```python theme={null}
maxTurns=6
```

<div id="effort" />

<div id="agentdefinition-effort" />

#### `effort`

```python theme={null}
EffortLevel = Literal["low", "medium", "high", "max"]
```

Controls the Agent's reasoning effort level. Higher `effort` is usually suitable for complex reviews, architecture analysis, and high-risk changes, but increases latency and token usage.

<div id="agentdefinition-permissionmode" />

#### `permissionMode`

Controls the permission mode for tool execution inside this Agent. It uses the same semantics as the session-level `permission_mode`, but its scope is limited to this Agent. For the session-level permission chain, the priority of `allowed_tools` / `disallowed_tools` / `can_use_tool`, and examples, see [Permission Control](/en/cli/sdk/python/permissions#controlling-default-policy-permission_mode).

```python theme={null}
PermissionMode = Literal[
    "default",
    "acceptEdits",
    "plan",
    "bypassPermissions",
    "yolo",
    "dontAsk",
    "auto",
]
```

| Value                 | Meaning                                                                                                                            | Suitable for                                                                     |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `"default"`           | Standard permission behavior. Tool calls still pass through tool sets, allow / deny rules, runtime approval, or CLI default policy | Most interactive subagents                                                       |
| `"acceptEdits"`       | Automatically accepts file-edit operations; other sensitive operations still follow the permission flow                            | A subagent that is approved to modify workspace files                            |
| `"bypassPermissions"` | Skips permission checks. High-risk mode, usually only for trusted automation or test environments                                  | Controlled CI, temporary validation, one-off automation                          |
| `"yolo"`              | Compatibility alias for `"bypassPermissions"`; also skips permission checks                                                        | Compatibility with older configs; not recommended for new code                   |
| `"plan"`              | Plan mode. Suitable for producing a plan first; by default it does not perform real changes                                        | Planning, design, review, or cases where the subagent should not modify files    |
| `"dontAsk"`           | Does not ask interactively; operations that are not pre-authorized or allowed by rules are denied                                  | Non-interactive environments, or workflows that should fail instead of prompting |
| `"auto"`              | Runtime capability decides allow or deny automatically                                                                             | Reduce confirmation interruptions while retaining runtime judgment               |

For permission semantics, see [Permission Control](/en/cli/sdk/python/permissions).

<div id="agentinfo" />

### `AgentInfo`

Agent summary returned by `QoderSDKClient.supported_agents()`.

```python theme={null}
class AgentInfo(TypedDict):
    name: str
    description: str
    model: NotRequired[str | None]
```

The Python SDK does not currently export a TypedDict named `AgentInfo`; the return type of `supported_agents()` is `list[dict[str, Any]]`. The structure above is the stable field convention of the actually returned dicts.

| Field         | Type          | Description                                                              |
| ------------- | ------------- | ------------------------------------------------------------------------ |
| `name`        | `str`         | Agent name                                                               |
| `description` | `str`         | Agent purpose description                                                |
| `model`       | `str \| None` | Agent model override; usually empty when unset or when `model="inherit"` |

```python theme={null}
from qoder_agent_sdk import AgentDefinition, QoderAgentOptions, QoderSDKClient


options = QoderAgentOptions(
    agents={
        "reviewer": AgentDefinition(
            description="Reviews code quality.",
            prompt="Review code and report findings.",
        ),
    },
)

client = QoderSDKClient(options=options)
await client.connect("List agents.")
agents = client.supported_agents()
await client.disconnect()
```

The returned list may include Agents registered through `agents`, and may also include built-in, project, user, or plugin Agents discovered by the current CLI. The actual available entries depend on the qodercli version and current configuration.

<div id="context-and-invocation-boundaries" />

<div id="contextandinvocationboundaries" />

### Context and Invocation Boundaries

* Subagents use independent context and do not receive the parent session's full history.
* The main information passed from the parent session to a subagent is the task prompt supplied to the `Agent` tool.
* A subagent's intermediate tool results do not directly enter the parent session; the parent session receives the subagent's final response.
* Subagents cannot spawn their own subagents, so do not put `Agent` in a subagent's `tools`.
* `initialPrompt` only takes effect for the main session Agent specified by `agent`.

<div id="related-documentation" />

<div id="relateddocumentation" />

### Related Documentation

* [Subagent Usage Guide](/en/cli/sdk/python/agents)
* [Tools Reference](#tools-reference)
* [MCP Integration](/en/cli/sdk/python/mcp)
* [Permission Control](/en/cli/sdk/python/permissions)
* [Skills](/en/cli/sdk/python/skills)

<div id="model-policy" />

<div id="modelpolicy" />

### Model Policy

Dynamic model-selection capability of `query()`. Two modes: fixed-model (no `resolve_model`, uses `options.model` or backend default) and dynamic-callback (pass `resolve_model`, the callback decides the model before every LLM call). For full concepts, triggers and error handling see [Model Policy](/en/cli/sdk/python/model-policy).

<div id="optionsresolve_model" />

<div id="options-resolve_model" />

#### `options.resolve_model`

**Type:** [`ModelPolicyProvider`](#modelpolicyprovider)

Entry point for dynamic-callback mode. Once passed, dynamic-callback mode is enabled and the SDK calls this callback before every LLM request to fetch the model. The `model` returned by the callback is the final model for that request; **there is no automatic fallback**.

<div id="optionsresolve_model_timeout_ms" />

<div id="options-resolve_model_timeout_ms" />

#### `options.resolve_model_timeout_ms`

**Type:** `int`, default `500`

Callback timeout (milliseconds). On timeout [`ModelPolicyTimeoutError`](#modelpolicytimeouterror) is thrown and the query fails (no fallback). Only effective when `resolve_model` is passed.

<div id="modelpolicyprovider" />

### `ModelPolicyProvider`

Callback function signature. May be synchronous or asynchronous.

```python theme={null}
from typing import Awaitable, Callable

ModelPolicyProvider = Callable[
    ["ModelPolicyContext"],
    "ModelPolicyResult | Awaitable[ModelPolicyResult]",
]
```

Triggering scenarios are distinguished by [`QoderModelPurpose`](#qodermodelpurpose):

| Scenario           | `purpose`     | Notes                                                                                |
| ------------------ | ------------- | ------------------------------------------------------------------------------------ |
| Main conversation  | `'main'`      | Re-invoked between turns / tools — a session may trigger many times                  |
| Subagent           | `'subagent'`  | Subagents share the same provider                                                    |
| WebFetch tool      | `'web_fetch'` | After WebFetch retrieves content, a second LLM call summarizes it                    |
| ImageGen tool      | `'image_gen'` | Used to pick the image-generation model                                              |
| Context compaction | `'compact'`   | Before compaction starts, the callback is queried for the compaction model           |
| BYOK               | any           | Set `model` to a [`CustomModel`](#custommodel) object to route via a third-party LLM |

Behavioral notes:

* The callback may be triggered many times within a single session (re-invoked before every turn / tool / sub-task).
* The `model` returned by the callback is the final model for that request; the SDK does not re-validate it.
* Throwing an exception or returning an empty `model` fails the query directly. See [Model Policy — Error handling](/en/cli/sdk/python/model-policy#error-handling).

<div id="modelpolicycontext" />

### `ModelPolicyContext`

The context passed to the callback on every invocation.

```python theme={null}
class ModelPolicyContext(TypedDict, total=False):
    purpose: str  # QoderModelPurpose
    sessionId: str
    agentId: str
    turnIndex: int
    availableModels: list[ModelInfo]
```

| Field             | Type                                      | Required | Description                                                                                                                             |
| ----------------- | ----------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `purpose`         | [`QoderModelPurpose`](#qodermodelpurpose) | Yes      | Purpose of this LLM call                                                                                                                |
| `sessionId`       | `str`                                     | Yes      | Current session ID; the same value is passed across callback invocations within a session, so it can be used as a cache / telemetry key |
| `agentId`         | `str`                                     | Yes      | Identifier of the Agent that initiated the call                                                                                         |
| `turnIndex`       | `int`                                     | Yes      | Current turn index                                                                                                                      |
| `availableModels` | [`ModelInfo`](#modelinfo)`[]`             | Yes      | The models currently available to the account in real time (the CLI carries the list with every `get_model_policy` request)             |

<div id="qodermodelpurpose" />

### `QoderModelPurpose`

```python theme={null}
from typing import Literal

QoderModelPurpose = Literal[
    "main",
    "plan",
    "task",
    "compact",
    "title",
    "suggestion",
    "generate",
    "hook_prompt",
    "subagent",
    "web_fetch",
    "image_gen",
    "compression",
    "utility",
]
```

| Value         | Triggering scenario                                  |
| ------------- | ---------------------------------------------------- |
| `'main'`      | Main-conversation LLM call                           |
| `'subagent'`  | Subagent call                                        |
| `'web_fetch'` | Secondary LLM call triggered by the WebFetch tool    |
| `'image_gen'` | Image-generation call triggered by the ImageGen tool |
| `'compact'`   | Context compaction / summarization                   |

<div id="modelpolicyresult" />

### `ModelPolicyResult`

The callback's return value.

```python theme={null}
class ModelPolicyResult(TypedDict, total=False):
    model: str | CustomModel
    parameters: dict[str, Any]
```

| Field        | Type                                   | Required | Description                                                           |
| ------------ | -------------------------------------- | -------- | --------------------------------------------------------------------- |
| `model`      | `str \| `[`CustomModel`](#custommodel) | Yes      | String: model identifier; object: BYOK credentials + model identifier |
| `parameters` | `dict[str, Any]`                       | No       | Model-parameter overrides (e.g. `temperature`, `max_tokens`)          |

`model` forms:

* **String** — any model ID supported by the backend (such as `auto` / `performance` / `glm51`); the exact set of valid values is returned in real time by [`client.get_available_models()`](#clientget_available_models). Must be **non-empty**, otherwise the query fails.
* **`CustomModel` object** (BYOK) — the SDK extracts the object's `model` field as the model identifier for this call, and forwards the remaining fields as credentials to the CLI for routing to a third-party LLM.

<div id="custommodel" />

### `CustomModel`

BYOK credentials. In the `resolve_model` callback, set the `model` field directly to this object, and that LLM request will be routed to a third-party provider.

```python theme={null}
class CustomModel(TypedDict, total=False):
    provider: str
    model: str
    api_key: str
    url: str
    style: str  # "openai" | "anthropic"
```

| Field      | Type  | Required | Description                                                                      |
| ---------- | ----- | -------- | -------------------------------------------------------------------------------- |
| `provider` | `str` | Yes      | Provider key — must match a [`BYOKProviderInfo.key`](#byokproviderinfo)          |
| `model`    | `str` | Yes      | Model identifier — extracted by the SDK as the model ID for this call            |
| `api_key`  | `str` | Yes      | The API Key supplied by the user                                                 |
| `url`      | `str` | No       | Override the default base URL                                                    |
| `style`    | `str` | No       | Upstream protocol style, e.g. `"openai"` / `"anthropic"`; defaults to `"openai"` |

Notes:

* `provider` must match a `key` in the catalog, otherwise backend authentication fails.
* A wrong `api_key` causes authentication to fail, which fails the query directly (dynamic-callback mode does not fall back).
* BYOK calls report `total_cost_usd` as 0 on the platform; token usage is reported as-is and billed by the provider side.

<div id="byok-catalog-types" />

<div id="byokcatalogtypes" />

### BYOK Catalog Types

The provider/model catalog returned by [`client.list_byok_providers()`](#clientlist_byok_providers).

```python theme={null}
class SDKControlGetByokConfigResponse(TypedDict, total=False):
    providers: list[BYOKProviderInfo]


class BYOKProviderInfo(TypedDict, total=False):
    key: str
    display_name: str
    api_key_url: str
    url: str
    fields: list[BYOKFieldInfo]
    types: list[BYOKModelTypeInfo]


class BYOKFieldInfo(TypedDict, total=False):
    key: str
    display_name: str
    type: str  # e.g. "free_input"
    mandatory: bool


class BYOKModelTypeInfo(TypedDict, total=False):
    key: str
    display_name: str
    models: list[BYOKModelInfo]


class BYOKModelInfo(TypedDict, total=False):
    key: str
    display_name: str
    is_vl: bool
    is_reasoning: bool
    format: str
    max_input_tokens: int
```

<div id="byokproviderinfo" />

#### `BYOKProviderInfo`

| Field          | Type                      | Description                                              |
| -------------- | ------------------------- | -------------------------------------------------------- |
| `key`          | `str`                     | Provider key — fill into `CustomModel.provider` for BYOK |
| `display_name` | `str`                     | Display name                                             |
| `api_key_url`  | `str`                     | URL pointing the user where to obtain an API Key         |
| `url`          | `str`                     | Base URL for inference requests                          |
| `fields`       | `list[BYOKFieldInfo]`     | List of fields the provider requires the user to fill in |
| `types`        | `list[BYOKModelTypeInfo]` | Model groups under this provider                         |

<div id="byokfieldinfo" />

#### `BYOKFieldInfo`

| Field          | Type   | Description                      |
| -------------- | ------ | -------------------------------- |
| `key`          | `str`  | Field key (e.g. `api_key`)       |
| `display_name` | `str`  | Display name shown to the user   |
| `type`         | `str`  | Field type (e.g. `"free_input"`) |
| `mandatory`    | `bool` | Whether the field is required    |

<div id="byokmodeltypeinfo" />

#### `BYOKModelTypeInfo`

| Field          | Type                  | Description                                             |
| -------------- | --------------------- | ------------------------------------------------------- |
| `key`          | `str`                 | Group key, common values: `cp` / `tp` / `pg` (optional) |
| `display_name` | `str`                 | Group display name                                      |
| `models`       | `list[BYOKModelInfo]` | Models within the group                                 |

<div id="byokmodelinfo" />

#### `BYOKModelInfo`

| Field              | Type   | Description                                     |
| ------------------ | ------ | ----------------------------------------------- |
| `key`              | `str`  | Model ID — fill into `CustomModel.model`        |
| `display_name`     | `str`  | Display name                                    |
| `is_vl`            | `bool` | Whether vision / multi-modal input is supported |
| `is_reasoning`     | `bool` | Whether this is a reasoning model               |
| `format`           | `str`  | Upstream protocol format (e.g. `openai`)        |
| `max_input_tokens` | `int`  | Maximum input token count                       |

<div id="modelinfo" />

### `ModelInfo`

Summary of an available model returned by [`client.get_available_models()`](#clientget_available_models). Also used as the element type of [`ModelPolicyContext.availableModels`](#modelpolicycontext).

```python theme={null}
class ModelInfo(TypedDict, total=False):
    value: str
    displayName: str
    isEnabled: bool
```

| Field         | Type                | Description                                                                                                                                |
| ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `value`       | `str`               | Model identifier; usable as [`ModelPolicyResult.model`](#modelpolicyresult) or as the argument of [`client.set_model()`](#clientset_model) |
| `displayName` | `str`               | Display name                                                                                                                               |
| `isEnabled`   | `bool \| undefined` | Whether currently available; `undefined` is treated as available                                                                           |

<div id="modelpolicytimeouterror" />

### `ModelPolicyTimeoutError`

```python theme={null}
class ModelPolicyTimeoutError(Exception): ...
```

Thrown by the SDK when the `resolve_model` callback exceeds `options.resolve_model_timeout_ms` without returning. The query fails directly, with no fallback.

<div id="clientset_model" />

<div id="client-set_model" />

### `client.set_model()`

```python theme={null}
async def set_model(self, model: str | None = None) -> None: ...
```

Switches the model in fixed-model mode at runtime. Takes effect on the next LLM call. Effective only in fixed-model mode; in dynamic-callback mode, calling it does not override the callback's result. Valid model IDs: see [`ModelInfo.value`](#modelinfo).

<div id="clientget_available_models" />

<div id="client-get_available_models" />

### `client.get_available_models()`

```python theme={null}
async def get_available_models(self) -> list[ModelInfo]: ...
```

Fetches the latest model list available to the current account in real time. Always returns the latest result, no caching; returns an empty array (does not throw) when the list cannot be fetched temporarily. In dynamic-callback mode, [`ModelPolicyContext.availableModels`](#modelpolicycontext) already carries the same up-to-date list, so calling this method explicitly is unnecessary.

<div id="clientlist_byok_providers" />

<div id="client-list_byok_providers" />

### `client.list_byok_providers()`

```python theme={null}
async def list_byok_providers(self) -> list[BYOKProviderInfo] | None: ...
```

Returns the BYOK provider/model catalog available to the current account as an array:

* Returns `None`: the CLI does not support this API (graceful fallback, no exception).
* Returns an array (may be empty): the list of providers available to the current account (an empty array means the account has not enabled BYOK).

For field semantics, see [BYOK Catalog Types](#byok-catalog-types).

<div id="canusetool" />

### `CanUseTool`

Host-defined custom tool permission approval callback.

```python theme={null}
from collections.abc import Awaitable, Callable
from typing import Any


CanUseTool = Callable[
    [str, dict[str, Any], ToolPermissionContext],
    Awaitable[PermissionResult],
]
```

<div id="toolpermissioncontext" />

#### `ToolPermissionContext`

```python theme={null}
import asyncio
from dataclasses import dataclass, field
from typing import Any


@dataclass
class ToolPermissionContext:
    signal: asyncio.Event | None = None
    suggestions: list[Any] = field(default_factory=list)
    blocked_path: str | None = None
    decision_reason: str | None = None
    decision_reason_type: str | None = None
    classifier_approvable: bool | None = None
    title: str | None = None
    display_name: str | None = None
    description: str | None = None
    tool_use_id: str | None = None
    agent_id: str | None = None
    exit_plan_mode: ExitPlanModeApprovalDetails | None = None
```

| Field                                    | Type                                  | Description                                                             |
| :--------------------------------------- | :------------------------------------ | :---------------------------------------------------------------------- |
| `signal`                                 | `asyncio.Event \| None`               | Set when cancelled                                                      |
| `suggestions`                            | `list[Any]`                           | Permission update suggestions from the CLI                              |
| `blocked_path`                           | `str \| None`                         | File path triggering authorization (file-related scenarios only)        |
| `decision_reason`                        | `str \| None`                         | Human-readable authorization reason from the CLI                        |
| `decision_reason_type`                   | `str \| None`                         | Permission reason classification                                        |
| `classifier_approvable`                  | `bool \| None`                        | Whether the current call can be auto-approved by the runtime classifier |
| `title` / `display_name` / `description` | `str \| None`                         | Human-readable authorization text generated at runtime                  |
| `tool_use_id`                            | `str \| None`                         | This tool invocation's ID                                               |
| `agent_id`                               | `str \| None`                         | Subagent ID initiating the call                                         |
| `exit_plan_mode`                         | `ExitPlanModeApprovalDetails \| None` | Approval details when exiting plan mode                                 |

For full usage and examples, see [Permission Control](/en/cli/sdk/python/permissions#canusetool).

<div id="permissionmode" />

### `PermissionMode`

```python theme={null}
PermissionMode = Literal[
    "default",
    "acceptEdits",
    "bypassPermissions",
    "yolo",
    "plan",
    "dontAsk",
    "auto",
]
```

| Value                 | Meaning                                                                                                                  | Suitable for                                                                     |
| :-------------------- | :----------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
| `"default"`           | Standard permission behavior. Tool calls are handled by `tools`, allow / deny rules, dynamic approval, or runtime policy | Most interactive sessions                                                        |
| `"acceptEdits"`       | Automatically accepts file-edit operations; other sensitive operations still follow the permission flow                  | Sessions that are approved to modify the workspace                               |
| `"bypassPermissions"` | Skips permission checks; must also set `allow_dangerously_skip_permissions=True`                                         | Trusted automation or test environments                                          |
| `"yolo"`              | Compatibility alias for `"bypassPermissions"`; must also set `allow_dangerously_skip_permissions=True`                   | Compatibility with older configs; not recommended for new code                   |
| `"plan"`              | Plan mode. Suitable for producing a plan first; by default it does not perform real changes                              | Planning, design, review                                                         |
| `"dontAsk"`           | Does not ask interactively; operations that are not pre-authorized or allowed by rules are denied                        | Non-interactive environments, or workflows that should fail instead of prompting |
| `"auto"`              | Runtime capability decides allow or deny automatically; safe workspace file edits may be auto-allowed                    | Reduce confirmation interruptions while retaining runtime judgment               |

For more details on the permission chain, see [Permission Control](/en/cli/sdk/python/permissions).

<div id="permissionresult" />

### `PermissionResult`

Return value of `CanUseTool`.

```python theme={null}
from dataclasses import dataclass
from typing import Any, Literal


@dataclass
class PermissionResultAllow:
    behavior: Literal["allow"] = "allow"
    updated_input: dict[str, Any] | None = None
    updated_permissions: list[PermissionUpdate | dict[str, Any]] | None = None
    decision_classification: PermissionDecisionClassification | None = None


@dataclass
class PermissionResultDeny:
    behavior: Literal["deny"] = "deny"
    message: str = ""
    interrupt: bool = False
    decision_classification: PermissionDecisionClassification | None = None


PermissionResult = PermissionResultAllow | PermissionResultDeny
```

`allow.updated_input`, when modified, replaces the actual parameters the tool receives. `deny.interrupt=True` denies and also interrupts the Agent.

The `tool_name` received by `can_use_tool` is the full tool name, for example `"Bash"`, `"Read"`, `"mcp__orders__lookup_order"`.

<div id="mcpserverconfig" />

### `McpServerConfig`

MCP server configuration, passed to `QoderAgentOptions.mcp_servers`.

```python theme={null}
McpServerConfig = (
    McpStdioServerConfig
    | McpSSEServerConfig
    | McpHttpServerConfig
    | McpSdkServerConfig
)
```

<div id="mcpstdioserverconfig" />

#### `McpStdioServerConfig`

```python theme={null}
class McpStdioServerConfig(TypedDict):
    type: NotRequired[Literal["stdio"]]
    command: str
    args: NotRequired[list[str]]
    env: NotRequired[dict[str, str]]
    tools: NotRequired[list[McpServerToolPolicy]]
```

<div id="mcpsseserverconfig" />

#### `McpSSEServerConfig`

```python theme={null}
class McpSSEServerConfig(TypedDict):
    type: Literal["sse"]
    url: str
    headers: NotRequired[dict[str, str]]
    tools: NotRequired[list[McpServerToolPolicy]]
```

<div id="mcphttpserverconfig" />

#### `McpHttpServerConfig`

```python theme={null}
class McpHttpServerConfig(TypedDict):
    type: Literal["http"]
    url: str
    headers: NotRequired[dict[str, str]]
    tools: NotRequired[list[McpServerToolPolicy]]
```

<div id="mcpsdkserverconfig" />

#### `McpSdkServerConfig`

```python theme={null}
class McpSdkServerConfig(TypedDict):
    type: Literal["sdk"]
    name: str
    instance: McpServer
    tools: NotRequired[list[McpServerToolPolicy]]
```

Returned by the `create_sdk_mcp_server()` factory; see [MCP - In-Process Server](/en/cli/sdk/python/mcp#in-process-server-recommended).

<div id="mcpservertoolpolicy" />

#### `McpServerToolPolicy`

```python theme={null}
class McpServerToolPolicy(TypedDict):
    name: str
    permission_policy: Literal["always_allow", "always_ask", "always_deny"]
```

<div id="sdkpluginconfig" />

### `SdkPluginConfig`

Load local plugins.

```python theme={null}
class SdkPluginConfig(TypedDict):
    type: Literal["local"]
    path: str
```

| Field  | Type      | Description                                       |
| :----- | :-------- | :------------------------------------------------ |
| `type` | `"local"` | Currently only local is supported                 |
| `path` | `str`     | Absolute or relative path to the plugin directory |

<div id="settingsource" />

### `SettingSource`

Controls which filesystem settings are loaded.

```python theme={null}
SettingSource = Literal["user", "project", "local"]
```

| Value       | Meaning                                      | Location                     |
| :---------- | :------------------------------------------- | :--------------------------- |
| `"user"`    | User-level global settings                   | `~/.qoder/settings.json`     |
| `"project"` | Project shared settings (version controlled) | `.qoder/settings.json`       |
| `"local"`   | Project local settings (gitignored)          | `.qoder/settings.local.json` |

When omitted, all sources are loaded per CLI defaults; pass `[]` to skip entirely.

<div id="tools-reference" />

<div id="toolsreference" />

## Tools Reference

This page summarizes the stable tool-related APIs, the built-in tool list, and type definitions. For usage paths and scenarios, see [Tools Usage Guide](/en/cli/sdk/python/tools).

> Note: The Python SDK currently does not export the built-in tool input / output type collections from the TypeScript SDK, such as `BashInput`, `FileReadInput`, or `ToolInputSchemas`. The implementation status is explicitly noted in the relevant sections.

<div id="toolconfig" />

### `ToolConfig`

The TypeScript SDK provides `options.toolConfig` to configure the behavior of certain built-in tools:

```typescript theme={null}
type ToolConfig = {
  askUserQuestion?: {
    previewFormat?: "markdown" | "html";
  };
};
```

The Python SDK does not currently export an equivalent `QoderAgentOptions.tool_config` field; `AskUserQuestion` can still be used as a runtime tool name in `tools`, `allowed_tools`, `disallowed_tools`, `can_use_tool`, and hook matchers.

<div id="built-in-tool-list" />

<div id="builtintoollist" />

### Built-in Tool List

In `tools`, `allowed_tools`, `disallowed_tools`, `can_use_tool`, hook matchers, and Agent tool allowlists, built-in tools use the runtime tool names in the table below.

| Category          | Tool name          | Description                      | Python SDK status                               |
| ----------------- | ------------------ | -------------------------------- | ----------------------------------------------- |
| Command execution | `Bash`             | Execute shell commands           | Usable in permission / tool-scope configuration |
| File operations   | `Read`             | Read file contents               | Usable in permission / tool-scope configuration |
| File operations   | `Edit`             | Edit files by string matching    | Usable in permission / tool-scope configuration |
| File operations   | `Write`            | Create or overwrite files        | Usable in permission / tool-scope configuration |
| Search            | `Glob`             | Search by filename pattern       | Usable in permission / tool-scope configuration |
| Search            | `Grep`             | Search by content regex          | Usable in permission / tool-scope configuration |
| Network           | `WebFetch`         | Fetch and process URL content    | Usable in permission / tool-scope configuration |
| Network           | `WebSearch`        | Web search                       | Usable in permission / tool-scope configuration |
| Agent             | `Agent`            | Invoke a subagent                | Usable in permission / tool-scope configuration |
| Interaction       | `AskUserQuestion`  | Ask the user a question          | Usable in permission / tool-scope configuration |
| Notebook          | `NotebookEdit`     | Edit notebook cells              | Usable in permission / tool-scope configuration |
| Background tasks  | `TaskOutput`       | Send output to a background task | Usable in permission / tool-scope configuration |
| Background tasks  | `TaskStop`         | Stop a background task           | Usable in permission / tool-scope configuration |
| Plan / worktree   | `ExitPlanMode`     | Exit plan mode                   | Usable in permission / tool-scope configuration |
| Plan / worktree   | `EnterWorktree`    | Enter a git worktree             | Usable in permission / tool-scope configuration |
| Plan / worktree   | `ExitWorktree`     | Exit a worktree                  | Usable in permission / tool-scope configuration |
| Config            | `Config`           | Read or write configuration      | Usable in permission / tool-scope configuration |
| Todo              | `TodoWrite`        | Manage todo items                | Usable in permission / tool-scope configuration |
| MCP resources     | `ListMcpResources` | List MCP resources               | Usable in permission / tool-scope configuration |
| MCP resources     | `ReadMcpResource`  | Read an MCP resource             | Usable in permission / tool-scope configuration |
| MCP invocation    | `Mcp`              | Generic MCP tool call            | Usable in permission / tool-scope configuration |

Custom MCP tool names use this format:

```text theme={null}
mcp__{serverName}__{toolName}
```

<div id="tool" />

### `tool()`

Creates an SDK MCP tool definition. The Python version is decorator-style; the handler is wrapped via `@tool(...)`.

```python theme={null}
from collections.abc import Awaitable, Callable
from typing import Any

from mcp.types import ToolAnnotations


def tool(
    name: str,
    description: str,
    input_schema: type | dict[str, Any],
    annotations: ToolAnnotations | None = None,
) -> Callable[[Callable[..., Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]:
    ...
```

| Parameter      | Type                      | Required | Semantics                                                                           | Current Qoder behavior                                                                                                                |
| -------------- | ------------------------- | -------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `name`         | `str`                     | Yes      | Unique identifier of the tool within the current MCP server                         | Forms the model-visible full tool name `mcp__{serverName}__{name}`; required to be non-empty at registration                          |
| `description`  | `str`                     | Yes      | Tool description shown to the model — when to use it, what it does, what it returns | Passed through to the tool list and directly affects whether the model invokes it correctly; required to be non-empty at registration |
| `input_schema` | `type \| dict[str, Any]`  | Yes      | Defines the tool's input parameters                                                 | The SDK generates an MCP input schema; supports a simple dict, a `TypedDict`, or a complete JSON Schema dict                          |
| `annotations`  | `ToolAnnotations \| None` | No       | Additional tool metadata                                                            | The SDK registers annotations on the MCP server; does not replace permission configuration                                            |

`tool()` itself only defines the tool; the decorated async handler is the function executed when the tool is called. Registration constraints such as `name`, `description`, and duplicate tool names are validated by `create_sdk_mcp_server()` when the tool is registered.

<div id="input_schema" />

#### `input_schema`

The Python SDK does not have the TypeScript SDK's `AnyZodRawShape` / `InferShape`. The Python version of `input_schema` supports the following forms:

```python theme={null}
# Simple dict: all fields required
{"query": str, "limit": int}

# Use Annotated to add a field description
{"query": Annotated[str, "Search keywords"]}

# TypedDict: supports NotRequired
class SearchInput(TypedDict):
    query: str
    limit: NotRequired[int]

# Complete JSON Schema dict
{
    "type": "object",
    "properties": {
        "query": {"type": "string"},
        "source": {"type": "string", "enum": ["docs", "wiki"]},
    },
    "required": ["query"],
}
```

Common Python type conversions:

| Python form           | JSON Schema semantics                                        |
| --------------------- | ------------------------------------------------------------ |
| `str`                 | `{"type": "string"}`                                         |
| `int`                 | `{"type": "integer"}`                                        |
| `float`               | `{"type": "number"}`                                         |
| `bool`                | `{"type": "boolean"}`                                        |
| `list[T]`             | array, with `items`                                          |
| `dict`                | object                                                       |
| `Annotated[T, "..."]` | adds `description` to `T`'s schema                           |
| `TypedDict`           | object; generates `required` based on required / NotRequired |

<div id="typescript-only-schema-helper-types" />

<div id="typescriptonlyschemahelpertypes" />

#### TypeScript-only schema helper types

| TypeScript reference type | Python SDK status              | Python equivalent capability                                                                                   |
| ------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| `AnyZodRawShape`          | Not implemented / not exported | Use `dict[str, type]`, `TypedDict`, or a complete JSON Schema dict                                             |
| `InferShape`              | Not implemented / not exported | The handler receives an `args` dict; if static typing is needed, declare your own `TypedDict` in business code |
| `ToolExtras`              | Not implemented / not exported | In Python, `annotations` is passed directly as the 4th argument to `@tool()`                                   |

<div id="sdkmcptool" />

#### `SdkMcpTool`

```python theme={null}
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from typing import Any, Generic, TypeVar

from mcp.types import ToolAnnotations


T = TypeVar("T")


@dataclass
class SdkMcpTool(Generic[T]):
    name: str
    description: str
    input_schema: type[T] | dict[str, Any]
    handler: Callable[..., Awaitable[dict[str, Any]]]
    annotations: ToolAnnotations | None = None
```

The `@tool()` decorator returns an `SdkMcpTool`. You typically do not need to construct one manually.

<div id="toolinvocationcontext" />

#### `ToolInvocationContext`

```python theme={null}
import asyncio
from dataclasses import dataclass, field


@dataclass
class ToolInvocationContext:
    signal: asyncio.Event = field(default_factory=asyncio.Event)
```

The handler may take one or two parameters:

```python theme={null}
@tool("watch", "Watch a counter.", {"max": int})
async def watch(args, extra: ToolInvocationContext):
    ...
```

When the handler accepts a second positional parameter, the SDK passes a `ToolInvocationContext`. `extra.signal` is set when the CLI cancels an in-flight tool call.

<div id="toolannotations" />

#### `ToolAnnotations`

The Python version directly uses `mcp.types.ToolAnnotations`.

```python theme={null}
from mcp.types import ToolAnnotations


ToolAnnotations(
    title="Search docs",
    readOnlyHint=True,
    destructiveHint=False,
    idempotentHint=True,
    openWorldHint=False,
    maxResultSizeChars=500_000,
)
```

| Field                | Type   | Optional | Semantics                                                    | Current Qoder behavior                                                                                                                                   |
| -------------------- | ------ | -------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title`              | `str`  | Yes      | Human-readable title of the tool                             | MCP metadata; not currently a verified Qoder behavior capability                                                                                         |
| `readOnlyHint`       | `bool` | Yes      | Marks the tool as not modifying state                        | The observable effect today is that read-only tools are eligible for concurrent execution within the same batch of tool calls; not a permission switch   |
| `destructiveHint`    | `bool` | Yes      | Marks the tool as potentially performing destructive updates | Risk metadata; does not currently auto-block authorized tool execution                                                                                   |
| `openWorldHint`      | `bool` | Yes      | Marks whether the tool interacts with an open external world | External-interaction metadata; does not currently auto-block authorized tool execution                                                                   |
| `maxResultSizeChars` | `int`  | Yes      | Marks the tool's result-size cap                             | The Python SDK writes it into `_meta["anthropic/maxResultSizeChars"]` for the CLI to read; this is the MCP type extension field currently used in Python |

These fields are metadata and scheduling hints, not permission switches. Whether execution is allowed is still determined by `tools`, `allowed_tools`, `disallowed_tools`, `permission_mode`, `can_use_tool`, and hooks.

<div id="create_sdk_mcp_server" />

### `create_sdk_mcp_server()`

Creates an MCP server that runs in the same process as the SDK.

```python theme={null}
from typing import Any


def create_sdk_mcp_server(
    name: str,
    version: str = "1.0.0",
    tools: list[SdkMcpTool[Any]] | None = None,
) -> McpSdkServerConfig:
    ...
```

| Parameter | Default   | Description                                              |
| --------- | --------- | -------------------------------------------------------- |
| `name`    | Required  | MCP server name; used in `mcp__{serverName}__{toolName}` |
| `version` | `"1.0.0"` | Server version info                                      |
| `tools`   | `None`    | List of tools registered to this server                  |

<div id="createsdkmcpserveroptions" />

#### `CreateSdkMcpServerOptions`

The TypeScript SDK uses a `CreateSdkMcpServerOptions` object parameter; the Python SDK does not export this type and does not use an options object. The Python equivalent is the three function parameters of `create_sdk_mcp_server(name, version="1.0.0", tools=None)`.

<div id="create_sdk_mcp_server-return-value" />

<div id="createsdkmcpserver-return-value" />

#### Return Value

Returns an `McpSdkServerConfig` that can be used directly as a value in `QoderAgentOptions.mcp_servers`.

```python theme={null}
from typing import Literal, TypedDict

from typing_extensions import NotRequired


class McpSdkServerConfig(TypedDict):
    type: Literal["sdk"]
    name: str
    instance: McpServer
    tools: NotRequired[list[McpServerToolPolicy]]
```

#### `McpServerToolPolicy`

```python theme={null}
class McpServerToolPolicy(TypedDict):
    name: str
    permission_policy: Literal["always_allow", "always_ask", "always_deny"]
```

The `tools` policy field exists in the Python types. It is primarily used for tool permission policy at the MCP server configuration layer; common in-process SDK server integrations are still controlled through `allowed_tools`, `disallowed_tools`, `permission_mode`, `can_use_tool`, and hooks.

<div id="calltoolresult" />

### `CallToolResult`

The Python SDK does not export its own `CallToolResult` type. Handlers return a dict, and the SDK converts it into the MCP `CallToolResult`.

```python theme={null}
from typing import Literal, TypedDict

from typing_extensions import NotRequired


class TextToolContent(TypedDict):
    type: Literal["text"]
    text: str


class ImageToolContent(TypedDict):
    type: Literal["image"]
    data: str
    mimeType: str


class ResourceLinkToolContent(TypedDict):
    type: Literal["resource_link"]
    uri: str
    name: NotRequired[str]
    description: NotRequired[str]
    mimeType: NotRequired[str]


class EmbeddedResourceValue(TypedDict, total=False):
    uri: str
    mimeType: str
    text: str
    blob: str


class EmbeddedResourceToolContent(TypedDict):
    type: Literal["resource"]
    resource: EmbeddedResourceValue


ToolContent = (
    TextToolContent
    | ImageToolContent
    | ResourceLinkToolContent
    | EmbeddedResourceToolContent
)


class ToolHandlerResult(TypedDict):
    content: list[ToolContent]
    is_error: NotRequired[bool]
```

<div id="mcptoolresultcontent" />

#### `McpToolResultContent`

The Python SDK currently recognizes the following content blocks:

| Type                     | Python handler dict                                                            | Current Qoder behavior                                                    |
| ------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
| Text                     | `{"type": "text", "text": "..."}`                                              | Converted to `TextContent`                                                |
| Image                    | `{"type": "image", "data": "...", "mimeType": "image/png"}`                    | Converted to `ImageContent`                                               |
| Resource link            | `{"type": "resource_link", "uri": "...", "name": "...", "description": "..."}` | Downgraded to `TextContent`, concatenating `name` / `uri` / `description` |
| Embedded text resource   | `{"type": "resource", "resource": {"text": "..."}}`                            | Converted to `TextContent`                                                |
| Embedded binary resource | `{"type": "resource", "resource": {"blob": "..."}}`                            | Currently skipped, with a warning logged                                  |

Differences from the TS reference:

* The Python handler uses `is_error`, not the MCP / TypeScript `isError` field name; the SDK maps it to MCP `isError` internally.
* Top-level `_meta` on the Python handler is not currently passed through to `CallToolResult`.
* The Python `call_tool` conversion logic does not currently handle the `audio` content block; even though MCP types import `AudioContent`, a handler returning `{"type": "audio"}` will fall into the unsupported-content warning branch.

<div id="can_use_tool" />

### `can_use_tool`

The tool permission approval callback is defined in the common types and is repeated here for ease of reference.

```python theme={null}
from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from typing import Any, Literal


@dataclass
class ToolPermissionContext:
    signal: asyncio.Event | None = None
    suggestions: list[Any] = field(default_factory=list)
    blocked_path: str | None = None
    decision_reason: str | None = None
    decision_reason_type: str | None = None
    classifier_approvable: bool | None = None
    title: str | None = None
    display_name: str | None = None
    description: str | None = None
    tool_use_id: str | None = None
    agent_id: str | None = None
    exit_plan_mode: ExitPlanModeApprovalDetails | None = None


CanUseTool = Callable[
    [str, dict[str, Any], ToolPermissionContext],
    Awaitable[PermissionResult],
]
```

#### `PermissionResult`

```python theme={null}
@dataclass
class PermissionResultAllow:
    behavior: Literal["allow"] = "allow"
    updated_input: dict[str, Any] | None = None
    updated_permissions: list[PermissionUpdate | dict[str, Any]] | None = None
    decision_classification: PermissionDecisionClassification | None = None


@dataclass
class PermissionResultDeny:
    behavior: Literal["deny"] = "deny"
    message: str = ""
    interrupt: bool = False
    decision_classification: PermissionDecisionClassification | None = None


PermissionResult = PermissionResultAllow | PermissionResultDeny
```

The `tool_name` received by `can_use_tool` is the full tool name, for example `Bash`, `Read`, `mcp__orders__lookup_order`.

<div id="mcp-status-tool-information" />

<div id="mcpstatustoolinformation" />

### MCP Status Tool Information

The Python SDK exports `McpToolInfo` and `McpToolAnnotations` to describe the per-server tool information returned by `QoderSDKClient.get_mcp_status()`.

```python theme={null}
from typing import TypedDict

from typing_extensions import NotRequired


class McpToolAnnotations(TypedDict, total=False):
    readOnly: bool
    destructive: bool
    openWorld: bool


class McpToolInfo(TypedDict):
    name: str
    description: NotRequired[str]
    annotations: NotRequired[McpToolAnnotations]
```

Note: the annotation field names in status are the CLI-projected `readOnly`, `destructive`, `openWorld`, not the `readOnlyHint`, `destructiveHint`, `openWorldHint` from the `ToolAnnotations` input. `idempotentHint` is not currently echoed in the status tool list.

<div id="built-in-tool-input-output-types" />

<div id="builtintoolinputoutputtypes" />

<div id="built-in-tool-inputoutput-types" />

<div id="built-in-tool-input-and-output-types" />

### Built-in Tool Input/Output Types

The TypeScript SDK provides input / output structures for built-in tools at the type level. The Python SDK currently does not export these TypedDicts, nor does it export `ToolInputSchemas` / `ToolOutputSchemas` union types. Note: the type names in the table below are the TypeScript reference type names; Python permission configuration and tool allowlists still use the runtime tool names from [Built-in Tool List](#built-in-tool-list).

| TypeScript type                                    | Python SDK status                               |
| -------------------------------------------------- | ----------------------------------------------- |
| `AgentInput` / `AgentOutput`                       | Not exported                                    |
| `BashInput` / `BashOutput`                         | Not exported                                    |
| `FileReadInput` / `FileReadOutput`                 | Not exported; runtime tool name remains `Read`  |
| `FileEditInput` / `FileEditOutput`                 | Not exported; runtime tool name remains `Edit`  |
| `FileWriteInput` / `FileWriteOutput`               | Not exported; runtime tool name remains `Write` |
| `GlobInput` / `GlobOutput`                         | Not exported                                    |
| `GrepInput` / `GrepOutput`                         | Not exported                                    |
| `WebFetchInput` / `WebFetchOutput`                 | Not exported                                    |
| `WebSearchInput` / `WebSearchOutput`               | Not exported                                    |
| `AskUserQuestionInput` / `AskUserQuestionOutput`   | Not exported                                    |
| `NotebookEditInput` / `NotebookEditOutput`         | Not exported                                    |
| `TaskOutputInput`                                  | Not exported                                    |
| `TaskStopInput` / `TaskStopOutput`                 | Not exported                                    |
| `ExitPlanModeInput` / `ExitPlanModeOutput`         | Not exported                                    |
| `ConfigInput` / `ConfigOutput`                     | Not exported                                    |
| `EnterWorktreeInput` / `EnterWorktreeOutput`       | Not exported                                    |
| `ExitWorktreeInput` / `ExitWorktreeOutput`         | Not exported                                    |
| `TodoWriteInput` / `TodoWriteOutput`               | Not exported                                    |
| `ListMcpResourcesInput` / `ListMcpResourcesOutput` | Not exported                                    |
| `ReadMcpResourceInput`                             | Not exported                                    |
| `McpInput` / `McpOutput`                           | Not exported                                    |
| `ToolInputSchemas`                                 | Not exported                                    |
| `ToolOutputSchemas`                                | Not exported                                    |

What the Python side exposes as stable and configurable is the runtime tool names in [Built-in Tool List](#built-in-tool-list). If you need strongly-typed built-in tool parameters in a Python application, define your own `TypedDict` or dataclass on the business side.

### Related Documentation

* [Tools Usage Guide](/en/cli/sdk/python/tools)
* [MCP Integration](/en/cli/sdk/python/mcp)
* [Permission Control](/en/cli/sdk/python/permissions)
* [Hooks](/en/cli/sdk/python/hooks)
* [Subagent Usage Guide](/en/cli/sdk/python/agents)

<div id="hooks-reference" />

<div id="hooksreference" />

## Hooks Reference

For usage guides and examples, see [Hooks](/en/cli/sdk/python/hooks).

<div id="event-overview" />

<div id="eventoverview" />

### Event Overview

| Event                | Trigger                                 | Controllable behavior                    |
| -------------------- | --------------------------------------- | ---------------------------------------- |
| `PreToolUse`         | Before a tool call                      | Block / allow / modify input             |
| `PostToolUse`        | After a tool succeeds                   | Audit / inject context / override output |
| `PostToolUseFailure` | After a tool fails                      | Error handling / logging                 |
| `UserPromptSubmit`   | Before a user prompt is sent            | Inject context / block                   |
| `SessionStart`       | When a session starts                   | Initialize / inject context              |
| `SessionEnd`         | When a session ends                     | Cleanup / logging                        |
| `Stop`               | When AI stops generating                | Block stop and force continuation        |
| `SubagentStart`      | When a subagent starts                  | Observe / log                            |
| `SubagentStop`       | When a subagent stops                   | Observe / log                            |
| `PreCompact`         | Before context compaction               | Observe / log                            |
| `PostCompact`        | After context compaction                | Observe / log                            |
| `CwdChanged`         | When the working directory changes      | Observe / log                            |
| `InstructionsLoaded` | When an instructions file is loaded     | Observe / log                            |
| `FileChanged`        | When a file is created/modified/deleted | Observe / log                            |
| `PermissionRequest`  | When a permission is requested          | Auto-approve / deny permission requests  |

<div id="hookevent" />

### `HookEvent`

Union type of registrable hook events.

```python theme={null}
HookEvent = Literal[
    "PreToolUse",
    "PostToolUse",
    "PostToolUseFailure",
    "UserPromptSubmit",
    "SessionStart",
    "SessionEnd",
    "Stop",
    "SubagentStart",
    "SubagentStop",
    "PreCompact",
    "PostCompact",
    "CwdChanged",
    "InstructionsLoaded",
    "FileChanged",
    "PermissionRequest",
]
```

<div id="hookcallback" />

### `HookCallback`

```python theme={null}
HookCallback = Callable[
    [HookInput, str | None, HookCallbackOptions],
    Awaitable[HookJSONOutput],
]
```

<div id="hookmatcher" />

### `HookMatcher`

```python theme={null}
@dataclass
class HookMatcher:
    hooks: list[HookCallback]
    matcher: str | None = None
    timeout: int | None = None  # seconds, default 60
```

| Field     | Type                 | Description                             |
| :-------- | :------------------- | :-------------------------------------- |
| `hooks`   | `list[HookCallback]` | List of callbacks executed when matched |
| `matcher` | `str \| None`        | Optional regex; filters by `tool_name`  |
| `timeout` | `int \| None`        | Optional timeout (seconds); default 60  |

<div id="basehookinput" />

### `BaseHookInput`

Common input fields for all hook events.

```python theme={null}
@dataclass
class BaseHookInput:
    hook_event_name: str
    session_id: str
    transcript_path: str
    cwd: str
```

| Field             | Type  | Description                                        |
| :---------------- | :---- | :------------------------------------------------- |
| `hook_event_name` | `str` | Event-type identifier (e.g. `"PreToolUse"`)        |
| `session_id`      | `str` | Unique identifier of the current session           |
| `transcript_path` | `str` | Path to the session transcript file (JSONL format) |
| `cwd`             | `str` | Current working directory of the session           |

<div id="hookjsonoutput" />

### `HookJSONOutput`

Return type of a hook callback.

```python theme={null}
@dataclass
class HookJSONOutput:
    continue_: bool | None = None       # JSON key is "continue"
    stop_reason: str | None = None      # JSON key is "stopReason"
    decision: str | None = None
    reason: str | None = None
    hook_specific_output: dict | None = None  # JSON key is "hookSpecificOutput"
```

| Field                  | Type           | Default                       | Description                                                                                                                                               |
| :--------------------- | :------------- | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `continue_`            | `bool \| None` | `None` (equivalent to `True`) | Set to `False` to terminate the session. Effective only for `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `UserPromptSubmit`, `Stop`, `SubagentStop` |
| `stop_reason`          | `str \| None`  | `None`                        | Human-readable reason for stopping the session (used with `continue_=False`)                                                                              |
| `decision`             | `str \| None`  | `None`                        | `"approve"` or `"block"`. `"block"` blocks tool execution; for the `Stop` event, `"block"` blocks stopping and forces continuation                        |
| `reason`               | `str \| None`  | `None`                        | Reason for the decision (shown to the model; for the `"block"` decision on a `Stop` event, injected as a continuation prompt into the context)            |
| `hook_specific_output` | `dict \| None` | `None`                        | Event-specific output (see each event type)                                                                                                               |

> In the Python SDK, `continue_` corresponds to the JSON key `"continue"` (to avoid the keyword conflict).
> When multiple hooks return conflicting `decision` values, `"deny"` / `"block"` takes priority (the strictest rule wins).

<div id="pretoolusehookinput" />

### `PreToolUseHookInput`

```python theme={null}
@dataclass
class PreToolUseHookInput(BaseHookInput):
    hook_event_name: Literal["PreToolUse"]
    permission_mode: str | None
    tool_name: str
    tool_input: Any
```

| Field             | Type          | Description                            |
| :---------------- | :------------ | :------------------------------------- |
| `permission_mode` | `str \| None` | Current permission mode of the session |
| `tool_name`       | `str`         | Name of the tool being invoked         |
| `tool_input`      | `Any`         | Parameters passed to the tool          |

**`hook_specific_output` (`hookSpecificOutput`):**

| Field                      | Type           | Description                                                 |
| :------------------------- | :------------- | :---------------------------------------------------------- |
| `hookEventName`            | `"PreToolUse"` | Must be set                                                 |
| `permissionDecision`       | `str`          | `"allow"` / `"deny"` / `"ask"` / `"defer"`                  |
| `permissionDecisionReason` | `str`          | Reason for the permission decision                          |
| `updatedInput`             | `dict`         | Modified tool input that replaces the original `tool_input` |
| `additionalContext`        | `str`          | Extra context injected into the model's next turn           |

<div id="posttoolusehookinput" />

### `PostToolUseHookInput`

```python theme={null}
@dataclass
class PostToolUseHookInput(BaseHookInput):
    hook_event_name: Literal["PostToolUse"]
    tool_name: str
    tool_input: Any
    tool_response: Any
```

| Field           | Type  | Description                    |
| :-------------- | :---- | :----------------------------- |
| `tool_name`     | `str` | Name of the tool being invoked |
| `tool_input`    | `Any` | Parameters passed to the tool  |
| `tool_response` | `Any` | Result of tool execution       |

**Output behavior:**

| Field                                  | Location              | Behavior                                                                |
| :------------------------------------- | :-------------------- | :---------------------------------------------------------------------- |
| `hookSpecificOutput.updatedToolOutput` | Event-specific output | **Overrides** `tool_response`; the model only sees the overridden value |
| `hookSpecificOutput.additionalContext` | Event-specific output | **Appends** supplemental context without modifying the original result  |
| `decision: "block"` + `reason`         | Top-level output      | Prevents the agent from further processing the tool result              |

**`hook_specific_output` (`hookSpecificOutput`):**

| Field               | Type            | Description                                      |
| :------------------ | :-------------- | :----------------------------------------------- |
| `hookEventName`     | `"PostToolUse"` | Must be set                                      |
| `updatedToolOutput` | `str`           | Override the tool response content               |
| `additionalContext` | `str`           | Extra context appended alongside the tool result |

> When multiple hooks set `updatedToolOutput`, **the last non-empty value** wins. For chained transformations, perform them sequentially within a single callback.

<div id="posttoolusefailurehookinput" />

### `PostToolUseFailureHookInput`

```python theme={null}
@dataclass
class PostToolUseFailureHookInput(BaseHookInput):
    hook_event_name: Literal["PostToolUseFailure"]
    tool_name: str
    tool_input: Any
    error: str
    is_interrupt: bool | None
```

| Field          | Type           | Description                          |
| :------------- | :------------- | :----------------------------------- |
| `tool_name`    | `str`          | Name of the failed tool              |
| `tool_input`   | `Any`          | Parameters passed to the tool        |
| `error`        | `str`          | Error message                        |
| `is_interrupt` | `bool \| None` | Whether caused by an interrupt/abort |

<div id="userpromptsubmithookinput" />

### `UserPromptSubmitHookInput`

```python theme={null}
@dataclass
class UserPromptSubmitHookInput(BaseHookInput):
    hook_event_name: Literal["UserPromptSubmit"]
    prompt: str
```

| Field    | Type  | Description              |
| :------- | :---- | :----------------------- |
| `prompt` | `str` | Text entered by the user |

**`hook_specific_output` (`hookSpecificOutput`):**

| Field               | Type                 | Description                               |
| :------------------ | :------------------- | :---------------------------------------- |
| `hookEventName`     | `"UserPromptSubmit"` | Must be set                               |
| `additionalContext` | `str`                | Extra context appended to the user prompt |

<div id="sessionstarthookinput" />

### `SessionStartHookInput`

```python theme={null}
@dataclass
class SessionStartHookInput(BaseHookInput):
    hook_event_name: Literal["SessionStart"]
    source: str
```

| Field    | Type  | Description                                                                         |
| :------- | :---- | :---------------------------------------------------------------------------------- |
| `source` | `str` | Reason for starting the session: `"startup"` / `"resume"` / `"clear"` / `"compact"` |

**`hook_specific_output` (`hookSpecificOutput`):**

| Field               | Type             | Description                                  |
| :------------------ | :--------------- | :------------------------------------------- |
| `hookEventName`     | `"SessionStart"` | Must be set                                  |
| `additionalContext` | `str`            | Context injected at the start of the session |

<div id="sessionendhookinput" />

### `SessionEndHookInput`

```python theme={null}
@dataclass
class SessionEndHookInput(BaseHookInput):
    hook_event_name: Literal["SessionEnd"]
    reason: str
```

| Field    | Type  | Description                                                                                                                         |
| :------- | :---- | :---------------------------------------------------------------------------------------------------------------------------------- |
| `reason` | `str` | Reason the session ended: `"clear"` / `"resume"` / `"logout"` / `"prompt_input_exit"` / `"other"` / `"bypass_permissions_disabled"` |

<div id="stophookinput" />

### `StopHookInput`

```python theme={null}
@dataclass
class StopHookInput(BaseHookInput):
    hook_event_name: Literal["Stop"]
    stop_hook_active: bool
```

| Field              | Type   | Description                                        |
| :----------------- | :----- | :------------------------------------------------- |
| `stop_hook_active` | `bool` | Whether a Stop hook is currently blocking stopping |

Returning `{"decision": "block", "reason": "..."}` blocks the AI from stopping and forces continuation. `reason` is injected into the model context as a continuation prompt.

<div id="subagentstarthookinput" />

### `SubagentStartHookInput`

```python theme={null}
@dataclass
class SubagentStartHookInput(BaseHookInput):
    hook_event_name: Literal["SubagentStart"]
    agent_id: str
    agent_type: str
```

| Field        | Type  | Description                                |
| :----------- | :---- | :----------------------------------------- |
| `agent_id`   | `str` | Unique identifier of the subagent instance |
| `agent_type` | `str` | Type / role of the subagent                |

<div id="subagentstophookinput" />

### `SubagentStopHookInput`

```python theme={null}
@dataclass
class SubagentStopHookInput(BaseHookInput):
    hook_event_name: Literal["SubagentStop"]
    stop_hook_active: bool
```

| Field              | Type   | Description                                        |
| :----------------- | :----- | :------------------------------------------------- |
| `stop_hook_active` | `bool` | Whether a Stop hook is currently blocking stopping |

<div id="precompacthookinput" />

### `PreCompactHookInput`

```python theme={null}
@dataclass
class PreCompactHookInput(BaseHookInput):
    hook_event_name: Literal["PreCompact"]
    trigger: str
    custom_instructions: str | None
```

| Field                 | Type          | Description                                    |
| :-------------------- | :------------ | :--------------------------------------------- |
| `trigger`             | `str`         | Trigger reason: `"manual"` / `"auto"`          |
| `custom_instructions` | `str \| None` | Custom instructions for the compaction summary |

<div id="postcompacthookinput" />

### `PostCompactHookInput`

```python theme={null}
@dataclass
class PostCompactHookInput(BaseHookInput):
    hook_event_name: Literal["PostCompact"]
    trigger: str
    compact_summary: str
```

| Field             | Type  | Description                                |
| :---------------- | :---- | :----------------------------------------- |
| `trigger`         | `str` | Trigger reason: `"manual"` / `"auto"`      |
| `compact_summary` | `str` | Summary generated after context compaction |

<div id="cwdchangedhookinput" />

### `CwdChangedHookInput`

```python theme={null}
@dataclass
class CwdChangedHookInput(BaseHookInput):
    hook_event_name: Literal["CwdChanged"]
    old_cwd: str
    new_cwd: str
```

| Field     | Type  | Description                         |
| :-------- | :---- | :---------------------------------- |
| `old_cwd` | `str` | Working directory before the change |
| `new_cwd` | `str` | Working directory after the change  |

<div id="instructionsloadedhookinput" />

### `InstructionsLoadedHookInput`

```python theme={null}
@dataclass
class InstructionsLoadedHookInput(BaseHookInput):
    hook_event_name: Literal["InstructionsLoaded"]
    load_reason: str
```

| Field         | Type  | Description                                             |
| :------------ | :---- | :------------------------------------------------------ |
| `load_reason` | `str` | Load reason: `"nested_traversal"` / `"path_glob_match"` |

<div id="filechangedhookinput" />

### `FileChangedHookInput`

```python theme={null}
@dataclass
class FileChangedHookInput(BaseHookInput):
    hook_event_name: Literal["FileChanged"]
    file_path: str
    event: str
```

| Field       | Type  | Description                                         |
| :---------- | :---- | :-------------------------------------------------- |
| `file_path` | `str` | Path of the changed file                            |
| `event`     | `str` | Filesystem event: `"change"` / `"add"` / `"unlink"` |

<div id="permissionrequesthookinput" />

### `PermissionRequestHookInput`

```python theme={null}
@dataclass
class PermissionRequestHookInput(BaseHookInput):
    hook_event_name: Literal["PermissionRequest"]
    tool_name: str
    tool_input: Any
    permission_suggestions: list | None
```

| Field                    | Type           | Description                |
| :----------------------- | :------------- | :------------------------- |
| `tool_name`              | `str`          | Tool requesting permission |
| `tool_input`             | `Any`          | Tool input parameters      |
| `permission_suggestions` | `list \| None` | Suggested permission rules |

**`hook_specific_output` (`hookSpecificOutput`):**

| Field           | Type                  | Description                     |
| :-------------- | :-------------------- | :------------------------------ |
| `hookEventName` | `"PermissionRequest"` | Must be set                     |
| `decision`      | `dict`                | Permission decision (see below) |

`decision` is one of:

* **Approve:** `{"behavior": "allow", "updatedInput": {...}, "updatedPermissions": [...]}`
* **Deny:** `{"behavior": "deny", "message": "..."}`

<div id="message-types" />

<div id="messagetypes" />

## Message Types

<div id="assistantmessage" />

### `AssistantMessage`

The complete AI reply, delivered once per turn. `content` is a list of `TextBlock` and `ToolUseBlock`.

```python theme={null}
@dataclass
class AssistantMessage:
    content: list[TextBlock | ToolUseBlock]
    parent_tool_use_id: str | None = None
    session_id: str | None = None
    uuid: str | None = None
```

<div id="resultmessage" />

### `ResultMessage`

The final message at the end of the entire session.

```python theme={null}
@dataclass
class ResultMessage:
    subtype: str  # "success" | "error_max_turns" | "error_during_execution" | ...
    duration_ms: int
    num_turns: int
    session_id: str
    total_cost_usd: float | None = None
    result: str | None = None  # only present when subtype == "success"
```

<div id="systemmessage" />

### `SystemMessage`

Session system message. When `subtype == "init"`, `data` carries initialization information (session\_id, model, tools, etc.).

```python theme={null}
@dataclass
class SystemMessage:
    subtype: str  # "init" | "compact_boundary" | "status" | "mcp_status_change" | ...
    data: dict[str, Any]
```

<div id="streamevent" />

### `StreamEvent`

Requires `include_partial_messages=True`; streamed token-by-token.

```python theme={null}
@dataclass
class StreamEvent:
    uuid: str
    session_id: str
    event: dict[str, Any]  # upstream-compatible raw stream event
    parent_tool_use_id: str | None = None
```

`event["type"]` values:

| `event["type"]`       | Description                                                     |
| :-------------------- | :-------------------------------------------------------------- |
| `message_start`       | Message begins                                                  |
| `content_block_start` | Block starts (text / tool\_use / thinking)                      |
| `content_block_delta` | Increment: `text_delta` / `input_json_delta` / `thinking_delta` |
| `content_block_stop`  | Block ends                                                      |
| `message_delta`       | Message-level state change (e.g. stop\_reason)                  |
| `message_stop`        | Complete turn ends                                              |

For full usage, see [Streaming Output](/en/cli/sdk/python/streaming-output).

<div id="content-blocks" />

<div id="contentblocks" />

### Content Blocks

Elements in `AssistantMessage.content`:

```python theme={null}
@dataclass
class TextBlock:
    text: str


@dataclass
class ToolUseBlock:
    id: str
    name: str
    input: dict[str, Any]
```
