1> ## Documentation Index
2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.
4
5# 承認とユーザー入力を処理する
6
7> Claude の承認リクエストと確認質問をユーザーに表示し、その決定を SDK に返します。
8
9タスクに取り組んでいる間、Claude はユーザーに確認を取る必要がある場合があります。ファイルを削除する前に許可が必要な場合もあれば、新しいプロジェクト用にどのデータベースを使用するかを尋ねる必要がある場合もあります。アプリケーションはこれらのリクエストをユーザーに表示して、Claude がユーザーの入力で続行できるようにする必要があります。
10
11Claude がユーザー入力をリクエストするのは 2 つの状況です。ツールを使用する**許可が必要な場合**(ファイルの削除やコマンドの実行など)と、**確認質問がある場合**(`AskUserQuestion` ツール経由)です。どちらも `canUseTool` コールバックをトリガーし、応答を返すまで実行を一時停止します。これは Claude が終了して次のメッセージを待つ通常の会話ターンとは異なります。
12
13確認質問については、Claude が質問とオプションを生成します。あなたの役割は、それらをユーザーに提示して、ユーザーの選択を返すことです。このフローに独自の質問を追加することはできません。ユーザーに何か尋ねる必要がある場合は、アプリケーションロジックで別途実行してください。
14
15コールバックは無期限に保留中のままにすることができます。実行はコールバックが返されるまで一時停止したままであり、SDK はクエリ自体がキャンセルされた場合にのみ待機をキャンセルします。ユーザーがプロセスが合理的に実行し続けることができるより長く応答するのに時間がかかる可能性がある場合、TypeScript SDK は [`defer` フック決定](/ja/hooks#defer-a-tool-call-for-later)をサポートしており、プロセスを終了して後で永続化されたセッションから再開できます。このオプションは Python SDK では利用できません。
16
17このガイドでは、各タイプのリクエストを検出し、適切に応答する方法を示します。
18
19## Claude が入力を必要とする場合を検出する
20
21クエリオプションで `canUseTool` コールバックを渡します。Claude がユーザー入力を必要とするたびにコールバックが発火し、ツール名と入力を引数として受け取ります。
22
23<CodeGroup>
24 ```python Python theme={null}
25 async def handle_tool_request(tool_name, input_data, context):
26 # ユーザーにプロンプトを表示して、許可または拒否を返す
27 ...
28
29
30 options = ClaudeAgentOptions(can_use_tool=handle_tool_request)
31 ```
32
33 ```typescript TypeScript theme={null}
34 async function handleToolRequest(toolName, input, options) {
35 // options には { signal: AbortSignal, suggestions?: PermissionUpdate[] } が含まれます
36 // ユーザーにプロンプトを表示して、許可または拒否を返す
37 }
38
39 const options = { canUseTool: handleToolRequest };
40 ```
41</CodeGroup>
42
43コールバックは 2 つのケースで発火します。
44
451. **ツールが承認を必要とする場合**:Claude が [許可ルール](/ja/agent-sdk/permissions)またはモードによって自動承認されていないツールを使用したい場合。`tool_name` でツール(例:`"Bash"`、`"Write"`)を確認します。
462. **Claude が質問をする場合**:Claude が `AskUserQuestion` ツールを呼び出します。`tool_name == "AskUserQuestion"` をチェックして、異なる方法で処理します。`tools` 配列を指定する場合は、これが機能するように `AskUserQuestion` を含めます。詳細は [確認質問を処理する](#handle-clarifying-questions)を参照してください。
47
48<Note>
49 ユーザーにプロンプトを表示せずにツールを自動的に許可または拒否するには、代わりに [フック](/ja/agent-sdk/hooks)を使用します。フックは `canUseTool` の前に実行され、独自のロジックに基づいてリクエストを許可、拒否、または変更できます。また、[`PermissionRequest` フック](/ja/agent-sdk/hooks#available-hooks)を使用して、Claude が承認を待っているときに外部通知(Slack、メール、プッシュ)を送信することもできます。
50</Note>
51
52## ツール承認リクエストを処理する
53
54クエリオプションで `canUseTool` コールバックを渡すと、Claude が自動承認されていないツールを使用したい場合に発火します。コールバックは 3 つの引数を受け取ります。
55
56| 引数 | 説明 |
57| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
58| `toolName` | Claude が使用したいツールの名前(例:`"Bash"`、`"Write"`、`"Edit"`) |
59| `input` | Claude がツールに渡しているパラメーター。内容はツールによって異なります。 |
60| `options`(TS)/ `context`(Python) | 再度プロンプトを表示しないための提案された `PermissionUpdate` エントリを含むオプション `suggestions` とキャンセル信号を含む追加コンテキスト。TypeScript では、`signal` は `AbortSignal` です。Python では、信号フィールドは将来の使用のために予約されています。Python については [`ToolPermissionContext`](/ja/agent-sdk/python#toolpermissioncontext)を参照してください。 |
61
62`input` オブジェクトにはツール固有のパラメーターが含まれます。一般的な例:
63
64| ツール | 入力フィールド |
65| ------- | ------------------------------------- |
66| `Bash` | `command`、`description`、`timeout` |
67| `Write` | `file_path`、`content` |
68| `Edit` | `file_path`、`old_string`、`new_string` |
69| `Read` | `file_path`、`offset`、`limit` |
70
71完全な入力スキーマについては SDK リファレンスを参照してください。[Python](/ja/agent-sdk/python#tool-input%2Foutput-types) | [TypeScript](/ja/agent-sdk/typescript#tool-input-types)。
72
73この情報をユーザーに表示して、アクションを許可するか拒否するかを決定してから、適切な応答を返すことができます。
74
75次の例では、Claude にテストファイルを作成して削除するよう要求します。Claude が各操作を試みるたびに、コールバックはツールリクエストをターミナルに出力し、y/n 承認を求めます。
76
77<CodeGroup>
78 ```python Python theme={null}
79 import asyncio
80
81 from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
82 from claude_agent_sdk.types import (
83 HookMatcher,
84 PermissionResultAllow,
85 PermissionResultDeny,
86 ToolPermissionContext,
87 )
88
89
90 async def can_use_tool(
91 tool_name: str, input_data: dict, context: ToolPermissionContext
92 ) -> PermissionResultAllow | PermissionResultDeny:
93 # ツールリクエストを表示する
94 print(f"\nTool: {tool_name}")
95 if tool_name == "Bash":
96 print(f"Command: {input_data.get('command')}")
97 if input_data.get("description"):
98 print(f"Description: {input_data.get('description')}")
99 else:
100 print(f"Input: {input_data}")
101
102 # ユーザーの承認を取得する
103 response = input("Allow this action? (y/n): ")
104
105 # ユーザーの応答に基づいて許可または拒否を返す
106 if response.lower() == "y":
107 # 許可:ツールは元の(または変更された)入力で実行される
108 return PermissionResultAllow(updated_input=input_data)
109 else:
110 # 拒否:ツールは実行されず、Claude はメッセージを見る
111 return PermissionResultDeny(message="User denied this action")
112
113
114 # 必須の回避策:ダミーフックはストリームを canUseTool 用に開いたままにします
115 async def dummy_hook(input_data, tool_use_id, context):
116 return {"continue_": True}
117
118
119 async def prompt_stream():
120 yield {
121 "type": "user",
122 "message": {
123 "role": "user",
124 "content": "Create a test file in /tmp and then delete it",
125 },
126 }
127
128
129 async def main():
130 async for message in query(
131 prompt=prompt_stream(),
132 options=ClaudeAgentOptions(
133 can_use_tool=can_use_tool,
134 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
135 ),
136 ):
137 if isinstance(message, ResultMessage) and message.subtype == "success":
138 print(message.result)
139
140
141 asyncio.run(main())
142 ```
143
144 ```typescript TypeScript theme={null}
145 import { query } from "@anthropic-ai/claude-agent-sdk";
146 import * as readline from "readline";
147
148 // ターミナルでユーザー入力を求めるヘルパー
149 function prompt(question: string): Promise<string> {
150 const rl = readline.createInterface({
151 input: process.stdin,
152 output: process.stdout
153 });
154 return new Promise((resolve) =>
155 rl.question(question, (answer) => {
156 rl.close();
157 resolve(answer);
158 })
159 );
160 }
161
162 for await (const message of query({
163 prompt: "Create a test file in /tmp and then delete it",
164 options: {
165 canUseTool: async (toolName, input) => {
166 // ツールリクエストを表示する
167 console.log(`\nTool: ${toolName}`);
168 if (toolName === "Bash") {
169 console.log(`Command: ${input.command}`);
170 if (input.description) console.log(`Description: ${input.description}`);
171 } else {
172 console.log(`Input: ${JSON.stringify(input, null, 2)}`);
173 }
174
175 // ユーザーの承認を取得する
176 const response = await prompt("Allow this action? (y/n): ");
177
178 // ユーザーの応答に基づいて許可または拒否を返す
179 if (response.toLowerCase() === "y") {
180 // 許可:ツールは元の(または変更された)入力で実行される
181 return { behavior: "allow", updatedInput: input };
182 } else {
183 // 拒否:ツールは実行されず、Claude はメッセージを見る
184 return { behavior: "deny", message: "User denied this action" };
185 }
186 }
187 }
188 })) {
189 if ("result" in message) console.log(message.result);
190 }
191 ```
192</CodeGroup>
193
194<Note>
195 Python では、`can_use_tool` は [ストリーミングモード](/ja/agent-sdk/streaming-vs-single-mode)と `{"continue_": True}` を返す `PreToolUse` フックが必要で、ストリームを開いたままにします。このフックがないと、許可コールバックが呼び出される前にストリームが閉じます。
196</Note>
197
198この例では y/n フローを使用しており、`y` 以外の入力は拒否として扱われます。実際には、ユーザーがリクエストを変更したり、フィードバックを提供したり、Claude を完全にリダイレクトしたりできるより豊富な UI を構築する可能性があります。すべての応答方法については [ツールリクエストに応答する](#respond-to-tool-requests)を参照してください。
199
200### ツールリクエストに応答する
201
202コールバックは 2 つの応答タイプのいずれかを返します。
203
204| 応答 | Python | TypeScript |
205| ------ | ------------------------------------------ | ------------------------------------- |
206| **許可** | `PermissionResultAllow(updated_input=...)` | `{ behavior: "allow", updatedInput }` |
207| **拒否** | `PermissionResultDeny(message=...)` | `{ behavior: "deny", message }` |
208
209許可する場合、ツール入力(元の、または変更された)を渡します。拒否する場合、理由を説明するメッセージを提供します。Claude はこのメッセージを見て、アプローチを調整する可能性があります。
210
211<CodeGroup>
212 ```python Python theme={null}
213 from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny
214
215 # ツールの実行を許可する
216 return PermissionResultAllow(updated_input=input_data)
217
218 # ツールをブロックする
219 return PermissionResultDeny(message="User rejected this action")
220 ```
221
222 ```typescript TypeScript theme={null}
223 // ツールの実行を許可する
224 return { behavior: "allow", updatedInput: input };
225
226 // ツールをブロックする
227 return { behavior: "deny", message: "User rejected this action" };
228 ```
229</CodeGroup>
230
231許可または拒否を超えて、ツールの入力を変更したり、Claude がアプローチを調整するのに役立つコンテキストを提供したりできます。
232
233* **承認**:ツールを Claude がリクエストしたとおりに実行させる
234* **変更を加えて承認**:実行前に入力を変更する(例:パスをサニタイズ、制約を追加)
235* **拒否**:ツールをブロックして Claude に理由を伝える
236* **代替案を提案**:ブロックするが、ユーザーが望むものに向かって Claude をガイドする
237* **完全にリダイレクト**:[ストリーミング入力](/ja/agent-sdk/streaming-vs-single-mode)を使用して Claude に完全に新しい指示を送信する
238
239<Tabs>
240 <Tab title="承認">
241 ユーザーはアクションをそのまま承認します。コールバックから `input` をそのまま渡し、ツールは Claude がリクエストしたとおりに実行されます。
242
243 <CodeGroup>
244 ```python Python theme={null}
245 async def can_use_tool(tool_name, input_data, context):
246 print(f"Claude wants to use {tool_name}")
247 approved = await ask_user("Allow this action?")
248
249 if approved:
250 return PermissionResultAllow(updated_input=input_data)
251 return PermissionResultDeny(message="User declined")
252 ```
253
254 ```typescript TypeScript theme={null}
255 canUseTool: async (toolName, input) => {
256 console.log(`Claude wants to use ${toolName}`);
257 const approved = await askUser("Allow this action?");
258
259 if (approved) {
260 return { behavior: "allow", updatedInput: input };
261 }
262 return { behavior: "deny", message: "User declined" };
263 };
264 ```
265 </CodeGroup>
266 </Tab>
267
268 <Tab title="変更を加えて承認">
269 ユーザーは承認しますが、最初にリクエストを変更したいと考えています。ツールが実行される前に入力を変更できます。Claude は結果を見ますが、何かを変更したことは伝えられません。パラメーターをサニタイズ、制約を追加、またはアクセスをスコープするのに役立ちます。
270
271 <CodeGroup>
272 ```python Python theme={null}
273 async def can_use_tool(tool_name, input_data, context):
274 if tool_name == "Bash":
275 # ユーザーが承認しましたが、すべてのコマンドをサンドボックスにスコープします
276 sandboxed_input = {**input_data}
277 sandboxed_input["command"] = input_data["command"].replace(
278 "/tmp", "/tmp/sandbox"
279 )
280 return PermissionResultAllow(updated_input=sandboxed_input)
281 return PermissionResultAllow(updated_input=input_data)
282 ```
283
284 ```typescript TypeScript theme={null}
285 canUseTool: async (toolName, input) => {
286 if (toolName === "Bash") {
287 // ユーザーが承認しましたが、すべてのコマンドをサンドボックスにスコープします
288 const sandboxedInput = {
289 ...input,
290 command: input.command.replace("/tmp", "/tmp/sandbox")
291 };
292 return { behavior: "allow", updatedInput: sandboxedInput };
293 }
294 return { behavior: "allow", updatedInput: input };
295 };
296 ```
297 </CodeGroup>
298 </Tab>
299
300 <Tab title="拒否">
301 ユーザーはこのアクションが実行されることを望んでいません。ツールをブロックして、理由を説明するメッセージを提供します。Claude はこのメッセージを見て、別のアプローチを試す可能性があります。
302
303 <CodeGroup>
304 ```python Python theme={null}
305 async def can_use_tool(tool_name, input_data, context):
306 approved = await ask_user(f"Allow {tool_name}?")
307
308 if not approved:
309 return PermissionResultDeny(message="User rejected this action")
310 return PermissionResultAllow(updated_input=input_data)
311 ```
312
313 ```typescript TypeScript theme={null}
314 canUseTool: async (toolName, input) => {
315 const approved = await askUser(`Allow ${toolName}?`);
316
317 if (!approved) {
318 return {
319 behavior: "deny",
320 message: "User rejected this action"
321 };
322 }
323 return { behavior: "allow", updatedInput: input };
324 };
325 ```
326 </CodeGroup>
327 </Tab>
328
329 <Tab title="代替案を提案">
330 ユーザーはこの特定のアクションを望んでいませんが、別のアイデアがあります。ツールをブロックして、メッセージにガイダンスを含めます。Claude はこれを読んで、フィードバックに基づいて進行方法を決定します。
331
332 <CodeGroup>
333 ```python Python theme={null}
334 async def can_use_tool(tool_name, input_data, context):
335 if tool_name == "Bash" and "rm" in input_data.get("command", ""):
336 # ユーザーは削除を望んでいません。代わりにアーカイブに圧縮することを提案します
337 return PermissionResultDeny(
338 message="User doesn't want to delete files. They asked if you could compress them into an archive instead."
339 )
340 return PermissionResultAllow(updated_input=input_data)
341 ```
342
343 ```typescript TypeScript theme={null}
344 canUseTool: async (toolName, input) => {
345 if (toolName === "Bash" && input.command.includes("rm")) {
346 // ユーザーは削除を望んでいません。代わりにアーカイブに圧縮することを提案します
347 return {
348 behavior: "deny",
349 message:
350 "User doesn't want to delete files. They asked if you could compress them into an archive instead."
351 };
352 }
353 return { behavior: "allow", updatedInput: input };
354 };
355 ```
356 </CodeGroup>
357 </Tab>
358
359 <Tab title="完全にリダイレクト">
360 方向の完全な変更(単なるナッジではなく)の場合は、[ストリーミング入力](/ja/agent-sdk/streaming-vs-single-mode)を使用して Claude に新しい指示を直接送信します。これは現在のツールリクエストをバイパスし、Claude に完全に新しい指示に従うように指示します。
361 </Tab>
362</Tabs>
363
364## 確認質問を処理する
365
366Claude が複数の有効なアプローチを持つタスクについてさらに方向性が必要な場合、`AskUserQuestion` ツールを呼び出します。これは `toolName` が `AskUserQuestion` に設定された `canUseTool` コールバックをトリガーします。入力には Claude の質問が複数選択肢として含まれており、これらをユーザーに表示して、ユーザーの選択を返します。
367
368<Tip>
369 確認質問は特に [`plan` モード](/ja/agent-sdk/permissions#plan-mode-plan)で一般的です。Claude はコードベースを探索し、計画を提案する前に質問をします。これにより、プラン モードは Claude が変更を加える前に要件を収集したい対話的なワークフローに最適です。
370</Tip>
371
372次のステップは、確認質問を処理する方法を示しています。
373
374<Steps>
375 <Step title="canUseTool コールバックを渡す">
376 クエリオプションで `canUseTool` コールバックを渡します。デフォルトでは、`AskUserQuestion` が利用可能です。Claude の機能を制限するために `tools` 配列を指定する場合(例:`Read`、`Glob`、`Grep` のみを持つ読み取り専用エージェント)、その配列に `AskUserQuestion` を含めます。そうしないと、Claude は確認質問をすることができません。
377
378 <CodeGroup>
379 ```python Python theme={null}
380 async for message in query(
381 prompt="Analyze this codebase",
382 options=ClaudeAgentOptions(
383 # ツールリストに AskUserQuestion を含める
384 tools=["Read", "Glob", "Grep", "AskUserQuestion"],
385 can_use_tool=can_use_tool,
386 ),
387 ):
388 print(message)
389 ```
390
391 ```typescript TypeScript theme={null}
392 for await (const message of query({
393 prompt: "Analyze this codebase",
394 options: {
395 // ツールリストに AskUserQuestion を含める
396 tools: ["Read", "Glob", "Grep", "AskUserQuestion"],
397 canUseTool: async (toolName, input) => {
398 // ここで確認質問を処理する
399 }
400 }
401 })) {
402 console.log(message);
403 }
404 ```
405 </CodeGroup>
406 </Step>
407
408 <Step title="AskUserQuestion を検出する">
409 コールバックで、`toolName` が `AskUserQuestion` と等しいかどうかをチェックして、他のツールとは異なる方法で処理します。
410
411 <CodeGroup>
412 ```python Python theme={null}
413 async def can_use_tool(tool_name: str, input_data: dict, context):
414 if tool_name == "AskUserQuestion":
415 # ユーザーから回答を収集するための実装
416 return await handle_clarifying_questions(input_data)
417 # 他のツールを通常どおり処理する
418 return await prompt_for_approval(tool_name, input_data)
419 ```
420
421 ```typescript TypeScript theme={null}
422 canUseTool: async (toolName, input) => {
423 if (toolName === "AskUserQuestion") {
424 // ユーザーから回答を収集するための実装
425 return handleClarifyingQuestions(input);
426 }
427 // 他のツールを通常どおり処理する
428 return promptForApproval(toolName, input);
429 };
430 ```
431 </CodeGroup>
432 </Step>
433
434 <Step title="質問入力を解析する">
435 入力には Claude の質問が `questions` 配列に含まれています。各質問には `question`(表示するテキスト)、`options`(選択肢)、`multiSelect`(複数選択が許可されているかどうか)があります。
436
437 ```json theme={null}
438 {
439 "questions": [
440 {
441 "question": "How should I format the output?",
442 "header": "Format",
443 "options": [
444 { "label": "Summary", "description": "Brief overview" },
445 { "label": "Detailed", "description": "Full explanation" }
446 ],
447 "multiSelect": false
448 },
449 {
450 "question": "Which sections should I include?",
451 "header": "Sections",
452 "options": [
453 { "label": "Introduction", "description": "Opening context" },
454 { "label": "Conclusion", "description": "Final summary" }
455 ],
456 "multiSelect": true
457 }
458 ]
459 }
460 ```
461
462 完全なフィールド説明については [質問形式](#question-format)を参照してください。
463 </Step>
464
465 <Step title="ユーザーから回答を収集する">
466 質問をユーザーに提示して、ユーザーの選択を収集します。これをどのように行うかは、アプリケーションによって異なります。ターミナルプロンプト、Web フォーム、モバイルダイアログなど。
467 </Step>
468
469 <Step title="Claude に回答を返す">
470 `answers` オブジェクトをレコードとして構築します。各キーは `question` テキストで、各値は選択されたオプションの `label` です。
471
472 | 質問オブジェクトから | 使用方法 |
473 | ------------------------------------------------------- | ---- |
474 | `question` フィールド(例:`"How should I format the output?"`) | キー |
475 | 選択されたオプションの `label` フィールド(例:`"Summary"`) | 値 |
476
477 複数選択質問の場合、ラベルの配列を渡すか、`", "` で結合します。[自由テキスト入力をサポート](#support-free-text-input)する場合は、ユーザーのカスタムテキストを値として使用します。
478
479 <CodeGroup>
480 ```python Python theme={null}
481 return PermissionResultAllow(
482 updated_input={
483 "questions": input_data.get("questions", []),
484 "answers": {
485 "How should I format the output?": "Summary",
486 "Which sections should I include?": ["Introduction", "Conclusion"],
487 },
488 }
489 )
490 ```
491
492 ```typescript TypeScript theme={null}
493 return {
494 behavior: "allow",
495 updatedInput: {
496 questions: input.questions,
497 answers: {
498 "How should I format the output?": "Summary",
499 "Which sections should I include?": "Introduction, Conclusion"
500 }
501 }
502 };
503 ```
504 </CodeGroup>
505 </Step>
506</Steps>
507
508### 質問形式
509
510入力には Claude が生成した質問が `questions` 配列に含まれています。各質問には以下のフィールドがあります。
511
512| フィールド | 説明 |
513| ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
514| `question` | 表示する完全な質問テキスト |
515| `header` | 質問の短いラベル(最大 12 文字) |
516| `options` | 2~4 個の選択肢の配列。各選択肢には `label` と `description` があります。TypeScript:オプションで `preview`(下記の [オプションプレビュー](#option-previews-type-script)を参照) |
517| `multiSelect` | `true` の場合、ユーザーは複数のオプションを選択できます |
518
519コールバックが受け取る構造:
520
521```json theme={null}
522{
523 "questions": [
524 {
525 "question": "How should I format the output?",
526 "header": "Format",
527 "options": [
528 { "label": "Summary", "description": "Brief overview of key points" },
529 { "label": "Detailed", "description": "Full explanation with examples" }
530 ],
531 "multiSelect": false
532 }
533 ]
534}
535```
536
537#### オプションプレビュー(TypeScript)
538
539`toolConfig.askUserQuestion.previewFormat` は各オプションに `preview` フィールドを追加して、アプリがラベルと一緒に視覚的なモックアップを表示できるようにします。この設定がない場合、Claude はプレビューを生成せず、フィールドは存在しません。
540
541| `previewFormat` | `preview` に含まれるもの |
542| :-------------- | :---------------------------------------------------------------------------------- |
543| 未設定(デフォルト) | フィールドは存在しません。Claude はプレビューを生成しません。 |
544| `"markdown"` | ASCII アートとフェンスコードブロック |
545| `"html"` | スタイル付き `<div>` フラグメント(SDK はコールバックが実行される前に `<script>`、`<style>`、`<!DOCTYPE>` を拒否します) |
546
547形式はセッション内のすべての質問に適用されます。Claude は視覚的な比較が役立つオプション(レイアウト選択、配色)に `preview` を含め、役立たないオプション(はい/いいえの確認、テキストのみの選択)では省略します。レンダリング前に `undefined` をチェックしてください。
548
549```typescript theme={null}
550import { query } from "@anthropic-ai/claude-agent-sdk";
551
552for await (const message of query({
553 prompt: "Help me choose a card layout",
554 options: {
555 toolConfig: {
556 askUserQuestion: { previewFormat: "html" }
557 },
558 canUseTool: async (toolName, input) => {
559 // input.questions[].options[].preview は HTML 文字列または undefined です
560 return { behavior: "allow", updatedInput: input };
561 }
562 }
563})) {
564 // ...
565}
566```
567
568HTML プレビュー付きのオプション:
569
570```json theme={null}
571{
572 "label": "Compact",
573 "description": "Title and metric value only",
574 "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>"
575}
576```
577
578### 応答形式
579
580各質問の `question` フィールドを選択されたオプションの `label` にマップする `answers` オブジェクトを返します。
581
582| フィールド | 説明 |
583| ----------- | ------------------------------ |
584| `questions` | 元の質問配列をパススルーする(ツール処理に必須) |
585| `answers` | キーが質問テキストで、値が選択されたラベルであるオブジェクト |
586
587複数選択質問の場合、ラベルの配列を渡すか、`", "` で結合します。自由テキスト入力の場合は、ユーザーのカスタムテキストを直接使用します。
588
589```json theme={null}
590{
591 "questions": [
592 // ...
593 ],
594 "answers": {
595 "How should I format the output?": "Summary",
596 "Which sections should I include?": ["Introduction", "Conclusion"]
597 }
598}
599```
600
601#### 自由テキスト入力をサポートする
602
603Claude の定義済みオプションがユーザーが望むものをカバーしていない場合があります。ユーザーが独自の回答を入力できるようにするには:
604
605* Claude のオプションの後に追加の「その他」選択肢を表示して、テキスト入力を受け入れます
606* ユーザーのカスタムテキストを回答値として使用します(「その他」という単語ではなく)
607
608完全な実装については、下記の [完全な例](#complete-example)を参照してください。
609
610### 完全な例
611
612Claude は、タスクを進めるためにユーザー入力が必要な場合に確認質問をします。たとえば、モバイルアプリのテックスタックを決定するのに役立つよう求められた場合、Claude はクロスプラットフォーム対応 vs ネイティブ、バックエンド設定、またはターゲットプラットフォームについて質問する可能性があります。これらの質問は、Claude がユーザーの設定に合致する決定を下すのに役立ちます。推測ではなく。
613
614この例は、ターミナルアプリケーションでこれらの質問を処理します。各ステップで何が起こるかは以下の通りです。
615
6161. **リクエストをルーティングする**:`canUseTool` コールバックはツール名が `"AskUserQuestion"` であるかどうかをチェックし、専用ハンドラーにルーティングします
6172. **質問を表示する**:ハンドラーは `questions` 配列をループして、各質問を番号付きオプションで出力します
6183. **入力を収集する**:ユーザーはオプションを選択する番号を入力するか、自由テキストを直接入力できます(例:「jquery」、「i don't know」)
6194. **回答をマップする**:コードは入力が数値(オプションのラベルを使用)か自由テキスト(テキストを直接使用)かをチェックします
6205. **Claude に返す**:応答には元の `questions` 配列と `answers` マッピングの両方が含まれます
621
622<CodeGroup>
623 ```python Python theme={null}
624 import asyncio
625
626 from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
627 from claude_agent_sdk.types import HookMatcher, PermissionResultAllow
628
629
630 def parse_response(response: str, options: list) -> str:
631 """ユーザー入力をオプション番号または自由テキストとして解析します。"""
632 try:
633 indices = [int(s.strip()) - 1 for s in response.split(",")]
634 labels = [options[i]["label"] for i in indices if 0 <= i < len(options)]
635 return ", ".join(labels) if labels else response
636 except ValueError:
637 return response
638
639
640 async def handle_ask_user_question(input_data: dict) -> PermissionResultAllow:
641 """Claude の質問を表示してユーザーの回答を収集します。"""
642 answers = {}
643
644 for q in input_data.get("questions", []):
645 print(f"\n{q['header']}: {q['question']}")
646
647 options = q["options"]
648 for i, opt in enumerate(options):
649 print(f" {i + 1}. {opt['label']} - {opt['description']}")
650 if q.get("multiSelect"):
651 print(" (Enter numbers separated by commas, or type your own answer)")
652 else:
653 print(" (Enter a number, or type your own answer)")
654
655 response = input("Your choice: ").strip()
656 answers[q["question"]] = parse_response(response, options)
657
658 return PermissionResultAllow(
659 updated_input={
660 "questions": input_data.get("questions", []),
661 "answers": answers,
662 }
663 )
664
665
666 async def can_use_tool(
667 tool_name: str, input_data: dict, context
668 ) -> PermissionResultAllow:
669 # AskUserQuestion を質問ハンドラーにルーティングする
670 if tool_name == "AskUserQuestion":
671 return await handle_ask_user_question(input_data)
672 # この例では他のツールを自動承認する
673 return PermissionResultAllow(updated_input=input_data)
674
675
676 async def prompt_stream():
677 yield {
678 "type": "user",
679 "message": {
680 "role": "user",
681 "content": "Help me decide on the tech stack for a new mobile app",
682 },
683 }
684
685
686 # 必須の回避策:ダミーフックはストリームを canUseTool 用に開いたままにします
687 async def dummy_hook(input_data, tool_use_id, context):
688 return {"continue_": True}
689
690
691 async def main():
692 async for message in query(
693 prompt=prompt_stream(),
694 options=ClaudeAgentOptions(
695 can_use_tool=can_use_tool,
696 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
697 ),
698 ):
699 if isinstance(message, ResultMessage) and message.subtype == "success":
700 print(message.result)
701
702
703 asyncio.run(main())
704 ```
705
706 ```typescript TypeScript theme={null}
707 import { query } from "@anthropic-ai/claude-agent-sdk";
708 import * as readline from "readline/promises";
709
710 // ターミナルでユーザー入力を求めるヘルパー
711 async function prompt(question: string): Promise<string> {
712 const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
713 const answer = await rl.question(question);
714 rl.close();
715 return answer;
716 }
717
718 // ユーザー入力をオプション番号または自由テキストとして解析する
719 function parseResponse(response: string, options: any[]): string {
720 const indices = response.split(",").map((s) => parseInt(s.trim()) - 1);
721 const labels = indices
722 .filter((i) => !isNaN(i) && i >= 0 && i < options.length)
723 .map((i) => options[i].label);
724 return labels.length > 0 ? labels.join(", ") : response;
725 }
726
727 // Claude の質問を表示してユーザーの回答を収集する
728 async function handleAskUserQuestion(input: any) {
729 const answers: Record<string, string> = {};
730
731 for (const q of input.questions) {
732 console.log(`\n${q.header}: ${q.question}`);
733
734 const options = q.options;
735 options.forEach((opt: any, i: number) => {
736 console.log(` ${i + 1}. ${opt.label} - ${opt.description}`);
737 });
738 if (q.multiSelect) {
739 console.log(" (Enter numbers separated by commas, or type your own answer)");
740 } else {
741 console.log(" (Enter a number, or type your own answer)");
742 }
743
744 const response = (await prompt("Your choice: ")).trim();
745 answers[q.question] = parseResponse(response, options);
746 }
747
748 // Claude に回答を返す(ツール処理に元の質問を含める必須)
749 return {
750 behavior: "allow",
751 updatedInput: { questions: input.questions, answers }
752 };
753 }
754
755 async function main() {
756 for await (const message of query({
757 prompt: "Help me decide on the tech stack for a new mobile app",
758 options: {
759 canUseTool: async (toolName, input) => {
760 // AskUserQuestion を質問ハンドラーにルーティングする
761 if (toolName === "AskUserQuestion") {
762 return handleAskUserQuestion(input);
763 }
764 // この例では他のツールを自動承認する
765 return { behavior: "allow", updatedInput: input };
766 }
767 }
768 })) {
769 if ("result" in message) console.log(message.result);
770 }
771 }
772
773 main();
774 ```
775</CodeGroup>
776
777## 制限事項
778
779* **サブエージェント**:`AskUserQuestion` は現在、Agent ツール経由で生成されたサブエージェントでは利用できません
780* **質問の制限**:各 `AskUserQuestion` 呼び出しは 1~4 個の質問と 2~4 個のオプションをサポートします
781
782## ユーザー入力を取得する他の方法
783
784`canUseTool` コールバックと `AskUserQuestion` ツールはほとんどの承認と明確化のシナリオをカバーしていますが、SDK はユーザーから入力を取得する他の方法を提供しています。
785
786### ストリーミング入力
787
788以下が必要な場合は [ストリーミング入力](/ja/agent-sdk/streaming-vs-single-mode)を使用します。
789
790* **エージェントをタスク途中で中断する**:Claude が作業中にキャンセル信号を送信するか、方向を変更する
791* **追加コンテキストを提供する**:Claude が尋ねるのを待たずに、Claude が必要とする情報を追加する
792* **チャットインターフェースを構築する**:長時間実行される操作中にユーザーがエージェントと対話できるようにする
793
794ストリーミング入力は、承認チェックポイントだけでなく、実行全体を通じてユーザーがエージェントと対話する会話型 UI に最適です。
795
796### カスタムツール
797
798以下が必要な場合は [カスタムツール](/ja/agent-sdk/custom-tools)を使用します。
799
800* **構造化入力を収集する**:`AskUserQuestion` の複数選択形式を超えたフォーム、ウィザード、または複数ステップのワークフローを構築する
801* **外部承認システムを統合する**:既存のチケット、ワークフロー、または承認プラットフォームに接続する
802* **ドメイン固有の対話を実装する**:コードレビューインターフェースやデプロイメントチェックリストなど、アプリケーションのニーズに合わせたツールを作成する
803
804カスタムツールは対話を完全に制御できますが、組み込みの `canUseTool` コールバックを使用するよりも実装作業が必要です。
805
806## 関連リソース
807
808* [権限を設定する](/ja/agent-sdk/permissions):権限モードとルールを設定する
809* [フックで実行を制御する](/ja/agent-sdk/hooks):エージェントライフサイクルの重要なポイントでカスタムコードを実行する
810* [TypeScript SDK リファレンス](/ja/agent-sdk/typescript#canusetool):完全な canUseTool API ドキュメント