DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Websearch Dsh — DeepSeek Harness 插件(DSH Plugin)
← Plugins
W

websearch-dsh

Websearch Dsh

为 DeepSeek Harness 提供原生多提供商网页搜索:Tavily、Exa、Firecrawl、Brave Search 和 Serper 作为需要密钥的提供商,并自动回退到无需密钥的 DuckDuckGo;接入 harness 的 ctx.web 接口,使内置的 web_search 工具即使没有 DeepSeek API 密钥也能正常工作。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:takasurazeem/websearch-dsh#03d43bc55e9950e63bdd6b2a3b96bbba2a0b8845
README兼容性版本

兼容性与来源证明

Websearch Dsh 以 websearch-dsh 发布,当前版本为 0.1.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/8/24

版本

0.1.0stable
2026/8/24

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/8/24
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

相关插件

继续浏览 search-research 分类下经过校验的插件。

Weknora@wxg-prc-cpg/dsh-weknora适用于 DeepSeek Harness (dsh) 的 WeKnora 知识检索工具:通过自有知识库进行语义搜索、文档阅读以及 RAG/代理回答。Anysearch Dsh@anysearch/anysearch-dsh适用于 DeepSeek Harness 的 AnySearch 网络搜索和获取提供程序及高级工具Builtin Browserdsh-builtin-browserDeepSeek Harness 的共享真实浏览器插件:安装即用——可由人类接管的可见原生浏览器,由代理通过 CDP 驱动。支持 DOM 级交互(React/Vue 安全)、按任务隔离会话、Cookie 持久化(browser_auth)、CAPTCIndustry Researchdsh-industry-research适用于 DeepSeek Harness 的行业与公司研究领域包:方法论技能、行业链结构模型(industry_map)、通过 ctx.web 进行的公开来源政策/新闻跟踪(industry_track)、公司扫描卡片(company_scan)以及可审计的研究

README

websearch-dsh

A native DeepSeek Harness (dsh) plugin that replaces the harness's built-in single-provider web_search backend with a resilient multi-provider fallback chain — so web_search works without a DEEPSEEK_API_KEY, survives per-provider rate limits / quota exhaustion / outages, and — when no key is configured at all — still answers via keyless DuckDuckGo.

MIT-licensed. No MCP server, no sidecar process: it registers on the harness web seam, and the built-in web_search tool stays the single model-facing tool. Sister plugin: gmail-dsh.


Table of contents

  • What you get
  • How the chain works
  • The on/off toggle
  • Setup
  • Provider reference
  • Failure classification
  • The DuckDuckGo leg
  • Verification
  • Configuration reference
  • Developing the plugin
  • FAQ
  • Troubleshooting
  • Reverting / uninstalling
  • License

What you get

SurfaceWhat it does
The web_search toolUnchanged from the harness's perspective — same tool, same model, now served by the chain.
websearch_status toolRead-only: which providers have keys, the live chain, last serving provider, recent per-provider failures, toggle state.
/websearch status commandSame info, rendered for humans in chat.
Model-context sectionOne stable paragraph (zero tokens until it matters): what serves web_search, in what order, and whether the user has it enabled. Re-reads the live state on each render.
GUI toggle cardSettings → Plugins → Configurable → Web Search in the dsh web client: one click, no restart.
Bundle patch layerPoints the harness web row's searchProvider at this plugin, so search works with no DeepSeek key at all.

How the chain works

web_search "…"
   │
   ├─ toggle off?  ──▶  decline immediately (no provider called, no credit spent)
   │
   ├─ resolve keys per call ──▶ credential store refs first, then process env
   │
   ├─ Tavily ──fail──▶ Exa ──fail──▶ Firecrawl ──fail──▶ Brave ──fail──▶ Serper
   │        (only providers with a non-empty key are in the chain; priority order)
   │
   └─ DuckDuckGo (always last, no key required)
         └─ 3 legs in order: d.js JSON → lite HTML → Instant API

Key properties:

  • Keys are resolved on every search — from the DSH credential store (the refs the web Credentials screen writes) first, then process.env. A key added to ~/.dsh/.env or stored in the Credentials screen after startup is picked up on the next search, no restart. Keys are never stored by the plugin.
  • Any failure falls through. Auth, quota, rate limit, timeout, network, and parse failures each advance the chain to the next provider. Only a caller abort (the model/harness cancelling the search) stops the walk early.
  • One combined error is reported only when every leg fails, naming each provider and why: All web search providers failed for "…": tavily: auth — …; serper: quota — …; duckduckgo: network — ….
  • Provenance (default on-fallback): when the keyless DuckDuckGo leg is what served, the result carries a note the model can see — e.g. Served by DuckDuckGo (keyless fallback) after tavily (rate), exa (auth) failed. always annotates every provider; off stays silent.
  • Clamping: maxResults is clamped to 1–50 (default 10); empty queries are rejected before any network I/O.

The on/off toggle

The plugin registers a websearch-dsh namespace in the harness settings document (~/.dsh/settings.yaml) through the live-watched settings service:

# created on first GUI click (or by hand):
websearch-dsh:
  enabled: false
  • Default is on (the registered base layer is {enabled: true}; an absent section means enabled).
  • The search chain checks the flag on every call. Off → web_search declines with a model-friendly message ("disabled by the user … do not call web_search until it is re-enabled") before any provider is contacted — so zero API credit is spent while off. websearch_status, the /websearch command, and the model-context section all reflect the state.
  • The GUI card (a plain JS bundle shipped as client.js — no build step) renders on Settings → Plugins → Configurable. One click on the checkbox performs one revision-fenced write of enabled; the committed change is published by the settings service, the client mirror folds it in, and the server side sees it on the next call. No restart, ever.
  • External edits work too: the settings file is watched, so editing ~/.dsh/settings.yaml by hand applies live.
  • Loopback only: dsh's settings transport serves the local browser. A remote browser sees the card as read-only (the flag is still global on the machine).
  • Tolerant: a composition without the settings service simply has no toggle; search stays enabled. The plugin never hard-depends on it.

Setup

1. API keys (any subset — all optional)

dsh loads two env layers at startup: the project <cwd>/.env and the user layer ~/.dsh/.env (skipped when the project directory is your home directory). .env values only fill names not already exported in the launching shell — shell exports win. Quoted or unquoted values parse identically (Node's built-in env parser); # starts a comment.

# ~/.dsh/.env   (chmod 600 recommended)
TAVILY_API_KEY="tvly-…"      # best all-round; generous free tier
EXA_API_KEY="…"              # neural/keyword search
FIRECRAWL_API_KEY="fc-…"     # search + scrape
BRAVE_API_KEY="BSA…"         # Brave Search API
SERPER_API_KEY="…"           # Google SERP data

Or store them in the dsh web GUI's Credentials screen under the same names — credential refs are checked before the environment and are re-read on every search.

If no key is configured, everything works via DuckDuckGo (see The DuckDuckGo leg).

2. Wire it into your profile

In your profile's package.json (e.g. ~/.dsh/profiles/web/package.json):

{
  "dependencies": {
    "websearch-dsh": "file:/path/to/websearch-dsh"
  },
  "dsh": {
    "bundles": [
      "@deepseek-ai/dsh-base",
      "@deepseek-ai/dsh-web-app",
      "websearch-dsh"
    ]
  }
}

then pnpm install in the profile directory. For live development, symlink the checkout instead of copying:

ln -sfn /path/to/websearch-dsh node_modules/websearch-dsh

The plugin's bundle patch layer (cordis.patch.yml) does the rest:

  • inserts a search-multi row carrying the deployment knobs (timeout, provenance, context), and
  • re-points the web row: searchProvider: websearch-dsh.

It deliberately carries only deployment knobs — never the key names or priority, which stay in code so they can't go stale.

3. Restart dsh

dsh web

A restart is needed once for the composition. After that: keys, the toggle, and every other runtime change apply live.

Provider reference

#ProviderEndpointAuthFree tier (at time of writing)Notes
1TavilyPOST api.tavily.com/searchAuthorization: Bearer (legacy api_key also accepted)~1,000 credits/moStrong for research queries; search_depth: advanced used; results include snippets + relevance score.
2ExaPOST api.exa.ai/searchx-api-key header~1,000 credits/moNeural/keyword/auto search; maps text → snippet, carries publishedDate.
3FirecrawlPOST api.firecrawl.dev/v1/searchAuthorization: Bearer~500 creditsAlso a scraping API; in-band success:false responses are classified (quota text → quota).
4Brave SearchGET api.search.brave.com/res/v1/web/searchX-Subscription-Token header~2,000 queries/moGET with query string; maps the web.results block.
5SerperPOST google.serper.dev/searchX-API-KEY header~2,500 creditsGoogle SERP data; maps organic (skips ads/answers).
6DuckDuckGo3 legs, see belownoneunlimited (rate-limited)Keyless last resort; serialized across concurrent searches.

Each provider module (src/providers/<id>.js) is a pure function of (query, {key, maxResults, timeoutMs, legTimeoutMs, signal, fetch}) → {sources, truncated} — trivially swappable or extendable. To add a provider: implement it, add it to src/providers/index.js, extend DEFAULT_PRIORITY/DEFAULT_ENV_KEYS in src/config.js.

Failure classification

src/http.js classifies every failure so the chain (and the status tool) can report why:

CodeTriggered byChain behavior
auth401 / 403 (no quota text)skip provider, try next
quota402, or 403/429 with quota-ish text ("quota", "credits", "exhausted", …)skip provider, try next
rate429 (bare)skip provider, try next
timeoutper-provider budget exceeded (default 10 s; DDG legs also cap at 8 s each)skip provider, try next
networkfetch rejects (DNS, connection reset, TLS)skip provider, try next
parse2xx but the body isn't the expected shapeskip provider, try next
abortedthe caller's signal abortedstop the whole walk and rethrow

Failures are remembered per provider in websearch_status (most recent first) and named in the combined all-failed error.

The DuckDuckGo leg

Three attempts, in order, per search (a vendor's API has changed underfoot — the npm duckduckgo-search package is broken against current endpoints, so this plugin ships a fixed, dependency-free port):

  1. d.js — harvest a vqd token from the HTML search page, then query links.duckduckgo.com/d.js for the JSON result set (deduped, tag/entity stripped).
  2. Lite HTML — lite.duckduckgo.com/lite results page; result links are uddg-resolved and decoded.
  3. Instant API — api.duckduckgo.com/?q=…&format=json (title + first paragraph + related topics).

Concurrency is serialized across searches (one in-flight leg at a time) as a politeness measure. Current status (2026-08): serving normally; from some networks/IPs DuckDuckGo intermittently returns an anti-bot 202 challenge instead of results — the three legs usually get through, and when a challenge does bite you get the clear combined error naming it, not a hang.

Verification

CheckWhat to look for
web_search in chat, no DEEPSEEK_API_KEY setWorks: keyed providers first, DDG last.
websearch_status tool / /websearch statusWeb search: enabled. (or DISABLED), live chain, key presence per provider, last serving provider, recent failures.
Settings → Plugins → Configurable in the web GUIThe Web Search card with the on/off checkbox.
~/.dsh/settings.yaml after a clickA websearch-dsh: { enabled: … } section appears.
node test.mjs in the plugin directory66/66 (one network test self-skips when offline).

Configuration reference

All fields optional; defaults shown. (These are the fields of the search-multi row the bundle patch inserts; a full custom composition can also set them on any plugin row.)

FieldDefaultMeaning
envKeys{ TAVILY_API_KEY, EXA_API_KEY, FIRECRAWL_API_KEY, BRAVE_API_KEY, SERPER_API_KEY }Env/credential names, per provider.
priority[tavily, exa, firecrawl, brave, serper]Chain order; unknown ids dropped, order preserved.
providerTimeoutMs10000Overall budget per provider call (each DDG leg also caps at 8 s).
provenanceon-fallbackoff — never annotate · on-fallback — annotate when keyless DDG served · always — annotate every serving provider.
context.enabledtrueEmit the model-context section at all.
context.order118Ordering among model-context sections.

Developing the plugin

node test.mjs        # offline suite, plain Node, no framework

The suite covers: config normalization/~standard validation, the HTTP + classification layer, per-call key resolution, every provider's request shape and response mapping (faked fetch routes), the fallback chain (fall-throughs, provenance, clamping, abort semantics, DDG serialization, live key changes, the toggle), the status tool, and the settings/client contract (fake settings service + a fake window.__ModuleLoader__ harness that runs the real client.js). Run it from a checkout with the dsh packages resolvable (e.g. your live-symlinked profile's node_modules) for the full variant against real dsh-tools/dsh-credentials/dsh-settings/schemastery; from a bare clone it degrades gracefully and still exercises everything that doesn't need them.

Composition check with a scratch DSH_HOME:

dsh --profile <name> --dump-config   # expect the websearch-dsh rows, no errors

FAQ

Why one composite provider instead of five? The harness's web seam selects one searchProvider at construction time (dsh-web pins it — there is no runtime re-selection seam). A single composite provider is the only way to do failover and keep the built-in web_search tool as the single model-facing surface.

Why not an MCP server? A native plugin skips the sidecar process and its own lifecycle: it registers directly on the harness seams (tools, settings, credentials, system prompt), so the on/off toggle, approval semantics, and live config behave like first-party features.

Does it work outside the web GUI? Yes — the chain, tools, command, and context section are server-side and work in dsh chat/headless too. Only the toggle card is a web-client feature (the settings.yaml file works everywhere).

Will it spend my credits while I'm just chatting? Only when web_search is actually called and the toggle is on. The model-context section is one static paragraph.

Can I reorder providers? Yes — priority in the row config (e.g. put serper first). Unknown ids are dropped with a validation warning, never a crash.

Troubleshooting

SymptomFix
web_search says "disabled by the user"The toggle is off — flip it in the GUI or set websearch-dsh.enabled: true in ~/.dsh/settings.yaml.
Status shows a provider without a key although you set the envCheck you set it in a layer dsh reads (~/.dsh/.env or the launching shell) and restarted dsh once; or use the Credentials screen (live).
Every search is served by DuckDuckGo with a provenance noteThe keyed providers are failing — read websearch_status → failures (auth = bad key, quota = free tier exhausted, rate = throttled).
Combined "All web search providers failed" errorNetwork-level: the machine can't reach any provider API (or DDG is challenging you). The error lists each provider's code — fix the one that matches your setup.
The GUI card is missingYou're on a pre-0.1.2 GUI build, or a remote browser (loopback-only feature) — or the plugin isn't in the profile's dsh.bundles.
web_search still says "no API key for DEEPSEEK_API_KEY" after installYou haven't restarted dsh web since wiring the profile, or the bundle patch layer isn't applied (check dsh --dump-config for searchProvider: websearch-dsh).

Reverting / uninstalling

Re-point the web row in a profile-level bundle patch (searchProvider: deepseek-official) or simply remove websearch-dsh from the profile's dsh.bundles, then restart. Nothing else the plugin does depends on it; the settings section (if any) is inert without the plugin.

License

MIT — see LICENSE.