Agent SDKのパーミッション制御 — モードとallow/denyルール
Agent SDKは6段階の評価フロー(hooks→deny→ask→モード→allow→canUseTool)でツール許可を判定します。モード一覧と設定パターンを解説します。
Agent SDKの権限判定は何が優先されるか
Claudeがツールを呼び出そうとするたびに、Agent SDKは6つのステップを順番にチェックします。上から順に見て、どこかで許可か拒否が決まればそこで終わりです。
ツール呼び出しの6段階の評価
- 1
hooks
最初に動きます。ここで拒否すれば、以降のステップに進まずブロックされます。
allowを返しても下のdenyルール・askルールは飛ばされず、通常どおり評価されます。 - 2
denyルール
disallowed_toolsとsettings.jsonのdenyが対象で、一致すればbypassPermissionsでも遮断されます。Bashのようにツール名だけのdenyは、評価の前にツール定義ごと取り除かれます。この段階で実際に照合されるのはBash(rm *)のようなスコープ付きルールだけです。 - 3
askルール
settings.jsonのaskに一致すると、bypassPermissionsでもcanUseToolコールバックへ回ります。AskUserQuestionと、MCPサーバー側が_meta["anthropic/requiresUserInteraction"]を付けたツールも、allowルールに一致していても同じです。このMCPアノテーションはClaude Code v2.1.199以降が前提です。組織がaskに設定したclaude.aiコネクタのツールもここで抜けます。dontAskではコールバックを呼ばず拒否します。 - 4
権限モード
ここでモードが効きます。
planは、ファイル編集系とシェル書き込み系をallowの有無にかかわらずcanUseToolへ送ります。touchやrmのようなシェル書き込みがコールバックへ届くのは、Claude Code v2.1.212以降です。 - 5
allowルール
allowed_toolsとsettings.jsonのallowに一致すれば承認されます。ファイルの読み取りや読み取り専用のBashコマンドのように、ツール自身が承認不要と判断する呼び出しもこの段階で通ります。 - 6
canUseToolコールバック
どこでも決まらなかった呼び出しだけが届きます。
dontAskではこのステップ自体が飛ばされ、拒否になります。
TypeScript SDKでpermissionPrompts: 'none'を指定すると、最後のステップでもコールバックは呼ばれません。PermissionRequestフックが判断し、決めなければ拒否です。このオプションはClaude Code v2.1.259以降が前提です。拒否をUIに出すイベントの出方はAgent SDKのpermission_deniedイベントで拒否をUIに出す方法にあります。
6つの権限モードは何を自動承認するか
| モード | 説明 | ツールの挙動 |
|---|---|---|
| default | 説明標準の権限動作 | ツールの挙動自動承認なし。未解決のツールはcanUseToolコールバックへ |
| acceptEdits | 説明ファイル編集を自動承認 | ツールの挙動Edit/Writeとmkdir・rm・mv等のファイルシステム操作を自動承認 |
| dontAsk | 説明確認せず拒否 | ツールの挙動allowed_toolsやルールで事前承認されていない限りすべて拒否。canUseToolは呼ばれない |
| bypassPermissions | 説明権限チェックを回避 | ツールの挙動一部の例外を除きプロンプトなしで実行。取り扱い注意 |
| plan | 説明計画モード | ツールの挙動Claudeは調査・計画のみ行い、ソースファイルの編集は自動承認されない |
| auto | 説明モデルによる分類 | ツールの挙動分類器が権限プロンプトを承認・拒否する |
acceptEditsが自動承認するのは作業ディレクトリまたはadditionalDirectories配下に限られ、それ以外のパスや保護対象パスへの書き込みは確認を求められます。bypassPermissionsはallowed_toolsの内容にかかわらず全ツールを承認します。ただし、どのモードでも自動承認されない操作が残ります。askルールに一致したツール、ユーザー操作が必要なツール、rm・rmdirによるクリティカルパス(ファイルシステムのルート、トップレベルディレクトリ、ホームディレクトリ、作業ディレクトリとその親など)の削除などです。
クリティカルパスの削除は、allowルールにもPreToolUseフックのallowにも承認されません。その先の扱いはモード次第です。Agent SDKのセッションでautoモードなら、コールバックを呼ばずに既定で拒否されます。確認を求めるdefault・acceptEdits・bypassPermissionsと違い、確認も分類器も通りません(dontAskも拒否します)。
autoモードの分類器は、ルール側で承認・拒否が確定しなかった呼び出しを別の分類器モデルに判定させる仕組みです。リスクの高い操作はブロックされ、低リスクと判断された呼び出しは確認なしで進みます。
permissionModeを省略すると何が起きるか
permissionModeを渡さなかったとき、開始モードは固定のdefaultではありません。設定ファイルのpermissions.defaultModeがあればそれに従い、無ければ組み込みの既定になります。この既定は、機能フラグを取得するセッションではdefault、それ以外ではClaude Code v2.1.285以降でautoです。.claude/settings.jsonや.claude/settings.local.jsonにdefaultMode: "auto"を書いても効きません。
TypeScript Agent SDK v0.3.286より前は、省略はdefaultを渡すのと同じでした。それ以降は、autoで始まったセッションがBashのような広いallowルールを捨てます。defaultや広いallowを前提にしたアプリケーションは、permissionMode: "default"を明示するほうが挙動が固定されます。
CLIの側では、defaultは「Manual」という表示名で出てきます。claude --helpの--permission-modeの選択肢にはdefaultが無く、manualが並びます(v2.1.287)。
claude --help | grep -A3 -e "--permission-mode" -e "--permission-prompts"v2.1.287での出力(抜粋)です。
--permission-mode <mode> Permission mode to use for the session
(choices: "acceptEdits", "auto",
"bypassPermissions", "manual",
"dontAsk", "plan")
--permission-prompts <target> Who answers permission prompts with
--print: "host" (the SDK host or
--permission-prompt-tool) or "none"
(nobody: anything that would prompt is
denied automatically; the permission
mode still decides everything else)manualはdefaultの別名です。SDKやフックの設定値としてはdefaultのままで、manualの表示と別名はClaude Code v2.1.200以降のものです。--permission-prompts noneはSDKのpermissionPrompts: 'none'に当たるCLI側の指定で、-p実行では「確認が必要な呼び出しは自動的に拒否」になります。
allow/denyルールの書き方で挙動はどう変わるか
allowed_toolsとdisallowed_tools(TypeScriptではallowedTools/disallowedTools)は、この評価フローのallow・denyルール一覧に項目を追加する役割です。書き方によって粒度が変わります。
| 書き方 | 効果 |
|---|---|
allowed_tools=["Read", "Grep"] | 効果ReadとGrepを自動承認。ここに無いツールは存在はするが権限モードへ回る |
disallowed_tools=["Bash"] | 効果Bashのツール定義自体を取り除く。Claudeはそのツールを認識すらしない |
disallowed_tools=["Bash(rm *)"] | 効果Bash自体は使えるが、rm *と書かれた呼び出しはbypassPermissionsでも拒否。/bin/rmのような別の書き方はモードへ回る |
disallowed_tools=["*"] | 効果全ツール定義を除去。"mcp__*"のようなワイルドカードもdenyルールでは有効 |
Bash(rm *)が防ぐのは、その書き方のコマンドだけです。パスを変えた/bin/rmは別の呼び出しとして扱われるため、rmを確実に止めたいならBashの取り除きやフックを併用します。
allowルールでツール名ワイルドカードが効くのは、mcp__<サーバー名>__というリテラルなプレフィックスの後だけです。mcp__puppeteer__*はpuppeteerサーバーの全ツールに一致します。サーバー名部分にワイルドカードを含むallowed_tools=["*"]やallowed_tools=["mcp__*"]は起動時警告とともに無視され、何も自動承認しません。
ReadとEditのスコープ付きルールはパスパターンを取ります。Edit(path)はWriteやNotebookEditを含む書き込み系の組み込みツール全般に適用されます。逆にWrite(path)というルールは、ファイル権限チェックでは一致しません。
パスの指定には、先頭スラッシュの数で意味が変わる落とし穴があります。Edit(//secrets/**)は実ファイルシステムの/secrets以下を止めます。Edit(/secrets/**)はルールの取得元が起点で、allowed_tools/disallowed_tools経由ならセッションの作業ディレクトリ基準になり、ディスク上の/secretsは止まりません。
.claude/settings.jsonに書いたallow・deny・askのルールも使えます。読み込まれるのはprojectの設定ソースが有効なときで、query()の既定オプションでは有効です。settingSourcesを明示する場合は"project"を含めないと、これらのルールは効きません。
ツールの範囲を固定したい無人稼働のエージェントには、allowedToolsとpermissionMode: "dontAsk"を組み合わせる構成が向きます。
const options = {
allowedTools: ["Read", "Glob", "Grep"],
permissionMode: "dontAsk",
};リストにあるツールは承認され、確認が必要になる呼び出しは確認を挟まず拒否されます。読み取り専用のBashコマンドや作業ディレクトリ内のファイル読み取りのように、defaultでも確認が要らない呼び出しは、リストに無くても動く点に注意が要ります。ツールをClaudeの手の届かない所へ置くなら、素の名前をdisallowedToolsに入れます。
同じallowed_tools=["Read"]でも、組み合わせるモードで意味が正反対になります。
allowed_toolsを足す相手で変わる意味
bypassPermissionsと併用
Read以外のBashやWriteも、権限モードの段階でbypassPermissionsがまとめて承認します。止めたいツールはdisallowed_toolsに入れます。
dontAskと併用
リストにあるツールだけが通り、他は確認なしで拒否されます。無人稼働の絞り込みに使えるのはこちらです。
サブエージェントは親の権限モードをどう引き継ぐか
サブエージェントは既定で、親セッションの権限モードで動きます。AgentDefinitionのpermissionModeで個別に上書きできるのは、親セッションがdefault・dontAsk・planのいずれかのときに限られます。親がbypassPermissions・acceptEdits・autoで動いている場合、そのモードがセッション内の全サブエージェントに及び、サブエージェント側の指定は効きません。
さらに、サブエージェント側に"bypassPermissions"を書いても、Claude Codeは適用しません。サブエージェントがbypassPermissionsで動くのは、親セッション自身がそのモードのときだけです。この例外はClaude Code v2.1.267以降のものです。サブエージェントの定義自体の設計はAgent SDKのサブエージェント定義にまとめています。
この継承には実務上の注意点があります。サブエージェントは親と異なるシステムプロンプトを持ち、制約の少ない振る舞いをすることがあります。親がbypassPermissionsなら、確認なしでシステム全体に触れる権限が、そのままサブエージェントへ渡ります。管理設定のpermissions.disableBypassPermissionsModeを"disable"にすれば、組織としてこのモード自体を塞げます。
もう一つ見落としやすいのが、canUseToolコールバックが呼ばれない自動承認のケースです。permissionMode: "bypassPermissions"と、"Read"のようにツール名だけを書いたallowedToolsの項目は、コールバックの手前で承認してしまいます。TypeScript SDKはこの構成を検知すると、CLAUDE_SDK_CAN_USE_TOOL_SHADOWEDというコードのNode.jsプロセス警告を1回だけ発行します。
Bash(ls *)のようなスコープ付きの項目やacceptEditsは対象外です。設定ファイルから来るallowルールも、この検査には見えません。警告が出ないからコールバックが必ず呼ばれる、とは言えない点に注意が必要です。
hooksとcanUseToolコールバックはどう役割分担するか
Claude Codeの権限判定では、hooksとコールバックが評価フローの両端を受け持ちます。
両端の2つは何を決めるか
hooks
ルールやモードの設定にかかわらず、呼び出しを拒否したり、入力を書き換えたりできます。
canUseTool
ユーザーへの確認が必要な呼び出しを受け取ります。前段で自動承認された呼び出しは、コールバックに現れません。
実行時にユーザーへ都度確認を取りたいだけならcanUseToolで足ります。モードやルールの選び間違いに備えるなら、全呼び出しの手前で動くPreToolUseフックを併用すると、選び間違いに強い構成です。止めたいツールが決まっているならdenyルールで足り、フックが担うのは呼び出しの中身を見て止める判断です。
まとめ — モードとルールは別レイヤーとして設計する
モードはセッション全体の既定を決め、ルールは個々のツールの例外を作ります。自動化の範囲はモードとallowで決め、「何があっても止める」条件はdenyルールとhooksに置くと、設定の選び間違いが事故になりにくくなります。
Claude Code CLIでの権限モデルの変遷はClaude Code権限モデルの変遷、settings.jsonでのルール記法はClaude Code settings.json完全ガイドにあります。
関連する記事
Agent SDK をもっと見る →Claude Agent SDK入門 — Python / TypeScriptで最小エージェントを組む
Agent Skillsの効果をSkillsBenchが実測 — 最適は3モジュール以下
Claude Agent SDKがLinuxでmusl版バイナリを優先するバグの原因と対処
MCPのツール定義はなぜコンテキストを圧迫するのか — Tool Searchが必要になった理由
Claude Codeサブエージェントのモデル配分設計 — 役割ごとにOpus/Sonnet/Haikuを割り当てる
Claude Managed Agentsの使い分け — 自作Agent SDKとClaude Codeとの比較