Claude Media
Claude Codeの警告「matches no tool」の意味とdenyルールの直し方

Claude Codeの警告「matches no tool」の意味とdenyルールの直し方

起動バナーの下に出る「Deny rule Bsh matches no tool · check for typos」は、denyやaskのツール名が認識されず、ルールが効いていない合図です。直し方を示します。

Claude Codeの起動バナーの下に ⚠ Deny rule Bsh matches no tool · check for typos と出たら、その Bsh のdenyルールは、どのツールにも当たっていません。拒否したつもりのツールは、拒否されないまま動きます。

この警告はdenyとaskのルールにだけ出ます。ここでは、行の読み方、原因の見分け方、直し方、直ったかどうかの確かめ方を順に示します。

この警告は何を伝えているのか

警告の行は、次の形です。

⚠ Deny rule Bsh matches no tool · check for typos

読みどころは3点あります。

  • Deny rule の後ろの Bsh は、あなたが書いたルールそのままの文字列です
  • matches no tool は、その名前に対応するツールが存在しないという意味です
  • 警告であってエラーではないので、Claude Code自体は起動します

怖いのは3点目です。起動できてしまうため、気づかないまま運用に入れます。Bash を止めたいのに Bsh と書いたdenyルールは、何も止めません。

どこに表示されるのか

対話セッションでは、起動バナーの下の1行として出ます。バナーが流れた後で探すより、起動直後に見るのが確実です。

実行モードによって、出る場所と文面が変わります。

実行形態出る場所文面
対話セッション出る場所起動バナーの下の行文面Deny rule Bsh matches no tool · check for typos
非対話モード(テキスト出力)出る場所stderr文面Permission deny rule "Bsh" matches no known tool — check for typos.
バックグラウンドセッション、--output-format json / stream-json出る場所デバッグログ文面stderrには出ない

JSONで出力を機械処理するパイプラインでは、stdoutもstderrも汚さないためにデバッグログへ回ります。--debug を付けて起動すると、~/.claude/debug/<session-id>.txt に書かれます。CIで claude -p を回している場合、警告に気づかないまま走り続ける経路がここにあります。

バナーが流れてしまったときはデバッグログを読む

対話セッションの起動時警告は、まず起動バナーの下の行を見ます。そこで見落としたなら、次に見る場所はデバッグログです。

このログはデバッグモードのときだけ残ります。あとから探すのではなく、出力先を指定して起動し直します。

claude --debug-file ./claude-debug.log

起動後に、指定したパスのファイルを開いて matches no tool を検索します。ログに残る警告はこの1行だけではないので、同じ起動で出た他の設定警告も並べて確かめられます。~/.claude/debug/<session-id>.txt に書かれる経路は、バックグラウンドセッションやJSON出力の場合の話で、対話セッションでは出力先を自分で決めておくほうが探す手間がありません。

v2.1.305より前は、対話セッションでも長い文面がstderrに出ていました。古いバージョンで見かけたのは、この長い形です。

なぜツール名が認識されないのか

典型は3パターンです。

  • 綴りの間違い: Bsh、Raed、Grpe のような打ち間違い
  • トランスクリプトの表示名で書いた: 画面に出るラベルは正式なツール名と違うことがあり、公式の例では Stop Task と表示されるツールの正式名は TaskStop です
  • 存在しないツール名を書いた: 実在しない名前は、綴りが合っていても同じ警告になります

2つ目は見落としやすい型です。権限ルールとhookのマッチャーは、表示ラベルには当たりません。Stop Task と書いたルールは、画面で見たとおりの名前なのに無効です。

ルールに書く名前は、ツールリファレンスにある正式名が基準です。たとえば Bash、Edit、Read、WebFetch、PowerShell です。大文字小文字を含め、一覧の表記をそのまま写します。

前と後

denyルールのツール名を正式名に直す

BEFORE

警告が出る書き方

{
  "permissions": {
    "deny": ["Bsh", "Stop Task"]
  }
}
AFTER

警告が消える書き方

{
  "permissions": {
    "deny": ["Bash", "TaskStop"]
  }
}

警告が出ないケースもある

権限ルールの説明には、警告の対象外が2つ書かれています。

  • ツール名に _ や * を含むルールは、検査の対象外です。MCPツールの mcp__github__create_issue や、mcp__* のような名前のglobがこれに当たります
  • Claude Codeが廃止したツール名も対象外です。例として TaskOutput が挙げられています

つまり mcp__githb__create_issue のようにMCPのサーバー名を打ち間違えても、この警告では気づけません。MCPツールのルールは、サーバー名とツール名を別の方法で確かめる必要があります。書き方はMCP権限ルールの記事にまとめています。

ツール名のglobを打ち間違えた場合

denyとaskは、ツール名の位置にglobを書けます。パターンはツール名の全体に一致する必要があり、* は全ツール、mcp__* は全MCPツールを指します。

{
  "permissions": {
    "deny": ["mcp__*", "Bsh*"]
  }
}

1つ目の mcp__* は意図どおり全MCPツールを止めます。2つ目の Bsh* は Bash* の打ち間違いで、どのツール名にも一致しません。ところが名前に * を含むため、先ほどの検査の対象外になり、matches no tool も出ません。通常の Bsh なら警告で気づけたのに、globにした途端に静かに無効化されます。

globを使うルールは、起動して実際にツールが止まるかを確かめる必要があります。

もう1つ、EndConversation には例外があります。bare名のglobでdenyしても、他のツールが残っているかぎり取り除けません。askのglobも、このツールには確認を出しません。* で全部止めたつもりでも、このツールだけは残ります。

もう1点、警告が出るのはdenyとaskだけです。allowルールのタイポには、この行は出ません。allowのタイポは、承認が省略されないだけで済みます。denyの打ち間違いは、静かに保護を失う点が違います。

タイポのdenyが残ると何が起きるか

具体例で見ます。次の設定は、Bashを丸ごと止めるつもりで書かれています。

{
  "permissions": {
    "allow": ["Bash(npm run *)"],
    "deny": ["Bsh"]
  }
}

Bsh は未認識なので、denyはどのツールにも当たりません。Bashは取り除かれず、npm run 系のコマンドはallowのとおり承認なしで通ります。それ以外のコマンドも、拒否はされず通常の承認の流れに乗ります。

正しく "deny": ["Bash"] と書けば、裸のツール名のdenyはそのツールをClaudeのコンテキストから取り除きます。Bash(*) も同じ意味です。設計意図が「Bashを使わせない」なら、警告が出た時点で保護がゼロだったことになります。

askルールでも同じ

askは「毎回確認する」ルールです。WebFetch を確認付きにしたくて WebFetc と書くと、確認は入らず、通常の承認の流れに戻ります。確認を挟む前提で運用していた手順が、気づかないうちに外れます。

hookのマッチャーにも同じ落とし穴がある

表示ラベルに当たらないのは権限ルールだけでなく、hookのマッチャーも同じです。PreToolUse のhookで特定のツールだけを検査したいとき、画面で見たラベルを書くとマッチしません。hookのマッチャーは権限ルールと別の設定なので、正式名で書いたかどうかは、実際に一度発火させて確かめるのが確実です。

原因のルールを見つける手順

どのファイルのどのルールかは、/permissions で追えます。

手順

警告の出所を特定する

  1. 1

    セッションで /permissions を開く

    ルール一覧が出ます。警告の行に書かれた文字列と同じルールを探します。

  2. 2

    該当ルールを選んで詳細を見る

    詳細に出所が書かれています。From project local settings のような設定ファイル由来の表示と、CLIの --disallowedTools で渡した値を示す From CLI argument があります。

  3. 3

    出所に応じて直す

    設定ファイルならその場で正式名に直します。From enterprise managed settings と出たら、自分では変更できません。

管理設定由来の場合は、警告の行をそのまま、管理設定の担当者へ転送します。行にはルールの文字列が入っているので、担当者はどのルールか特定できます。

CLIフラグ由来なら、シェルのエイリアスやスクリプトに書いた --disallowedTools の値を確認します。

claude --disallowedTools Bsh

直したあとの確かめ方

設定ファイルを正式名に直したら、セッションを開き直します。起動バナーの下から警告の行が消えれば、ツール名が認識されています。

ただし、警告が消えたことは「ツール名が実在する」ことの確認にとどまります。ルールが意図した範囲を止めているかは別の問題です。Bash(git push *) のように指定子を付けたルールは、書き方が合っているかを実際の挙動で確かめます。指定子の書き方は権限ルールのパラメータ指定で扱っています。

評価の順序も押さえておきます。ルールはdeny、ask、allowの順に評価され、最初に当たったものが結果を決めます。denyがask・allowより先に見られるので、正しく書いたdenyは、他のどのallowでも覆りません。逆に、タイポで当たらないdenyは、後ろのallowに素通しされます。

似た警告との切り分け

権限ルールまわりの警告は、原因が違っても似た見た目で出ます。

警告原因参考
matches no tool原因denyかaskのツール名が未認識参考この記事
Invalid permission rule ... was skipped原因括弧が崩れていて、ルールが読み込まれない参考括弧の崩れの直し方
is not matched by file permission checks原因Write や Glob にパスを付けたルール。ファイル権限は Edit と Read で判定される参考書き換え先の選び方
Agent would be spawned with zero tools原因サブエージェントの tools 欄の名前がすべて不一致参考サブエージェントのtools欄

最後の行は、tools 欄にも Grpe のような打ち間違いが起こりやすい点で、この警告と原因が似ています。ただし対象は権限ルールではなくサブエージェントの定義で、出る場面も別です。

Write(docs/**) のdenyルールには、matches no tool ではなく never matches · use Edit(docs/**) の行が出ます。ツール名自体は実在するため、この警告の対象にはなりません。

打ち間違いを防ぐ運用

警告を起動時に見つけるだけでなく、書く側でも減らせます。

  • ルールを足すときは、ツールリファレンスの名前を写す。画面に出たラベルから起こさない
  • チームで共有する .claude/settings.json を変えたPRでは、手元で一度起動して警告の行が出ないことを確かめる
  • 非対話モードのCIでは、stderrを捨てずにログへ残す

最後の点は実害に直結します。拒否を前提にした自動化で、denyが効いていないことにCIのログからしか気づけない場合があるためです。

まとめ

matches no tool は、denyやaskの名前がどのツールにも結びついていないという通知です。直し方は、正式名への置き換えだけで済みます。

怖いのは、警告がエラーではなく、出る場所も実行形態で変わる点です。対話セッションでは起動バナーの下、非対話ではstderr、JSON出力やバックグラウンドではデバッグログを確認してください。拒否を頼りにしている設定ほど、起動のたびに1行を見る価値があります。

この記事を共有:XはてブLinkedIn