AI & DevelopmentDeveloper Tools

Claude API: Change Tools Mid-Session Without Cache Miss

Claude API conversation timeline showing dynamic tool addition and removal while preserving prompt cache

Anthropic just patched one of the sharpest pain points in long-running agentic Claude sessions: you can now add or remove tools mid-conversation without destroying your prompt cache. Three features — mid-conversation tool changes, turn-scoped system messages, and defer_loading — shipped between July and September 2026. If you’re building production agents with the Claude API, this changes your architecture and, more directly, your invoice.

Why Changing Tools Mid-Session Was Expensive

The prompt cache hashes your request prefix in a fixed order: tools → system → messages. The tools array sits at position zero. Touch it — add a tool, remove one, reorder anything — and the cache invalidates for the entire conversation. For a long-running agent accumulating millions of cached tokens over hundreds of turns, that’s not a minor inconvenience. That’s paying full input-token price for context you already paid to write.

Before this feature, developers had two equally bad options: keep the entire tool set active throughout the session (which bloats context and dilutes model attention across irrelevant tools), or start a fresh session at each phase transition (which loses all cached context). Both are architectural compromises. Anthropic’s fix is more surgical.

The Fix: tool_addition and tool_removal

The core idea: declare your complete tool set in the tools array at the start and never change it. Instead, use tool_addition and tool_removal content blocks inside a role: "system" message to control what the model can actually see at any point in the conversation. Because the tools array itself never changes, the cache prefix stays byte-identical and the cache hit holds.

response = client.beta.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    betas=["mid-conversation-tool-changes-2026-07-01"],
    tools=[
        {
            "name": "web_search",
            "description": "Search the web.",
            "input_schema": {
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"]
            },
        },
        {
            "name": "write_file",
            "description": "Write content to a file.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "path": {"type": "string"},
                    "content": {"type": "string"}
                },
                "required": ["path", "content"]
            },
            "defer_loading": True,  # Hidden until surfaced
        },
    ],
    messages=[
        {"role": "user", "content": "Research quantum computing advances."},
        # ... research phase turns ...
        {
            "role": "system",
            "content": [
                {"type": "tool_removal", "tool": {"type": "tool_reference", "name": "web_search"}},
                {"type": "tool_addition", "tool": {"type": "tool_reference", "name": "write_file"}},
            ],
        },
        {"role": "user", "content": "Now write up your findings."},
    ],
)

The blocks reference tools by name ({"type": "tool_reference", "name": "..."}) rather than re-defining them — the full definition stays in tools, untouched. Referencing a name not in tools returns a 400 error with error.details.error_code: tool_reference_unresolved.

defer_loading: Solving Tool Soup

Complex agents with large tool registries run into a subtler problem: too many tools active at once confuse the model and waste context on definitions it doesn’t need yet. defer_loading: true solves this. Declare a tool in tools with that flag and the model won’t see it until you explicitly surface it with a tool_addition block. You get a stable, cache-safe declaration up front and surgical reveal on demand.

This is especially useful for security-gated capabilities. Declare execute_shell with defer_loading: true; surface it only after the user grants explicit consent mid-session. The tool’s schema is registered, the cache is stable, and the model is none the wiser until you decide otherwise.

Mid-Conversation System Messages (No Beta Header Required)

The same cache logic that breaks on tools changes also applies to the top-level system field — it sits just one position later in the hash. Edit it mid-session to add a new constraint and you blow the system-plus-messages portion of the cache.

Mid-conversation system messages sidestep this by appending {"role": "system"} to the messages array instead of editing the top-level field. Everything before the new message stays cached. The instruction still carries operator-level authority, taking precedence over user messages if they conflict. This feature has been GA since May 2026 with no beta header required, on Claude Fable 5\/5.1, Mythos 5\/5.1, Opus 4.8, and Opus 5.

Turn-Scoped Messages: Per-Turn Nudges Without Accumulation

Long agentic sessions often need periodic nudges — reminders like “request independent reads together” or “the user hasn’t heard from you in a while.” The naive approach is injecting these as regular system messages. The problem: they accumulate. By turn 200, you’re paying token cost for 200 nudges, most of which are irrelevant to the current turn.

Turn-scoped system messages fix this with clear_at: "next_user_message". The message renders for the current turn only. After the next user message, it remains in conversation history but costs zero tokens to process — frozen in place without consuming context.

messages.append({
    "role": "system",
    "content": "Request independent reads together when possible.",
    "clear_at": "next_user_message"
})

Beta header: mid-conversation-system-clear-at-2026-08-21.

Practical Decision Guide

  • Phase-based workflows (research → execute → review): Use tool_removal and tool_addition at each phase boundary. No session restart, no cache miss.
  • Security-gated tools: defer_loading: true on powerful tools; surface via tool_addition after explicit permission.
  • Mid-session policy changes: Mid-conversation system message (no beta header). Inject operator constraints without touching the cached prefix.
  • Per-turn reminders: Turn-scoped messages with clear_at. Renders once, disappears from active token count.
  • Rate-limited tools: tool_removal when the external API is throttling; re-add with tool_addition later. No new session required.

One gotcha: batch your tool changes. Each role: "system" message with tool blocks is itself uncached new content. If you’re making three changes, put them in one message, not three. The cache miss cost applies per message, not per tool change.

What’s Not Supported

Claude Sonnet 5 does not support mid-conversation system messages or tool changes. Use the top-level system field there. All features require models from the Fable 5\/Mythos 5 generation or Opus 4.8 and above.

Mid-conversation tool changes (mid-conversation-tool-changes-2026-07-01) and turn-scoped messages (mid-conversation-system-clear-at-2026-08-21) are still beta. Basic mid-conversation system messages need no header — they graduated to GA. Check the Claude API release notes for current status as these features approach general availability.

The Bigger Picture

These features are part of a broader pattern in Anthropic’s 2026 API work: on-demand conversation compaction (compact-2026-09-04), per-message effort changes, and thinking display updates all shipped in the same window. Anthropic is treating long-running sessions as a first-class use case, not an edge case to architect around.

For developers running Claude at scale, the cache is the primary cost lever. At 0.025x base input price on Fable 5.1 cache reads, a well-managed cache across a multi-million-token session is the difference between an economically viable production system and one that burns budget on redundant processing. These features exist to keep that cache alive longer, across more complex workflows. Read the full documentation and start using them.

ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *