query() session. It can restrict which tools are visible to the model, set default authorization policies, delegate tool execution approval to the host application, and apply new rules to the current session after user authorization.
Permission control is not a standalone API but a set of configurations placed in QoderAgentOptions. Typically, you first decide which tools the model is allowed to use in this session, then decide under what conditions those tools can execute, and finally integrate runtime approval, dynamic rule updates, settings, or hooks as needed.
Read, Grep, and Bash; Read and Grep are pre-authorized; Bash is denied. In real projects, you can further add can_use_tool to route unauthorized operations to your product UI, approval system, or risk control service.
Capability Overview
The permission-related options onquery() fall into roughly four categories. The first determines the default policy — for example, plan mode, auto-allow edits, or no interactive prompts. The second determines tool scope and tool rules. The third lets the host application participate in runtime approval. The fourth covers more advanced settings, hooks, and MCP tool policies.
Common selection patterns:
- Read-only code review with no modifications: set
tools, pre-authorizeReadandGrepviaallowed_tools, and denyWrite,Edit,Bashviadisallowed_tools. - Plan first without making actual changes: use
permission_mode="plan". - Auto-approve safe edits: use
permission_mode="acceptEdits"or"auto". - Show your own approval dialog: use
can_use_tool. - Let the user select “always allow this session”: return
updated_permissionsfrom the allow result ofcan_use_tool. - No interactive prompts at all, deny anything not pre-authorized: use
permission_mode="dontAsk". - Access shared directories outside a monorepo: use
add_dirs.
query(). The Python SDK can use the local qodercli login state by default; if your application needs explicit authentication, pass your own auth configuration via QoderAgentOptions.auth.
Quick Start: Have the Host Application Approve Tool Calls
When you need to route tool calls through your own approval logic, usecan_use_tool. The SDK passes the tool name, tool input, and a set of displayable approval information to your callback at runtime. When the callback returns PermissionResultAllow, the tool continues executing; when it returns PermissionResultDeny, the tool is rejected.
read_order is an SDK MCP tool. When the model invokes it, the full tool name will be mcp__orders__read_order. can_use_tool only allows this tool to execute and returns the original input as updated_input.
Controlling Default Policy: permission_mode
permission_mode determines the session’s default permission policy. Use it to express “what mode is this session overall in” — for example, plan first, auto-accept edits, deny without asking, or skip permission checks in controlled environments.
plan mode is designed for having the model produce a plan first, with no actual changes by default.
To switch modes within the same session, use
QoderSDKClient:
bypassPermissions and yolo are both high-risk modes. The SDK requires explicitly passing allow_dangerously_skip_permissions=True to prevent callers from accidentally turning a normal session into one that skips permission checks.
Controlling Tool Scope: tools, allowed_tools, disallowed_tools
Tool control answers “which tools can the model see, and which tools are allowed or denied by default.” These three fields often appear together but have different semantics.Read, Grep, and Bash tools for this session; Read and Grep are pre-authorized; Bash is denied — even if the model wants to call it, it will not execute.
When the same tool matches both allow and deny, deny takes priority. This ensures deny rules cannot be bypassed by broader allow rules.
MCP tools also use full tool name matching. For example, with an SDK MCP server named
orders and a tool named read_order, the full tool name is mcp__orders__read_order.
Runtime Approval: can_use_tool
can_use_tool is designed for scenarios where the host application needs to participate in approval. For example, you want to display permission requests in your own UI for the user to click “allow once,” “always allow this session,” or “deny”; or you need to call an enterprise risk control service to determine whether a command can execute.
can_use_tool signature:
Returning
PermissionResultAllow means the tool continues executing:
updated_input is the final parameters the tool receives. You can return them as-is, or modify them after approval — for example, add a tenant ID to queries, rewrite paths to a safe directory, or remove disallowed fields.
Returning PermissionResultDeny means the tool is rejected:
deny.message is required; it becomes part of the denial reason, available to the model, logs, or host application. When the SDK receives a CLI authorization request but no can_use_tool is configured, it returns an error rather than defaulting to allow.
When the permission system directly denies a tool call, a structured permission denial message may appear in the message stream:
permission_mode="dontAsk", auto-deny, or rule-deny scenarios. Host applications can use them to update UI state or write audit logs.
Updating Permissions Within a Session: PermissionUpdate
PermissionUpdate is used to update permission rules in the current session after an approval. The most common scenario is when a user selects “always allow this session” in the approval UI. You can return the runtime-provided suggestions as-is, or construct explicit rules yourself.
Recommended to write dynamic permission updates to the current session:
session only affects permission checks for the remainder of the current query session. When persistence to local, project, or user-level configuration is needed, prefer using the settings management workflow rather than relying on dynamic updates in a single tool approval callback.
Accessing Additional Directories: add_dirs
By default, the session usescwd as the primary working directory. When the model needs to read or modify directories outside cwd, explicitly pass add_dirs.
/repo/app, and the model is also allowed to access /repo/packages/shared. This works well for monorepos, cross-repository debugging, shared library investigation, and similar scenarios.
During execution, directory authorization can also be adjusted via PermissionUpdate:
add_dirs as a universal default; the safer approach is to add the minimal directory set needed per task.
External Authorization Tool: permission_prompt_tool_name
permission_prompt_tool_name is used to delegate permission requests to a permission prompt tool in the runtime environment, rather than implementing can_use_tool in the SDK host. Use this when you have existing external approval tools, remote execution environments, or unified permission gateways.
permission_prompt_tool_namemust be a prompt tool name recognizable by the current runtime environment.permission_prompt_tool_nameandcan_use_toolare mutually exclusive; they cannot be passed simultaneously.- When the SDK host needs to handle approval itself, prefer
can_use_tool.
allow.updatedInput is the final parameters used when executing the tool. If you want to keep the original parameters, return the received input as-is. deny.message is required. interrupt=True means deny and also interrupt the current Agent flow.
Using settings to Provide Permission Rules
settings is ideal for providing static permission configuration before the session starts. It is more appropriate than can_use_tool for expressing “what this project allows by default, what it denies, and what additional directories exist.”
If your application reads and applies the default permission mode from settings, consider performing your own product-level confirmation before executing high-risk modes. Modes like
bypassPermissions and yolo should only appear in explicitly trusted environments.
Using hooks for Advanced Interception and Auditing
Hooks are suitable when you have already integrated the SDK hooks system and want finer-grained control in the tool lifecycle. Compared tocan_use_tool, hooks are better suited for cross-cutting concerns such as auditing, alerting, unified interception, and recording denial reasons.
PreToolUse can return:
permissionDecision can be "allow", "deny", "ask", or "defer".
PermissionRequest can return a permission result similar to tool approval:
PermissionDenied is typically used for observing results, not for allowing tools. Its input includes the denied tool name, tool input, tool invocation ID, and denial reason.
MCP Tool Policy
If the permission policy naturally belongs to a specific MCP server, you can declare tool-level permission policy directly in the MCP server config. This way the policy follows the MCP server configuration rather than being scattered in globalallowed_tools or disallowed_tools.
name can be the MCP tool’s original name or the full tool name, e.g., mcp__repo_tools__search. During actual matching, the runtime maps policy names to the current MCP tool invocation.