Handle approvals and user input 处理审批与用户输入
Surface Claude's approval requests and clarifying questions to users, then return their decisions to the SDK.
将 Claude 的审批请求和澄清问题呈现给用户,并将用户的决策反馈给 SDK。
While working on a task, Claude sometimes needs to check in with users. It might need permission before deleting files, or need to ask which database to use for a new project. Your application needs to surface these requests to users so Claude can continue with their input.
在执行任务时,Claude 有时需要向用户进行确认。例如,在删除文件前它可能需要获得许可,或者需要询问某个新项目应该使用哪个数据库。你的应用程序需要将这些请求呈现给用户,以便 Claude 能够根据用户的输入继续执行后续操作。
Claude requests user input in two situations: when it needs permission to use a tool (like deleting files or running commands), and when it has clarifying questions (via the AskUserQuestion tool). Both trigger your canUseTool callback, which pauses execution until you return a response. This is different from normal conversation turns where Claude finishes and waits for your next message.
Claude 在两种情况下会请求用户输入:当它需要使用工具的权限时(例如删除文件或执行命令),以及当它有澄清性问题时(通过 AskUserQuestion 工具)。这两种情况都会触发你的 canUseTool 回调函数,此时程序执行会暂停,直到你返回响应。这与普通的对话轮次(Turn)不同——在普通轮次中,Claude 会先完成当前回复,然后才等待你的下一条消息。
For clarifying questions, Claude generates the questions and options. Your role is to present them to users and return their selections. You can't add your own questions to this flow; if you need to ask users something yourself, do that separately in your application logic.
对于澄清性问题,Claude 会生成相应的问题和选项。你的职责是将它们呈现给用户并返回其选择的结果。在此流程中,你不能添加自定义问题;如果你需要向用户询问其他事项,请在应用程序的逻辑中另行处理。
This guide shows you how to detect each type of request and respond appropriately.
本指南将向你展示如何检测每种类型的请求并做出相应的响应。
Detect when Claude needs input(检测 Claude 何时需要输入)
Pass a canUseTool callback in your query options. The callback fires whenever Claude needs user input, receiving the tool name and input as arguments:
在查询选项(query options)中传入一个 canUseTool 回调函数。每当 Claude 需要用户输入时,该回调就会被触发,并接收工具名称(tool name)和输入参数(input)作为参数:
async function handleToolRequest(toolName, input, options) {
// options includes { signal: AbortSignal, suggestions?: PermissionUpdate[] }
// Prompt user and return allow or deny
}
const options = { canUseTool: handleToolRequest };
The callback fires in two cases:
该回调在以下两种情况下触发:
- Tool needs approval: Claude wants to use a tool that isn't auto-approved by permission rules or modes. Check
tool_namefor the tool (e.g.,"Bash","Write"). - 工具需要审批:Claude 想要使用一个未被权限规则或模式自动授权的工具。通过检查
tool_name即可识别具体工具(例如"Bash"、"Write")。 - Claude asks a question: Claude calls the
AskUserQuestiontool. Check iftool_name == "AskUserQuestion"to handle it differently. If you specify atoolsarray, includeAskUserQuestionfor this to work. See Handle clarifying questions for details. - Claude 提出问题:Claude 调用了
AskUserQuestion工具。请检查tool_name == "AskUserQuestion"以进行针对性处理。如果你指定了tools数组,请务必包含AskUserQuestion以确保此功能生效。详情请参阅处理澄清性问题。
To automatically allow or deny tools without prompting users, use hooks instead. Hooks execute before
canUseTooland can allow, deny, or modify requests based on your own logic. You can also use thePermissionRequesthook to send external notifications (Slack, email, push) when Claude is waiting for approval.
若要根据逻辑自动允许或拒绝工具的使用而无需提示用户,请改用 钩子 (Hooks)。钩子会在canUseTool之前执行,并可以根据你定义的逻辑来允许、拒绝或修改请求。你还可以使用PermissionRequest钩子,在 Claude 等待审批时发送外部通知(如 Slack、邮件或推送通知)。
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 isn't auto-approved. Your callback receives three arguments:
一旦你在查询选项中传入了 canUseTool 回调函数,当 Claude 想要使用一个未被自动授权的工具时,该回调就会被触发。你的回调函数会接收到三个参数:
| Argument | Description |
|---|---|
toolName | The name of the tool Claude wants to use (e.g., "Bash", "Write", "Edit") tool_name:Claude 想要使用的工具名称(例如 "Bash"、"Write"、"Edit")。 |
input | The parameters Claude is passing to the tool. Contents vary by tool. tool_input:Claude 传给该工具的参数。其内容因工具而异。 |
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.context:额外上下文信息,包括可选的 suggestions(建议的 PermissionUpdate 条目,用于避免重复提示)以及一个取消信号(cancellation signal)。在 TypeScript 中,signal 是一个 AbortSignal;在 Python 中,该信号字段预留供将来使用。详情请参阅 Python 的 ToolPermissionContext。 |
The input object contains tool-specific parameters. Common examples:
input 对象包含了特定工具的参数。常见示例如下:
| 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 | TypeScript.
有关完整的输入架构(Schema),请参阅 SDK 参考文档:Python | TypeScript。
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.
以下示例请求 Claude 创建并删除一个测试文件。当 Claude 尝试执行每项操作时,回调函数会将工具请求打印到终端,并提示用户输入 y/n 以进行审批。
import { query } from "@anthropic-ai/claude-agent-sdk";
import * as readline from "readline";
// Helper to prompt user for input in the terminal
function prompt(question: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) =>
rl.question(question, (answer) => {
rl.close();
resolve(answer);
})
);
}
for await (const message of query({
prompt: "Create a test file in /tmp and then delete it",
options: {
canUseTool: async (toolName, input) => {
// Display the tool request
console.log(`\nTool: ${toolName}`);
if (toolName === "Bash") {
console.log(`Command: ${input.command}`);
if (input.description) console.log(`Description: ${input.description}`);
} else {
console.log(`Input: ${JSON.stringify(input, null, 2)}`);
}
// Get user approval
const response = await prompt("Allow this action? (y/n): ");
// Return allow or deny based on user's response
if (response.toLowerCase() === "y") {
// Allow: tool executes with the original (or modified) input
return { behavior: "allow", updatedInput: input };
} else {
// Deny: tool doesn't execute, Claude sees the message
return { behavior: "deny", message: "User denied this action" };
}
}
}
})) {
if ("result" in message) console.log(message.result);
}
In Python,
can_use_toolrequires streaming mode and aPreToolUsehook that returns{"continue_": True}to keep the stream open. Without this hook, the stream closes before the permission callback can be invoked.
在 Python 中,can_use_tool需要配合流式模式使用,并且需要一个返回{"continue_": True}的PreToolUse钩子(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 for all the ways you can respond.
此示例采用 y/n 流程,任何非 y 的输入都会被视为拒绝。在实际应用中,你可能会构建更丰富的 UI,允许用户修改请求、提供反馈或完全重新引导 Claude。有关所有可能的响应方式,请参阅响应工具请求。
Respond to tool requests 响应工具请求
Your callback returns one of two response types:
你的回调函数会返回以下两种响应类型之一:
| Response | TypeScript |
|---|---|
| Allow | { behavior: "allow", updatedInput } |
| Deny | { behavior: "deny", message } |
When allowing, pass the tool input (original or modified). When denying, provide a message explaining why. Claude sees this message and may adjust its approach.
允许或拒绝(Allow or Deny):允许时,传入工具输入(原始输入或修改后的输入)。拒绝时,提供一条解释原因的消息。Claude 会看到这条消息并可能据此调整其策略。
// Allow the tool to execute
return { behavior: "allow", updatedInput: input };
// Block the tool
return { behavior: "deny", message: "User rejected this action" };
Beyond allowing or denying, you can modify the tool's input or provide context that helps Claude adjust its approach:
除了允许或拒绝外,你还可以修改工具的输入,或提供上下文信息以帮助 Claude 调整其处理方式:
- Approve: let the tool execute as Claude requested
- 批准 (Approve):允许工具按 Claude 请求的方式执行。
- Approve with changes: modify the input before execution (e.g., sanitize paths, add constraints)
- 批准并修改 (Approve with changes):在执行前修改输入(例如:净化路径、添加约束条件)。
- Reject: block the tool and tell Claude why
- 拒绝 (Reject):阻止工具执行并告知 Claude 原因。
- Suggest alternative: block but guide Claude toward what the user wants instead
- 建议替代方案 (Suggest alternative):阻止执行,但引导 Claude 转向用户真正想要的方向。
- Redirect entirely: use streaming input to send Claude a completely new instruction
- 完全重定向 (Redirect entirely):使用流式输入给 Claude 发送一条全新的指令。
The user approves the action as-is. Pass through the input from your callback unchanged and the tool executes exactly as Claude requested.
用户按原样批准了该操作。请原封不动地透传回调(callback)中的 input,工具将完全按照 Claude 的要求执行。
canUseTool: async (toolName, input) => {
console.log(`Claude wants to use ${toolName}`);
const approved = await askUser("Allow this action?");
if (approved) {
return { behavior: "allow", updatedInput: input };
}
return { behavior: "deny", message: "User declined" };
};
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.
用户批准了操作,但希望先修改请求。您可以在工具执行前更改输入。Claude 会看到执行结果,但不会被告知您进行了任何修改。这在清理参数、添加约束或限定访问范围时非常有用。
canUseTool: async (toolName, input) => {
if (toolName === "Bash") {
// User approved, but scope all commands to sandbox
const sandboxedInput = {
...input,
command: input.command.replace("/tmp", "/tmp/sandbox")
};
return { behavior: "allow", updatedInput: sandboxedInput };
}
return { behavior: "allow", updatedInput: input };
};
The user doesn't want this action to happen. Block the tool and provide a message explaining why. Claude sees this message and may try a different approach.
用户不希望执行此操作。请拦截该工具并提供一段说明原因的消息。Claude 会看到这条消息,并可能尝试其他方案。
canUseTool: async (toolName, input) => {
const approved = await askUser(`Allow ${toolName}?`);
if (!approved) {
return {
behavior: "deny",
message: "User rejected this action"
};
}
return { behavior: "allow", updatedInput: input };
};
The user doesn't want this specific action, but has a different idea. Block the tool and include guidance in your message. Claude will read this and decide how to proceed based on your feedback.
用户不希望执行此特定操作,但有其他的想法。请拦截该工具并在消息中包含引导信息。Claude 会读取此内容,并根据您的反馈决定如何继续。
canUseTool: async (toolName, input) => {
if (toolName === "Bash" && input.command.includes("rm")) {
// User doesn't want to delete, suggest archiving instead
return {
behavior: "deny",
message:
"User doesn't want to delete files. They asked if you could compress them into an archive instead."
};
}
return { behavior: "allow", updatedInput: input };
};
For a complete change of direction (not just a nudge), use streaming input to send Claude a new instruction directly. This bypasses the current tool request and gives Claude entirely new instructions to follow.
若要彻底改变执行方向(而不仅仅是微调),请使用流式输入直接向 Claude 发送新指令。这将绕过当前的工具请求,并让 Claude 遵循完全不同的新指令。
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.
当 Claude 在处理具有多种可行方案的任务、需要进一步指引时,它会调用 AskUserQuestion 工具。这会触发您的 canUseTool 回调,并将 toolName 参数设置为 AskUserQuestion。输入(input)中包含了 Claude 以多项选择形式提出的问题,您需要将其展示给用户并返回用户的选择结果。
Clarifying questions are especially common in
planmode, where Claude explores the codebase and asks questions before proposing a plan. This makes plan mode ideal for interactive workflows where you want Claude to gather requirements before making changes.
澄清性问题在plan模式中尤为常见。在这种模式下,Claude 会在提出计划之前先探索代码库并进行提问。这使得计划模式成为交互式工作流的理想选择,适用于你希望 Claude 在做出更改之前先收集需求的场景。
The following steps show how to handle clarifying questions:
以下步骤演示了如何处理澄清性问题:
1. Pass a canUseTool callback 传递一个 canUseTool 回调函数
Pass a canUseTool callback in your query options. By default, AskUserQuestion is available. If you specify a tools array to restrict Claude's capabilities (for example, a read-only agent with only Read, Glob, and Grep), include AskUserQuestion in that array. Otherwise, Claude won't be able to ask clarifying questions:
在您的查询选项中传入 canUseTool 回调。默认情况下,AskUserQuestion 是可用的。如果您通过指定 tools 数组来限制 Claude 的功能(例如,一个仅包含 Read、Glob 和 Grep 的只读代理),请务必在该数组中包含 AskUserQuestion。否则,Claude 将无法提出澄清性问题:
for await (const message of query({
prompt: "Analyze this codebase",
options: {
// Include AskUserQuestion in your tools list
tools: ["Read", "Glob", "Grep", "AskUserQuestion"],
canUseTool: async (toolName, input) => {
// Handle clarifying questions here
}
}
})) {
console.log(message);
}
2.Detect AskUserQuestion 识别 AskUserQuestion
In your callback, check if toolName equals AskUserQuestion to handle it differently from other tools:
在您的回调函数中,检查 toolName 是否等于 AskUserQuestion,以便将其与其他工具区分处理:
canUseTool: async (toolName, input) => {
if (toolName === "AskUserQuestion") {
// Your implementation to collect answers from the user
return handleClarifyingQuestions(input);
}
// Handle other tools normally
return promptForApproval(toolName, input);
};
3.Parse the question input 解析问题输入
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):
输入参数的 questions 数组中包含了 Claude 提出的问题。每个问题都包含 question(要显示的文本)、options(选项)以及 multiSelect(是否允许多选):
{
"questions": [
{
"question": "How should I format the output?",
"header": "Format",
"options": [
{ "label": "Summary", "description": "Brief overview" },
{ "label": "Detailed", "description": "Full explanation" }
],
"multiSelect": false
},
{
"question": "Which sections should I include?",
"header": "Sections",
"options": [
{ "label": "Introduction", "description": "Opening context" },
{ "label": "Conclusion", "description": "Final summary" }
],
"multiSelect": true
}
]
}
请参阅 问题格式 以获取完整的字段说明。
4.Collect answers from the user 收集用户的回答
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.
将问题展示给用户并收集他们的选择。具体实现方式取决于您的应用程序:例如终端提示符、网页表单、移动端对话框等。
5.Return answers to Claude 将答案返回给 Claude
Build the answers object as a record where each key is the question text and each value is the selected option's label:
将 answers 对象构建为一个记录(record),其中每个键(key)为 question 文本,每个值(value)为所选选项的 label:
| From the question object 源自问题对象 | Use as 用作 |
|---|---|
question field (e.g., "How should I format the output?") question 字段(例如:"我该如何格式化输出?") | Key |
Selected option's label field (e.g., "Summary") 所选选项的 label 字段(例如:"Summary") | Value |
For multi-select questions, join multiple labels with ", ". If you support free-text input, use the user's custom text as the value.
对于多选问题,请使用 ", "(逗号加空格)连接多个标签。如果您支持自由文本输入,请使用用户的自定义文本作为该项的值。
return {
behavior: "allow",
updatedInput: {
questions: input.questions,
answers: {
"How should I format the output?": "Summary",
"Which sections should I include?": "Introduction, Conclusion"
}
}
};
Question format 问题格式
The input contains Claude's generated questions in a questions array. Each question has these fields:
输入参数的 questions 数组中包含了由 Claude 生成的问题。每个问题都包含以下字段:
| Field | Description |
|---|---|
question | The full question text to display 用于显示的完整问题文本 |
header | Short label for the question (max 12 characters) 问题的短标签(最多 12 个字符) |
options | Array of 2-4 choices, each with label and description. TypeScript: optionally preview (see below) 包含 2 到 4 个选项的数组,每个选项都带有 label(标签)和 description(描述)。在 TypeScript 中,还包含可选的 preview(预览)字段(详见下文)。 |
multiSelect | If true, users can select multiple options 若为 true,则允许用户选择多个选项。 |
The structure your callback receives:
您的回调函数接收到的结构如下:
{
"questions": [
{
"question": "How should I format the output?",
"header": "Format",
"options": [
{ "label": "Summary", "description": "Brief overview of key points" },
{ "label": "Detailed", "description": "Full explanation with examples" }
],
"multiSelect": false
}
]
}
Option previews (TypeScript) 选项预览 (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.
toolConfig.askUserQuestion.previewFormat 会为每个选项添加一个 preview 字段,以便您的应用程序在标签(label)旁展示视觉预览效果。如果没有此设置,Claude 将不会生成预览内容,该字段也不会出现在数据中。
previewFormat | preview contains (preview 包含 ) |
|---|---|
| unset (default) | Field is absent. Claude does not generate previews. 字段缺失。Claude 不生成预览。 |
"markdown" | ASCII art and fenced code blocks ASCII 艺术和围栏代码块 |
"html" | A styled <div> fragment (the SDK rejects <script>, <style>, and <!DOCTYPE> before your callback runs) 一个带样式的 <div> 片段(在您的回调运行之前,SDK 会拦截 <script>、<style> 和 <!DOCTYPE>) |
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.
该格式适用于会话中的所有问题。Claude 会在有助于视觉对比的场景(如布局选择、配色方案)中为选项提供 preview 字段,而在不需要视觉对比的场景(如‘是/否’确认、纯文本选项)中则会省略。在渲染之前,请检查该字段是否为 undefined。
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Help me choose a card layout",
options: {
toolConfig: {
askUserQuestion: { previewFormat: "html" }
},
canUseTool: async (toolName, input) => {
// input.questions[].options[].preview is an HTML string or undefined
return { behavior: "allow", updatedInput: input };
}
}
})) {
// ...
}
An option with an HTML preview:
一个带有 HTML 预览的选项:
{
"label": "Compact",
"description": "Title and metric value only",
"preview": "<div style="padding:12px;border:1px solid #ddd;border-radius:8px"><div style="font-size:12px;color:#666">Active users</div><div style="font-size:28px;font-weight:600">1,284</div></div>"
}
Response format 响应格式
Return an answers object mapping each question's question field to the selected option's label:
返回一个 answers 对象,该对象将每个问题的 question 字段映射到所选选项的 label:
| Field | Description |
|---|---|
questions | Pass through the original questions array (required for tool processing) 透传原始的 questions 数组(工具处理所需) |
answers | Object where keys are question text and values are selected labels 一个对象,其键(key)为问题文本,值(value)为所选标签 |
For multi-select questions, join multiple labels with ", ". For free-text input, use the user's custom text directly.
对于多选问题,请使用 ", " 连接多个标签。对于自由文本输入,请直接使用用户的自定义文本。
{
"questions": [
// ...
],
"answers": {
"How should I format the output?": "Summary",
"Which sections should I include?": "Introduction, Conclusion"
}
}
Support free-text input 支持自由文本输入
Claude's predefined options won't always cover what users want. To let users type their own answer:
Claude 预定义的选项并不总能覆盖用户的需求。要允许用户输入自己的答案:
- Display an additional "Other" choice after Claude's options that accepts text input
- 在 Claude 提供的选项之后,显示一个额外的“其他”选项,并允许用户输入文本。
- Use the user's custom text as the answer value (not the word "Other")
- 请使用用户的自定义文本作为回答的值(而不是“其他”这个词本身)。
See the complete example below for a full implementation.
完整的实现方式请参阅下方的完整示例。
Complete example 完整示例
Claude asks clarifying questions when it needs user input to proceed. For example, 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.
当需要用户输入才能继续执行时,Claude 会提出澄清性问题。例如,当被要求帮助决定移动应用的技术栈时,Claude 可能会询问关于跨平台与原生、后端偏好或目标平台等问题。这些问题有助于 Claude 做出符合用户偏好的决策,而不是凭空猜测。
This example handles those questions in a terminal application. Here's what happens at each step: 本示例在一个终端应用程序中处理这些问题。以下是每个步骤的详细说明:
- Route the request: The
canUseToolcallback checks if the tool name is"AskUserQuestion"and routes to a dedicated handler - 路由请求:
canUseTool回调会检查工具名称是否为“AskUserQuestion”,并将其路由至一个专门的处理程序。 - Display questions: The handler loops through the
questionsarray and prints each question with numbered options - 显示问题:该处理程序会遍历
questions数组,并打印出每个问题及其带编号的选项。 - Collect input: The user can enter a number to select an option, or type free text directly (e.g., "jquery", "i don't know")
- 收集输入:用户可以输入数字来选择一个选项,也可以直接输入自由文本(例如,“jquery”、“我不知道”)。
- Map answers: The code checks if input is numeric (uses the option's label) or free text (uses the text directly)
- 映射答案:代码会检查输入是数字(此时使用选项的标签)还是自由文本(此时直接使用该文本)。
- Return to Claude: The response includes both the original
questionsarray and theanswersmapping - 返回给 Claude:响应中同时包含原始的
questions数组和answers映射。
import { query } from "@anthropic-ai/claude-agent-sdk";
import * as readline from "readline/promises";
// Helper to prompt user for input in the terminal
async function prompt(question: string): Promise<string> {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const answer = await rl.question(question);
rl.close();
return answer;
}
// Parse user input as option number(s) or free text
function parseResponse(response: string, options: any[]): string {
const indices = response.split(",").map((s) => parseInt(s.trim()) - 1);
const labels = indices
.filter((i) => !isNaN(i) && i >= 0 && i < options.length)
.map((i) => options[i].label);
return labels.length > 0 ? labels.join(", ") : response;
}
// Display Claude's questions and collect user answers
async function handleAskUserQuestion(input: any) {
const answers: Record<string, string> = {};
for (const q of input.questions) {
console.log(`\n${q.header}: ${q.question}`);
const options = q.options;
options.forEach((opt: any, i: number) => {
console.log(` ${i + 1}. ${opt.label} - ${opt.description}`);
});
if (q.multiSelect) {
console.log(" (Enter numbers separated by commas, or type your own answer)");
} else {
console.log(" (Enter a number, or type your own answer)");
}
const response = (await prompt("Your choice: ")).trim();
answers[q.question] = parseResponse(response, options);
}
// Return the answers to Claude (must include original questions)
return {
behavior: "allow",
updatedInput: { questions: input.questions, answers }
};
}
async function main() {
for await (const message of query({
prompt: "Help me decide on the tech stack for a new mobile app",
options: {
canUseTool: async (toolName, input) => {
// Route AskUserQuestion to our question handler
if (toolName === "AskUserQuestion") {
return handleAskUserQuestion(input);
}
// Auto-approve other tools for this example
return { behavior: "allow", updatedInput: input };
}
}
})) {
if ("result" in message) console.log(message.result);
}
}
main();
Limitations 局限性
- Subagents:
AskUserQuestionis not currently available in subagents spawned via the Agent tool - 子Agent(Subagents):
AskUserQuestion目前在通过 Agent 工具生成的子代理中不可用。 - Question limits: each
AskUserQuestioncall supports 1-4 questions with 2-4 options each - 问题数量限制:每次
AskUserQuestion调用支持 1-4 个问题,每个问题支持 2-4 个选项。
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:
canUseTool 回调和 AskUserQuestion 工具覆盖了大多数批准和澄清的场景,但 SDK 也提供了其他获取用户输入的方式:
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
- 在代理执行任务的中途进行干预:在 Claude 工作时发送取消信号或改变方向。
- Provide additional context: add information Claude needs without waiting for it to ask
- 提供额外上下文:无需等待 Claude 提问,即可补充其所需的信息。
- Build chat interfaces: let users send follow-up messages during long-running operations
- 构建聊天界面:允许用户在长时间运行的操作期间发送后续消息。
Streaming input is ideal for conversational UIs where users interact with the agent throughout execution, not just at approval checkpoints.
流式输入非常适合对话式用户界面(UI),在这种界面中,用户可以在整个执行过程中与代理进行互动,而不仅仅是在审批节点进行交互。
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 - 收集结构化输入:构建表单、向导或多步骤工作流,其功能超出了
AskUserQuestion的多项选择格式。 - 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
- 实现领域特定的交互:创建根据您的应用需求量身定制的工具,例如代码审查界面或部署清单。
Custom tools give you full control over the interaction, but require more implementation work than using the built-in canUseTool callback.
自定义工具让您能够完全控制交互过程,但相比使用内置的 canUseTool 回调,它需要更多的实现工作。
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
- TypeScript SDK 参考:完整的
canUseToolAPI 文档