Deferred Tools と ToolSearch
Claude Code がツールスキーマを遅延させ、ToolSearch でオンデマンドにロードする仕組み — 大規模な MCP ツール群がコンテキストウィンドウを圧迫しなくなる理由。
学ぶこと
セッション 5 では、スキルをスケールさせる2層のトリックを見ました。名前と1行の説明はシステムプロンプトに常駐し、完全な定義はスキルが呼び出されたときにだけロードされる、という仕組みです。Fable 5 世代は、この同じ発想をツールそのものに一般化します。もはやすべてのツールスキーマが最初からコンテキストにロードされるわけではありません — 多くのツールは名前だけの状態から始まり、専用の ToolSearch ツールが必要に応じて完全なスキーマを取得します。
読み終える頃には、次のことが理解できているはずです:
- **deferred tool(遅延ツール)**とは何か、セッションの中でどのように現れるか
- ToolSearch の3つのクエリ形式:
select:、キーワード検索、+require - ロードしてから呼び出すフロー — スキーマを取得するまで遅延ツールを呼び出せない理由
- この設計が存在する理由(ネタバレ:コンテキストは予算です)
- 大規模な MCP で何が変わるか — サブエージェントやワークフローエージェントも含めて
- 自動化で刺さる唯一の落とし穴:ヘッドレス実行における対話的認証の MCP サーバー
課題
モデルがツールを呼び出すには、そのツールの完全なパラメータスキーマ — すべてのフィールド、型、制約、説明 — がコンテキストに存在しなければなりません。組み込みツールが数個であれば問題ありません。しかし現実のセッションのツール群は、小さいままではいてくれません:
- ファイル、シェル、検索、タスク、Artifacts、スケジューリングのための組み込みツール
- 接続するすべての MCP サーバー。それぞれが独自のツール群を持ち込む
- そしてそのすべてがすべてのサブエージェントの数だけ倍増する。生成されるエージェントごとに独自のコンテキストウィンドウを持つからです
つらいのはここです。旧来のモデルでは、これらのツールが一度も呼ばれなかったとしても、そのスキーマ全部のコストを毎ターン支払っていました。チケットを1件調べるために大きな MCP サーバーを接続すると、そのツールカタログ全体がセッションの残り全部 — そして生成するすべてのサブエージェント — のコンテキストに乗り続けます。
これはまさに、セッション 5 でスキルが解決した問題が1つ下のレイヤーで再浮上したものです。スキルの定義は遅延ロードされていたのに、ツールのスキーマはそうではなかった。修正のかたちも同じです。
仕組み
Deferred Tools:スキーマを持たない名前
現在のビルドでは、すべてのツールスキーマが最初からコンテキストにロードされるわけではありません。遅延ツールは名前だけで現れ、<system-reminder> ブロック(想起されたメモリやモード変更を運んでくる、あのハーネス注入の通知)の中にリストされます。パラメータスキーマは存在しません — つまりまだ呼び出せないのです。モデルはツールが存在することは知っていますが、呼び出し方はまだ知りません。
Context window
┌────────────────────────────────────────────────────┐
│ Loaded tools — full schemas, callable now │
│ • Read (full JSON schema) │
│ • Edit (full JSON schema) │
│ • Bash (full JSON schema) │
├────────────────────────────────────────────────────┤
│ Deferred tools — names only, NOT callable yet │
│ <system-reminder> │
│ The following deferred tools are available │
│ via ToolSearch: ... │
│ (schemas not loaded) │
│ </system-reminder> │
└────────────────────────────────────────────────────┘
セッション 5 のスキルの図と見比べてください — 同じ2層アーキテクチャです。レイヤー 1 は安価なインデックス(名前)。レイヤー 2 は高価なペイロード(完全なスキーマ)で、オンデマンドにロードされます。
ToolSearch:3つのクエリ形式
ToolSearch は2つのレイヤーをつなぐ橋です。クエリを受け取り、遅延ツールのリストと照合し、マッチしたツールの完全なスキーマ定義を返します。クエリ形式は3つあります:
| クエリ | 形式 | 動作 |
|---|---|---|
"select:Read,Edit,Grep" | 完全指定 | 指定した名前のツールをそのまま取得する |
"notebook jupyter" | キーワード検索 | 遅延リストに対してキーワードを照合し、最良のマッチを返す |
"+slack send" | 必須 + ランク付け | ツール名に slack を必須とし、残りの語で結果をランク付けする |
ツール名をすでに知っているなら select: を使います(system-reminder が教えてくれています)。何をしたいかは分かるがツールが何と呼ばれているか分からないときは、キーワード検索。ツールがどのサーバーやファミリーに属するかは分かっていて、その中で絞り込みたいときは +require を使います。
ロードしてから呼び出す
ToolSearch がツールのスキーマを返せば、そのツールは最初からロードされていたツールとまったく同じように呼び出せます。フローは常に同じ2ステップです:
Deferred tool (name only)
│
│ ToolSearch("select:<name>")
▼
Schema now in context
│
│ normal tool call
▼
Tool executes
第3の状態はなく、特別な呼び出し規約もありません。スキーマ取得後は、遅延されていたことは見えなくなります。
この設計が存在する理由:コンテキストという予算
使われないツールスキーマに費やされるトークンは、あなたのコード、会話、エージェントの推論に使えなくなるトークンです。スキルが先鞭をつけた遅延ロードの哲学 — 名前と説明は事前に、完全な定義はオンデマンドに — は、固定の税金を従量課金のコストに変えます。Grep のスキーマの代金を払うのは、まさに grep しようとするその瞬間であって、何も検索しないセッションの毎ターンではありません。
大規模な MCP
これが最も効くのは MCP です。MCP サーバーはツール群が本当に大きくなる場所であり、遅延ロードはその経済性を2つの面で変えます:
-
大きなサーバーが毎ターンに課税しなくなる。 セッションに接続された MCP サーバーのツールは、名前として遅延リストに置かれます。大規模な MCP ツール群がすべてのコンテキストウィンドウを肥大化させることはもうありません — カタログ全体ではなく、実際に必要な2つのツールだけをロードします。
-
サブエージェントとワークフローエージェントは、エージェントごとにオンデマンドでスキーマをロードする。 ワークフローやサブエージェントのコンテキストは、ToolSearch を通じてセッションに接続されたすべての MCP ツールに到達でき、スキーマはそれを必要とするエージェントの中で個別にロードされます。10 エージェントへのファンアウトが、全 MCP スキーマの 10 コピーを意味するわけではありません — 各エージェントは自分のタスクに必要なものだけを取得します。
設計時に織り込んでおくべき落とし穴が1つあります。対話的に認証される MCP サーバー — たとえば claude.ai コネクタ — は、ヘッドレス実行や cron 実行では存在しないことがあります。 認証フローをクリックして通したから対話セッションで使えているツールは、同じプロンプトがスケジュール実行されるときには単に存在しないかもしれません。自動化がそうしたサーバーに依存するなら、あると仮定するのではなく、その存在を確認してください。
ハンズオン例
フルサイクルを一巡してみましょう。遅延ツールを発見し、ロードし、呼び出します。
ステップ 1 — 何が遅延されているかはセッションが教えてくれる。 セッションの序盤に、system-reminder が遅延ツール名をリストします:
<system-reminder>
The following deferred tools are available via ToolSearch.
Their schemas are NOT loaded — use ToolSearch to load tool
schemas before calling them:
Grep, ... (names only)
</system-reminder>
この時点で Grep は見えていますが、呼び出せません — 有効な呼び出しを組み立てるためのスキーマがコンテキストに存在しないからです。
ステップ 2 — 正確な名前でロードする。 リマインダーが名前を教えてくれたので、select: 形式を使います:
ToolSearch("select:Grep")
複数をまとめて取得することもできます — "select:Read,Edit,Grep" なら1回の呼び出しで3つすべてを取得します。必要になると分かっているなら、まとめてロードするほうが3往復するより安上がりです。
ステップ 3 — 普通に呼び出す。 ToolSearch が完全なスキーマを返したので、ツールは常時ロードされているツールと同じように振る舞います。Claude は通常のパラメータで Grep を呼び出し、通常の結果を受け取ります。以上です。
発見バリアント。 ツールの名前を知らないとしましょう — Jupyter ノートブックを扱う必要があることだけ分かっている。キーワード検索がマッチングを代行してくれます:
ToolSearch("notebook jupyter")
最もよくマッチする遅延ツールが、完全なスキーマとともに返ってきます。そして、ツールが Slack の MCP サーバー上にあることは分かるが正確な名前を思い出せないなら:
ToolSearch("+slack send")
これはツール名に slack を必須とし、候補を send でランク付けします — 遅延リスト全体のノイズにマッチさせるのではなく、1つのサーバーのツールに絞り込むのです。
実際のところ、これを手動でトリガーすることはほとんどありません。遅延ツールがその仕事に適していると判断したとき、Claude 自身が ToolSearch のステップを実行します。それでも仕組みを知っていれば、知らなければ奇妙に見えるセッションの挙動に説明がつきます — ツールを使う前に1ターンかけて「調べる」ような間や、対話では動くのに cron 実行では消える MCP ツールのような挙動です。
何が変わったか
| 以前(すべてのスキーマを事前ロード) | 以後(遅延 + ToolSearch) |
|---|---|
| すべてのツールスキーマがすべてのコンテキストウィンドウに常駐 | 名前だけ事前に、スキーマはオンデマンドで取得 |
| 大きな MCP サーバーの接続が毎ターンに課税 | 未使用の MCP ツールはロードされるまでほぼゼロコスト |
| 各サブエージェントがツール群全体を抱えていた | 各エージェントは自分のタスクに必要なスキーマだけロード |
| ツール群の成長がすべてのセッションを劣化させた | ツール群は毎ターンの税なしにスケールする |
| スキルは遅延、ツールは常に即時ロード | 両レイヤーに共通の遅延ロード哲学が1つ |
重要なポイント
コンテキストは予算であり、ツールは従量課金になりました。
より深いパターンには名前を付けておく価値があります。このコースの中で、あなたはすでに3回それを見てきたからです。スキルは名前を事前に、本文をオンデマンドにロードします(セッション 5)。メモリはインデックス(MEMORY.md)を事前に、個々のメモリファイルをオンデマンドにロードします(セッション 29)。そして今度は、ツールが名前を事前に、スキーマをオンデマンドにロードします。このアーキテクチャは同じ賭けを続けています:すべてのコンテキストウィンドウに安価なインデックスを置き、高価なペイロードは使う瞬間にだけ取得する。
この賭けこそが、エコシステムを劣化させずに成長させるものです。MCP サーバーを増やし、スキルを追加し、メモリを蓄積しても、その追加が将来のすべてのセッションを遅くしたり押しのけたりすることはありません。能力を持つことの限界コストはゼロに近づき、支払うのは使うときだけです。
自分の MCP サーバーやエージェント構成を設計するときは、この原則を受け継いでください。インデックスは安価で説明的に(良いツール名と明確な1行の説明が、ツールを見つけやすく選びやすくします)。そしてカタログのサイズを、かつてのように心配する必要はもうありません。
次のセッション
セッション 31 では Scheduled Autonomy: Loops, Wakeups & Cron(スケジュールされた自律性) を扱います — あなたがループに入らなくても Claude Code が走り続ける方法:/loop とその動的なセルフペーシングモード、ScheduleWakeup ツールとそのプロンプトキャッシュの経済学、cron ジョブと単発ウェイクアップの違い、そして眠るべきときを知っているエージェントのほうが決して眠らないエージェントより安く済む理由です。