mid-conversationのツール変更でtools配列をキャッシュごと保つ
Claude APIのmid-conversationツール変更は、tools配列を編集せずにツールの提供・撤回を切り替え、プロンプトキャッシュを壊さない仕組みです。実装パターンと配置制約を扱います。
Claude APIで会話の途中からツールを増やしたり止めたりしたいとき、tools配列を書き換えるとプロンプトキャッシュ全体が無効になります。mid-conversationのツール変更は、tools配列は最初から一度も変えず、role: "system"メッセージの中のtool_additionとtool_removalブロックでツールの提供・撤回だけを切り替える仕組みです。ベータ機能で、mid-conversation-tool-changes-2026-07-01ヘッダーが必要です。
tools配列を編集するとキャッシュ全体が壊れる理由
tools配列は、キャッシュのハッシュ対象になるリクエストのプレフィックスの中で、トップレベルのsystemフィールドよりもさらに前に位置します。プロンプトキャッシュはプレフィックスの先頭から一致する範囲までしか再利用できないため、toolsを1個増減させるだけで、そこから後ろの全ターン(システムプロンプト・会話履歴の全体)がキャッシュミスになります。これはmid-conversationシステムメッセージが解決する「systemフィールドを編集するとキャッシュが壊れる」問題と、まったく同じ構造の問題がツール側にもあるということです。
mid-conversationのツール変更は、この問題をシステムメッセージと同じ発想で解決します。ツールの完全な集合は最初のリクエストで一度だけtoolsに宣言し、以後は一切編集しません。会話の途中でツールを増減させたいときは、toolsを触る代わりに、そのタイミングでsystemメッセージを1件追加し、その中のtool_addition・tool_removalブロックでツールの提供状態だけを切り替えます。tools配列自体が変わらないので、それより前のキャッシュ済みプレフィックスはそのまま再利用されます。
tool_additionとtool_removalの書き方
tool_additionとtool_removalは、role: "system"メッセージのcontent配列に入れるブロックで、同じメッセージ内でtextブロックと混在させられます。それぞれのtoolフィールドは、ツールを新たに定義するのではなく、toolsに宣言済みのツールを名前で参照する構造です。
curl https://api.anthropic.com/v1/messages \
-H "anthropic-beta: mid-conversation-tool-changes-2026-07-01" \
-d '{
"model": "claude-opus-5",
"tools": [{"name": "get_weather", "description": "...", "input_schema": {...}}],
"messages": [
{"role": "user", "content": "Say OK."},
{
"role": "system",
"content": [
{"type": "tool_removal", "tool": {"type": "tool_reference", "name": "get_weather"}}
]
}
]
}'ツールの参照方法は3種類あります。通常のツールは{"type": "tool_reference", "name": "..."}でtools配列内の名前を指定します。MCPコネクタ経由のツールは個別に参照するmcp_tool_reference(server_nameとnameの組)と、サーバー単位でまとめて参照するmcp_toolset_reference(server_nameのみ)の2通りが使えます。toolsに宣言されていない名前を参照すると400エラーになるため、撤回・追加どちらの操作でも名前の対応関係を先に確認しておく必要があります。
未提供のまま置いておくdefer_loading
toolsに宣言したツールは、defer_loading: trueを付けない限り会話の最初から常にClaudeへ提供されます。defer_loading: trueを付けたツールはtoolsに定義自体は載っていても、tool_additionブロックで明示的に呼び出すまでモデルには見えません。段階的にツールを解放していく設計に向いており、たとえば調査フェーズでは検索系ツールだけを見せ、実行フェーズに入ったタイミングで書き込み系ツールをtool_additionで追加する、といった使い分けができます。
tool_additionにはもう1つ用途があります。一度tool_removalで撤回したツールを、後からtool_additionで再提供できます。これはdefer_loadingのツールを初めて見せる場合と同じブロックで実現でき、「撤回」と「まだ見せていない」を区別せずに扱える設計です。
調査フェーズから実行フェーズへ段階的にツールを開放する例
defer_loadingとtool_additionを組み合わせると、会話の進み方に応じてツールを段階的に見せる設計ができます。たとえばコードベースを調査してから修正を加えるエージェントでは、最初のリクエストでread_file・grepのような読み取り系ツールだけを通常どおり宣言し、write_fileのような書き込み系ツールはdefer_loading: trueを付けて宣言だけしておきます。Claudeには最初、読み取り系ツールしか見えていません。
調査が一段落し、Claudeが実際に修正へ進んでよいと判断できるタイミング(たとえばユーザーが承認の返信をしたuserターンの直後)で、tool_additionブロックを含むsystemメッセージを1件追加し、write_fileをtool_referenceで参照して開放します。tools配列自体は最初のリクエストから一度も変わっていないため、調査フェーズで積み上がったキャッシュはそのまま活きます。逆に、修正が終わって以降のターンでは書き込み系ツールをもう必要としないケースもあり、その場合はtool_removalで撤回しておくと、モデルが誤って書き込み系ツールを使う余地自体をなくせます。
ブロックの使い分け早見表
| ブロック / 宣言 | 効果 | 向く場面 |
|---|---|---|
toolsに通常宣言(defer_loadingなし) | 効果会話の最初からモデルに提供される | 向く場面会話全体で常に使うツール |
toolsにdefer_loading: trueで宣言 | 効果宣言はあるがtool_additionまで非提供 | 向く場面フェーズが進んでから見せたいツール |
tool_addition | 効果未提供のツールを提供状態にする(初回開放・再提供どちらも) | 向く場面フェーズの切り替え、撤回済みツールの復活 |
tool_removal | 効果提供済みのツールを撤回する | 向く場面危険な操作を特定フェーズ以降だけ禁じたいとき |
どこに置けてどこに置けないか
通常のmid-conversationシステムメッセージは、userターン(tool_resultを含むものも可)の直後、またはサーバーツール結果で終わるassistantターンの直後に置けます。ただしtool_addition・tool_removalを含む場合はこの一般則より制約が狭く、許されるのはuserターンの直後(tool_resultを含むものも可)だけで、サーバーツール結果で終わるassistantターンの直後には置けません。いずれの場合も、その次はassistantターンが続くか、メッセージ配列の末尾である必要があります。tool_useブロックとそれに対応するtool_resultの間に挟むことはできず、規則に反する位置に置くと400エラーが返ります。
| 状況 | tool_addition / tool_removalの可否 |
|---|---|
| userターンの直後 | tool_addition / tool_removalの可否可 |
| tool_resultを含むuserターンの直後 | tool_addition / tool_removalの可否可(エージェントループの定番配置) |
| サーバーツール結果で終わるassistantターンの直後 | tool_addition / tool_removalの可否不可。ターンを再開してから次のsystemメッセージで送る |
| tool_useブロックとtool_resultの間 | tool_addition / tool_removalの可否不可(400エラー) |
| メッセージ配列の先頭 | tool_addition / tool_removalの可否不可(contentを持つsystemメッセージは先頭に置けない) |
サーバーツール結果で終わる、つまり一時停止(pause)したassistantターンの直後は例外的な扱いです。ブロックだけの通常のシステムメッセージは許容されますが、・は受け付けられません。一時停止したターンを再開させてから、次のsystemメッセージでツール変更を送る2段階の手順が必要です。
もう1点、連続するsystemメッセージは1つのまとまり(システムセクション)として扱われ、配置ルールもまとめて1つのブロックとして判定されます。tool_addition・tool_removalだけのメッセージの直後にtextだけのメッセージを続けて送ると、両者は合算されて同じ配置制約が適用されるため、片方だけなら許される位置でも、組み合わせた結果としては制約に触れることがあります。
プロンプトキャッシュと組み合わせるときの実務
キャッシュはリクエストにcache_controlが含まれるときだけ働きます。自動キャッシュの明示的なブレークポイントを、変わらない範囲の末尾(ツール定義の末尾や会話履歴の安定した位置)に置く運用は、mid-conversationのツール変更を使う場合でも変わりません。ツール変更のsystemメッセージはキャッシュ済みプレフィックスより後ろに追加されるので、プレフィックスのハッシュ自体は変化せず、次のリクエストもそこまではキャッシュから読まれます。
一度送信したmid-conversationのシステムメッセージ(ツール変更を含む)を後から編集・削除すると、その時点以降のキャッシュがすべて無効になります。これはcompactionとプロンプトキャッシュを両立させるときに気をつける「履歴を書き換えない」という原則と同じで、ツール変更のシステムメッセージも一度送ったら書き換えずにそのまま残し、変更が必要なら新しいsystemメッセージを追記する形にします。
セキュリティ上の注意 — tool_addition/tool_removalの判断材料に外部データを使わない
systemメッセージの中身は、Claudeにとって運営者からの指示として扱われます。これはtool_addition・tool_removalブロックにも当てはまり、「どのツールを提供・撤回するか」という判断そのものが、モデルにとって強い権限を持つ指示として届きます。したがって、ツール結果や外部から取得したドキュメントの内容をそのまま条件にしてtool_addition・tool_removalを機械的に組み立てる実装は避けます。信頼できないテキストに基づいてツールの提供・撤回を切り替えると、攻撃者が仕込んだ入力によって意図しないツールが開放されたり、必要なツールが撤回されたりする経路になりかねません。ツール結果そのものは通常どおりtool_resultブロックに収め、tool_addition・tool_removalを発行するかどうかの判断はアプリケーション側のロジックで行います。
よくあるつまずき
- ベータヘッダーを付け忘れる:
mid-conversation-tool-changes-2026-07-01が無いとtool_addition・tool_removalは未知のフィールドとして扱われます。 - Sonnet 5で使おうとする: mid-conversationのツール変更はFable 5.1・Mythos 5.1・Fable 5・Mythos 5・Opus 4.8・Opus 5でのみ利用でき、Sonnet 5では使えません。
toolsに無い名前を参照する:tool_referenceのnameがtools配列に存在しないと400エラーになります。ツール名の追加・削除をtools側では一切行わない前提を忘れないようにします。- 一時停止したターンの直後に直接ツール変更を送る: サーバーツール結果で終わるassistantターンの直後は
tool_addition・tool_removalを受け付けません。ターンを再開してから送ります。 - 撤回したツールを
toolsから消してしまう:tool_removalは一時的な非提供であって削除ではありません。再提供したくなったときのために、tools側の定義はそのまま残しておきます。
まとめ
mid-conversationのツール変更は、tools配列を最初の宣言のまま固定し、systemメッセージのtool_addition・tool_removalでツールの提供状態だけを切り替える仕組みです。効果が出るのはキャッシュ済みの会話が長く、かつ途中でツール構成を変えたいエージェントループで、配置制約は通常のmid-conversationシステムメッセージと共通です。ベータヘッダーとモデルの対応範囲(Sonnet 5は非対応)を確認したうえで導入すると、既存のキャッシュ設計を崩さずにツールの出し入れを組み込めます。導入時は、配置ルールの400エラーと、信頼できない入力を判断材料にしないというセキュリティ上の注意の2つを先に押さえておくと、実装のやり直しを減らせます。