qwen-code/docs/developers/tools/web-search.md
tanzhenxin 443b81e180
feat(core): add opt-in built-in web_search backed by the DashScope Responses API (#7215)
* feat(core): add opt-in built-in web_search backed by the DashScope Responses API

Claude-Session: https://claude.ai/code/session_01KwsYFzWZ6VLCxVN8MbeFXb

* fix(core): require HTTPS for the web_search backend; fail closed on unresolved agent allow-lists

Review follow-ups on #7215: the endpoint gate now rejects plaintext
endpoints (the side request carries a bearer key), and an agent allow-list
whose names resolve to no registered tool keeps its dead entries instead of
widening to the inherited toolset — an agent restricted to the unavailable
WebSearch now runs tool-less rather than gaining shell/write.

Claude-Session: https://claude.ai/code/session_01KwsYFzWZ6VLCxVN8MbeFXb

* chore(cli): remove test-leaked debug artifacts; gitignore the leaked dirs

The CLI unit-test suites write debug logs relative to the package dir
(custom/, first/, from-env/, workspace/); a merge-commit git add swept
them in. Remove them and ignore the directories until the tests are
pointed at temp dirs.

Claude-Session: https://claude.ai/code/session_01KwsYFzWZ6VLCxVN8MbeFXb

* fix(core): honor web_search's own result budget and salvage in-stream-error partials

- Override maxOutputChars (result limit + envelope headroom) so the
  scheduler's global 25k threshold no longer slices results before the
  tool's section-aware truncation can protect URL evidence sections.
- Route in-stream backend errors through the shared terminal-failure
  tail so results streamed (and billed) before the error surface as a
  partial result, matching the transport-error path.
- Strengthen gate tests: assert gate.ok before webExtractor, exercise
  the https-only endpoint guard, and make the config mock disambiguate
  same-id entries by baseUrl like the real Config.

* fix(cli): treat whitespace-only WEB_SEARCH_API_KEY as unset

Apply the function's set-but-empty-is-unset rule to the API key env
var like every sibling env read, and add loadCliConfig coverage for
the web search settings resolution (env precedence, empty-env
fallthrough, base-URL key selection).

* fix(core): salvage failed-terminal web_search results and name the exact endpoint disqualifier

- Route the failed/cancelled terminal paths through the shared
  terminal-failure tail so search evidence streamed (and billed) before
  the backend gave up is salvaged, consistent with the in-stream-error
  and transport-error paths; regression test included.
- Classify base-URL gate rejections so the startup notice blames the
  actual disqualifier: a plaintext-HTTP endpoint now gets an "use
  https://" notice at both the env-declared and modelProviders sites
  instead of the misleading "non-DashScope endpoint" text.
- Cover WEB_SEARCH in the speculation boundary-tools test and the US
  regional host in the DashScope provider test — both behavioral
  changes this PR introduced without direct test coverage.

* test(cli): cover web search suppression in safe and bare modes

The bareMode/safeMode guard is the escape hatch that keeps web search
(external, billed API calls) off in troubleshooting modes; assert that
an enabled settings config resolves to no web search settings under
--safe-mode and --bare.

* fix(core): parse the search model selector once for both gate paths

A selector written for the modelProviders path ("openai:<model-id>", as
the gate's own OAuth notice suggests) was sent verbatim to DashScope
when WEB_SEARCH_BASE_URL overrode the backend, failing with
InvalidParameter. Hoist the resolveModelId parse above the env branch
so both paths share one interpretation of the selector.

Also cover two review gaps: the Claude extension WebSearch tool mapping
and the ACP startup-warning emission that surfaces WebSearch
misconfiguration notices in the client log.

* fix(core): handle response.cancelled in the web_search terminal-event switch and trim gate env keys

- Add response.cancelled to the terminal-event switch so the
  status === 'cancelled' handler is reachable instead of dead code
- Trim API key env vars in the gate (all three check sites), matching
  the CLI-side whitespace rule from 302cf3bb7
- Add tests: cancelled with/without prior search, whitespace-only env
  key rejection, schema getter month/year embedding

* fix(core): cap opened URLs, suppress failed-item progress, note retry budget (#7215)

* fix(core): reject unresolved selector on env-declared web_search path (#7215)

---------

Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
2026-07-21 10:59:36 +00:00

9.8 KiB

Web Search

Qwen Code provides web search two ways:

  1. Built-in web_search tool (opt-in) — backed by the DashScope Responses API server-side search. Works with a standard Bailian (DashScope) API key; no extra provider or MCP setup.
  2. MCP (Model Context Protocol) integrations — connect any external search service (Tavily, GLM, and others). Use this when you don't have a DashScope key.

Built-in web_search (opt-in)

The built-in tool issues a self-contained search request to a small auxiliary model with DashScope's server-side web_search (and web_extractor) tools, and returns the narrated findings plus source URLs. It never activates implicitly — two settings are required:

{
  "modelProviders": {
    "openai": [
      {
        "id": "qwen3.6-plus",
        "envKey": "DASHSCOPE_API_KEY",
        "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1"
      }
    ]
  },
  "tools": {
    "webSearch": {
      "enabled": true,
      "model": "qwen3.6-plus"
    }
  }
}
Setting Env override Meaning
tools.webSearch.enabled ENABLE_WEB_SEARCH Opt-in flag. Required.
tools.webSearch.model WEB_SEARCH_MODEL Search model selector, resolved against modelProviders like fastModel (modelId or authType:modelId). Required — no default. Recommended: qwen3.6-plus.
tools.webSearch.webExtractor WEB_SEARCH_EXTRACTOR Let the search agent open result pages for better-grounded answers (default true; billed separately by DashScope).

Env-only configuration (no settings.json)

For environments where you cannot write a settings file (locked-down containers, CI with env injection only), the tool can be configured entirely through environment variables — no modelProviders entry needed:

export ENABLE_WEB_SEARCH=true
export WEB_SEARCH_MODEL=qwen3.6-plus
export WEB_SEARCH_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
export DASHSCOPE_API_KEY=sk-...        # or set WEB_SEARCH_API_KEY instead

WEB_SEARCH_BASE_URL mirrors a modelProviders entry's baseUrl and must be a DashScope-compatible endpoint; when it is set, it takes precedence over modelProviders resolution and WEB_SEARCH_MODEL is used as the plain DashScope model id. The API key is read from WEB_SEARCH_API_KEY if set, otherwise from DASHSCOPE_API_KEY. Misconfiguration still surfaces as a startup notice.

Notes:

  • The selector must resolve to a DashScope-compatible modelProviders entry carrying a direct API key via envKey. Your main model can be any provider — only the search side request needs a DashScope entry. Qwen OAuth cannot back the tool.
  • If enabled but misconfigured, the tool stays off and a startup notice explains which condition failed.
  • Searches bill your DashScope key (usage.x_tools counts). The tool asks for confirmation by default; approving with "always allow" persists a standard WebSearch permission rule, like other tools.
  • There is no client-side model allowlist; a model the Responses endpoint does not serve fails loudly on first use.

MCP alternatives

If you don't have a DashScope key, web search is available by connecting an external MCP server — see the services below.

⚠️ Historical Breaking Change: original built-in web_search removed

Affected versions: V0.0.7+ through the last release with the original multi-provider built-in web search.

The original built-in web_search tool (Tavily/Google/GLM/DashScope multi-provider) and its configuration were removed. The new opt-in built-in tool above is a different implementation with different configuration. If you were using any of the following, migrate either to the new built-in tool (DashScope) or to MCP:

Removed What to do
webSearch block in settings.json Configure an MCP server in mcpServers instead (see below)
advanced.tavilyApiKey in settings.json Use the Tavily MCP server
TAVILY_API_KEY environment variable Use the Tavily MCP server
DASHSCOPE_API_KEY for web search Use the built-in web_search tool
GLM_API_KEY for web search Use the GLM WebSearch Prime MCP
--tavily-api-key / --glm-api-key / --dashscope-api-key CLI flags Configure via mcpServers in settings.json

Migration Examples

Before (Tavily via built-in tool):

{
  "webSearch": {
    "provider": [{ "type": "tavily", "apiKey": "tvly-xxx" }],
    "default": "tavily"
  }
}

After (Tavily via MCP):

{
  "mcpServers": {
    "tavily": {
      "httpUrl": "https://mcp.tavily.com/mcp/?tavilyApiKey=tvly-xxx"
    }
  }
}

Before (DashScope via built-in tool):

{
  "webSearch": {
    "provider": [{ "type": "dashscope", "apiKey": "sk-xxx" }],
    "default": "dashscope"
  }
}

After (Alibaba Cloud Bailian WebSearch via MCP):

{
  "mcpServers": {
    "WebSearch": {
      "httpUrl": "https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp",
      "headers": {
        "Authorization": "Bearer sk-xxx"
      }
    }
  }
}

Supported MCP Web Search Services

Alibaba Cloud Bailian WebSearch

The official web search MCP service provided by Alibaba Cloud Bailian platform, powered by DashScope. If you have a DashScope key, prefer the built-in web_search tool above — it uses a stronger search path than this MCP service.

Setup

Method 1: CLI command

qwen mcp add WebSearch \
  -t http \
  "https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp" \
  -H "Authorization: Bearer ${DASHSCOPE_API_KEY}"

Method 2: settings.json

{
  "mcpServers": {
    "WebSearch": {
      "httpUrl": "https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp",
      "headers": {
        "Authorization": "Bearer ${DASHSCOPE_API_KEY}"
      }
    }
  }
}

Replace ${DASHSCOPE_API_KEY} with your actual API key, or set it as an environment variable so Qwen Code picks it up automatically.


Tavily WebSearch

A production-ready MCP server providing real-time web search, extract, map, and crawl capabilities.

Available Tools

  • tavily_search — Real-time web search
  • tavily_extract — Intelligent data extraction from web pages
  • tavily_map — Create a structured map of a website
  • tavily_crawl — Systematically explore websites

Setup

Method 1: CLI command (Remote MCP)

qwen mcp add tavily \
  -t http \
  "https://mcp.tavily.com/mcp/?tavilyApiKey=${TAVILY_API_KEY}"

Method 2: settings.json (Remote MCP)

{
  "mcpServers": {
    "tavily": {
      "httpUrl": "https://mcp.tavily.com/mcp/?tavilyApiKey=${TAVILY_API_KEY}"
    }
  }
}

Replace ${TAVILY_API_KEY} with your actual API key, or set it as an environment variable.

Method 3: settings.json (Local NPX)

{
  "mcpServers": {
    "tavily-mcp": {
      "command": "npx",
      "args": ["-y", "tavily-mcp@latest"],
      "env": {
        "TAVILY_API_KEY": "your-api-key-here"
      }
    }
  }
}

GLM WebSearch Prime (ZhipuAI)

The official web search Remote MCP service provided by ZhipuAI (智谱AI), designed for GLM Coding Plan users. Provides real-time web search including news, stock prices, weather, and more.

Available Tools

  • webSearchPrime — Web search returning page title, URL, summary, site name, and favicon

Setup

Method 1: CLI command

qwen mcp add web-search-prime \
  -t http \
  "https://open.bigmodel.cn/api/mcp/web_search_prime/mcp" \
  -H "Authorization: Bearer ${GLM_API_KEY}"

Method 2: settings.json

{
  "mcpServers": {
    "web-search-prime": {
      "httpUrl": "https://open.bigmodel.cn/api/mcp/web_search_prime/mcp",
      "headers": {
        "Authorization": "Bearer ${GLM_API_KEY}"
      }
    }
  }
}

Replace ${GLM_API_KEY} with your actual ZhipuAI API key, or set it as an environment variable.