dsh-web-search-anysearch
Verified@aiwayds/dsh-web-search-anysearch Β· v0.8.0 Β· MIT Β· Web UI
AnySearch web search provider plugin for DeepSeek Harness (dsh)
Install
dsh plugin add @aiwayds/dsh-web-search-anysearch Confirm the layer applied with dsh --profile default --dump-config β see the install guide.
Source
Tags
Readme
dsh-web-search-anysearch
A DeepSeek Harness (dsh) plugin that provides web search through AnySearch β "AI Search Infrastructure for Agents".
Requires dsh >= 0.2.0-rc.2 β this plugin targets the dsh RC/stable line only (CI and releases resolve the newest of the latest/next dist-tags at runtime). The alpha line is no longer supported.
The plugin registers a WebSearchProvider (id: "anysearch") with the official
ctx.web capability seam,
so the model keeps calling the same web_search tool with the same citation formatting
and bounds. No third-party SDK is used: the transport follows AnySearch's own reference
CLI (anysearch-ai/anysearch-skill).
How it talks to AnySearch
Per the official interface spec (scripts/shared/doc_spec.md in the skill repo):
POST https://api.anysearch.com/mcpβ JSON-RPC 2.0tools/call, stateless (noinitialize, no session, one connection per request).Authorization: Bearer <API_KEY>is optional β anonymous access works with lower rate limits. A free key is available at anysearch.com/console/api-keys.max_resultsis capped at 10 by the API.- Responses are structured Markdown for agents; the provider parses result blocks into
citeable sources, and degrades to passing the whole payload through as
contentif the format ever drifts β a search never fails just because the parser did. - Raw
node:httpsper request (notfetch/undici), matching the official CLI and avoiding a known undici connection-pool hang on this endpoint.
Vertical routing (domain/sub_domain via get_sub_domains), batch_search, and
extract remain richer MCP/skill-layer features; this provider deliberately exposes the
general search tool to dsh's model-facing web_search (a domain can be pinned in
config). Point an MCP client at AnySearch for the full toolset.
Install
One command:
dsh plugin --profile <name> add @aiwayds/dsh-web-search-anysearch
dsh plugin installs the package into the profile (via pnpm) and registers it in
dsh.profile.bundles automatically β the package declares dsh.bundle, so the
official CLI reconciles the layer stack by itself. Restart dsh. That is the whole
install: the plugin mounts itself AND claims the web_search default at the bundle
layer, so with zero configuration the model's web_search tool runs on AnySearch
(anonymous access needs no key).
Local development: dsh plugin --profile <name> add link:/path/to/dsh-web-search-anysearch
(relative path specs anchor to your invoking directory). The manual route β adding the
dependency and the bundle entry to ~/.dsh/profiles/<name>/package.json yourself and
running pnpm install there β works identically.
@deepseek-ai/*packages are peer dependencies by design: they must resolve to the profile's single shared dsh closure. Putting them independenciesinstalls a second cordis instance and crashes the loader.
Uninstall
dsh plugin --profile <name> remove @aiwayds/dsh-web-search-anysearch
The host auto-cleans the whole install: the bundles entry is spliced out of the
profile's package.json, the dependency is removed, and the plugin's patch
layer drops with the package β including the searchProvider: anysearch
claim the patch placed on the host web row, so web_search reverts to the
host default (deepseek-official). Restart dsh and it is as if the plugin was
never installed.
There is no on-disk state to clean up: the plugin is stateless (no cache, no config writes). The optional API key, if you ever stored one, lives in the host-owned credentials document β remove it there if unwanted.
One caveat: if you hand-copied the selection into your own patch layer
(searchProvider: anysearch in the profile or home cordis.patch.yml), that
line outlives the plugin β searches then fail gracefully with
WEB_PROVIDER_UNAVAILABLE (no provider with that id is registered) until you
delete the line.
Configure (all optional)
API key β only needed for higher rate limits. Anonymous access is valid AnySearch
usage; when the anonymous quota runs out, the search error names the credential to
configure. Store the key in dsh's managed credentials document
(~/.dsh/.credentials.yaml):
version: 1
refs:
ANYSEARCH_API_KEY: <your_key>
Or fill it from the GUI: Settings β Plugins β the dsh-web-search-anysearch bundle β
its web-search-anysearch row β Configure. That page writes the same
ANYSEARCH_API_KEY reference through dsh's credentials service and reports whether a
key is currently configured. The field is write-only β a stored key never rides a
response back to the page β and a blank field keeps the current key. The page always
manages the default ANYSEARCH_API_KEY reference: if you re-point the provider's
apiKeyEnv config at a different reference, the GUI row and the provider will
disagree β keep the default and edit the credentials file directly for custom refs.
One more click
(Clear key, which asks once) removes the reference again, returning searches to the
anonymous limits. A key supplied by the launching environment or a .env fallback is
read-only, and the page says so instead of offering a write.
Both routes land in the same document under the same reference, so they are interchangeable: a key typed in the GUI is visible to a hand edit and vice versa. Nothing is written into a configuration file, and the provider re-resolves the reference on every search, so no restart is needed either way.
Keep another engine as the default? Your own patch layer always outranks the plugin's bundle patch β one entry, no need to touch the plugin:
# profile cordis.patch.yml or ~/.dsh/cordis.patch.yml
- id: web
config:
searchProvider: tavily # or deepseek-official, exa, ...
Verify which engine owns web_search:
dsh --profile <name> --dump-config | grep -A3 'id: web$'
Plugin config (all optional; via a patch layer, read at startup):
| Field | Default | Notes |
|---|---|---|
apiKeyEnv |
ANYSEARCH_API_KEY |
Credential reference resolved per search |
apiKey |
β | Literal key; wins over the reference |
endpoint |
https://api.anysearch.com/mcp |
Env override: ANYSEARCH_ENDPOINT |
timeoutMs |
30000 |
Per-request budget |
domain |
β (general) | Pin a vertical: finance, academic, code, β¦ (17 total) |
maxResults |
8 |
Default when the tool sends no bound; API caps at 10 |
Behavior
- Markdown result blocks map to sources:
title,url, snippet text; relativedate:/sitelinks:annotations stay inside the snippet (no ISO dates βpublishedAtis never set); duplicate URLs dedupe. - Seam error contract: cancellation as
WEB_ABORTED; transport failures, non-2xx statuses, JSON-RPC errors, andisErrortool results asWEB_PROVIDER_ERRORwith the provider's message surfaced. available()is a local check only (parseable endpoint) β anonymous access means no key is required to be usable.
Develop
npm install
npm run check # tsc --noEmit
npm test # build + node --test (transport runs against a local HTTP server;
# markdown fixtures come from real API payloads)
After editing src/, run npm run build and restart dsh.
The browser half (src/client.ts β lib/client.js) is a classic script the Web page
evaluates directly, so npm run build also runs scripts/build-client.mjs: TypeScript
always marks a .ts file in this package as a module, and that step strips the marker it
adds and fails the build if the artifact ever carries module syntax. npm test covers
the Client half too β artifact shape, the row page it claims, and the credential writes
it performs.
License
MIT