WebFetchTool / WebSearchTool: Cline account's network tools
Responsibilities
These two handlers are Cline's network-egress tools. Both implement IFullyManagedTool, and both require before execution that the provider is cline, the user has enabled the clineWebToolsEnabled setting, and the corresponding feature flag is on (WebFetch class:20-21, WebSearch class:21-22). The essential difference from BrowserToolHandler is that the browser tool uses a local or remote Chrome to fetch pages directly, whereas these two tools send HTTP requests to Cline's official API, letting the Cline server do the fetching or searching and then returning the result to the model.
- WebFetchToolHandler (
ClineDefaultTool.WEB_FETCH): the model supplies a URL and a prompt; the tool POSTs both to${apiBaseUrl}/api/v1/search/webfetch, and the server uses the prompt to extract content from the URL and returns a text result (webfetch axios:150). - WebSearchToolHandler (
ClineDefaultTool.WEB_SEARCH): the model supplies a query and optionalallowed_domains/blocked_domainsallow/blacklist (mutually exclusive); the tool POSTs to/api/v1/search/websearchand returns a set of {title, url} results (websearch axios:174).
The output of both tools is not shown to the user directly; it is only stuffed back into userMessageContent as a tool result for the next LLM turn. They need no local resources (filesystem, terminal, browser) — they are purely network egress, so operationIsLocatedInWorkspace: false.
Design motivation
- Server-side proxy instead of local fetch: in many scenarios Cline runs in restricted environments where direct internet access is unavailable; the server-side proxy unifies authentication, rate limiting, and fetch logic (JavaScript rendering, anti-crawl bypass), and the client only sends requests (
webfetch request:150-166). - Three gates: the provider must be cline, the user setting clineWebToolsEnabled must be on, and the feature flag must be enabled; failing any one returns
toolError("Cline web tools are currently disabled.")directly, not letting the model continue (feature flag gate:55-59). - 15-second timeout: both tools use a 15-second axios timeout to prevent long-tail requests from stalling the whole turn (
timeout:163). - Domain allow/blacklist mutual exclusion: web_search validates that
allowed_domainsandblocked_domainsare not supplied together; on violationmistake+++ toolError (mutual exclusivity:75-78). - Streaming partial-array parsing:
parsePartialArrayStringcan extract already-complete elements even when streaming emits incomplete JSON like["a", "b", so the domain list can be displayed during the partial-block phase (parsePartialArrayString:71-72). - Explicit auth errors: when the auth token cannot be obtained, it throws
CLINE_ACCOUNT_AUTH_ERROR_MESSAGEtelling the user to log in to their Cline account, rather than sending a 401 to the model (auth check:146-148).
Key files
WebFetch class:20—name = ClineDefaultTool.WEB_FETCH.handlePartialBlock:27— during streaming just says a "Fetching URL: ..." placeholder, non-blocking.execute:44— main flow: gate → parameter validation → approval → hook → axios POST → return result.feature flag gate:55-59— provider !== "cline" or any setting/flag off returns toolError directly.approval flow:81-128—shouldAutoApproveTooldecides between say and ask.axios POST webfetch:143-166— get baseUrl + authToken, POST/api/v1/search/webfetch, body{Url, Prompt}, header carriesX-Task-ID.WebSearch class:21—name = ClineDefaultTool.WEB_SEARCH.domain parse + mutex:70-78—parsePartialArrayStringparses the domain array + mutual-exclusion check.axios POST websearch:150-183— POST/api/v1/search/websearch, body only carries the corresponding field when the array is non-empty.format results:188-200— concatenates{title, url}[]into1. title\n url\n\ntext format.CLINE_ACCOUNT_AUTH_ERROR_MESSAGE— unified prompt text for auth errors.AuthService—getInstance().getAuthToken()retrieves the current account token.
Data flow
The flows of the two handlers are nearly identical; using WebFetchToolHandler as the example, the core is the POST to the Cline API after approval:
// 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() adds extra tracing headers, and getAxiosSettings() injects user-configured proxy/CA settings into axios. The response shape is { data: { result: "..." } } (note axios adds an extra data wrapper); the inner field is extracted and wrapped with formatResponse.toolResult as the tool result for the LLM.
WebSearchToolHandler has one extra step for result formatting:
// 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 are only included in the request body when non-empty; the mutual-exclusion check rejects with mistake++ and toolError directly without sending a request.
Boundaries and failure modes
- Three gates unmet: provider is not cline, the user has turned off web tools setting, or the feature flag is off — any of these returns a disabled toolError directly without incrementing
mistake(feature flag gate:55-59). - Not logged in: when
AuthService.getInstance().getAuthToken()returns empty, throwsCLINE_ACCOUNT_AUTH_ERROR_MESSAGE, which the catch turns intoError fetching web content: ...(auth check:146-148). - Domain allow/blacklist both given: when web_search is given both
allowed_domainsandblocked_domains,mistake+++ toolError intercepts directly (mutual exclusivity:75-78). - PreToolUse hook cancellation: when the hook throws
PreToolUseHookCancellationError, returns toolDenied and sends no request (PreToolUse hook:131-140). - Request failure fallback: axios errors or non-200 server responses are wrapped in the catch as
Error fetching web content: .../Error performing web search: ..., not propagated to the Task, letting the model see the error (error catch:173-175). - Timeout: 15-second axios timeout; long-tail requests are cut off and go through the error catch (
timeout:163). - Empty results: when web_search returns 0 results, returns
Search completed (0 results found)with no following lines (empty results:193-199).
Summary
WebFetch and WebSearch are the two handlers Cline uses to do network fetching/searching through its own server-side proxy. They share the same gate (provider=cline + setting + flag), the same approval/hook flow, and the same 15-second timeout. WebFetch returns the text extracted by the server using the prompt; WebSearch returns a structured {title, url} list concatenated into text in 1./2./3. format. Neither tool writes to disk or opens a browser — they are purely network egress.
To dig deeper, continue with:
- Browser tools (local fetching):
/cap-tools/browser - File and command tools:
/cap-tools/file-ops - Tool scheduling chain:
/agent-loop/task-class