Handle approvals and user input
기준일: 2026-07-26
공식 기준: Handle approvals and user input
Handle approvals and user input 문서는 Claude Code 공식 문서(agent-sdk/user-input)를 한국어로 정리한 가이드입니다. Surface Claude's approval requests and clarifying questions to users, then return their decisions to the SDK. 명령·설정 키·코드 예시는 공식 문서를 그대로 보존하며, 해석과 절차 안내는 한국어로 제공합니다. 최종 동작은 설치 버전과 공식 원문을 확인하세요.
핵심 요약
Surface Claude's approval requests and clarifying questions to users, then return their decisions to the SDK.
한국어 가이드 범위: agent-sdk/user-input 경로의 설정·명령·제약·예시를 학습용으로 재구성합니다.
문서 구성
공식 문서의 주요 섹션은 다음과 같습니다.
- Handle approvals and user input
- Detect when Claude needs input
- Handle tool approval requests
- Respond to tool requests
- Handle clarifying questions
- Question format
- Response format
- Complete example
- 제한 사항
- Other ways to get user input
- Streaming input
- Custom tools
- Related resources
상세 내용
Handle approvals and user input
Surface Claude's approval requests and clarifying questions to users, then return their decisions to the SDK.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Detect when Claude needs input
Pass a canUseTool callback in your query options. The callback fires whenever Claude needs user input, receiving the tool name and input as arguments:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Handle tool approval requests
Once you've passed a canUseTool callback in your query options, it fires when Claude wants to use a tool that nothing earlier in the permission flow has approved. Your callback receives three arguments:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
| Argument | Description |
|---|---|
toolName |
The name of the tool Claude wants to use (e.g., "Bash", "Write", "Edit") |
input |
The parameters Claude is passing to the tool. Contents vary by tool. |
options (TS) / context (Python) |
Additional context including optional suggestions (proposed PermissionUpdate entries to avoid re-prompting) and a cancellation signal. In TypeScript, signal is an AbortSignal; in Python, the signal field is reserved for future use. See ToolPermissionContext for Python. |
| Tool | Input fields |
|---|---|
Bash |
command, description, timeout |
Write |
file_path, content |
Edit |
file_path, old_string, new_string |
Read |
file_path, offset, limit |
Respond to tool requests
Your callback returns one of two response types:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Approve: let the tool execute as Claude requested
- Approve with changes: modify the input before execution (e.g., sanitize paths, add constraints)
- Approve and remember: echo a suggested permission rule back so matching calls skip the prompt next time
- Reject: block the tool and tell Claude why
- Suggest alternative: block but guide Claude toward what the user wants instead
- Redirect entirely: use streaming input to send Claude a completely new instruction
| Response | Python | TypeScript |
|---|---|---|
| Allow | PermissionResultAllow(updated_input=...) |
{ behavior: "allow", updatedInput } |
| Deny | PermissionResultDeny(message=...) |
{ behavior: "deny", message } |
Beyond allowing or denying, you can modify the tool's input or provide context that helps Claude adjust its approach:
* **Approve**: let the tool execute as Claude requested
* **Approve with changes**: modify the input before execution (e.g., sanitize paths, add constraints)
* **Approve and remember**: echo a suggested permission rule back so matching calls skip the prompt next time
* **Reject**: block the tool and tell Claude why
* **Suggest alternative**: block but guide Claude toward what the user wants instead
* **Redirect entirely**: use [streaming input](/docs/en/agent-sdk/streaming-vs-single-mode) to send Claude a completely new instruction
The user approves the action as-is. Pass through the `input` from your callback unchanged and the tool executes exactly as Claude requested.
The user approves but wants to modify the request first. You can change the input before the tool executes. Claude sees the result but isn't told you changed anything. Useful for sanitizing parameters, adding constraints, or scoping access.
Handle clarifying questions
When Claude needs more direction on a task with multiple valid approaches, it calls the AskUserQuestion tool. This triggers your canUseTool callback with toolName set to AskUserQuestion. The input contains Claude's questions as multiple-choice options, which you display to the user and return their selections.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
In your callback, check if `toolName` equals `AskUserQuestion` to handle it differently from other tools:
The input contains Claude's questions in a `questions` array. Each question has a `question` (the text to display), `options` (the choices), and `multiSelect` (whether multiple selections are allowed):
See [Question format](#question-format) for full field descriptions.
Present the questions to the user and collect their selections. How you do this depends on your application: a terminal prompt, a web form, a mobile dialog, etc.
Build the `answers` object as a record where each key is the `question` text and each value is the selected option's `label`:
| From the question object | Use as |
| ------------------------------------------------------------ | ------ |
| `question` field (e.g., `"How should I format the output?"`) | Key |
| Selected option's `label` field (e.g., `"Summary"`) | Value |
For multi-select questions, pass an array of labels or join them with `", "`. If you [support free-text input](#support-free-text-input), use the user's custom text as the value.
Question format
The input contains Claude's generated questions in a questions array. Each question has these fields:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
| Field | Description |
|---|---|
question |
The full question text to display |
header |
Short label for the question (max 12 characters) |
options |
Array of 2-4 choices, each with label and description. TypeScript: optionally preview (see below) |
multiSelect |
If true, users can select multiple options |
previewFormat |
preview contains |
|---|---|
| unset (default) | Field is absent. Claude does not generate previews. |
"markdown" |
ASCII art and fenced code blocks |
"html" |
A styled <div> fragment (the SDK rejects <script>, <style>, and <!DOCTYPE> before your callback runs) |
#### Option previews (TypeScript)
`toolConfig.askUserQuestion.previewFormat` adds a `preview` field to each option so your app can show a visual mockup alongside the label. Without this setting, Claude does not generate previews and the field is absent.
| `previewFormat` | `preview` contains |
| :-------------- | :------------------------------------------------------------------------------------------------------------ |
| unset (default) | Field is absent. Claude does not generate previews. |
| `"markdown"` | ASCII art and fenced code blocks |
| `"html"` | A styled `<div>` fragment (the SDK rejects `<script>`, `<style>`, and `<!DOCTYPE>` before your callback runs) |
The format applies to all questions in the session. Claude includes `preview` on options where a visual comparison helps (layout choices, color schemes) and omits it where one wouldn't (yes/no confirmations, text-only choices). Check for `undefined` before rendering.
An option with an HTML preview:
Response format
Return an answers object mapping each question's question field to the selected option's label:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Display an additional "Other" choice after Claude's options that accepts text input
- Use the user's custom text as the answer value (not the word "Other")
| Field | Description |
|---|---|
questions |
Pass through the original questions array (required for tool processing) |
answers |
Object where keys are question text and values are selected labels |
response |
Optional freeform reply the user typed instead of answering the structured questions |
Complete example
Claude asks clarifying questions when it needs user input to proceed. 예를 들어, when asked to help decide on a tech stack for a mobile app, Claude might ask about cross-platform vs native, backend preferences, or target platforms. These questions help Claude make decisions that match the user's preferences rather than guessing.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
제한 사항
주요 항목:
- Subagents:
AskUserQuestionis not currently available in subagents spawned via the Agent tool - Question limits: each
AskUserQuestioncall supports 1-4 questions with 2-4 options each
Other ways to get user input
The canUseTool callback and AskUserQuestion tool cover most approval and clarification scenarios, but the SDK offers other ways to get input from users:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Streaming input
Use streaming input when you need to:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Interrupt the agent mid-task: send a cancel signal or change direction while Claude is working
- Provide additional context: add information Claude needs without waiting for it to ask
- Build chat interfaces: let users send follow-up messages during long-running operations
Custom tools
Use custom tools when you need to:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Collect structured input: build forms, wizards, or multi-step workflows that go beyond
AskUserQuestion's multiple-choice format - Integrate external approval systems: connect to existing ticketing, workflow, or approval platforms
- Implement domain-specific interactions: create tools tailored to your application's needs, like code review interfaces or deployment checklists
Related resources
주요 항목:
- Configure permissions: set up permission modes and rules
- Control execution with hooks: run custom code at key points in the agent lifecycle
- TypeScript SDK reference: full canUseTool API documentation
실습 체크리스트
- 공식 문서와 로컬/SDK 버전을 대조합니다.
- 관련 CLI·SDK 옵션은 공식 페이지와
--help로 교차 확인합니다. - Agent SDK 예시는 TypeScript/Python 패키지 최신 API를 우선합니다.
- 권한·호스팅·보안 설정 변경 후 통합 테스트를 실행합니다.
자주 쓰는 명령·설정 예시
The callback fires in two cases:
1. **Tool needs approval**: Claude wants to use a tool that isn't auto-approved by a [permission rule](/docs/en/agent-sdk/permissions) or permission mode. Check `tool_name` for the tool (e.g., `"Bash"`, `"Write"`).
2. **Claude asks a question**: Claude calls the `AskUserQuestion` tool. Check if `tool_name == "AskUserQuestion"` to handle it differently. If you specify a `tools` array, include `AskUserQuestion` for this to work. See [Handle clarifying questions](#handle-clarifying-questions) for details.
**The callback never fires for auto-approved tools.** Any approval earlier in the [permission evaluation flow](/docs/en/agent-sdk/permissions#how-permissions-are-evaluated), an allow rule or a mode like `acceptEdits` or `bypassPermissions`, resolves the call before `canUseTool` is consulted. If you list a tool bare in `allowed_tools`, a `canUseTool` check for that tool never runs unless an ask rule or `plan` mode routes the call back to a prompt. For logic that must apply to every tool call, use a [`PreToolUse` hook](/docs/en/agent-sdk/hooks), which executes before the rest of the flow and can allow, deny, or modify requests.
`AskUserQuestion`, MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool), and connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) reach the callback even when an allow rule matches. In `dontAsk` mode these calls are denied instead, without invoking the callback.
You can also use the [`PermissionRequest` hook](/docs/en/agent-sdk/hooks#available-hooks) to send external notifications (Slack, email, push) when Claude is waiting for approval.
## Handle tool approval requests
Once you've passed a `canUseTool` callback in your query options, it fires when Claude wants to use a tool that nothing earlier in the permission flow has approved. Your callback receives three arguments:
| Argument | Description |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `toolName` | The name of the tool Claude wants to use (e.g., `"Bash"`, `"Write"`, `"Edit"`) |
| `input` | The parameters Claude is passing to the tool. Contents vary by tool. |
| `options` (TS) / `context` (Python) | Additional context including optional `suggestions` (proposed `PermissionUpdate` entries to avoid re-prompting) and a cancellation signal. In TypeScript, `signal` is an `AbortSignal`; in Python, the signal field is reserved for future use. See [`ToolPermissionContext`](/docs/en/agent-sdk/python#toolpermissioncontext) for Python. |
The `input` object contains tool-specific parameters. Common examples:
| Tool | Input fields |
| ------- | --------------------------------------- |
| `Bash` | `command`, `description`, `timeout` |
| `Write` | `file_path`, `content` |
| `Edit` | `file_path`, `old_string`, `new_string` |
| `Read` | `file_path`, `offset`, `limit` |
See the SDK reference for complete input schemas: [Python](/docs/en/agent-sdk/python#tool-input%2Foutput-types) | [TypeScript](/docs/en/agent-sdk/typescript#tool-input-types).
You can display this information to the user so they can decide whether to allow or reject the action, then return the appropriate response.
The following example asks Claude to create and delete a test file. When Claude attempts each operation, the callback prints the tool request to the terminal and prompts for y/n approval.
In Python, `can_use_tool` requires [streaming mode](/docs/en/agent-sdk/streaming-vs-single-mode). When you pass a finite message stream through `query(prompt=generator)` or `ClaudeSDKClient.connect(prompt=async_iterable)`, the SDK closes the input stream after the last message, before the permission callback can be invoked, unless a registered hook or in-process MCP server is keeping it open. The example above keeps it open with a `PreToolUse` hook that returns `{"continue_": True}`. Connecting with no prompt and sending messages through `ClaudeSDKClient.query()` keeps the stream open on its own and needs no hook.
This example uses a `y/n` flow where any input other than `y` is treated as a denial. In practice, you might build a richer UI that lets users modify the request, provide feedback, or redirect Claude entirely. See [Respond to tool requests](#respond-to-tool-requests) for all the ways you can respond.
### Respond to tool requests
Your callback returns one of two response types:
| Response | Python | TypeScript |
| --------- | ------------------------------------------ | ------------------------------------- |
| **Allow** | `PermissionResultAllow(updated_input=...)` | `{ behavior: "allow", updatedInput }` |
| **Deny** | `PermissionResultDeny(message=...)` | `{ behavior: "deny", message }` |
When allowing, the tool runs with the input Claude requested unless you return a modified input, `updatedInput` in TypeScript or `updated_input` in Python. {/* min-version: 2.1.207 */}Before v2.1.207, Claude Code rejected an allow result that omitted `updatedInput` and denied the tool call with a validation error.
When denying, provide a message explaining why. Claude sees this message and may adjust its approach.
Beyond allowing or denying, you can modify the tool's input or provide context that helps Claude adjust its approach:
* **Approve**: let the tool execute as Claude requested
* **Approve with changes**: modify the input before execution (e.g., sanitize paths, add constraints)
* **Approve and remember**: echo a suggested permission rule back so matching calls skip the prompt next time
* **Reject**: block the tool and tell Claude why
* **Suggest alternative**: block but guide Claude toward what the user wants instead
* **Redirect entirely**: use [streaming input](/docs/en/agent-sdk/streaming-vs-single-mode) to send Claude a completely new instruction
The user approves the action as-is. Pass through the `input` from your callback unchanged and the tool executes exactly as Claude requested.
The user approves but wants to modify the request first. You can change the input before the tool executes. Claude sees the result but isn't told you changed anything. Useful for sanitizing parameters, adding constraints, or scoping access.
관련 링크
- 공식 원문: agent-sdk/user-input
- 문서 홈
이 가이드는 공식 문서를 한국어 학습용으로 재구성한 것입니다. 옵션 기본값·API 이름은 설치 버전에 따라 달라질 수 있습니다.