Skip to content

fix(tool-search): use the API's native search tool name and inject only on Anthropic hosts - #1332

Open
JJONGFILM wants to merge 1 commit into
code-yeongyu:mainfrom
JJONGFILM:fix/native-tool-search-name-and-endpoint-gate
Open

fix(tool-search): use the API's native search tool name and inject only on Anthropic hosts#1332
JJONGFILM wants to merge 1 commit into
code-yeongyu:mainfrom
JJONGFILM:fix/native-tool-search-name-and-endpoint-gate

Conversation

@JJONGFILM

@JJONGFILM JJONGFILM commented Sep 3, 2026

Copy link
Copy Markdown

What

Two defects in the Anthropic native tool-search adapter (builtin/tool-search/native-search.ts) that together break every anthropic-messages request once the extension catalog contains an inactive tool:

  1. Wrong tool name. The adapter appended { type: "tool_search_tool_bm25_20251119", name: "tool_search" }. The Messages API only accepts name: "tool_search_tool_bm25" for that type and rejects the whole request:
    400 tools.39.tool_search_tool_bm25_20251119.name: Input should be 'tool_search_tool_bm25'
    
  2. Injected on every anthropic-messages endpoint. The gate was api === "anthropic-messages", so third-party hosts that speak the Anthropic wire format (Kimi Code api.kimi.com/coding, OpenRouter, proxies) also received the server-side tool and answered with an opaque 400 {"error":{"type":"invalid_request_error","message":"Invalid request Error"}}.

Why it surfaced now

The adapter has been in since #811, but enabled() only fires when the catalog has an inactive extension tool. #1314 (eval-only routing for bash/workflow/monitor as the default) makes that true for every session, so 2026.9.3-3 turned native injection on for everyone and both defects hit at once. The 400 fallback (noteResponseStatus) only kicks in after the first failed turn of each session and, on third-party hosts, hid the real cause behind a generic error.

Changes

  • ANTHROPIC_TOOL_SEARCH_NAME"tool_search_tool_bm25".
  • AnthropicNativeToolSearchAdapter.applyBeforeRequest(model, payload) now takes the request model (event.model ?? ctx.model) and skips injection unless isFirstPartyAnthropicEndpoint(model.baseUrl) — hostname anthropic.com or a subdomain. A missing baseUrl keeps the previous behaviour, so existing callers/tests are unaffected. addAnthropicNativeToolSearch itself is unchanged (still the pure api + payload transform).
  • Test validator mock (native-search-mocks.ts) now enforces the tool name the way the API does, so the old name fails the existing HARD-RULES tests instead of passing silently.
  • New cases in test/tool-search/native-anthropic.test.ts: exact tool name, pre-fix name is a 400 in the validator, host classification, and that an anthropic-messages model on a third-party host gets the untouched payload while a later 400 from that host is not attributed to native search.
  • tool-search/changes.md entry.

No CHANGELOG.md edit (maintainer-owned).

Verification

  • npx vitest --run test/tool-search test/mcp/tool-search-promotion.test.ts test/mcp/native-anthropic.test.ts — green.
  • npm run check — green.
  • Live reproduction on omo-ai 5.0.0-0.beta.38/39 (senpi 2026.9.3-3), session log excerpts with the two error shapes above against anthropic/claude-fable-5, anthropic/claude-opus-5, and kimi-coding/k3. Applying the same two changes to the installed dist/ restored both providers; the Kimi model keeps working through the local tool_search tool.

Notes for reviewers

  • A corporate proxy in front of api.anthropic.com on a non-anthropic.com hostname loses native tool search with this change and falls back to local tool_search (no request failure). If that should be opt-in-able, a compat flag on the model config would be the natural follow-up; I kept this PR to the minimal fix.

Summary by cubic

Fixes the Anthropic native tool-search adapter so requests no longer fail with 400 errors. The injected tool now uses the API-required name tool_search_tool_bm25, and injection only runs on first-party anthropic.com hosts; third-party hosts now fall back to the local tool_search tool.

Bug Fixes

  • Renames the native search tool to tool_search_tool_bm25; the API rejects any other name with a 400.
  • Skips injection for anthropic-messages models on third-party hosts like Kimi Code and OpenRouter, which don't implement native tool search.
  • Models without a baseUrl keep the previous behavior so existing callers and tests are unaffected.
  • The test validator now enforces the tool name like the API does, with new tests covering the name, host gating, and third-party 400 attribution.

Written for commit 475598d. Summary will update on new commits.

Review in cubic

…ly on Anthropic hosts

The Anthropic native tool-search adapter appended
`{ type: "tool_search_tool_bm25_20251119", name: "tool_search" }`, but the
Messages API only accepts `name: "tool_search_tool_bm25"` for that type and
rejects the whole request with 400. It was also injected for every
`anthropic-messages` model, so third-party hosts speaking the Anthropic wire
format (Kimi Code, OpenRouter, proxies) answered with an opaque
`400 Invalid request Error`.

- rename the native search tool to `tool_search_tool_bm25`
- pass the request model (`event.model ?? ctx.model`) to the adapter and skip
  injection unless the model's baseUrl host is anthropic.com or a subdomain;
  a missing baseUrl keeps the previous behaviour
- make the request-validator mock enforce the tool name like the API does and
  cover the name, the host gate, and third-party 400 attribution in tests
- record the change in tool-search/changes.md

Surfaced by code-yeongyu#1314: eval-only routing leaves the catalog non-empty for every
session, which turned the adapter on for everyone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@code-yeongyu

Copy link
Copy Markdown
Owner

Both defects this PR describes are now fixed on main and released, so this branch (currently DIRTY against main) is superseded:

  1. Wrong tool name — fixed by d0f197eb9 ("name the Anthropic native tool-search tool tool_search_tool_bm25"), released in 2026.9.4. Verified on the published artifact: dist/core/extensions/builtin/tool-search/native-search.js exports ANTHROPIC_TOOL_SEARCH_NAME = "tool_search_tool_bm25" with ANTHROPIC_TOOL_SEARCH_TYPE = "tool_search_tool_bm25_20251119".
  2. Injected on every anthropic-messages endpoint — fixed by fix(coding-agent): gate Anthropic native tool-search on model support and emit tool_reference.tool_name #1354 (merged as ec595f7e6, released in 2026.9.4-3), which replaced the api-only gate with supportsAnthropicNativeToolSearch() in the new builtin/tool-search/native-support.ts. It mirrors pi-ai's private supportsToolReferences default: injection requires api === "anthropic-messages" and an Anthropic-provider model on the Opus/Sonnet 4.5+ or Fable line, so gateways/proxies (Kimi Code, OpenRouter, ccapi) no longer receive the server tool. Executed against the published 2026.9.4-3 tarball: 9/9 gate cases correct, including a gateway provider on anthropic-messages returning false.

#1354 also fixed a third defect this PR does not cover: buildToolReferenceBlocks() emitted tool_reference blocks under name instead of the API's tool_name, and the mock fixture encoded the same bug so both expansion tests passed over it.

Closing is the owner's call — flagging the overlap so this does not get rebased onto work that already landed.

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