概要
Claude がツール使用許可や作業方針の確認を必要とする場合に canUseTool コールバックを通じてユーザーとやり取りする機能。
仕様
canUseTool コールバックの発火条件
| 状況 | 発火するか |
|---|---|
| allow ルール・パーミッションモードで自動承認済みのツール | 発火しない |
| パーミッションフローで未解決のツール呼び出し | 発火する |
AskUserQuestion ツール呼び出し | 発火する(toolName == "AskUserQuestion" で識別) |
dontAsk モードのとき | 発火せず拒否される |
コールバックは戻り値を返すまで実行を一時停止する。SDKはクエリ自体がキャンセルされるまで待ち続ける。プロセスを終了して後で再開したい場合は defer フックデシジョンを返す。
コールバックの引数
| 引数 | Python | TypeScript | 説明 |
|---|---|---|---|
| ツール名 | tool_name: str | toolName: string | ツール名(例: "Bash", "Write", "AskUserQuestion") |
| 入力データ | input_data: dict | input: object | ツールへのパラメータ |
| コンテキスト | context: ToolPermissionContext | options: { signal, suggestions? } | キャンセルシグナルと提案パーミッション更新 |
主要ツールの入力フィールド
| ツール | 入力フィールド |
|---|---|
Bash | command, description, timeout |
Write | file_path, content |
Edit | file_path, old_string, new_string |
Read | file_path, offset, limit |
レスポンス形式
| 種別 | Python | TypeScript |
|---|---|---|
| 許可 | PermissionResultAllow(updated_input=input_data) | { behavior: "allow", updatedInput: input } |
| 拒否 | PermissionResultDeny(message="理由") | { behavior: "deny", message: "理由" } |
- 許可時:
updated_input/updatedInputを省略すると Claude Code v2.1.207 以前では拒否扱いになるため必ず渡す - 拒否時:
messageは Claude に渡され、次の行動の判断に使われる
パーミッション更新の保持
updatedPermissions (TypeScript) / updated_permissions (Python) に context.suggestions / options.suggestions の要素を返すと、次回以降の同パターン呼び出しをプロンプトなしで承認するルールを保存できる。
destination == "localSettings"の提案を返すと.claude/settings.local.jsonにルールを書き込む- Python では
claude-agent-sdk0.1.80 以降が必要
AskUserQuestion ツール
Claude が複数の選択肢から方針を確認するときに使うツール。canUseTool コールバックが toolName == "AskUserQuestion" で発火する。
tools 配列でツールを制限している場合(例: 読み取り専用エージェント)、AskUserQuestion を明示的に含める必要がある。
入力スキーマ
{ "questions": [ { "question": "質問テキスト(answers のキーに使う)", "header": "短いラベル(最大12文字)", "options": [ { "label": "選択肢ラベル", "description": "説明" } ], "multiSelect": false } ]}- 各
AskUserQuestion呼び出しで 1〜4 問まで - 各問に 2〜4 つの選択肢
multiSelect: trueのとき複数選択可
レスポンス形式
{ "questions": ["(受け取った配列をそのまま)"], "answers": { "質問テキスト": "選択されたラベル", "複数選択の質問": ["ラベルA", "ラベルB"] }}オプションフィールド:
response: ユーザーが構造化質問に回答せず自由記述した場合のみ設定。設定すると Claude は per-question の回答リストではなく"The user responded: …"として受け取る
questions をそのまま返すことが必須(ツール処理で必要)。
オプションプレビュー(TypeScript のみ)
toolConfig.askUserQuestion.previewFormat を設定すると、各選択肢に preview フィールドが追加される:
previewFormat | preview の内容 |
|---|---|
| 未設定(デフォルト) | フィールドなし(Claude はプレビューを生成しない) |
"markdown" | ASCII art・フェンスコードブロック |
"html" | スタイル付き <div> フラグメント(SDK が <script>・<style>・<!DOCTYPE> を事前除去) |
フォーマットはセッション全体に適用される。視覚的な比較が有用な選択肢にのみ preview が付き、有用でない選択肢には付かない。undefined チェックが必要。
Python における ストリーミングモード要件
Python の can_use_tool はストリーミングモードが必要。query(prompt=generator) や ClaudeSDKClient.connect(prompt=async_iterable) でプロンプトストリームが終わると SDK が入力ストリームを閉じ、コールバックが呼ばれる前にプロセスが終了する。
PreToolUse フックを登録してストリームを開いた状態に保つことで回避できる:
async def dummy_hook(input_data, tool_use_id, context): return {"continue_": True}
options = ClaudeAgentOptions( can_use_tool=can_use_tool, hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},)ClaudeSDKClient.query() でメッセージを送信する場合はこの問題は発生しない。
制約・注意点
AskUserQuestionは Agent ツール経由で起動されたサブエージェント内では現在使用不可- 全ツール呼び出しに対してチェックを行いたい場合は
PreToolUseフックを使う(canUseToolは自動承認済みのツールには呼ばれない) - パーミッション評価フローで承認されなかった、クリティカルパスへの
rm/rmdir等の特定の操作 はパーミッションモードやルールに関わらずcanUseToolに到達する場合がある
関連
facts/claude-code/agent-sdk-permissions.mdfacts/claude-code/agent-sdk-python.mdfacts/claude-code/agent-sdk-typescript.md