Claude CodeのallowedTools/disallowedToolsで起動時に権限を絞る
起動時のallowedTools/disallowedToolsは初回プロンプトを省略するだけで、使えるツールの集合を絞るのは--toolsの役割です。CIでの使い分けと落とし穴をまとめます。
Claude Codeの起動時に--allowedToolsと--disallowedToolsを渡すと、許可プロンプトを介さずにツールの可否を一括で指定できます。ハイフン区切りの--allowed-tools/--disallowed-toolsでも同じです。CIパイプラインやスクリプトからの非対話実行で、毎回のyes/noを止めたいときに使うフラグです。ただし、これらが決めるのは「呼び出しの可否」で、「利用できるツールの集合」ではありません。両者は混同しやすく、--toolsフラグとの役割分担を押さえると見分けられます。
allowedTools/disallowedToolsでできること
--allowedToolsはClaude Codeが自分でツールを呼び出す際に、プロンプトなしで実行してよい対象を宣言するフラグです。--disallowedToolsはその逆で、呼び出しを拒否する対象を宣言します。どちらも値はダブルクォートで囲んだ文字列を複数個、スペース区切りで渡します。
claude --allowedTools "Bash(git log *)" "Bash(git diff *)" "Read"この例ではgit logとgit diff系のBashコマンドと、ファイル読み取り(Read)がプロンプトなしで実行されます。ルールの書式はTool(全呼び出しにマッチ)かTool(specifier)(特定の呼び出しにマッチ)です。構文はsettings.jsonのpermissions.allow/permissions.denyと共通です。
allowedToolsは「許可」であって「制限」ではない
--allowedToolsに書いたツールだけが使える、と誤解しがちですが違います。公式ドキュメントは--allowedToolsの説明で「利用できるツールを絞りたいなら--toolsを使うこと」と明記しています。--allowedToolsはあくまでプロンプトをスキップするための許可リストで、そこに書かれていないツールも、通常の権限確認を経れば呼び出されます。逆に「Claudeに見えるツールの集合」そのものを縮小したいなら--toolsが必要です。
3つのフラグの役割
--allowedTools
プロンプトを省略してよい呼び出しだけを決めます。書いていないツールは通常どおりプロンプトされます。
--disallowedTools
プロンプトなしで拒否する呼び出しを決めます。裸のツール名ならツールごと消え、スコープ付きなら該当呼び出しだけが拒否されます。
--tools
Claudeに見せる組み込みツールの集合を決めます。挙げなかった組み込みツールは呼べず、MCPツールには効きません。
手元のClaude Code v2.1.287でclaude --helpを見ると、説明文の時点で役割が分かれています。--allowedToolsは「許可するツール名のリスト」、--toolsは「組み込みツールの中から利用可能なものを指定する」です。--tools ""で全ツールを無効化、--tools "default"で既定の集合(macOS・Linux・WSLではGlobとGrepを除く)を有効にできるのも--toolsだけです。
--allowedTools, --allowed-tools <tools...>
Comma or space-separated list of tool names to allow (e.g. "Bash(git *)
Edit")
--disallowedTools, --disallowed-tools <tools...>
Comma or space-separated list of tool names to deny (e.g. "Bash(git *)
Edit")
--tools <tools...> Specify the list of available tools from
the built-in set. Use "" to disable all
tools, "default" to use all tools, or
specify tool names (e.g.
"Bash,Edit,Read").ヘルプの表記ではカンマ区切りでもスペース区切りでも渡せます。--toolsで絞る場合もMCPツールは対象外である点が、実運用でつまずきやすいポイントです。MCP経由のツールをまとめて塞ぎたいときは、--toolsではなく--disallowedTools "mcp__*"を使います。
disallowedToolsの2つの効き方
--disallowedToolsは書き方によって効果が変わります。
裸のツール名とスコープ付きルール
裸のツール名
"Edit"を渡すと、そのツールはClaudeのコンテキストから取り除かれ、Claude自身が存在を認識しなくなります。"*"はすべてのツールを、"mcp__*"はすべてのMCPツールを同じ扱いで除去します。
スコープ付きルール
"Bash(rm *)"は、Bashツール自体は残したまま、パターンに一致する呼び出しだけを拒否します。ClaudeはBashが使えることを認識したうえで、rmから始まるコマンドだけ実行できません。
この違いは評価順序とも関わります。Bash権限ルールの複合コマンド評価で扱ったとおり、順序はdeny → ask → allowで、広いdenyルールは狭いallowルールより常に優先されます。たとえばBash(aws *)をdenyに置くと、allowにBash(aws s3 ls)があってもaws s3 lsは拒否されます。
なおEndConversationツールだけは例外で、他に残っているツールがある限り、disallowedToolsの裸のツール名指定では除去できません。
実践例 — CIでBashを読み取り専用に絞る
非対話実行(-p)と組み合わせると、CI上でリポジトリの調査だけをプロンプトなしで許可し、変更系コマンドは常に拒否する構成が作れます。
claude -p "このPRの変更点をレビューして" \
--allowedTools "Bash(git log *)" "Bash(git diff *)" "Bash(git show *)" "Read" "Grep" \
--disallowedTools "Bash(git push *)" "Bash(rm *)" "Edit" \
--permission-prompts none--permission-prompts none(Claude Code v2.1.259以降)を足すと、allowedTools/disallowedToolsのどちらにも該当しない呼び出しは黙って拒否されます。誰も答えられない環境で、プロンプトが宙に浮くことがなくなります。
複合コマンドとラッパーはどう判定されるか
allowとdenyでは、複合コマンド(&&、||、;、|、|&、&、改行でつないだコマンド)の扱いが非対称です。allowルールは各サブコマンドを独立に判定するので、Bash(git log *)を許可してもgit log && curl example.com全体は通りません。denyルールは逆で、サブコマンドが1つでも一致すればコマンド全体が拒否されます。forループの本体や$(...)の中に入れた形でも同じです。
Bashコマンド1件の判定の流れ
- 1
サブコマンドに分解する
&&や|などの区切りで分け、固定のラッパーを外します。外されるのはtimeout・time・nice・nohup・stdbufと、シェル組み込みのcommand・builtin、zshのnoglobです。timeout 30 npm testがBash(npm test *)に当たるのはこのためです。 - 2
denyを当てる
どれか1つでも一致すれば全体が拒否されます。ここで一致したものは、後段のallowで救えません。
- 3
askを当てる
一致すればプロンプトが出ます。
--permission-prompts noneを付けた非対話実行では、プロンプトになる呼び出しは自動で拒否されます。 - 4
allowを当てる
すべてのサブコマンドがallowに一致して初めて、プロンプトなしで実行されます。
外されるラッパーは固定で、設定では増やせません。npx、docker exec、devbox runのような環境ランナーはリストに入っていません。そのためBash(devbox run *)と書くとdevbox run rm -rf .まで一致します。ランナーを許可するなら、Bash(devbox run npm test)のようにランナーと中身を1組で書きます。フラグの付かないxargsは外されるため、Bash(grep *)はxargs grep patternにも一致します。xargs -n1 grep patternのようにフラグが付くとxargsコマンドとして判定され、内側のコマンド向けのルールは当たりません。
パラメータ単位でマッチさせる
--disallowedToolsのスコープ構文だけでは足りない場面もあります。ツールの入力パラメータ単位でマッチさせたいときは、Tool(param:value)という書き方が使えます。たとえばAgent(model:opus)は、Opusモデルを指定したAgent呼び出しにマッチします。Bash(run_in_background:true)なら、バックグラウンド実行を指定したBash呼び出しが対象です。パラメータ名はツール入力の直下フィールドに限られ、ネストしたオブジェクトやリストの中身は対象外です。値には*をワイルドカードとして使えますが、モデルが省略したパラメータには決してマッチしません。一致条件の全体はパラメータで書く権限ルールにまとめています。
MCPツールに対してこのパラメータマッチを使う場合、--disallowedToolsが唯一の経路になります。settings.json側は括弧付きのmcp__ルールを読み込み時にスキップするため、MCPツールのパラメータをピンポイントで拒否したいときは起動フラグで渡す必要があります。
claude --disallowedTools "Agent(model:opus)" "Bash(run_in_background:true)"各ツールの主要な入力フィールドは、このパラメータマッチでは狙えません。対象外になるのはcommand(Bash/PowerShell)、file_path(Read/Edit/Write)、path(Grep/Glob)、url(WebFetch)などです。Bash(command:rm *)のようなルールは複合コマンドで簡単にバイパスされるため、Claude Codeは起動時に警告を出してこのルールを無視します。こうした主要引数を絞るときは、前述のスコープ付きルール(Bash(rm *)のような形)を使います。Windowsで単独のBashをdenyするとPowerShellツールも止まります。詳しくはDenying Bashの警告にあります。
サブエージェントの利用を絞る
--disallowedToolsは組み込みのBashやReadだけでなく、Agent(AgentName)という形でサブエージェントの利用も制御できます。特定のサブエージェントを使わせたくない場合に、名前を指定して拒否します。
claude --disallowedTools "Agent(Explore)"CIで探索用のサブエージェントを自動起動させたくない、社内で認可したカスタムサブエージェントだけを使わせたい、といった場面でこの形が使えます。
よくあるつまずき
- ワイルドカードで抜け道ができる:
Bash(curl http://github.com/ *)でURLを制限したつもりでも、すり抜ける形があります。curl -X GET http://github.com/...のようにオプションが先に来る形はマッチしません。URL=http://github.com && curl $URLのような変数展開も同様です。ネットワーク先を絞るなら、Bash側のcurl自体をdisallowedToolsで塞ぎます。そのうえでWebFetchツールをWebFetch(domain:github.com)で許可する構成に寄せます。 - ルールはClaudeが書いたコマンド文字列にしかマッチしない: denyは通常の呼び出し形を止めるもので、プログラムそのものを囲う境界ではありません。下の表のとおり、別形での呼び出しは文字列が一致せず、止まりません。コマンド文字列に依存しない強制力が必要なら、OSレベルで実行を止めるサンドボックス機能か、PreToolUseフックでコマンド全文を検査する構成と組み合わせます。
| ルール | 止まる呼び出し | 止まらない呼び出し |
|---|---|---|
Bash(curl *) | 止まる呼び出しcurl https://example.com | 止まらない呼び出し/usr/bin/curl https://example.com / sh -c 'curl https://example.com' |
Bash(rm *) | 止まる呼び出しrm -rf build/ | 止まらない呼び出し/bin/rm -rf build/ / bash -c 'rm -rf build/' |
Bash(git push *) | 止まる呼び出しgit push origin main | 止まらない呼び出しgit -C . push origin main / git 'push' origin main |
止まらない列の呼び出しは、ほかのルールと権限モードが判定します。そのためdenyを書いた後は、右列の形がallowに一致して通っていないかも見ておきます。
- 一括許可のワイルドカードは無効化される:
--allowedTools "*"のようにツール名部分をワイルドカードにした許可ルールは、起動時の警告つきでスキップされ、何も許可されません。ツール名のワイルドカードが使えるのはmcp__<server>__*のように、サーバー名部分がリテラルなMCPルールに限られます。 *をサブコマンドより前に置くと意図より広く許可される:Bash(git * main)の*は、サブコマンドとその前のオプション全体に対応します。そのためgit merge mainやgit push origin mainまで許可対象に含まれます。git logだけを許可したいなら、サブコマンドの後ろに*を置いたBash(git log * main)のように書く必要があります。Claude Codeはサブコマンドより前に*がある許可ルールに対して起動時に警告を出しますが、書き方自体は防げません。--restrictedで外れたツールを戻すのは--tools:--restrictedは、コマンドやコードを実行する組み込みツールとWebFetchを外します。戻すには--toolsで個別に名指しする必要があり、defaultプリセット経由では戻せません。--allowedToolsで戻るとは書かれていないので、戻したいツールは--toolsで名指しします。--restrictedはユーザー・プロジェクト・ローカルの設定ファイルも読まない:--helpの説明では、有効なのはmanaged settingsと--settingsで渡した設定だけです。手元の.claude/settings.jsonに書いたallow/denyが効かないので、共有マシンで評価用ハーネスを動かす用途以外では、設定が効いていない理由として疑う項目になります。--toolsの既定集合はOSで違う: macOS・Linux・WSLの既定ツールセットにはGlobとGrepが入らず、ClaudeはfindとgrepをBash経由で実行します。起動フラグの--toolsか--allowedToolsでGlobかGrepを名指しすると戻り、--allowedToolsではどちらか一方を書けば両方が戻ります。設定ファイルのpermissions.allowに書いても戻らないので、CI例の"Grep"は起動フラグで渡すのが前提です。
MCPサーバーの権限と組み合わせる
MCP経由でツールを提供するサーバーを本番環境に接続する場合、--allowedTools/--disallowedToolsだけでなく、サーバー側の認可設計も合わせて検討する必要があります。読み取り専用ロールと承認フローの組み方はMCPで本番環境に接続する権限設計で扱っています。
シンボリックリンク経由のすり抜けに対しては、Read/Editのdenyルールがリンク自体と参照先の両方をチェックします。Claude Code v2.1.268では、シンボリックリンクのディレクトリに書いたdenyが実体パスに効かなかった経路が修正されました。詳細はClaude Code v2.1.268のリリースノートにまとめています。
追加のディレクトリを渡す--add-dirは、ここで扱った--allowedTools/--disallowedToolsとは別軸のフラグです。読み書きできる範囲が広がるほか、追加先のSkills・コマンド・サブエージェントは読み込まれますが、hooksは読み込まれません。settings.jsonのadditionalDirectoriesで足した場合は、権限だけが広がる点が違います。詳しくはadditionalDirectoriesは権限だけ拡張するにまとめています。
設定ファイルとの関係
--allowedTools/--disallowedToolsはその起動だけに効くフラグで、settings.jsonを書き換えるわけではありません。同じルールを毎回のセッションで有効にしたいなら、permissions.allow/permissions.denyとして設定ファイル側に書きます。対話中に「Yes, and don't ask again」を選んだときのルールも、同じpermissions.allowに追記されます。保存先はgitリポジトリのルートにある.claude/settings.local.jsonで、worktreeで作業している場合はメインの作業ツリー側です。CLIフラグでその場限りの検証をして、恒久化したいルールだけを設定ファイルへ書き写す、という使い分けが実務では扱いやすくなります。
まとめ
denyは通常の書き方を止める柵であって、別形の呼び出しまで止める保証はありません。無人実行では、許可リストと--permission-prompts noneに加えて、サンドボックスやフックで最後の1枚を持つ構成が現実的です。