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