defer_loadingでプロンプトキャッシュを壊さずツールを追加する仕組み
Claude APIのdefer_loadingは、ツール定義をシステムプロンプトのプレフィックスから外すことでプロンプトキャッシュを保ったままツールを増やせます。仕組みと制約を解説します。
defer_loadingはツール定義をどこから除外するのか
Claude APIのdefer_loading: trueは、ツール定義をリクエストのシステムプロンプトのプレフィックスから完全に除外するプロパティです。除外されたツールはキャッシュキーの計算対象そのものに入らないため、defer_loading付きのツールを増やしても既存のキャッシュエントリは無効になりません。
通常、tools配列に1つでもツールを追加・削除・変更するとプレフィックス階層全体(tools→system→messages)のキャッシュが丸ごと無効になります。defer_loading: trueはこの前提そのものを迂回する設計です。ツールは「後で発見されるもの」として最初から外に置かれるので、追加した瞬間にキャッシュが壊れるという事態が起きません。ツール数が増えるほどこの効果は大きくなり、数百〜数千のツールを抱える構成では、遅延させるかどうかがキャッシュヒット率をほぼ決定づけます。
キャッシュブレークポイント(cache_control)をどこに置くかという設計判断はプロンプトキャッシュのブレークポイントをツール定義のどこに置くかで扱っています。あちらがプレフィックス除外・tool_reference展開・strict modeのグラマー独立というdefer_loadingの基礎動作(本記事の§1〜§3に相当する部分)にも触れているのに対し、本記事はその基礎の上でdefer_loading固有の制約(cache_controlとの排他、全遅延禁止、MCP・エラー処理)を掘り下げる位置づけです。
遅延されたツールはどこに現れるのか — tool_referenceの展開
defer_loading: trueを付けたツールは、Claudeがそれを直接呼び出せません。まずTool Search Tool(通常はtool_search_tool_regexかtool_search_tool_bm25)がツール名の一覧を検索し、関連するツールを見つけたときにだけ、その完全な定義がtool_referenceブロックとして会話履歴の本文側にインライン展開されます。プレフィックス側は一切書き換わりません。Tool Search Toolが生まれた経緯とツール選択精度への効果はAdvanced Tool Useで扱っています。
{
"tools": [
{ "type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex" },
{
"name": "query_database",
"description": "Run a read-only SQL query against the reporting database",
"input_schema": { "type": "object", "properties": { "sql": { "type": "string" } } },
"defer_loading": true
}
]
}この例ではquery_databaseはプロンプトに最初から乗らず、Claudeが必要と判断してTool Search Toolで発見した時点で初めて展開されます。発見前も発見後も、tools配列のプレフィックス自体は変化しないため、キャッシュは発見前のターンから発見後のターンまで連続してヒットし続けます。
strict modeのグラマー構築とは独立して動く
defer_loadingはstrict mode(ツール呼び出しの出力をスキーマに厳密一致させる仕組み)のグラマー構築とは別軸で動きます。グラマーは遅延の有無に関わらず全ツールセットから構築されるため、どのツールを遅延させてもグラマーの再コンパイルは発生しません。プロンプトキャッシュとグラマーキャッシュの両方が、ツールを動的に読み込む場面でも維持される設計です。1つのツール定義にdefer_loadingとstrictを同時に設定すること自体は許可されており、遅延中は展開されていないツールでも、実際に発見・展開された時点でスキーマの厳密検証は変わらず効きます。
cache_controlと同じツールに同時指定できない
defer_loading: trueを付けたツールにはcache_controlを同時に指定できません。指定するとAPIは400エラーを返します。理由は単純で、遅延されたツールはキャッシュされるプレフィックスに含まれていないため、そこにブレークポイントを置く操作自体が意味を持たないからです。キャッシュブレークポイントは、常に遅延させていない(=プレフィックスに残っている)ツールの側に置く必要があります。
同様の制約はComputer use / Browser useのようなクライアントツールセットにもあります。これらはメンバーツール単位でconfigs内にdefer_loadingを設定しますが、cache_controlはツールセットのエントリ自体にしか置けません。メンバーが遅延している状態のツールセットエントリにcache_controlを置くこともできないため、ブレークポイントは遅延させていない別のツールに逃がす必要があります。
全部を遅延させると何が起きるか
defer_loading: trueを全ツールに付けると、Tool Search Tool自身を発見する手段がなくなり、APIは400エラー(At least one tool must have defer_loading=false)を返します。最低1つ、通常はTool Search Tool自体を非遅延で残す必要があります。
Computer use / Browser useのようなクライアントツールセットでは制約がもう一段厳しく、有効なメンバー全員のdefer_loadingを同じ値に揃える必要があります。一部のメンバーだけ遅延させる、という部分的な設定はできません。ツールセットは発見・展開が1つの単位として扱われるため、Tool Search Toolがそのツールセットを発見すると、有効なメンバー全員が一括で展開されます。
MCPとカスタム検索でのdefer_loading指定
MCPコネクタ(mcp_toolset)経由のツールには、個々のツール定義へdefer_loadingを直接書きません。代わりにサーバー全体へdefault_configで一括指定するか、サーバー内の特定ツールだけconfigsで個別に上書きします。
{
"type": "mcp_toolset",
"server_name": "enterprise-tools",
"default_config": { "defer_loading": true },
"configs": {
"search_docs": { "defer_loading": false }
}
}この例では、enterprise-toolsサーバーの全ツールが既定で遅延しつつ、search_docsだけは頻繁に使うため非遅延のまま残しています。個々のツール定義を書き換えずに済むため、MCPサーバー側の実装とキャッシュ設計の責務を分離できます。複数のMCPサーバーを同時に接続している場合も、サーバーごとにdefault_configを独立して設定できるので、サーバーAは全遅延・サーバーBは一部非遅延、というように運用ポリシーを分けられます。
独自の検索ロジック(embeddingベースの意味検索など)を組み込みたい場合は、カスタムツールとして実装し、ヒットしたツール名をtool_referenceブロックで返すことでビルトインのTool Search Toolと同じ展開経路に乗せられます。
{
"type": "tool_result",
"tool_use_id": "toolu_your_search_call",
"content": [{ "type": "tool_reference", "tool_name": "discovered_tool_name" }]
}この方式でも、参照先のツールはtools配列に通常defer_loading: true付きで定義しておく必要があります。ビルトインの正規表現・BM25検索では拾いきれない、意味的な近さでツールを探したい場合の逃げ道になります。
1リクエストあたりdefer_loading: trueを付けられるツールの上限は10,000個です。数百〜数千のMCPツールを束ねる構成でも、この上限に達することは通常ありません。
全部遅延させたときのエラーは実際どう返るか
「全ツールを遅延させると何が起きるか」で触れた400エラーは、実際には次の形で返ります。
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "At least one tool must have defer_loading=false. All tools cannot be deferred."
}
}このエラーの典型的な原因は、Tool Search Tool自体にもdefer_loading: trueを付けてしまうケースです。Tool Search Toolは「遅延したツールを見つける側」なので、自分自身を遅延させると発見手段が存在しなくなります。修正はTool Search Toolの定義からdefer_loadingを単に外すだけです。
もう1つ典型的なのが、tool_referenceが参照するツール名がtools配列のどこにも定義されていないケースです。こちらはTool reference 'unknown_tool' not found in available toolsというメッセージで返り、参照先のツール定義自体をtools配列に(通常はdefer_loading: true付きで)追加すれば解消します。
この2つはリクエスト自体が拒否される400エラーですが、Tool Search Toolの検索操作が実行段階で失敗する場合は、HTTPステータスとしては200を返しつつ、結果ブロックの中にエラーを埋め込む形になります。
{
"type": "tool_search_tool_result",
"tool_use_id": "srvtoolu_01ABC123",
"content": {
"type": "tool_search_tool_result_error",
"error_code": "invalid_tool_input",
"error_message": "Invalid regular expression pattern: missing ) at position 1"
}
}error_codeは4種類あります。
invalid_tool_input: 検索の入力が不正。正規表現パターンが壊れている、または200文字の上限を超えている場合などunavailable: 検索自体が実行できなかった。タイムアウトやサービス側の一時的な障害などtoo_many_requests: ツール検索操作に対するレート制限超過execution_time_exceeded: 検索が実行時間の上限を超えた
いずれも「ツールが見つからなかった」のではなく「検索という処理そのものが失敗した」ケースです。defer_loadingで外に出したツールカタログが大きいほど検索の負荷も上がるため、大量のツールを遅延させる構成ではこれらのエラーへのリトライ処理を組み込んでおく必要があります。
Tool Search Toolのストリーミング表現
ストリーミングを使っている場合、Tool Search Toolの検索とその結果はserver_tool_useブロックのcontent_block_startイベントとして流れてきます。検索結果に含まれるtool_referenceは、遅延させていたツールが見つかった瞬間に会話へインライン展開されるイベントで、この時点でようやくそのツールの完全な定義(名前・説明・スキーマ)がストリーム上に現れます。発見される前は名前の一覧しかClaude側に見えていないため、ストリームを逐次パースして「今どのツールが使えるようになったか」を追跡する実装では、このtool_referenceイベントを起点にする必要があります。
defer_loadingとキャッシュ設計の使い分け早見表
| 状況 | defer_loadingの向き | 理由 |
|---|---|---|
| 常時使う3〜5個の中核ツール | defer_loadingの向き付けない | 理由毎ターン使うツールを遅延させると、発見のための往復コストだけが増える |
| 使用頻度の低い数十〜数百のツール | defer_loadingの向き付ける | 理由プレフィックスから外れ、キャッシュヒット率を保ったまま追加できる |
| ツールセットの構成を頻繁に変える運用 | defer_loadingの向き積極的に付ける | 理由通常はツール変更でキャッシュ全体が無効化されるが、遅延対象なら影響しない |
| MCPサーバー経由のツール | defer_loadingの向きサーバー単位のdefault_configで一括指定 | 理由個々のツールに手を入れずに遅延をまとめて適用できる |
Claude Codeのようなクライアント実装では、MCPツールの遅延読み込みをENABLE_TOOL_SEARCH環境変数で制御できます。これは生のAPIでツール定義に直接defer_loadingを書くケースとは別の設定経路です。
defer_loadingがキャッシュ設計から切り離すもの
Prompt Cachingの基本設計では、「変わりにくいものを前に、変わりやすいものを後ろに置く」ことでヒット率を保ちます。defer_loadingが変えるのは、この設計にツール数の増減という要素をそもそも持ち込まなくてよくなる点です。以前は「頻繁に追加・削除されるツールがあるとキャッシュ全体が不安定になる」という制約が前提でしたが、対象のツールを遅延に倒しておけば、ツールカタログの入れ替えとキャッシュの安定性を別々に管理できます。
この設計変化が実務でとくに効くのは、複数のMCPサーバーを組み合わせたり、テナントごとにツール構成が異なるマルチテナント構成を運用したりする場面です。たとえばテナントAとテナントBでツールセットが違っても、両方が同じ中核ツール(非遅延)を共有していれば、その部分のキャッシュは共有され続けます。ツールの違いはtool_referenceが展開される会話本文側にしか影響しないため、キャッシュ設計をテナント差分ごとに作り直す必要がなくなります。
まとめ
defer_loading: trueは生のMessages APIでツールを定義するすべての場面に効く汎用のプロパティで、ユーザー定義ツールにもAnthropic提供のサーバーツールにも同じルールで適用されます。ツール定義をキャッシュ対象のプレフィックスから外し、Tool Search Toolが発見した時点で会話本文にtool_referenceとして展開する仕組みです。ツールを追加してもキャッシュキーが変わらないため、プロンプトキャッシュを維持したままツールカタログを増減できます。ただしcache_controlとの同時指定はできず、全ツールを遅延させることも、クライアントツールセットのメンバーを個別に遅延させることもできません。サーバーツールの実行結果側の自動キャッシュは本記事とは別の仕組みで、サーバーツール結果は5分TTLで自動キャッシュされる仕組みで扱っています。