Skip to content

Sanitize server_tool_use IDs for Anthropic-bound requests - #1401

Draft
andreasfoo wants to merge 2 commits into
mainfrom
claude/server-tool-use-id-format-suqunq
Draft

Sanitize server_tool_use IDs for Anthropic-bound requests#1401
andreasfoo wants to merge 2 commits into
mainfrom
claude/server-tool-use-id-format-suqunq

Conversation

@andreasfoo

@andreasfoo andreasfoo commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Problem

User report:

API Error: 400 ... messages.1.content.2.server_tool_use.id:
String should match pattern '^srvtoolu_[a-zA-Z0-9_]+$'

api.anthropic.com strictly validates the id of server_tool_use content blocks against ^srvtoolu_[a-zA-Z0-9_]+$. As a multi-provider gateway, tingly-box replays conversation history that may contain server_tool_use blocks (typically WebSearch) whose IDs were minted by a different upstream — e.g. OpenAI-style call_... IDs from a converted response, or a third-party Anthropic-compatible endpoint using its own ID format. When a later request in that conversation routes to real Anthropic, the history is forwarded verbatim and the entire request is rejected with a 400.

The codebase previously had no sanitization for server_tool_use IDs at all (no srvtoolu reference anywhere).

Fix

New op internal/protocol/ops/request_anthropic_server_tool_use.go:

  • SanitizeAnthropicV1ServerToolUseIDs / SanitizeAnthropicBetaServerToolUseIDs rewrite non-conforming server_tool_use IDs in the replayed message history:
    • Invalid characters are replaced with _; a srvtoolu_ prefix is added when needed (an already-prefixed ID with bad chars is not double-prefixed).
    • The rewrite is deterministic: the original ID is preserved inside the new one so the model can still correlate the block across turns, and the same old ID always maps to the same new ID.
    • Empty IDs get a per-request counter (srvtoolu_missing_N) so they stay distinct.
  • Every result block referencing a rewritten ID via tool_use_id (web_search_tool_result, web_fetch_tool_result, plain tool_result, etc. — via the SDK's GetToolUseID() accessor) is remapped so the use/result pairing stays intact.
  • Valid IDs and regular tool_use blocks are left untouched.

Wired into VendorTransform (internal/protocol/transform/vendor.go) for both the v1 and beta paths, only when the target provider URL is api.anthropic.com / claude.ai — consistent with the existing per-vendor dispatch. Requests to other providers are unaffected.

Tests

internal/protocol/ops/request_anthropic_server_tool_use_test.go:

  • Realistic JSON-unmarshalled beta request: invalid ID rewritten, web_search_tool_result remapped, serialized output no longer contains the old ID
  • Valid srvtoolu_... IDs kept as-is
  • Empty IDs get distinct replacement IDs
  • srvtoolu_-prefixed ID with invalid chars is sanitized without double prefix
  • Mixed valid/invalid IDs in one request: only invalid ones (and their results) touched
  • Plain tool_result referencing a rewritten ID is remapped
  • Regular tool_use blocks with call_... IDs untouched
  • Same old ID maps to the same new ID; nil request is a no-op

internal/protocol/transform/vendor_test.go:

  • Vendor dispatch: sanitizer runs for api.anthropic.com targets, no-op for non-Anthropic targets (api.deepseek.com)

All internal/protocol/ops and internal/protocol/transform tests pass.

Note

Beta mcp_tool_use blocks have an analogous prefix constraint (mcptoolu_) with the same cross-provider risk; kept out of scope here since the report only covers server_tool_use, but the same pattern extends directly.

🤖 Generated with Claude Code

claude added 2 commits July 22, 2026 09:39
Anthropic validates server_tool_use block IDs against
^srvtoolu_[a-zA-Z0-9_]+$ and rejects the whole request with a 400 when
replayed history carries IDs minted by another provider (OpenAI-style
call_... ids or third-party Anthropic-compatible endpoints). Rewrite
non-conforming IDs deterministically in the vendor transform when the
target is api.anthropic.com / claude.ai, and remap the tool_use_id of
every result block that references a rewritten ID so the pairing stays
intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXH5DRSAGo9j58rShPCX4g
Cover already-prefixed ids with invalid chars, mixed valid/invalid ids,
plain tool_result remapping, regular tool_use passthrough, nil requests,
and vendor-transform dispatch (sanitize on api.anthropic.com, no-op on
other targets).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXH5DRSAGo9j58rShPCX4g
@andreasfoo andreasfoo changed the title Sanitize Anthropic server_tool_use IDs to match API requirements Sanitize server_tool_use IDs for Anthropic-bound requests Jul 22, 2026
@andreasfoo
andreasfoo marked this pull request as draft July 22, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants