DeepSeek Harness Plugin Hub

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

探索

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

社区

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

相关链接

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

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

Mcp Relay — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
M

dsh-mcp-relay

Mcp Relay

通过 MCP 暴露正在运行的 DeepSeek Harness 实例,使外部代理能够列出该实例中的会话、向会话委派任务并回答问题。

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

npx -y @deepseek-ai/dsh plugin --profile web add github:Nigheart/dsh-mcp-relay#2ac327e17690ce01d308ef6c071561e9ee8bb116
README兼容性版本

兼容性与来源证明

Mcp Relay 以 dsh-mcp-relay 发布,当前版本为 0.6.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
github
Registry 更新时间
2026/10/17

版本

0.6.0stable
2026/10/17
0.5.0stable
2026/10/17

相关插件

正在加载相关插件…

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

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

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

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

认领这个 Plugin →
报告问题

相关插件

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

Acp App@deepseek-ai/dsh-acp-appdsh ACP 配置文件包:基于 dsh-base 的仅限自动化的 JSON-RPC stdio 和进程生命周期管理Experimental Plugin Market Bundle@deepseek-ai/dsh-experimental-plugin-market-bundle实验性自定义插件市场:用于浏览和安装来自插件市场索引的捆绑包的“发现”选项卡Ab Wechat Scrapedsh-ab-wechat-scrape--- description: "树外 wechat_scrape 工具:获取微信公众号文章,过滤推广区块,并将每篇文章保存为 Markdown 文件,供此机器的 dsh 配置文件维护者使用。" kind: "package-reference" ---Ab Publishdsh-ab-publish浏览器自动化发布工具,可登录任意网站并发布内容,整合为一个可发布的插件包

README

dsh-mcp-relay

dsh-mcp-relay

English | 简体中文

Expose a running DeepSeek Harness (DSH) instance over the Model Context Protocol, so an external agent — Codex, or any MCP client — can drive that instance's own sessions.

A DSH session behaves like a subagent from the caller's point of view: it keeps its own context across calls, works in the workspace you name, and returns a final answer. The relay also lets the caller answer the questions that DSH agent asks, so a delegated task never deadlocks waiting for a human who is not there.

Why

Driving a DSH instance through its web GUI from another agent costs a screenshot or DOM round trip per step. This relay replaces that with a small structured API: one tool call in, one result out.

Architecture

MCP client (stdio)
 └── bin/dsh-mcp-relay.mjs          MCP server; polls the relay, holds questions for the caller
      └── HTTP 127.0.0.1:<port>/dsh-relay
           └── lib/index.js    host Cordis plugin mounted inside the running DSH process
                └── ctx.sessionController → DSH agent

Two halves, and the split matters:

  • lib/index.js is a DSH host plugin. It runs in the same process as the Web GUI, so it can read and write the sessions that instance already has. It registers an HTTP route and does the DSH work.
  • bin/dsh-mcp-relay.mjs is a standalone MCP server that speaks stdio to your agent and HTTP to the plugin. It exists because DSH's own process owns stdout, so the plugin cannot serve stdio itself.

Waiting and polling happen in the MCP server, where they cost nothing; your model spends one tool call and receives the finished result.

Requirements

  • DSH with a Web profile (dsh web or the desktop app).
  • Node.js 18 or newer on the same machine as DSH.
  • An MCP client that can spawn a stdio server.

Install the plugin

Install straight from this repository — the package is not published to npm:

dsh plugin --profile web add github:Nigheart/dsh-mcp-relay

Then restart DSH. On startup the log should show:

[dsh-mcp-relay] mounted /dsh-relay (tokenRequired=false)

Verify it independently:

curl -s http://127.0.0.1:3080/dsh-relay/ping
# {"ok":true,"data":{"relay":"dsh-mcp-relay","version":"0.6.0","tokenRequired":false}}

If your instance listens on a port other than 3080, use that port here and set DSH_RELAY_URL for the MCP server (below).

Manual install

If you prefer to wire it up by hand, add the package to the profile's package.json and list it as a bundle:

{
  "dependencies": { "dsh-mcp-relay": "github:Nigheart/dsh-mcp-relay" },
  "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-mcp-relay"] } }
}

The package ships its own cordis.patch.yml, which inserts one loader row. That row declares inject: [webServer, sessionController] — keep it. Without inject the plugin can be activated before those services exist, find nothing to bind to, and mount no route at all.

Configure your MCP client

Codex (~/.codex/config.toml):

[mcp_servers.dsh]
command = "node"
args = ["/absolute/path/to/dsh-mcp-relay/bin/dsh-mcp-relay.mjs"]
startup_timeout_sec = 60

# Only needed when DSH is not on the default port, or when a token is set.
[mcp_servers.dsh.env]
DSH_RELAY_URL = "http://127.0.0.1:8080/dsh-relay"

The server sends its own usage notes in the MCP handshake (initialize.instructions), so the client learns the workflow without any extra prompting.

Environment variables:

VariableDefaultPurpose
DSH_RELAY_URLhttp://127.0.0.1:3080/dsh-relayWhere the plugin listens.
DSH_RELAY_TOKEN(unset)Sent as x-dsh-relay-token; must match the plugin's value.
DSH_RELAY_TIMEOUT_MS600000Default wait for one delegated turn.
DSH_RELAY_POLL_MS2000Progress poll interval.

Tools

ToolPurpose
dsh_list_sessionsList sessions of the running instance, optionally filtered to one workspace.
dsh_modelsList the provider/model ids this instance can route, with each model's reasoning efforts.
dsh_delegateCreate a session in a workspace, or continue one by sessionId, send an instruction, and wait for that turn to finish.
dsh_answerAnswer a question the DSH agent asked, so it can resume.
dsh_approvalChoose how a session resolves future approval requests: gui, auto-approve, or deny.
dsh_cancelCancel the active turn — the way out of an approval nobody can answer.
dsh_pollRead a session's status and latest output; use after a wait that returned waiting or timeout.

Delegating work

dsh_delegate({ workspace: "/home/me/project", instruction: "Add the missing tests for parser.js" })

Omit sessionId to start a new DSH agent; pass the returned sessionId back to continue the same agent, which still remembers the earlier work. status tells you what happened:

statusMeaning
completedThe turn finished; output holds the agent's final answer.
waitingBlocked on a human: either a question takeover is holding for you, or an open approval request.
timeoutStill running; call dsh_poll with the same baselineSeq.

The relay also attaches every session it creates (or reuses) to the DSH workspace owning the target directory. DSH groups sessions in its GUI by that explicit attachment rather than by working directory, so without it a delegated session runs fine but shows up as ungrouped. Sessions created before this behaviour existed can be repaired with one call to the relay's POST /dsh-relay/attach-all endpoint.

Choosing the model

A session runs on the instance's default model unless you say otherwise. dsh_models reports what this instance can route right now — provider groups, model ids, and each model's reasoning efforts:

dsh_models()
dsh_delegate({ workspace: "/home/me/project", instruction: "…",
               model: "deepseek-v4.1-pro", reasoningEffort: "high" })

model takes a model id; provider is inferred when that id is unambiguous, and provider/model is always accepted. reasoningEffort must be one of the efforts dsh_models lists for that model. A route this instance cannot serve fails the call before the instruction is sent, rather than quietly running it on some other model.

The selection applies to this turn and is also saved as the instance default for sessions created without one — the same stickiness DSH's own model picker has, so choose deliberately. Omit model to leave the session on whatever it already uses. dsh_delegate echoes the selection it installed as model (with modelNote); dsh_list_sessions and dsh_poll report each session's current model, read from DSH's model-selection projection (a hint: it is null until the session has recorded a request, and on deployments without that projection).

Answering questions

By default a delegated session runs under takeover: when the DSH agent calls its question tool, the call is suspended and handed to the caller instead of the GUI. dsh_delegate then returns immediately with the question rather than waiting:

{
  "status": "waiting",
  "waiting": {
    "kind": "user-question",
    "toolName": "ask_user_question",
    "intercepted": true,
    "detail": [{ "id": "choice", "question": "Which database?", "options": ["sqlite", "postgres"] }]
  }
}

Answer it with the ids from detail:

dsh_answer({ sessionId, answers: [{ id: "choice", selected: ["sqlite"] }] })

If nobody answers within about three minutes, the question falls back to the DSH GUI, so an unattended delegation cannot hang forever. Pass takeover: false to dsh_delegate to leave questions with the GUI from the start.

Approvals

Some operations need a decision before DSH will run them — for example escalating a sandbox so a tool can write outside the session workspace. The session then reports:

{
  "status": "waiting",
  "waiting": {
    "kind": "approval",
    "detail": [{
      "id": "d0f853e3-...",
      "toolName": "pwsh",
      "callId": "call_00_...",
      "reason": "escalate sandbox to danger-full-access: ... outside the session workspace"
    }]
  }
}

reason is DSH's own explanation of what it wants and why, so the caller can judge whether granting it is appropriate.

An approval that is already pending has only two exits: a person answers it in the DSH Web GUI, or the turn is abandoned with dsh_cancel. Nothing below can rescue the request that is already open.

dsh_approval({ sessionId, mode }) decides how future approvals in that session are handled. The policy takes effect at the agent's next step, so it never applies retroactively:

  • "gui" (the default) — leave them to the DSH Web GUI for a person to answer.
  • "deny" — set the session's policy to never: every subsequent request resolves as rejected without prompting. For unattended work this beats waiting forever — a clean failure instead of a hang.
  • "auto-approve" — this plugin answers the request itself with a one-shot grant. Whether that is even possible is deployment-dependent: the plugin has to sit upstream of the GUI's answerer. Registering the mode is therefore not proof that it works — check the relay's /dsh-relay/diagnostics (approvalListener.listenerHits); the endpoint reports effective: false with a warning when it cannot. On the deployment this was built against, listenerHits is 0, so only deny and gui take effect there.

A tool call that has simply not finished yet is not reported as waiting; such a session stays running and carries an informational pendingTool field.

Security

The relay is a control surface: whoever can reach it can send instructions to, and read the history of, every session in that DSH instance.

  • It serves loopback callers only and answers 403 to anything else. This matters because the shipped Web profile binds 0.0.0.0.
  • Set DSH_RELAY_TOKEN on both sides to additionally require an x-dsh-relay-token header.
  • The route is registered directly on DSH's HTTP server, so it is not covered by the /api trusted-host fence. Its own checks above are the only gate.

Limitations

  • The MCP server and DSH must run on the same machine, because the relay is loopback-only.
  • One delegated turn is bounded by DSH_RELAY_TIMEOUT_MS; there is no streaming of partial output.
  • cancel stops the active turn only — messages already queued in DSH keep running.
  • The plugin reads a session's tail through sessionController.page, so a very large session reports its recent history rather than all of it.

Development

The plugin can be developed without restarting DSH: load it as a dynamic Cordis plugin first, verify behaviour against the live services, then install it as a bundle package. Do finish with one real restart — dynamic plugins are activated only after the services they need already exist, so they cannot catch a startup-order bug such as a missing inject.

The relay's route table has a dependency-free test that stubs the Cordis context, so it needs neither DSH nor a restart:

npm test

License

MIT