Skip to content

feat(web-search): intercept web search tool calls and execute via SearXNG#135

Open
jsboige wants to merge 1 commit into
MadAppGang:mainfrom
jsboige:feat/web-search-searxng-interception
Open

feat(web-search): intercept web search tool calls and execute via SearXNG#135
jsboige wants to merge 1 commit into
MadAppGang:mainfrom
jsboige:feat/web-search-searxng-interception

Conversation

@jsboige
Copy link
Copy Markdown

@jsboige jsboige commented May 30, 2026

Problem

When providers (Z.AI, GLM, OpenRouter models) emit WebSearch/WebFetch tool calls, they fail because the proxy doesn't implement server-side web search. GLM models also emit <searchWeb>query</searchWeb> tags in their text output, which go unhandled. Additionally, Claude Code's built-in WebSearch tool fires sub-agent requests that get forwarded to the model provider instead of being executed locally.

Changes

New file: packages/cli/src/handlers/shared/web-search-executor.ts

A stream-safe SearXNG client that:

  • Queries a local SearXNG instance (SEARXNG_URL env var, defaults to http://search.myia.io)
  • Races SearXNG against a configurable deadline (default 3s)
  • Returns formatted results as text, never throws
  • Extracts search queries from tool call arguments JSON

packages/cli/src/proxy-server.ts

Add interceptWebTools() function that intercepts sub-agent web search/fetch requests BEFORE handler selection. Detects the standard Claude Code WebSearch sub-agent pattern ("Perform a web search for the query: X") and returns SearXNG results as a streaming or non-streaming Anthropic-format response.

packages/cli/src/handlers/shared/stream-parsers/openai-sse.ts

Three enhancements:

  1. WebSearch tool suppression -- When a structured WebSearch/WebFetch tool call is detected, set suppressed = true and buffer arguments. At finalization, execute SearXNG and inject results as a text block.

  2. GLM <searchWeb> tag detection -- Detect <searchWeb>query</searchWeb> tags in accumulated text and execute SearXNG for each query.

  3. Empty response handling -- When no text or tool blocks were started, inject a synthetic error message so downstream doesn't hang.

Providers affected

  • Z.AI (GLM models)
  • GLM direct
  • OpenRouter models that emit web search tool calls
  • Any provider where Claude Code's WebSearch sub-agent is routed through claudish

How to test

  1. Sub-agent interception:

    claudish --model zai@glm-4.7 "search the web for recent AI news"
    # Verify: SearXNG results appear in response instead of tool call error
  2. GLM tags:

    claudish --model glm-4.7 --debug "search for..."
    # Check debug log for [Streaming] Found N <searchWeb> tag(s) in text
  3. SearXNG URL configuration:

    SEARXNG_URL=http://localhost:8080 claudish --model glm-4.7 "search for..."

Notes

  • SearXNG is expected to be running and accessible. If SearXNG is unavailable, a timeout/failure message is injected instead of crashing.
  • The SEARXNG_URL env var should be documented in the main README as an optional configuration.

…rXNG

When providers (Z.AI, GLM, OpenRouter) emit WebSearch/WebFetch tool calls
or GLM's <searchWeb> tags, intercept them and execute via a local SearXNG
instance instead of letting them fail. This covers three interception
points:

1. Sub-agent requests in proxy-server.ts: Claude Code's WebSearch tool
   fires a sub-agent with a single user message. Intercept before handler
   selection and return SearXNG results.

2. Structured tool calls in openai-sse.ts: When the OpenAI SSE parser
   detects a WebSearch/WebFetch tool call, suppress it, execute SearXNG,
   and inject results as a text block in the stream.

3. GLM <searchWeb> tags: Detect these in accumulated text and execute
   SearXNG for each query, injecting results as text.

Also handles empty responses (no content blocks) with a synthetic error
message so downstream doesn't hang.

New file: web-search-executor.ts — stream-safe SearXNG client with
deadline-based timeout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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