WebFetchTool / WebSearchTool:Cline 账号的网络工具
职责
这两个 handler 是 Cline 的网络出口工具,都实现 IFullyManagedTool,都在执行前要求 provider 是 cline 且用户开了 clineWebToolsEnabled 设置且对应 feature flag 开启(WebFetch class:20-21,WebSearch class:21-22)。它们和 BrowserToolHandler 的本质区别是:浏览器工具是本地或远程 Chrome 直接抓页面,这两个工具是发 HTTP 请求到 Cline 官方 API,让 Cline 服务端做抓取或搜索,再把结果回给模型。
- WebFetchToolHandler(
ClineDefaultTool.WEB_FETCH):模型给一个 URL 和一个 prompt,工具把这两者 POST 到${apiBaseUrl}/api/v1/search/webfetch,服务端用 prompt 抽取 URL 内容并返回一段文本结果(webfetch axios:150). - WebSearchToolHandler(
ClineDefaultTool.WEB_SEARCH):模型给一个 query 和可选的allowed_domains/blocked_domains白黑名单(互斥),工具 POST 到/api/v1/search/websearch,返回一组 {title, url} 结果(websearch axios:174).
这两个工具的输出都不直接呈现给用户,只作为 tool result 塞回 userMessageContent 给下一轮 LLM。它们不需要本地资源(文件系统、terminal、浏览器),完全是网络出口,所以 operationIsLocatedInWorkspace: false。
设计动机
- 服务端代理而非本地抓:很多场景里 Cline 跑在受限环境,直连外网不通;服务端代理统一鉴权、统一限速、统一抓取逻辑(JavaScript 渲染、反爬绕过),客户端只发请求(
webfetch request:150-166)。 - 三闸门:provider 必须是 cline、用户设置 clineWebToolsEnabled、feature flag 开启,任一不满足直接返回
toolError("Cline web tools are currently disabled."),不让模型继续(feature flag gate:55-59)。 - 15 秒超时:两个工具都用 15 秒 axios 超时,避免长尾请求拖死整轮(
timeout:163)。 - 域名白黑名单互斥:web_search 校验
allowed_domains和blocked_domains不能同时给,违反时mistake+++ toolError(mutual exclusivity:75-78)。 - 流式部分数组解析:
parsePartialArrayString在流式吐["a", "b"这种不完整 JSON 时也能提取已完整的元素,让 partial block 阶段就能显示域名列表(parsePartialArrayString:71-72). - 认证错误明确化:auth token 取不到时抛
CLINE_ACCOUNT_AUTH_ERROR_MESSAGE,告知用户去登录 Cline 账号,而不是发 401 给模型(auth check:146-148).
关键文件
WebFetch class:20—name = ClineDefaultTool.WEB_FETCH。handlePartialBlock:27— 流式期间只 say 一个「Fetching URL: ...」placeholder,不阻塞。execute:44— 主流程:gate → 参数校验 → 审批 → hook → axios POST → 返回 result。feature flag gate:55-59— provider !== "cline" 或设置/flag 任一为否直接 toolError。approval flow:81-128—shouldAutoApproveTool决定走 say 还是 ask。axios POST webfetch:143-166— 取 baseUrl + authToken,POST/api/v1/search/webfetch,body{Url, Prompt},header 带X-Task-ID。WebSearch class:21—name = ClineDefaultTool.WEB_SEARCH。domain parse + mutex:70-78—parsePartialArrayString解析域名数组 + 互斥校验。axios POST websearch:150-183— POST/api/v1/search/websearch,body 只在数组非空时才带对应字段。format results:188-200— 把{title, url}[]拼成1. title\n url\n\n文本格式。CLINE_ACCOUNT_AUTH_ERROR_MESSAGE— 认证错误的统一提示文本。AuthService—getInstance().getAuthToken()取当前账号 token。
数据流
两个 handler 的流程几乎一致,以 WebFetchToolHandler 为例,核心是审批后 POST 到 Cline API:
// apps/vscode/src/core/task/tools/handlers/WebFetchToolHandler.ts
const baseUrl = ClineEnv.config().apiBaseUrl
const authToken = await AuthService.getInstance().getAuthToken()
if (!authToken) {
throw new Error(CLINE_ACCOUNT_AUTH_ERROR_MESSAGE)
}
const response = await axios.post(
`${baseUrl}/api/v1/search/webfetch`,
{ Url: url, Prompt: prompt },
{
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
"X-Task-ID": config.ulid || "",
...(await buildClineExtraHeaders()),
},
timeout: 15000,
...getAxiosSettings(),
},
)
const result = response.data.data.result
return formatResponse.toolResult(result)buildClineExtraHeaders() 加额外追踪头,getAxiosSettings() 给 axios 注入代理/CA 等用户配置。响应结构是 { data: { result: "..." } }(注意 axios 自动加了一层 data 包裹),直接取出来用 formatResponse.toolResult 包成 tool result 回给 LLM。
WebSearchToolHandler 多一步结果格式化:
// apps/vscode/src/core/task/tools/handlers/WebSearchToolHandler.ts
const data = response.data.data
const results = data.results || []
let resultText = `Search completed (${resultCount} results found)`
if (results.length > 0) {
resultText += ":\n\n"
results.forEach((result, index) => {
resultText += `${index + 1}. ${result.title}\n ${result.url}\n\n`
})
}
return formatResponse.toolResult(resultText)allowed_domains / blocked_domains 在请求体里只在非空时才带,互斥校验在 mistake++ 后直接 toolError 拒掉,不发请求。
边界与失败
- 三闸门未满足:provider 非 cline、用户关了 web tools 设置、feature flag 关闭,任一为否直接返回 disabled toolError,
mistake不增(feature flag gate:55-59)。 - 未登录:
AuthService.getInstance().getAuthToken()返回空时抛CLINE_ACCOUNT_AUTH_ERROR_MESSAGE,在 catch 里被转成Error fetching web content: ...(auth check:146-148)。 - 域名白黑名单同给:web_search 同时给
allowed_domains和blocked_domains时mistake+++ toolError,直接拦截(mutual exclusivity:75-78)。 - PreToolUse hook 取消:hook 抛
PreToolUseHookCancellationError时返回 toolDenied,不发请求(PreToolUse hook:131-140)。 - 请求失败兜底:axios 抛错或服务端非 200,catch 里包成
Error fetching web content: .../Error performing web search: ...,不向 Task 抛,让模型看错误(error catch:173-175)。 - 超时:15 秒 axios 超时,长尾请求被掐断并走 error catch(
timeout:163)。 - 结果空:web_search 返回 0 结果时返回
Search completed (0 results found),不带后续行(empty results:193-199)。
小结
WebFetch 和 WebSearch 是 Cline 通过自家服务端代理做网络抓取/搜索的两个 handler,共享同一套 gate(provider=cline + setting + flag)、同一套审批/hook 流程、同一个 15 秒超时。WebFetch 返回服务端用 prompt 抽取后的文本,WebSearch 返回结构化的 {title, url} 列表并按 1./2./3. 格式拼成文本。两个工具都不写盘、不开浏览器,完全是纯网络出口。
想往里钻可以接着看:
- 浏览器工具(本地抓取):
/cap-tools/browser - 文件与命令工具:
/cap-tools/file-ops - 工具调度链路:
/agent-loop/task-class