Description
Claude Code's web search tool does not work when ccrouter transforms Anthropic requests to OpenAI-format endpoints (e.g. GPT/Codex). The transformer currently has no explicit handling for web_search / search_result / search-specific content or tool types, and unknown content blocks are silently dropped.
Context
Current transform behavior (src/transform.rs, src/stream.rs, src/handler.rs):
Works
- text + image content
- standard Anthropic tools → OpenAI
function tools
- assistant
tool_use ↔ OpenAI tool_calls
- user
tool_result ↔ OpenAI role: "tool"
- streamed tool-call assembly back into Anthropic SSE
Missing / likely breakpoints for web search
convert_content_parts() only preserves text and image; any other block type is dropped
- all Anthropic tools are forced into OpenAI
{type: "function", ...} shape
- non-stream response conversion only understands string
message.content and tool_calls
- stream conversion only understands text deltas and tool-call deltas
- transformed profiles hard-coded to
POST {base_url}/chat/completions
- no tests covering any of this
Assessment
- Research difficulty: medium/high — mostly because protocol evidence is missing, not because the code is hard
- Generic translation across OpenAI-compatible providers: not realistic yet. Web search is one of the least standardized areas; support likely needs to be provider-specific
- Pass-through Anthropic profiles are unaffected (feature already works end-to-end with any Anthropic-compatible upstream)
Proposed approach
Two phases.
Phase 1 — fail fast (small, safe)
- detect search-related Anthropic constructs in the request
- if active profile is
format = \"openai\" and no search adapter exists, return a clear error instead of silently stripping fields
- document the limitation in README
Phase 2 — provider-specific adapters (after capturing real traffic)
- extend
Profile in src/config.rs with capability fields (endpoint family, search support mode)
- split
to_openai_request into a generic base + capability-specific post-processing
- extend non-stream + stream response conversion to carry search/citation output back to Claude Code
- make endpoint selection per-profile in
src/handler.rs so providers that only expose search on a non-chat endpoint (e.g. /responses) can be supported
Evidence needed before Phase 2
Protocol captures on both sides:
Acceptance criteria
Critical files (for reference)
src/transform.rs
src/stream.rs
src/handler.rs
src/config.rs
README.md
Description
Claude Code's web search tool does not work when ccrouter transforms Anthropic requests to OpenAI-format endpoints (e.g. GPT/Codex). The transformer currently has no explicit handling for
web_search/search_result/ search-specific content or tool types, and unknown content blocks are silently dropped.Context
Current transform behavior (
src/transform.rs,src/stream.rs,src/handler.rs):Works
functiontoolstool_use↔ OpenAItool_callstool_result↔ OpenAIrole: "tool"Missing / likely breakpoints for web search
convert_content_parts()only preservestextandimage; any other block type is dropped{type: "function", ...}shapemessage.contentandtool_callsPOST {base_url}/chat/completionsAssessment
Proposed approach
Two phases.
Phase 1 — fail fast (small, safe)
format = \"openai\"and no search adapter exists, return a clear error instead of silently stripping fieldsPhase 2 — provider-specific adapters (after capturing real traffic)
Profileinsrc/config.rswith capability fields (endpoint family, search support mode)to_openai_requestinto a generic base + capability-specific post-processingsrc/handler.rsso providers that only expose search on a non-chat endpoint (e.g./responses) can be supportedEvidence needed before Phase 2
Protocol captures on both sides:
/v1/messagesrequest with web search enabled (non-stream)/chat/completions,/responses, or provider-specific?)Acceptance criteria
Critical files (for reference)
src/transform.rssrc/stream.rssrc/handler.rssrc/config.rsREADME.md