description: "The web access service (ctx.web): how deployments and plugin authors search the web and fetch URLs through interchangeable providers, with one selection policy and error vocabulary."
English | 中文
Use dsh-web to search the web or fetch a URL without tying callers to a specific vendor. It selects a usable backend for each operation and gives callers consistent cancellation, errors, and result limits. Choose it for plugins or tools that call ctx.web.search() or ctx.web.fetch(); the shipped dsh-tool-web tools load it for you. A search or fetch requires a configured, usable provider because this package does not make network requests on its own.
A composition that needs web access loads the dsh-web service and mounts at least one backend — a search provider and/or a fetch provider — and plugin or tool authors then call ctx.web.search() and ctx.web.fetch() directly. The service resolves the backend for each call, so callers never see provider ids unless they configured one.
Choose the service when a plugin or tool must search or fetch without hard-coding a vendor; a deployment that only uses the shipped web_search/web_fetch tools gets it for free through dsh-tool-web. You do not need it when the composition never reaches the web. The service adds no network access of its own: without at least one usable provider, every call fails with a structured WebError.
Load the service and let a single mounted backend auto-select, or pin a provider id with searchProvider/fetchProvider. The environment variables $DSH_WEB_SEARCH_PROVIDER and $DSH_WEB_FETCH_PROVIDER feed the same fields and are not a separate priority chain.
- name: '@deepseek-ai/dsh-web'
- name: '@deepseek-ai/dsh-web-search-exa'
- name: '@deepseek-ai/dsh-web-fetch-http'
| Field | Default | Meaning |
|---|---|---|
searchProvider |
(unset) | Pinned search provider id; unset auto-selects when exactly one is usable |
fetchProvider |
(unset) | Pinned fetch provider id; unset auto-selects when exactly one is usable |
The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.
search() runs one query and returns an optional provider answer plus a list of citeable sources; the service enforces request.maxResults by truncating sources[] and setting truncated. fetch() retrieves one URL and returns its final URL, status code, decoded body, and a truncation flag; a non-2xx response is a result, not an error.
// Search the web; sources[] is capped to maxResults:
const result = await ctx.web.search({ query: 'deepseek harness', maxResults: 8 })
// Fetch one URL; a non-2xx response is a result, not an error:
const page = await ctx.web.fetch({ url: 'https://example.com' })
Both calls accept an optional AbortSignal that is forwarded to the provider for cancellation. The normalized request and result shapes are the contract callers build on; the vocabulary section of the web subsystem reference describes them exhaustively.
Each call resolves its provider at execution time, and registration or load order never matters. A configured provider id wins when it is registered and usable; without a configured id, the service runs the single usable provider or fails clearly:
| Situation | Outcome |
|---|---|
| configured id registered and usable | runs that provider |
| configured id not registered | WEB_PROVIDER_CONFIGURED_MISSING |
| configured id registered but unavailable | WEB_PROVIDER_CONFIGURED_UNAVAILABLE |
| no id, exactly one registered usable provider | runs it |
| no id, no usable provider | WEB_PROVIDER_UNAVAILABLE |
| no id, multiple usable providers | WEB_PROVIDER_AMBIGUOUS |
A provider's availability is a cheap local check — for example whether its API key is present — and never makes network calls, so selection stays fast and deterministic.
Failures throw WebError with a stable, machine-routable code; the message adds detail such as the missing provider id or the ambiguous candidate set. Callers route on the code and decide how to degrade. To change which backend a call uses, reconfigure the pinned id, mount or unmount providers, or fix the provider's configuration so its availability check passes.
Read these pages when the package-level contract is not enough. They move from the shared vocabulary to the shipped backends, the model-facing tools, and the design rationale.
web_search and web_fetch tools over this service.Indirectly, through dsh-tool-web, which renders the seam's normalized search results and fetch bodies to the model while this service contributes no prompt or schema.
No direct invalidation; the named consumer owns any request-prefix changes.
These limits define when the service is incomplete on its own. They are current package constraints.
WEB_PROVIDER_UNAVAILABLE with no per-provider reason enumeration (Agent Note).query and maxResults — provider-neutral controls (recency, domain filters, regional hints, search depth) are deferred until the backends can honor them (seam Agent Note).WebFetchBody has no pdf arm — text-extractable PDF support is named deferred work; the closed union makes adding it a compile-enforced change across the web packages.fetch() — a Firecrawl/Tavily-style web_extract capability is deferred rather than widening the fetch operation.