Agent SDK: WebFetch tool description flickers between query() calls, breaking prompt cache

Resolved 💬 3 comments Opened Feb 23, 2026 by RobGruhl Closed Mar 24, 2026

Package: @anthropic-ai/claude-agent-sdk
Severity: Cost — 12x input token pricing penalty, intermittent
Affects: Any multi-turn agent using the SDK's query() API with MCP tools registered

Summary

The SDK conditionally prepends a 288-character auth warning to the WebFetch tool description. This warning appears in some query() calls but not others, even within the same session. Each toggle causes two consecutive cache misses — one when the warning is added, one when it's removed — because Anthropic's prompt cache requires a byte-exact prefix match across tool definitions.

Root cause

The SDK conditionally prepends this warning to the WebFetch tool description:

IMPORTANT: WebFetch WILL FAIL for authenticated or private URLs. Before using
this tool, check if the URL points to an authenticated service (e.g. Google Docs,
Confluence, Jira, GitHub). If so, you MUST use ToolSearch first to find a
specialized tool that provides authenticated access.

The condition appears to be whether MCP tools providing authenticated access are registered. Since the SDK re-evaluates this per subprocess spawn, the result can vary between query() calls depending on MCP server startup timing or registration order.

Evidence from captured API traffic

In our captured sessions, the warning appeared on 1 of 12 Sonnet calls (session 2, call 1 only), changing the WebFetch description from 1,217 to 1,504 chars:

Call 4 (api #6): WebFetch desc len=1217  (no prefix)
Call 5 (api #1): WebFetch desc len=1504  (prefix present) ← cache miss
Call 6 (api #2): WebFetch desc len=1217  (no prefix)      ← cache miss (changed back)

This is the worst case: two consecutive cache misses for a single toggle event.

Cost impact

Each cache miss on tool definitions costs ~$0.06 (a full cache write of ~17K tokens at $3.75/Mtok) vs ~$0.005 for a cache hit. A single flicker event causes two misses = ~$0.12 wasted. In sessions with frequent MCP reconnection, this compounds.

Suggested fix

Either always include the warning or never include it. Toggling it per-call is the worst case — it causes two consecutive cache misses (one when added, one when removed).

The warning is useful guidance and is present in Claude Code proper, so always including it is the safer default.

Workaround

We detect the short-form description in the proxy and inject the auth warning so the description is always the longer (stable) version. This adds ~288 bytes, so content-length must be recalculated after normalization.

const WEBFETCH_SHORT_MARKER =
  '"\\n- Fetches content from a specified URL and processes it using an AI model';

const WEBFETCH_LONG_MARKER =
  '"IMPORTANT: WebFetch WILL FAIL for authenticated or private URLs. Before using ' +
  'this tool, check if the URL points to an authenticated service (e.g. Google ' +
  'Docs, Confluence, Jira, GitHub). If so, you MUST use ToolSearch first to find ' +
  'a specialized tool that provides authenticated access.\\n\\n- Fetches content ' +
  'from a specified URL and processes it using an AI model';

function normalizeRequestBody(body: string): string {
  if (body.includes(WEBFETCH_SHORT_MARKER) && !body.includes('IMPORTANT: WebFetch WILL FAIL')) {
    body = body.replace(WEBFETCH_SHORT_MARKER, WEBFETCH_LONG_MARKER);
  }
  // Recalculate content-length after this normalization
  return body;
}

Note: unlike the UUID bug (which is a same-length replacement), this normalization changes the body size. The proxy must update the content-length header after applying it.

Reproduction

  1. Register one or more MCP servers that provide authenticated access tools (e.g., Google Workspace, Confluence)
  2. Run query() multiple times in succession
  3. Capture request bodies and compare the WebFetch tool description across calls
  4. On some calls the auth warning prefix will be present, on others it won't
  5. Observe cache_creation_input_tokens > 0 on the calls where the description changed

Related

See also #27786 — the UUID bug in the Bash tool description is a separate but similar cache-invalidation issue.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗