The problem: changing tools invalidates the whole cache
Prompt caching works by hashing the request prefix in a fixed order: tools, then system, then messages. A cache hit requires that prefix to match a previous request byte for byte, up to the cache breakpoint. That ordering has a consequence that's easy to miss until it costs you: tools sits at the very front of the hash. Add, remove, or even reorder one tool definition partway through a long agentic session, and every cached turn since the conversation started misses the cache on the next request, the same problem mid-conversation system messages solve for the top-level system field, just one layer earlier in the prefix and correspondingly more expensive to invalidate.
Mid-conversation tool changes, a beta introduced alongside Claude Opus 5, close that gap for tools specifically. Instead of editing tools when the set of available tools needs to change, you declare the full tool set up front and then use content blocks to offer or withdraw individual tools from a specific point in the conversation onward. The tools array itself never changes, so the cached prefix stays byte-identical and the request keeps reading from cache.
How it works
You declare every tool the conversation might ever need in the top-level tools array, at the start, as normal. From that point on, two new content block types, placed inside a role: "system" message, control which of those declared tools are actually offered to the model at any given point:
tool_addition: offers a previously withheld or withdrawn tool from this point in the conversation onward.tool_removal: withdraws a currently offered tool from this point onward.
Each block's tool field references a tool by name rather than redefining it: {"type": "tool_reference", "name": "..."} names an ordinary declared tool. MCP connector tools have their own reference forms, mcp_tool_reference (with server_name and name) for a single tool, and mcp_toolset_reference (with server_name) for an entire server's toolset at once. Referencing a name that isn't in tools returns a 400 error.
Every declared tool is offered from the start of the conversation by default, unless it's declared with defer_loading: true, which withholds it until a tool_addition block surfaces it. tool_addition also works to re-offer a tool an earlier tool_removal withdrew.
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: mid-conversation-tool-changes-2026-07-01" \
-d '{
"model": "claude-opus-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"}}
]
}
]
}'
The comment worth internalizing about that example: the tools array is declared once and never touched again. Every earlier cached turn stays byte-identical, so the request that carries the tool_removal block still reads the earlier turns from cache instead of reprocessing them.
Which models support it
Mid-conversation tool changes are available on Claude Fable 5, Claude Mythos 5, Claude Opus 4.8, and Claude Opus 5, on the Claude API, Amazon Bedrock, and Google Cloud. They are not available on Claude Sonnet 5, which still requires editing the top-level tools array directly for any change in tool availability.
Claude Mythos 5 is worth flagging specifically: Anthropic's own documentation names it directly alongside Fable 5, Opus 4.8, and Opus 5 as a supported model, with a link to further detail. If you haven't encountered the name elsewhere, this feature's own reference page is a directly confirmed source that it exists as a real, current model in the lineup, distinct from Sonnet 5, Opus 5, and Fable 5.
Every request that uses tool_addition or tool_removal needs the mid-conversation-tool-changes-2026-07-01 beta header. This is a genuine beta requirement, not an optional flag; requests without it don't get the feature. This differs from mid-conversation system messages, the closely related feature for the system field, which needs no beta header at all on the same set of models.
Why you'd want this instead of just editing tools
A few concrete situations where this matters in practice:
A long agentic session that needs a tool late. An agent working through a multi-step task might not need a database-write tool until step twelve. Declaring it with defer_loading: true up front and surfacing it with tool_addition exactly when it becomes relevant avoids cluttering the model's tool choices for the first eleven steps, without paying a cache-invalidation cost when it finally appears.
Withdrawing a dangerous tool after use. A session that needed a destructive tool for one step can withdraw it immediately afterward with tool_removal, reducing the chance the model reaches for it again later in the same conversation, without restarting the session or editing the tool declarations.
Toggling an MCP server's entire toolset. Because mcp_toolset_reference operates on a whole MCP server at once by server_name, a session can turn an entire connected server's tools on or off as a unit, useful when a workflow moves between phases that each need a different external system.
State the application observes, not the user asks for. This mirrors the broader case for mid-conversation system messages generally: your application notices something Claude should treat as an operator-level fact, available tools changed, a budget threshold was crossed, and needs to relay it with system-level priority rather than as an ordinary user message.
The placement rules
tool_addition and tool_removal blocks live inside a role: "system" message, which follows the same placement constraints as any mid-conversation system message:
- It cannot be the first entry in
messages. Use the top-leveltoolsarray and normal tool declarations for anything that needs to be available from turn one. - It must immediately follow a
userturn, including one carryingtool_resultblocks, or anassistantturn ending in a server tool result, and must either be the last entry inmessagesor be immediately followed by anassistantturn. - It cannot sit between an
assistanttool_useblock and thetool_resultthat answers it. Any other position returns a 400 error.
In an agentic loop, the natural placement is right after the user message that delivers a batch of tool results, before Claude's next turn:
[
{"role": "user", "content": "Run the test suite and fix any failures."},
{"role": "assistant", "content": [{"type": "tool_use", "id": "toolu_01", "name": "run_tests", "input": {}}]},
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_01", "content": "12 passed, 0 failed"}]},
{
"role": "system",
"content": [
{"type": "tool_addition", "tool": {"type": "tool_reference", "name": "update_changelog"}}
]
}
]
Combining with prompt caching correctly
Mid-conversation tool changes only pay off when caching is actually enabled on the request. A few rules matter for getting the combination right:
- Caching is opt-in. It only happens with a top-level
cache_controlfield (automatic caching) or an explicit breakpoint on a content block. Atool_additionortool_removalblock doesn't create a cache entry on its own, and without caching turned on there's nothing to preserve. - Cache the stable prefix as usual, placing the breakpoint at the end of your tool definitions or a stable point in the message history, same as any other cached conversation.
- A tool change message is itself cacheable. Once it's in the conversation, it becomes part of the stable history, and a later cache breakpoint can move past it.
- Don't edit or remove a tool change message that's already been sent. Like any other edit to earlier history, that invalidates the cache from that point forward. Append a new
tool_additionortool_removalrather than rewriting an old one.
Limitations worth knowing before you build on this
Beyond the placement rules above, two things are easy to get wrong:
It's beta, and model-restricted. Sonnet 5 doesn't support it at all, so an application that runs both Sonnet 5 and Opus-tier models for different tasks needs a fallback path, editing tools directly, for requests that land on Sonnet 5.
Not a place for untrusted content. The same caution that applies to mid-conversation system messages generally applies here: Claude treats system content as operator instructions and follows it. A tool_addition or tool_removal block referencing a tool by name is fine, since it's just a reference, but don't be tempted to fold untrusted, externally-sourced text into the same system message alongside a tool change; keep that in tool_result blocks as usual.
Troubleshooting
A tool_addition or tool_removal block returns a 400 error. Check two things: that the referenced tool name is actually present in the request's tools array, and that the system message's position follows the placement rules, specifically not sitting between a tool_use block and its matching tool_result.
The feature seems to have no effect. Confirm the mid-conversation-tool-changes-2026-07-01 beta header is present on the request. Unlike mid-conversation system messages, this specific capability requires it on every call.
Caching isn't actually improving despite using tool changes. Confirm cache_control is set somewhere in the request. Mid-conversation tool changes prevent a specific kind of cache invalidation; they don't turn caching on by themselves.
It works on Opus 5 but fails on Sonnet 5 in the same application. That's expected model-restriction behavior, not a bug. Sonnet 5 isn't in the supported model list for this beta; route those requests through ordinary tools array edits instead.
Where this fits
This sits alongside Claude Code's promptCacheTtl settings and the advisor tool as part of the same broader Claude API surface this site tracks for teams building directly against the Messages API rather than through Claude Code. For the caching mechanics this feature is designed around, see Claude Code's prompt cache TTL settings for the CLI-side equivalent, and browse how to use agent skills with the Claude API if you're building an agent that combines skills with tool use directly against the API.
