Files
deepseek-harness/packages/web/web-fetch-local

@deepseek-ai/dsh-web-fetch-local

An anonymous public HTTP(S) WebFetchProvider for the harness web capability seam (ctx.web). It retrieves a concrete URL and returns a status code plus bounded decoded content.

This is an implementation package: it registers a provider into ctx.web, it does not own the key and it does not register a model-facing tool. It is a function/namespace plugin (inject: ['web']).

Responsibility split

The provider owns safe resource retrieval: URL validation, HTTP transport, redirect policy, a resource-backstop timeout, abort propagation, byte caps, charset decoding, content-type classification, and binary rejection. @deepseek-ai/dsh-tool-web owns presentation (HTML→markdown, truncation formatting). A non-2xx HTTP response is a result (status code + decoded body), not an error; WebError is reserved for failures to safely retrieve or represent the resource.

The provider's timeoutMs/maxTimeoutMs is a resource backstop for direct ctx.web.fetch() callers and misconfigured deployments — it is NOT the model-facing tool-call budget. The tool-call budget for web_fetch is deployment policy owned by @deepseek-ai/dsh-timeout-policy, which arms a per-call deadline on exec.signal. A shipped web-tool deployment sets the provider backstop above the tool-timeout budget, so the tool-call policy normally wins for model calls (returning TOOL_TIMEOUT); when the outer deadline signal reaches this provider first, it classifies as WEB_ABORTED and the outer wrapper replaces the result with TOOL_TIMEOUT. The provider's own WEB_FETCH_TIMEOUT only fires for a direct seam caller whose own budget elapsed.

Transport hygiene

  • Accepts only http: and https: URLs; rejects credentials in URLs (WEB_BLOCKED_URL) and over-long/malformed URLs (WEB_INVALID_URL).
  • Enforces a max URL length, response byte cap (WEB_FETCH_TOO_LARGE), decoded body character cap, timeout (WEB_FETCH_TIMEOUT), and redirect hop cap.
  • Propagates the caller's abort signal (WEB_ABORTED) into the network request and the streaming read.
  • Follows only same-origin redirects; a cross-origin redirect fails with WEB_REDIRECT_BLOCKED, requiring a fresh tool call (the model of Claude Code's WebFetch).
  • Sends an explicit product User-Agent, never a browser disguise.
  • Rejects unsupported (e.g. binary) content types with WEB_UNSUPPORTED_CONTENT_TYPE.

Config

Key Default Meaning
maxUrlLength 2048 Maximum accepted request URL length.
maxResponseBytes 5_000_000 Maximum response body size in bytes.
maxBodyChars 100_000 Maximum decoded body length in characters.
timeoutMs 30_000 Default fetch timeout — a resource backstop for direct ctx.web.fetch() callers, not the model-facing tool-call budget (that is dsh-timeout-policy).
maxTimeoutMs 120_000 Upper bound for a per-request timeout override (direct callers).
maxRedirects 5 Maximum same-origin redirect hops (0 follows none).
userAgent deepseek-harness/… User-Agent header.

The numeric limits are validated at plugin construction: every cap except maxRedirects must be a positive finite number, and maxRedirects must be a non-negative integer. An invalid value throws rather than silently constructing a provider with nonsensical limits.

Model Experience

Context surface What the model sees Token effect
Web fetch result, indirectly Through dsh-tool-web, the conversation model sees the final URL, HTTP status, and decoded text or markdown-shaped HTML, or a structured retrieval error. Redirects, headers, and transport mechanics are not added to context unless reflected in an error. Zero direct tokens. maxBodyChars bounds decoded result length before the tool records it; the retained result is resent until compaction.

Known Limitations and Deferred Work

  • SSRF / private-network protection is deferred — no blocking of private, loopback, link-local, multicast, or otherwise non-public destinations, no DNS-resolve-then-validate, no per-hop re-validation (see the web capability seam RFC). Until it lands, this provider is an SSRF primitive and must not be enabled in a deployment that can reach sensitive internal network targets.
  • Only textual content decodes — html/xhtml and text/*-plus-JSON/XML families; a missing Content-Type or any binary type throws WEB_UNSUPPORTED_CONTENT_TYPE, and text-extractable PDF decoding is named deferred work.
  • Charset comes only from the Content-Type header (UTF-8 default) — an HTML <meta charset> declaration is ignored, and a declared-but-unrecognized charset label throws rather than falling back.