Skip to content

fix(ai): fold gateway-namespaced Anthropic tool references onto the request's tools - #1480

Merged
code-yeongyu merged 3 commits into
mainfrom
fix/tool-search-deferred-ref-400
Sep 8, 2026
Merged

fix(ai): fold gateway-namespaced Anthropic tool references onto the request's tools#1480
code-yeongyu merged 3 commits into
mainfrom
fix/tool-search-deferred-ref-400

Conversation

@code-yeongyu

@code-yeongyu code-yeongyu commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Summary

A native Anthropic tool search can hand back tool_reference names under a wire-path namespace (mcp__<id>__<tool>) that the request never defined and that does not survive across requests. The tool_search_tool_result block replays verbatim on the next same-model request, and Anthropic rejects the whole request:

invalid_request_error: Tool reference 'mcp__925c__memory' not found in available tools

Observed live on 2026-09-08 (senpi 4adba7a, omo desktop, claude-fable-5-1): the turn hard-errored and fell back to another model. The same session's later native results carried a different namespace id (mcp__5153__), and other sessions carried 8e8c/7439/cdd2, so the namespace is not something senpi can predict or replay.

Root cause

demoteUnavailableToolReferences in packages/ai/src/api/anthropic-messages.ts collected the namespaced names as dangling but only rewrote references inside tool_result content; native tool_search_tool_result blocks fell through untouched (:974), and a dangling discovery still exempted a later tool_use from demotion (:900-901).

Fix

Availability is decided by the request's final tools array alone, and every reference site is repaired:

  • a tool_reference (client tool_result content or native tool_search_tool_result) under a gateway namespace is folded onto the request's own tool name when that tool is defined; one that still does not resolve is dropped;
  • a native search pair whose every reference stopped resolving is demoted to text together with its server_tool_use;
  • a history tool_use under a gateway namespace is renamed to the request's tool name; a tool_use whose only justification was a dangling discovery is demoted like any other unavailable call.

Evidence

  • RED (test patch on eab8c4b30): packages/ai bunx vitest --run test/anthropic-tool-reference-integrity.test.ts -> 5 failed | 5 passed (e.g. expected ['mcp__925c__memory'] to deeply equal ['memory']). GREEN with the fix: 10 passed. Run on a remote node (mengmotaMac, bun 1.4.0).
  • Full packages/ai suite with the fix: 266 files / 2591 tests passed; the single failing file (test/codex-apply-patch-wire-schema.test.ts, Failed to resolve entry for package "@earendil-works/pi-tui") fails identically on unpatched eab8c4b30 in the same fresh worktree.
  • coding-agent adjacent suites (test/mcp/native-anthropic.test.ts, test/tool-search/native-anthropic.test.ts, test/suite/regressions/0000-anthropic-native-tool-search-contract.test.ts): 3 files / 37 tests passed.
  • Incident replay harness (the real transcript's server_tool_use + tool_search_tool_result blocks through streamAnthropic with a capturing fake client): baseline emits five dangling references mcp__925c__{memory,task_send,todo,eval,web_search}; fixed emits memory, task_send, todo, eval, web_search with zero dangling references.
  • bun run check green; changelog gate PASS (packages/ai/src/changes.md entry + [Unreleased] Fixed entry).

Out of scope (filed separately)

  • A real HTTP 400 never reaches after_provider_response, so the native-search adapter's permanent-400 fallback is unreachable on the live error path.
  • The failed turn is not recovered after such a 400 (the session falls back to another model instead of retrying without native injection).

Summary by cubic

Fixes a 400 error on Anthropic requests after a native tool search: tool references that come back under a gateway namespace (mcp__<id>__<tool>) are now folded onto the request's own tool names, and unresolvable references are dropped instead of replaying verbatim.

Bug Fixes

  • demoteUnavailableToolReferences now decides availability solely from the request's tools array.
  • Namespaced tool_reference items in both tool_result and native tool_search_tool_result blocks are rewritten to the request's tool names.
  • A native search pair with no resolvable references is demoted to text along with its server_tool_use.
  • History tool_use blocks with namespaced names are renamed, and a dangling discovery no longer exempts a call from demotion.

Written for commit e9d914c. Summary will update on new commits.

Review in cubic

…equest's tools

A native tool search can hand back tool_reference names under a wire-path
namespace (mcp__<id>__<tool>) that the request never defined and that does
not survive across requests; replaying that tool_search_tool_result block
then fails the whole request with "Tool reference '<name>' not found in
available tools" (observed live on 2026-09-08 with claude-fable-5-1, which
hard-errored and fell back to another model).

demoteUnavailableToolReferences now decides availability from the final
tools array alone: namespaced references and history tool_use blocks are
folded onto the request's own tool names, references that still do not
resolve are dropped, a search pair left with no references is demoted to
text together with its server_tool_use, and a dangling discovery no longer
keeps a later tool_use alive.
…red-ref-400

# Conflicts:
#	packages/coding-agent/CHANGELOG.md
@code-yeongyu
code-yeongyu merged commit 933307a into main Sep 8, 2026
23 checks passed
@code-yeongyu
code-yeongyu deleted the fix/tool-search-deferred-ref-400 branch September 8, 2026 07:22
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.

1 participant