前言¶
DeepSeek Harness(dsh)把聯網能力拆成兩層:模型側始終看到名爲 web_search 的工具,真正發請求的是掛在 ctx.web 上的搜索 provider。默認那一層是內置的 web-search-deepseek,走 DeepSeek 官方搜索接口,需要能解析 DEEPSEEK_API_KEY,而且每次搜索都會消耗一次模型調用。
這在只用官方賬號時沒問題。一旦聊天走了別的網關、搜索額度想單獨結算,或者手頭已經有 Tavily、Brave、Exa 這類搜索 MCP,默認後端就會變成約束:工具名改不了,provider 卻綁在官方搜索上。官方倉庫的討論裏也有人碰到過類似情況——會話模型換了,web_search 仍打原來的 DeepSeek 搜索端點。
dsh-search-mcp 做的是替換這一層 provider,而不是再註冊一套 mcp__tavily__* 之類的新工具。模型還是調用 web_search,界面展示不變,請求改走你在 Web 設置頁配好的搜索 MCP 服務器。本文按社區目錄頁、GitHub 倉庫 README、package.json / cordis.patch.yml 源碼,以及 DeepSeek Harness 官方的 Web 能力說明覈對後整理。
這是什麼¶
dsh-search-mcp 是由 gxpppp 維護的社區插件,許可證 MIT,主要語言 JavaScript,當前 package.json 版本爲 0.1.0。GitHub 倉庫截至 2026 年 8 月 18 日爲 10 星;社區目錄頁當時顯示 7 星,以倉庫頁面爲準。
它在目錄裏歸在「界面增強」,原因是插件會向 Web 設置頁注入 search-mcp 配置卡片,並且 package.json 裏聲明瞭 dsh.client.platform 爲 web。真正替換的能力是搜索 provider:啓用期間把 web.searchProvider 切到 search-mcp,同時禁用內置的 web-search-deepseek。卸載後這一層 bundle 消失,內置搜索按原樣恢復。
需要先分清兩件事。DeepSeek Harness 官方的口號是「一切皆插件」,模型、工具、會話、UI 都可以在配置層替換,不必改核心源碼。社區插件目錄 deepseek-harness-plugin.com 是獨立站點,和 DeepSeek / 幻方沒有官方從屬關係,不能當成官方應用商店。
核心功能¶
工具名不變,後端整段換掉¶
插件通過 ctx.web.registerSearchProvider() 註冊 id 固定爲 search-mcp 的 provider。返回形狀與內置 provider 一致({ sources, truncated, content? }),由原生 web_search 工具負責格式化。因此模型側工具名、參數和展示都不必改,執行通道換成 MCP。
安裝時由包內的 cordis.patch.yml 自動做三件事:
- insert:
- id: search-mcp
- id: web
config:
searchProvider: search-mcp
- id: web-search-deepseek
disabled: true
README 寫明:這一層加在 dsh-base / dsh-web-app 之後、用戶自己的 cordis.patch.yml 之前;刪掉插件後整層消失,內置搜索還原。
範圍只覆蓋搜索。web_fetch 保持 dsh 默認關閉,插件不會順手打開網頁抓取。
一套設置頁切換多家搜索 MCP¶
打開 Web 界面 → 設置 → Plugins → search-mcp,維護 servers 列表。倉庫爲這些 kind 寫了預設:
| kind | 默認端點 / 啓動方式 | 鑑權 | 默認工具名 |
|---|---|---|---|
tavily |
https://mcp.tavily.com/mcp/ |
query tavilyApiKey |
tavily_search |
brave |
https://mcp.brave.com/mcp/ |
query braveApiKey |
brave_web_search |
exa |
https://mcp.exa.ai/mcp |
header x-api-key |
web_search_exa |
perplexity |
https://mcp.perplexity.ai/mcp/ |
query pplx_api_key |
pplx_search |
duckduckgo |
npx -y duckduckgo-mcp-server(stdio) |
無需 key | ddg_web_search |
custom |
自己填 | 自己選 | 自己填 |
傳輸方式支持 http(streamable-http,默認)和 stdio(本機命令,DuckDuckGo 走這條)。全局字段包括 defaultServer、maxResults(默認 8)、searchTimeoutMs(默認 30000)。單條服務器還可以覆蓋自己的 maxResults。
設置寫入 $DSH_HOME/settings.yaml 的 search-mcp: 段,優先於行配置。provider 每次搜索重新讀快照,改完立即生效,不必爲改配置再重啓。
默認 bundle 會插入一條 Tavily 服務器:defaultServer 爲 tavily,apiKeyEnv 爲 TAVILY_API_KEY。也就是說,裝上插件並不等於搜索立刻可用,還要把對應 key 配好,或改成免 key 的 DuckDuckGo。
密鑰不進倉庫,結果按 URL 歸一化¶
密鑰解析順序在 README 和 lib/index.js 裏一致:字面量 apiKey → dsh 憑證服務(apiKeyEnv,例如 $DSH_HOME/.credentials.yaml)→ 啓動環境變量。設置頁裏 apiKey 按密碼框顯示、落盤脫敏;倉庫的 cordis.patch.yml 明確寫了不提交任何 API key。
MCP 客戶端用 @modelcontextprotocol/sdk。每次搜索新建連接(http 或 stdio),結束時關閉;調用方的取消信號與 searchTimeoutMs 超時競速,中止時拋 WEB_ABORTED。結果側會遞歸掃描 MCP 返回的 JSON,凡帶 url 的對象當作 source(title / snippet / 發佈日期取常見字段名),answer 當作摘要,不綁死某一家廠商的字段名。
安裝與啓用¶
社區目錄頁給出的安裝命令是:
dsh plugin add github:gxpppp/dsh-search-mcp
dsh CLI 會從 GitHub 解析插件並裝進當前配置。插件聲明運行在 web 平臺,README 裏的本地開發示例也是裝進 web profile。如果當前默認 profile 不是 web,可以按 README 寫成:
dsh plugin --profile web add github:gxpppp/dsh-search-mcp
需要可復現安裝時,按目錄頁說明固定 commit 哈希:
dsh plugin add github:gxpppp/dsh-search-mcp#<commit>
把 <commit> 換成倉庫裏的具體哈希,不要留空。
本地改源碼時,README 的步驟是:先在倉庫目錄執行 npm install,再用 dsh plugin --profile web add link:<本倉庫路徑> 以 link 方式掛上,然後重啓 dsh web。Web bundle 未啓用 HMR,首次安裝或改插件代碼後必須重啓進程;之後只改設置頁裏的服務器列表,則不必重啓。
目錄頁和倉庫都提醒:插件以當前 dsh 進程的權限運行,安裝時可能執行代碼。裝之前應檢查源代碼倉庫和許可證。
配置與驗證¶
在設置頁補上服務器和密鑰¶
- 啓動
dsh web,打開網頁界面。 - 進入 設置 → Plugins → search-mcp。
- 確認
servers裏至少有一條記錄。默認是 Tavily;也可以改成 Brave / Exa / Perplexity,或加一條kind: duckduckgo的 stdio 服務器。 - 需要密鑰的服務,任選一種方式:
- 在卡片裏直接填apiKey;
- 或填apiKeyEnv(如TAVILY_API_KEY),並在$DSH_HOME/.credentials.yaml或啓動環境裏提供同名憑證。 - 把
defaultServer設成某條servers[].id,保存。
README 記錄的申請入口:Tavily(tavily.com)、Brave Search API(brave.com/search/api;README 寫明有免費 2000 次/月額度,具體以 Brave 當前說明爲準)、Exa(exa.ai)、Perplexity(perplexity.ai)。DuckDuckGo 這條預設不需要 key,但本機要能執行 npx。
如果以前在 cordis.patch.yml 裏直接插過 Tavily MCP,README 建議刪掉那一段 insert,避免工具目錄裏再出現 mcp__tavily__*,和 web_search 重複。
確認 provider 已經切過去¶
README 用 web profile 做組合層檢查。PowerShell 示例是:
dsh --profile web --dump-config | Select-String -Pattern "searchProvider|search-mcp|web-search-deepseek"
在 bash 下可以對同一份輸出做過濾:
dsh --profile web --dump-config | grep -E "searchProvider|search-mcp|web-search-deepseek"
預期能看到 searchProvider 已是 search-mcp,且 web-search-deepseek 處於 disabled。然後開一個新會話,讓智能體調用 web_search(README 的例子是「搜索 MCP 服務器列表」)。返回結果應來自當前 defaultServer 對應的 MCP,而不是內置 DeepSeek 搜索。
卸載¶
dsh plugin --profile web remove dsh-search-mcp
之後按 README:清掉 settings.yaml 裏的 search-mcp: 段(如果改過設置頁),重啓 dsh web。內置 web-search-deepseek 會隨 bundle 層一起恢復。
只在 cordis.patch.yml 裏給 search-mcp 行加 disabled: true 並不足夠:README 寫明,此時內置搜索仍處於被替換狀態,要同時還原 web 和 web-search-deepseek 兩行,或者直接卸載插件。
適用場景與注意事項¶
比較適合這幾類用法:
- 希望保留
web_search這個模型工具,但搜索流量走 Tavily / Brave / Exa / Perplexity 等專業搜索 API。 - 已經在用搜索類 MCP,不想再讓同一套能力以
mcp__*__*工具名暴露一份。 - 需要在設置頁裏切換多家搜索後端,而不改 dsh 源碼。
- 暫時沒有 DeepSeek 官方搜索額度,但有 DuckDuckGo MCP 或其它免 key / 自建搜索 MCP。
使用前注意這些邊界:
- 這是 Web 插件。
package.json的 client 注入指向@deepseek-ai/dsh-client-ui-settings,平臺爲web。headless 流程不在倉庫說明範圍內。 - 依賴版本寫死在 0.1.0-rc.6。
package.json裏@deepseek-ai/dsh-web、dsh-settings、dsh-credentials、dsh-launch-environment均爲0.1.0-rc.6,MCP SDK 爲^1.30.0。dsh 若已升到不兼容的版本,需要自己覈對。 - 空列表不能搜。
servers爲空會報configured web provider "search-mcp" is registered but unavailable或no search MCP servers configured。缺 key、defaultServer對不上 id、URL 不通、stdio 的npx不在 PATH,都會在搜索時以WEB_PROVIDER_ERROR一類錯誤冒出來。 - 只換搜索,不換抓取。
cordis.patch.yml把tool-web的fetch維持爲false,並把searchMaxResults提到 50,讓插件自己的maxResults決定實際條數。 - 第三方搜索各有條款和費用。 插件本身 MIT、可免費安裝;Tavily / Brave / Exa / Perplexity 的配額以各家控制檯爲準,不要把 README 裏的免費額度理解成永久保證。
- 權限與來源。 插件與當前 dsh 進程同權,能讀憑證、能拉起 stdio 子進程。安裝前看倉庫源碼和 MIT 許可證;需要可復現環境時固定 commit。
小結¶
dsh-search-mcp 把 dsh 的搜索 provider 從內置 DeepSeek 換成可配置的搜索 MCP,同時保住 web_search 這個工具名。配置集中在 Web 設置頁,卸載即可還原。它解決的是「後端想換、模型側不想改」這一類需求,並不是再做一套並行的搜索工具。
目錄頁:https://deepseek-harness-plugin.com/zh-CN/plugins/dsh-search-mcp/
GitHub:https://github.com/gxpppp/dsh-search-mcp