アーカイブへ戻る

Claude Code ソースコード解説シリーズ 第6章: ツール概要

Claude Code のツールシステムを、プロトコル、権限、実行フローの観点から整理します。

『Claude Code ソースコード解析シリーズ』第6章|Tools 概要

ここまでの章で、メインラインはすでに整った。

QueryEngine は ReAct ループを駆動し、Prompt Runtime は毎ターンモデルに作業台を整え、Context 管理は長大なタスクで情報が埋もれないようにする。

この章では、最後の鍵となるピースを見ていく。モデルが「次に何をするか」を決めたあと、Claude Code はその意図をどのようにして、実際に実行可能で、制約可能で、回復可能なエンジニアリングアクションへと変えているのか。

すでに述べたように、モデルが出力するのは構造化された意図である。Claude Code を「会話ができる」から「仕事ができる」へと変えているのは、モデルの外側にあるこのツールシステムにほかならない。

引き続き、このシリーズで使っている例をなぞろう。

ユーザー:このプロジェクトで失敗しているテストを修正してほしい。

前述のとおり、Claude Code は「推測」で止まるわけにはいかない。このタスクでは、実際に次のことを行う必要がある。

  • プロジェクト構造の検索
  • 関連ファイルの読み取り
  • コードの編集
  • テストの実行
  • エラーに応じた追加の調整
  • リスクの高い操作の前の確認要求

これらのアクションの背後にあるのが、Tools システムだ。

05.0核心机制-Tools 図 1

つまり、この章で答えようとしている本質的な問いは「Claude Code にはどんなツールがあるか」ではない。こうだ。

Claude Code は、モデルの行動意図を、実行可能で、制約可能で、回復可能で、監査可能なエンジニアリングアクションへと、どのように変換しているのか?

1. モデルに「やりたい放題」させてはいけない理由

最も素朴な発想はこうだ。モデルが「src/foo.ts を読みたい」と言うなら、そのままコマンドを出力させればいいのではないか。

たとえばモデルが次のように発話したとする。

cat src/foo.ts
npm test
sed -i 's/old/new/g' src/foo.ts

一見うまくいきそうだが、すぐにいくつかの問題に突き当たる。

第一に、入力が構造化されていない。

モデルが吐き出すのは単なるテキストの塊であり、ホストプログラム側からはファイルを読もうとしているのか、書き換えようとしているのか、それともコマンドを実行しようとしているのか、まったく見当がつかない。

第二に、権限制御が不可能。

同じ Bash であっても、npm testrm -rf ではリスクの大きさがまったく異なる。すべての操作がシェルスクリプトの文字列に混ざり込んでしまい、システムとしてきめ細かく制御できない。

第三に、状態追跡が困難。

Claude Code は、どのファイルを読み取ったか、どのファイルを変更したか、ツールの実行結果をメッセージストリームに戻すべきか、UI にどう表示するか、といった情報を把握する必要がある。生のコマンド文字列では、こうした情報を載せられない。

第四に、拡張が制御不能になる。

今日はファイル操作のツールを追加し、明日は MCP、明後日は LSP、その次はマルチエージェント。能力を追加するたびに場当たり的なプロトコルをでっち上げていたら、システムはすぐにカオスと化す。外部システム同士がバラバラに話し始め、プロトコル層の技術的負債が指数関数的に膨れ上がってしまう。

そこで Claude Code は、統一された Tool プロトコルを導入した。

2. Tool.ts が解決するのは「アクションはまずプロトコル化されなければならない」ということ

Tool.ts は特定のツールではなく、すべてのツールが従うべき契約である。

これは「ツールの身分証明書」のようなものだと考えればよい。各ツールは以下を明示しなければならない。

  • 自分の名前
  • 受け取るパラメータ
  • 読み取り専用かどうか
  • 並列実行の可否
  • 必要な権限
  • 実行時に取得できるコンテキスト
  • 実行後に結果をシステムに返す方法

このステップは極めて重要だ。アクションが先にプロトコル化されて初めて、システムはそれを統治できるようになる。

05.0核心机制-Tools 図 2

成熟したツールは、単なる関数であってはならない。「どう呼び出すか」「呼び出していいか」「どこで呼び出すか」「呼び出した後どう締めくくるか」に同時に答えられなければならない。

これが Claude Code ツールシステムの第一層の核心である。

Tool とは機能ボタンではない。モデルの行動が現実世界に入る前に署名しなければならない、ランタイム契約である。

05.0核心机制-Tools スケッチ図 1:Tool.ts はランタイム契約

3. inputSchema がモデルの出力を「自然言語」から「構造化された意図」に変える

ツールプロトコルにおいて最も過小評価されがちなのが inputSchema だ。

その役割は TypeScript で見栄えをよくすることではない。モデルの出力をパース可能なデータに収束させることだ。

たとえば「ファイルを読む」という動作を考える。モデルが単にこう言っただけでは、

我想看看 src/foo.ts

ホストプログラム側でその意図を推測しなければならない。しかし、モデルがツール呼び出しとして次のような出力を返せば、

{
  "tool": "Read",
  "input": {
    "file_path": "src/foo.ts"
  }
}

システムは明確に以下を把握できる。

  • どのツールを呼び出すか
  • パラメータは何か
  • パラメータは合法か
  • このアクションは読み取り・書き込み・検索・実行のいずれか
  • この後どの権限・実行パスに進むべきか

これこそが function calling、tool use と通常のプロンプトの決定的な違いだ。モデルは単に「何をしたいか話す」のではなく、プロトコルに従って実行可能なリクエストを提出する。

05.0核心机制-Tools 図 3

つまり inputSchema の価値は「パラメータを定義すること」だけにとどまらない。

モデルの曖昧な意図を、システムが処理できるエンジニアリングオブジェクトに変換するものなのだ。

05.0核心机制-Tools スケッチ図 2:自然语言意図变成结构化工具调用

4. ToolUseContext —— ツールは独立した関数ではない

単体のツールだけを見ると、次のように理解してしまいがちだ。

入力パラメータ -> 関数実行 -> 結果を返す

(デモレベルの Agent フレームワークの多くは、実際にこの設計で作られている。問題が表面化するのは本番環境だ。)

しかし、Claude Code のツールはこのようには動作しない。

ツールが実行される際には、完全な ToolUseContext が渡される。このコンテキストには、現在のセッションの実行に必要な大量の情報が含まれている。たとえば:

  • 現在有効なツールセット
  • MCP クライアントと MCP リソース
  • 現在の AppState
  • メッセージ履歴
  • ファイル読み取りキャッシュ
  • 中断コントローラ
  • 通知機能
  • タスクおよびファイル履歴のアップデータ

つまり、ツールは「孤島」ではない。一度のアクションの実行が、セッション全体に影響を及ぼす。

具体的に、先ほどの「テスト失敗の修正」の例で見てみよう。

  • Grep で失敗テストに関連するファイルを検索すれば、次ラウンドのモデルのコンテキストに影響する。
  • Read でファイルを読めば、システムは既読状態を記録する。
  • Edit でファイルを修正すれば、UI に diff を表示する必要がある。
  • Bash でテストを実行して失敗すれば、エラーログがメッセージストリームに戻る。
  • ユーザーが中断すれば、長時間実行中のコマンドはキャンセルまたは後始末が可能でなければならない。

05.0核心机制-Tools 図 4

したがって、ツールシステムは単なる関数呼び出しの層ではない。

それは Claude Code のランタイムの一部なのだ。

5. tools.ts はツールディレクトリだが、最終的なメニューではない

個々の Tool を理解したら、次は tools.ts を見ていく。

Claude Code の基本機能をツールプールとして登録する役割を担う。ここでは多様な種類のツールを確認できる:

  • ファイル系:Read、Edit、Write、Notebook
  • 検索系:Glob、Grep
  • 端末系:Bash、PowerShell
  • ネットワーク系:WebFetch、WebSearch、WebBrowser
  • コラボレーション系:Agent、SendMessage、AskUserQuestion
  • ワークフロー系:Todo、Task、Plan、Worktree
  • 拡張系:MCP、LSP、ToolSearch、Skill

ただし、ここには特にハマりやすいポイントがある:

getAllBaseTools() は候補ツールプールにすぎず、モデルが最終的に目にするツールメニューではない。

多くの人がソースコードを読む際にここで誤判定してしまい、「登録されているツールの数だけモデルが直接使える」と思い込んでしまう。実際はそうではない。

Claude Code はまず大きな候補プールを用意し、その後、環境・モード・ルール・ランタイム状態に応じて段階的に絞り込み、最終的にそのターンで可視化されるツールを生成する。

05.0核心机制-Tools 図 5

この一連の流れは、成熟した Agent システムの基本原則を示している:

機能は多ければ多いほど良いわけではない。機能はシナリオ、権限、コストに応じて動的に刈り込まれなければならない。

6. なぜツールは先にフィルタリングしてからモデルに公開するのか

ここには非常に重要なセキュリティ設計がある。

Claude Code は、モデルがツールを呼び出すのを待ってから実行可否を判断するわけではない。まず「ツール可視性フィルタリング」を行う。

deny ルールによって全面的に禁止されているツールは、モデルにとってこのターンではそもそも存在しないものになる。

これは二重の門として捉えると理解しやすい。

05.0核心机制-Tools 図 6

端的に言えば、モデルがあるツールの存在を知らなければ、それを軸にタスクを計画することはない。「見せてから拒否する」よりはるかに安全だ。

一つ目の門が解決するのは:

モデルはこのターン、そのツールを見る資格があるか?

二つ目の門が解決するのは:

モデルの今回の具体的な呼び出しは、実際に実行可能か?

この二つは混同してはならない。

これが「セキュリティの前倒し」の意味するところだ。

(権限設計において我々はよく「まずモデルにすべてを見せて、実行時にブロックする」という誘惑に駆られる。Claude Code の選択はその逆——見せるべきでないものは最初から見せない。この判断の代償としてツール一覧は頻繁に変動するが、安全性は一桁上がる。)

05.0核心机制-Tools スケッチ図 3:ツール可視性と実行権限の二重の門

7. ToolPermissionContext —— ツール権限のコンテキストバッグ

ツールフィルタリングもツール実行も、ToolPermissionContext なしには成り立たない。

これは単純な true / false のスイッチではない。権限に関するコンテキスト一式をまるごと抱えるもので、通常は以下を含む:

  • 現在の permission mode
  • ユーザーレベルのルール
  • プロジェクトレベルのルール
  • ローカルルール
  • ポリシールール
  • コマンドラインルール
  • セッションレベルのルール
  • allow / deny / ask の三種類のアクション
  • bypass の許可有無
  • ポップアップ回避の要否
  • 追加のワーキングディレクトリ境界

これを見れば、Claude Code の権限システムがなぜ「重い」のかがわかる。

扱っているのが「あるツールが使えるかどうか」という単純な話ではないからだ。実際にはこういうことだ:

現在のプロジェクトにおいて、
現在の権限モードで、
ユーザー設定、プロジェクト設定、ポリシー設定、コマンドライン引数、セッションの一時ルールをすべて加味したうえで、
このツールはモデルの前に提示されるべきか?
仮にモデルがこのツールを実際に呼び出した場合、その呼び出しは allow、ask、deny のどれに該当するのか?

05.0核心机制-Tools 図 7

なかでも最も重要な鉄則はこれだ:

deny は allow に優先する。

どこかでツールが許可されていたとしても、より具体的なルールが明示的に拒否しているなら、システムは拒否しなければならない。セキュリティシステムは「デフォルトで信頼する」姿勢であってはならず、明示的な拒否により高い優先度を与える必要がある。

(これはファイアウォールのルールマッチングロジックと同じだ。より具体的なルールほど優先度が高く、deny ルールにヒットした時点で後続の評価は打ち切られる。)

8. ツール実行は「関数呼び出し」ではなく、一つのライフサイクルである

モデルが実際に tool_use を発行すると、Claude Code は実行パイプラインに進む。

典型的なツールのライフサイクルは次のようになる。

05.0核心机制-Tools 図 8

このパイプラインの各ステップは、いずれも飾りではない。

パラメータ検証は、モデルが構造を誤って渡すのを防ぐためだ。

権限チェックは、危険な操作を防ぐためだ。

スケジューリング実行は、どのツールが並列可能で、どれが直列でなければならないかを判断する。

結果のシリアライズは、モデルが次のターンで直前に起きたことを正しく解釈できるようにするためだ。

メッセージの書き戻しは、セッション全体が一回限りの動作ではなく、継続的に進行するループであることを保証する。

これらをすべて取り除くと、Claude Code は次のように退化してしまう。

モデルが一言発する -> プログラムが賭けに出る -> コマンドが無造作に走る -> 結果が適当に押し戻される

これでは実際のエンジニアリングプロジェクトを支えられないことは明らかだ。

05.0核心机制-Tools スケッチ図 4:ツール実行ライフサイクル

9. なぜツールを読み取り専用・破壊的操作・並列実行安全性で区別するのか

一般的なデモでは、ツールといえば「呼び出せるかどうか」だけが問題になる。

しかし Claude Code のような実際の開発環境では、ツールには少なくとも三つの問いに答えさせる必要がある。

第一に、読み取り専用かどうか。

ReadGrepGlob は通常低リスクのツールに分類される。これらは主にプロジェクトを観察するだけで、直接変更を加えないからだ。一方、EditWriteBash はファイルや環境を変更する可能性があり、リスクが高い。

第二に、破壊的操作かどうか。

同じ Bash でも、npm testrm -rf では危険度がまったく異なる。ツールシステムはより細かい粒度でのリスク判定を可能にしなければならない。

第三に、並列実行できるかどうか。

二つの読み取りツールを同時に実行しても通常は問題にならない。しかし、二つの書き込みツールが同じ領域を同時に変更しようとしたり、ある Bash コマンドが別のコマンドの結果に依存していたりする場合は、無造作に並列化できない。

05.0核心机制-Tools 図 9

これこそが、ツールプロトコルに一見「余分」に見えるメタ情報がこれほど多く含まれている理由である。

これらはインターフェースを複雑にするためではなく、システムに「この操作はどう扱われるべきか」を知らせるためにある。

10. 組み込みツールは「名前の羅列」ではなく、5つのカテゴリで捉える

40以上のツールをただ列挙しても、読者はすぐに迷子になる。

より良い理解のしかたは、「どのような問題を解決するのか」で分類することだ。

カテゴリ代表的なツール解決する問題
ファイルと検索Read、Edit、Write、Glob、GrepAgent がプロジェクトを理解し、変更できるようにする
Shell 実行Bash、PowerShellAgent が検証・ビルド・テストできるようにする
セッション制御AskUserQuestion、Todo、PlanAgent が計画・明確化・タスク状態の維持を行えるようにする
協調タスクAgent、Task、SendMessage複雑な作業を分割・追跡し、結果を回収できるようにする
外部拡張MCP、LSP、WebFetch、WebSearch、Skill能力の境界を外部サービスや再利用可能なフローに拡張する

これらのカテゴリが示すのは、まさに次の一点だ。

Claude Code は単に「ファイルを操作できる」のではなく、実際のソフトウェア開発プロセスを、管理可能な操作インターフェースの集合へと分解しているのである。

テスト修正時、Agent は次のように動くかもしれない。

05.0核心机制-Tools 図 10

これは「単一のツール呼び出し」ではなく、ツールとモデルが交互に進行する一連の閉ループなのである。

11. MCP、LSP、Skill がなぜ同じシステムに統合できるのか

統一された Tool プロトコルにはもう一つ大きな利点がある。拡張機能を追加する際に、アーキテクチャ全体をひっくり返す必要がないのだ。

MCP ツールであれ、LSP ツールであれ、Skill ツールであれ、本質的にはすべて Claude Code が理解できるツールビューに変換される。

  • 名前がある
  • 入力スキーマがある
  • 説明がある
  • 有効化条件がある
  • 権限セマンティクスがある
  • 実行結果がある

05.0核心机制-Tools 図 11

これこそが、統一プロトコルが削減する技術的負債である。

統一プロトコルがなければ、外部システムを接続するたびに新しいルールを一から考案しなければならない。接続先が増えるほど、システムは複雑さに押しつぶされていく。

統一プロトコルがあることで、新たな機能を追加する際に問うべきは次のことだけになる。

自身をどう記述するか?
入力をどう受け取るか?
どう実行するか?
リスクをどう宣言するか?
結果をどうメインループに返すか?

12. ツールシステムが体現するClaude Codeのエンジニアリング哲学

Toolsシステムを読み解いた後で最も重要なのは、個々のツール名を覚えることではなく、Claude Codeのエンジニアリング上の志向性を理解することだ。

モデルは実行者ではない。ランタイムこそが実行者である。

モデルが担うのは、次のアクションに移るべきかどうかの判断と、その意図が何であるかの特定だ。実際に動作を実行するのは、ホストプログラム内のツールシステムである。

ツールはプラグインではなく、ランタイムプロトコルである。

すべてのツールは、スキーマ、コンテキスト、権限、スケジューリング、結果の埋め戻し、UI表示という一連の完全なパイプラインを通らなければならない。

セキュリティは最後のポップアップではなく、ツールの公開とツールの実行という二段階のガバナンスである。

モデルが何を見ることができるか、それ自体がセキュリティ境界の一部を構成する。モデルが実際に何を呼び出すかは、第二の境界層となる。

拡張は多ければ良いというものではなく、トリミング可能・フィルタリング可能・監査可能でなければならない。

Claude CodeがMCP、LSP、Skill、マルチエージェントを接続できるのは、すべての機能を無造作にモデルへ渡しているからではない。それらすべての機能が同じ一本のツールパイプラインを通過するからだ。

13. 章全体を一枚の図にまとめる

最後に、Claude Code のツールシステムを一枚の全体図にまとめます。

05.0核心机制-Tools 図 12

この図は、Tool.tstools.tstoolExecution.ts、そして権限関連のコードを読む際の地図として活用できます。

14. ソースコードリーディングで追うべきツールの呼び出しチェーン

ツールシステムを実際にソースコードレベルで理解したい場合、特定のツールから入るのではなく、まず完全な呼び出しチェーンを一本追うことをおすすめします。

Tool.ts
-> tools.ts
-> query.ts
-> toolExecution.ts
-> permissions.ts
-> tool_result を messages に埋め戻す

最初に見るのは Tool.ts です。着目すべきはツール名ではなく、Tool プロトコルそのもの——inputSchemacallvalidateInputcheckPermissionsisReadOnlyisConcurrencySafeisDestructiveinterruptBehaviormaxResultSizeChars です。これらのフィールドはすべて、一つの問いに答えるためのものです。すなわち「モデルが発行したアクションが実際のエンジニアリング環境に入る前に、システムはどのようなガバナンス情報を知っておく必要があるのか」ということです。

次に見るのは tools.ts です。getAllBaseTools() はあくまで候補プールであり、モデルに最終的に提示されるメニューではありません。モデルに公開される前には、モードフィルタリング、パーミッションの deny ルールによるフィルタリング、MCP ツールのマージ、ソート、重複排除、キャッシュの安定性処理を経由します。ここで特に注意すべき点は、ツールの可視性それ自体がパーミッションの一部であるということです。blanket deny されたツールは、モデルが呼び出してから拒否するのではなく、モデルに見える前に消しておくのが理想的な振る舞いです。

三番目に query.ts に戻ります。モデルが返した tool_use ブロックは収集され、runTools() または StreamingToolExecutor に渡されます。ここで見えるのは、ツールシステムと ReAct メインループとのインターフェースです。ツールは UI ボタンではなく、次の状態遷移を決める分岐点なのです。

四番目は toolExecution.ts で、単一呼び出しのライフサイクルを追います。

ツール定義の検索
-> inputSchema バリデーション
-> ツールレベルの validateInput
-> PreToolUse フック
-> パーミッション判定
-> tool.call()
-> 結果のシリアライズ
-> PostToolUse フック
-> tool_result の生成

このライフサイクルこそが、プロダクショングレードの Agent と単純な function map の違いだ。エラーが直接メインループを破壊することはなく、可能な限り、モデルが次のターンで理解できる tool result へと変換される。

第五段階では、具体的なツールを一つ選んで読んでみる。たとえば FileReadTool だ。これは単なる fs.readFile() ではなく、パス検証、大規模ファイルのバジェット管理、offset / limit、PDF / 画像処理、重複読み取りの排除、権限チェック、Skill のトリガー、UI 表示まで担っている。これを読むと、なぜ Claude Code がツールを「意味を持つプロトコル」として設計し、すべての動作を Bash に押し込まないのかがより理解しやすくなる。

この一連の流れを読み終えると、Tools の核心は次のように整理できる:

モデルは構造化された意図を提示するだけ。
ツールプロトコルが動作の境界を記述する。
エグゼキューターがライフサイクルを統治する。
権限システムが実行の可否を決定する。
tool_result が現実世界をモデルに再び持ち帰る。

15. まとめ

Claude Code のツールシステムは、一言でこう表せる。

Tools とは、モデルの意図を実際のエンジニアリングアクションに変換するランタイムプロトコル層である。モデルに手足を与えると同時に、その手足に境界・権限・回路を組み込む役割を担う。

Tools を理解すれば、Claude Code を「チャットモデルにプラグインをいくつか足したもの」とは見なさなくなる。むしろ、Agent Harness に近い。モデルは思考を、ツールは行動を、権限は境界を、状態は一つひとつのアクションを連結して持続的に推進可能なエンジニアリングのクローズドループを担う。