ツールを1つ増やしただけでPrompt Cacheが全滅する問題、Claude APIの新機能「会話途中のツール変更」で防ぐ
AI実用ラボ
ツールを1つ増やしただけでPrompt Cacheが全滅する問題、Claude APIの新機能「会話途中のツール変更」で防ぐ
【中級〜上級者向け】Claude APIで`tools`とPrompt Cache(`cache_control`)を使っている開発者向けの内容です。
エージェントを長時間動かしていると、途中から「このツールも使わせたい」という場面が出てきます。素直に`tools`配列へ追記して次のリクエストを送ると、それまで何十ターン分も効いていたPrompt Cacheが軒並みミスし、レイテンシと料金が跳ね上がることがあります。原因は、Claude APIがキャッシュ対象のプレフィックスを`tools`→`system`→`messages`の順でハッシュ化しているためです。
TL;DR - `tools`配列そのものを書き換えずに、会話の途中からツールを増減できる「Mid-conversation tool changes」がベータ提供されている(2026-09-22発表、`inline-tools-2026-09-15`ベータヘッダ)。 - 既存ツールの参照追加・削除に加え、新しいベータヘッダでは会話の途中で初めて登場するツールを「値」で定義したり、MCPサーバーを丸ごと追加したりできる。 - 配置ルールを間違えると400エラーになる。エラーコードごとの対処まで含めて説明する。
この記事でできること
- Prompt Cacheを壊さずにツールを増減できる仕組み(Mid-conversation system messages / tool changes)の全体像を理解する
- `tool_addition` / `tool_removal`を使った3つの具体的な使い方(参照追加・値による定義・MCPサーバー追加)を把握する
- 配置ルール違反や参照エラーで発生する400エラーの原因と対処を知る
- 対応モデル・プラットフォームの制限を確認し、自分の構成で使えるか判断する
対象読者
- Claude API(またはAgent SDK)で、`tools`とPrompt Cachingを組み合わせた長時間稼働のエージェントを運用している人
- `cache_control`によるキャッシュブレークポイントの基本的な仕組みをすでに知っている人(初出の専門用語は本文で簡潔に補足します)
確認環境・確認日
- 確認日: 2026-09-26
- 情報源: Claude Platform公式ドキュメント「Mid-conversation system messages」、および同日付のClaude Platform Release Notes
- 本機能はベータであり、記載内容は今後変更される可能性がある(ベータヘッダの提示が前提)
背景:なぜツールを1つ足すだけでキャッシュが全滅するのか
Prompt Cacheは、リクエストの`tools`→`system`→`messages`という並び順のバイト列をそのまま先頭からハッシュ化し、直近のリクエストと完全一致した範囲までをキャッシュヒットとして扱います。
トップレベルの`tools`配列や`system`フィールドは、この並びの一番手前にあります。ここを少しでも書き換えると、ハッシュ化される範囲の早い位置が変わってしまうため、それより後ろにある`system`以降・`messages`のキャッシュも道連れで全ミスします。数十ターン分のキャッシュを抱えたエージェントほど、この一発ミスの被害が大きくなります。
対して、会話の末尾に新しいメッセージを追記するだけなら、それより前のバイト列は一切変わらないので、既存のキャッシュエントリはそのままヒットし、新規に処理されるのは追記した分だけで済みます。この「末尾への追記」を、システム指示だけでなくツール構成の変更にも使えるようにしたのが、今回扱う機能です。

トップレベルのsystemフィールドは、もう書き換えちゃダメってことですか?

会話全体に効かせたい指示は今まで通りトップレベルのsystemでいいですよ。問題になるのは「頻繁に書き換える」場合です。書き換えるたびにそこから後ろが全部キャッシュミスするので、頻繁に変わる指示やツール構成は末尾に追記する側に回した方がいい、という話です。
何が便利になったか:旧ヘッダと新ヘッダの違い
Mid-conversation tool changes自体は2026年7月頃のヘッダ命名(`mid-conversation-tool-changes-2026-07-01`)から存在していたと見られますが、7月時点のリリースノート本文までは今回確認できていません。確実に一次情報で確認できたのは、2026-09-22にClaude Opus 5.5と同時に追加された新ベータヘッダ`inline-tools-2026-09-15`で、できることが大きく広がった点です。
| `mid-conversation-tool-changes-2026-07-01`(旧) | `inline-tools-2026-09-15`(新、2026-09-22発表) | |
| 既存ツールを名前で参照追加・削除 | 対応 | 対応 |
| 会話の途中で初めて出てくるツールを値で定義 | 非対応 | 対応 |
| MCPサーバーを会話の途中で追加(`mcp-client-2026-09-15`と併用) | 非対応 | 対応 |
| 対応プラットフォーム | Claude API / Amazon Bedrock / Google Cloud | Claude APIのみ |
新ヘッダは対応範囲が広い代わりに、今のところClaude APIでしか使えません。BedrockやGoogle Cloud経由で参照方式の追加・削除だけを使いたい場合は旧ヘッダのままで構いません。

旧ヘッダのmid-conversation-tool-changes-2026-07-01は、もう使わなくていいんですか?

BedrockやGoogle Cloud経由で、参照によるツールの追加・削除だけを使うなら旧ヘッダのままで大丈夫です。新ヘッダのinline-tools-2026-09-15は今のところClaude API限定なので、プラットフォームで選ぶことになります。
必要なもの
- Claude API(Messages API)を直接叩ける環境、またはClaude Agent SDK
- 対応モデルへのアクセス:Claude Fable 5.1、Claude Mythos 5.1、Claude Fable 5、Claude Mythos 5、Claude Opus 5.5、Claude Opus 4.8、Claude Opus 5(Claude Sonnet 5は非対応。Sonnet 5では従来どおりトップレベルの`system`フィールドを使う)
- ベータヘッダを送る前提知識(`anthropic-beta`ヘッダの指定方法)
全体像:どこに何を追記するのか
普段のエージェントループは「ユーザー入力 → ツール呼び出し → ツール結果」を繰り返しますが、Mid-conversation system messageはこのループの中に`role: "system"`のメッセージを差し込みます。差し込む位置は次のいずれかです。
- `user`ターン(`tool_result`ブロックを含むものも可)の直後
- サーバーツール結果で終わる`assistant`ターンの直後
差し込んだメッセージの`content`に`text`ブロックを入れれば指示の追加、`tool_addition`/`tool_removal`ブロックを入れればツール構成の変更になります。`tools`配列自体は最初に送ったまま変わらないため、キャッシュのブレークポイントより前の部分は一切書き換わらず、キャッシュはそのままヒットします。
具体的な使い方
ツールを「参照」で追加・削除する(両ヘッダ共通)
`tools`にあらかじめ全ツールを宣言しておき、`role: "system"`メッセージの中で`tool_reference`(名前で参照)を使って一時的に取り下げたり、再提示したりします。MCPコネクタのツールは`mcp_tool_reference`(個別)または`mcp_toolset_reference`(サーバーごと)で参照できます。
公式ドキュメント記載のサンプル(`get_weather`ツールを最初のuserターンの後に取り下げる例):
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: inline-tools-2026-09-15" \
-d '{
"model": "claude-opus-5-5",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
},
"required": ["location"]
}
}
],
"messages": [
{ "role": "user", "content": "Say OK." },
{
"role": "system",
"content": [
{
"type": "tool_removal",
"tool": {"type": "tool_reference", "name": "get_weather"}
}
]
}
]
}'
`tools`に宣言していない名前を参照すると400エラーになり、`error.details.error_code`は`tool_reference_unresolved`になります。最初から見せたくないツールは`defer_loading: true`を付けて宣言しておけば、`tool_addition`が出てくるまで非表示のまま保留できます。
ツールを「値」で定義する(新ヘッダのみ)
`inline-tools-2026-09-15`があれば、会話の最初には存在しなかったツールを、`role: "system"`メッセージの追加だけで導入できます。定義は`tool_definition`型でラップします。
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": {
"type": "tool_definition",
"definition": {
"name": "db_query",
"description": "Run a read-only SQL query against the analytics database.",
"input_schema": {
"type": "object",
"properties": { "sql": { "type": "string" } },
"required": ["sql"]
}
}
}
}
]
}
同じ定義を再送しても何も変わらない(冪等)ので、リトライ時にそのまま送り直せます。スキーマを変えたい場合は同じ名前で異なる定義を送れば、その位置から新しい定義に置き換わります。ただし別の種類のツールに同じ名前を使うと400エラー(`tool_name_conflict`)になります。
注意点が1つ:`tools`配列に「今すぐ使えるツール(非deferred)」を最低1つは残しておく必要があります。0個の状態で初めて値によるツール定義を行うと、その1回だけプロンプトの先頭が変わってしまい、キャッシュが丸ごと1回ミスします。
MCPサーバーを会話の途中で追加する
`inline-tools-2026-09-15`に加えて`mcp-client-2026-09-15`ベータヘッダを送ると、`tool_addition`の`definition`を`mcp_toolset`にできます。`tools`を編集しなくても、そのMCPサーバーのツール群がまとめて使えるようになります。
{
"role": "system",
"content": [
{
"type": "tool_addition",
"tool": {
"type": "tool_definition",
"definition": { "type": "mcp_toolset", "mcp_server_name": "calendar" }
}
}
]
}
サーバーの接続情報(URLやトークン)は常に`mcp_servers`側に置き、`tool_addition`ブロック自体は接続情報を一切持ちません。サーバーが使えるようになったタイミングで、この`tool_addition`を追記するだけです。
動作確認:サンプルの読み解き
上記の`get_weather`取り下げサンプルを例に、レスポンスのどこを見れば動作を確認できるかを整理します。
- 取り下げた直後のターンでモデルが`get_weather`を呼び出そうとしないこと(ツール一覧から見えなくなっている)
- `tool_removal`より前のキャッシュ対象範囲(`tools`宣言・それ以前の`system`/`messages`)を変えていないので、次のリクエストでも同じ`cache_control`位置までは通常どおりキャッシュヒットする
- `tools`に無い名前を参照した場合は、レスポンスがエラーになり`error.details.error_code`が`tool_reference_unresolved`になる
いずれも上記の公式ドキュメントに明記された仕様に基づく例です。
応用・実践パターン
パターンA: エージェントループでツールを段階的に開示する
最初から全ツールを見せると、モデルが早い段階で不要なツールに気を取られることがあります。使う可能性のあるツールは`tools`に`defer_loading: true`で宣言しておき、実際にその段階へ進んだタイミングで`tool_addition`(参照)を送って初めて見せる、という組み方ができます。`tools`配列自体は変わらないため、キャッシュを保ったまま「今のフェーズで使えるツールだけ」を段階的に開示できます。
パターンB: 1ターン限りの指示はターン限定メッセージで送る
`role: "system"`メッセージに`clear_at: "next_user_message"`を設定すると、次の`user`メッセージが来るまでだけ表示され、それ以降は配列に残っても描画されずトークンも消費しません(`mid-conversation-system-clear-at-2026-08-21`ベータヘッダが必要)。
{
"role": "system",
"clear_at": "next_user_message",
"content": "First privately list what you need next; then request every item that doesn't depend on another's result in this one response."
}
「今回のツール呼び出しだけまとめて」のような一時的な指示に向いています。永続的な指示を積み重ねて会話履歴を汚さずに済みます。
エラー対処
| エラー | error_code | 主な原因 | 対処 |
| 400 | `tool_reference_unresolved` | `tools`に無い名前を`tool_reference`で参照した | 参照名と`tools`の`name`が一致しているか確認する |
| 400 | `tool_name_conflict` | 既存ツールと別種類のツールに同じ名前で値定義した | 名前を変えるか、同じ種類のツール定義として送り直す |
| 400 | `available_tools_limit_exceeded` | 利用可能なツール数(10,000個)またはツール定義の合計サイズ(4MB)を超えた | 使わなくなった定義を`tool_removal`で明示的に取り下げる |
| 400(コード無し、配置ルール違反) | - | `tool_addition`/`tool_removal`を含むsystemメッセージを許可された位置以外に置いた、または一時停止した(サーバーツール結果で終わる)ターンの直後に置いた | 「`user`ターンの直後」「サーバーツール結果で終わる`assistant`ターンの直後」以外に置いていないか確認し、一時停止ターンは再開してから送る |

tool_reference_unresolvedが出たときは、まず何を疑えばいいですか?

参照した名前が、そのリクエストのtools配列に本当に宣言されているかどうかです。defer_loadingを付けて隠しているだけなら参照できますが、そもそもtoolsに載せ忘れていると必ずこのエラーになります。
注意点・制限事項
- Claude Sonnet 5では使えない。Sonnet 5ではトップレベルの`system`フィールドを使う設計のまま。
- 新ヘッダ`inline-tools-2026-09-15`はClaude APIのみ対応。Bedrock/Google Cloudでは値による定義やMCPサーバーの途中追加はできない。
- 一度送信済みのmid-conversation systemメッセージを後から編集・削除すると、それ以降のキャッシュが通常の履歴編集と同様に無効になる(Claude Fable 5.1とClaude Opus 5.5では、それ以降の全アシスタントターンのthinkingブロックも無効化される)。指示を更新したいときは古いものを書き換えず、新しいsystemメッセージを追記する。
- `role: "system"`メッセージは「オペレーターの指示」としてそのまま従われる。生のツール出力や外部から取得した文書など、信頼できない文字列を直接ここに置いてはいけない(プロンプトインジェクション対策上、そうしたデータは`tool_result`ブロックに留める)。
- ベータ機能のため、computer useツールなど一部のツール種別はまだ値による定義に対応していない(400エラーになる。`tools`側で宣言して参照で追加する)。

外部から取ってきた文章をそのままsystemメッセージに入れるのはダメなんですよね?

はい。systemの中身はClaudeが「オペレーターの指示」としてそのまま従うので、ツールの生の出力やWebから取得した文章を直接ここに置くと指示として乗っ取られるおそれがあります。そういうデータはtool_resultブロックに留めてください。
早見表
| 項目 | 値 |
| Mid-conversation system messages | ベータヘッダ不要 |
| 旧ヘッダ(参照の追加・削除のみ) | `mid-conversation-tool-changes-2026-07-01` |
| 新ヘッダ(値による定義・MCP追加も可) | `inline-tools-2026-09-15`(2026-09-22発表) |
| MCPサーバーの途中追加に追加で必要なヘッダ | `mcp-client-2026-09-15` |
| ターン限定メッセージに必要なヘッダ | `mid-conversation-system-clear-at-2026-08-21` |
| 参照名が`tools`に無い | 400 / `tool_reference_unresolved` |
| ツール名の種類が食い違う | 400 / `tool_name_conflict` |
| 利用可能ツール数・サイズ超過 | 400 / `available_tools_limit_exceeded` |
| 対応モデル | Fable 5.1 / Mythos 5.1 / Fable 5 / Mythos 5 / Opus 5.5 / Opus 4.8 / Opus 5(Sonnet 5は非対応) |
まとめ
- Prompt Cacheは`tools`→`system`→`messages`の順でハッシュ化されるため、`tools`配列そのものを書き換えるとそれ以降のキャッシュが全滅する
- Mid-conversation tool changesを使えば、`tools`を変えずに会話の末尾へ`tool_addition`/`tool_removal`を追記するだけでツール構成を変えられ、既存のキャッシュを保てる
- 2026-09-22発表の新ヘッダ`inline-tools-2026-09-15`では、参照追加・削除に加えて「値によるツール定義」と「MCPサーバーの途中追加」が可能になった(ただしClaude APIのみ)
- 配置ルール違反や参照先の不備は400エラーになるため、エラーコードごとの原因を押さえておくと切り分けが早い
- Claude Sonnet 5では使えない点、ベータ機能である点は導入前に確認しておく
参考にした公式情報
- Claude Platform Documentation「Mid-conversation system messages」(確認日2026-09-26)
- Claude Platform Release Notes(確認日2026-09-26、"September 22, 2026"の項目で本機能とClaude Opus 5.5が同時掲載されていることを確認)
この記事が少しでも参考になったら、AI実用ラボをフォローしていただけるとうれしいです。初心者にも分かりやすいAI活用情報をこれからもお届けします。
