🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

会话桥接 Nigheart/dsh-mcp-relay

DeepSeek Harness
Nigheart14 天前有提交已提供中文文档扫描:含敏感能力

把一个正在运行的 DeepSeek HarnessDSH实例通过

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/17 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Nigheart/dsh-mcp-relay
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · dev
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 14 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 7 处,展示前 7 条)
  • 读取密钥环境变量
    lib/index.js:91
    const token = process.env.DSH_RELAY_TOKEN || ''
  • 读取密钥环境变量
    bin/dsh-mcp-relay.mjs:24
    const RELAY_TOKEN = process.env.DSH_RELAY_TOKEN || ''
  • 发起网络请求
    bin/relay-client.mjs:31
    response = await fetch(url + path, init)
  • 读取本地文件
    bin/dsh-bench.mjs:95
    const stat = statSync(path)
  • 读取本地文件
    bin/dsh-bench.mjs:97
    for (const entry of readdirSync(path)) {
  • 读取本地文件
    bin/dsh-bench.mjs:104
    return files.sort((a, b) => statSync(a).mtimeMs - statSync(b).mtimeMs)
  • 读取本地文件
    bin/dsh-bench.mjs:109
    const rl = createInterface({ input: createReadStream(file), crlfDelay: Infinity })
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

把运行中的 DeepSeek Harness 实例通过 MCP 暴露出来,让外部智能体可以列出该实例自己的会话、向其中委派任务并回答问题。

README

由 DeepSeek 最新模型翻译生成
dsh-mcp-relay dsh-mcp-relay 把一个正在运行的 DeepSeek Harness(DSH)实例通过 MCP 暴露出来,让外部 agent——Codex,或任何 MCP 客户端——直接驱动该实例自己的会话。 对调用方来说,一个 DSH 会话就像一个子代理:它有自己的上下文、在指定工作区里干活、最后返回一个结果。 这个中继还允许调用方代替人回答 DSH agent 提出的问题,因此被委派的任务不会因为找不到人而卡死。 为什么需要它 用另一个 agent 通过 Web GUI 去操作 DSH,每一步都要付出一次截图或 DOM 往返。这个中继把这些换成一个小而结构化的 API:一次工具调用进,一个结果出。 架构 MCP 客户端(stdio) └── bin/dsh-mcp-relay.mjs MCP server;轮询中继,并把提问扣下来交给调用方 └── HTTP 127.0.0.1:<port>/dsh-relay └── lib/index.js 跑在 DSH 进程内的 host Cordis 插件 └── ctx.sessionController → DSH agent 分成两半,而且这个分法是有原因的: - lib/index.js 是 DSH host 插件,与 Web GUI 同进程,因此能读写该实例已有的会话。
展开完整文档(README 剩余 1 节)
dsh-mcp-relay

dsh-mcp-relay

把一个正在运行的 DeepSeek Harness(DSH)实例通过 MCP 暴露出来,让外部 agent——Codex,或任何 MCP 客户端——直接驱动该实例自己的会话。

对调用方来说,一个 DSH 会话就像一个子代理:它有自己的上下文、在指定工作区里干活、最后返回一个结果。
这个中继还允许调用方代替人回答 DSH agent 提出的问题,因此被委派的任务不会因为找不到人而卡死。

为什么需要它

用另一个 agent 通过 Web GUI 去操作 DSH,每一步都要付出一次截图或 DOM 往返。这个中继把这些换成一个小而结构化的 API:一次工具调用进,一个结果出。

架构

MCP 客户端(stdio) └── bin/dsh-mcp-relay.mjs MCP server;轮询中继,并把提问扣下来交给调用方 └── HTTP 127.0.0.1:<port>/dsh-relay └── lib/index.js 跑在 DSH 进程内的 host Cordis 插件 └── ctx.sessionController → DSH agent

分成两半,而且这个分法是有原因的:

- lib/index.js 是 DSH host 插件,与 Web GUI 同进程,因此能读写该实例已有的会话。它注册一条 HTTP 路由,并负责所有与 DSH 的交互。
- bin/dsh-mcp-relay.mjs 是独立的 MCP server,对你的 agent 讲 stdio、对插件讲 HTTP。它之所以存在,是因为 DSH 进程自己占用了 stdout,插件无法直接提供 stdio 服务。

等待与轮询都发生在 MCP server 内部,那里不产生任何模型开销;你的模型只花一次工具调用,然后拿到完成的结果。

环境要求

- 带 Web profile 的 DSH(dsh web 或桌面端)。
- 与 DSH 同一台机器上的 Node.js 18 或更高版本。
- 能启动 stdio server 的 MCP 客户端。

安装插件

直接从本仓库安装——该包未发布到 npm:

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

然后重启 DSH。启动日志里应出现:

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

也可以单独验证:

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

如果你的实例监听的不是 3080,请把这里和下面的 DSH_RELAY_URL 一起改成实际端口。

手动安装

若想自己接线,把包加进 profile 的 package.json 并列入 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"] } }
}

包自带 cordis.patch.yml,只插入一行 loader 配置。那一行声明了 inject: [webServer, sessionController], 不要删掉它。没有 inject 时,插件可能在那些服务存在之前就被激活,于是找不到可绑定的对象,最终一条路由都不会挂上。

配置 MCP 客户端

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

仅在 DSH 不在默认端口、或设置了 token 时需要 [mcp_servers.dsh.env]
DSH_RELAY_URL = "http://127.0.0.1:8080/dsh-relay"

server 会在 MCP 握手时(initialize.instructions)下发自己的用法说明,客户端无需额外提示词就能知道工作流程。

环境变量:

| 变量 | 默认值 | 用途 |
| DSH_RELAY_URL | http://127.0.0.1:3080/dsh-relay | 插件监听的地址。 |
| DSH_RELAY_TOKEN | 未设置 | 作为 x-dsh-relay-token 发送,需与插件侧一致。 |
| DSH_RELAY_TIMEOUT_MS | 600000 | 单次委派轮次的默认等待上限。 |
| DSH_RELAY_POLL_MS | 2000 | 进度轮询间隔。 |

工具

| 工具 | 用途 |
| dsh_list_sessions | 列出该实例的会话,可按工作区过滤。 |
| dsh_models | 列出该实例当前能路由的 provider/model id,以及每个模型可用的推理档位。 |
| dsh_delegate | 在工作区新建会话、或按 sessionId 继续一个会话,发送指令并等待该轮结束。 |
| dsh_answer | 回答 DSH agent 提出的问题,让它继续。 |
| dsh_approval | 选择该会话后续审批请求如何处理:gui、auto-approve 或 deny。 |
| dsh_cancel | 中断当前轮次——用于放弃一笔无人能应答的审批。 |
| dsh_poll | 读取会话状态与最新输出;用于等待返回 waiting 或 timeout 之后。 |

委派任务

dsh_delegate({ workspace: "/home/me/project", instruction: "给 parser.js 补上缺失的测试" })

省略 sessionId 会新建一个 DSH agent;把返回值里的 sessionId 传回来,就是继续同一个 agent,它还记得之前做过的事。status 告诉你发生了什么:

| status | 含义 |
| completed | 该轮结束,output 是 agent 的最终答复。 |
| waiting | 卡在等人:要么是被接管机制扣下的提问,要么是一笔待处理的审批。 |
| timeout | 仍在运行;用同一个 baselineSeq 再调 dsh_poll。 |

选择模型

会话默认跑实例的默认模型,除非调用方另行指定。dsh_models 报告该实例当前能路由什么——按 provider 分组的模型 id,以及每个模型的推理档位:

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

model 填模型 id;该 id 不歧义时 provider 会自动推断,也随时可以写成 provider/model。
reasoningEffort 必须是 dsh_models 为该模型列出的档位之一。实例路由不了的选择会在发送指令之前 直接让这次调用失败,而不是悄悄换一个模型把活干了。

选中的模型既作用于这一轮,也会被存为该实例对新建会话的默认模型——与 DSH 自带模型选择器的行为一致,所以要有意识地选。省略 model 就保持会话原来的模型。dsh_delegate 会把自己装上的选择回显为 model (并附 modelNote);dsh_list_sessions 和 dsh_poll 会报告每个会话当前的 model,读自 DSH 的 model-selection projection(只是提示:会话尚未记录过请求、或部署缺少该 projection 时为 null)。

回答问题

被委派的会话默认处于 takeover 状态:当 DSH agent 调用它的提问工具时,这次调用会被挂起并交给调用方,而不是丢给 GUI。此时 dsh_delegate 会立刻带着问题返回,而不是干等:

{
"status": "waiting",
"waiting": {
"kind": "user-question",
"toolName": "ask_user_question",
"intercepted": true,
"detail": [{ "id": "choice", "question": "用哪个数据库?", "options": ["sqlite", "postgres"] }]
}
}

用 detail 里的 id 回答:

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

若约三分钟内无人回答,问题会回落到 DSH GUI,因此无人值守的委派不会永久挂住。给 dsh_delegate 传 takeover: false 则从一开始就把问题留给 GUI。

审批

有些操作需要先获得决定,DSH 才会执行——例如升级沙箱,好让某个工具能写入会话工作区之外的位置。此时会话会报告:

{
"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 是 DSH 自己给出的说明,讲清它想做什么、为什么需要,调用方据此判断该不该批准。

已经挂起的那笔审批只有两条出路:由人在 DSH Web GUI 里回答,或者用 dsh_cancel 放弃整轮。
下面的模式都救不了它。

dsh_approval({ sessionId, mode }) 决定该会话后续审批怎么处理。策略在 agent 的下一步生效,
因此不会追溯:

- "gui"(默认)—— 留给 DSH Web GUI,由人回答。
- "deny" —— 把该会话策略设为 never:之后每个请求都直接判定为拒绝,不再询问。无人值守时这比无限等待更好——你会拿到一个干净的失败,而不是挂住。
- "auto-approve" —— 由本插件直接以一次性授权应答。能否生效取决于部署:插件必须排在 GUI answerer 的上游。所以注册成功并不等于它能工作——请查中继的 /dsh-relay/diagnostics (approvalListener.listenerHits);做不到时该端点会返回 effective: false 并附带警告。本插件实际开发所依据的那套部署里 listenerHits 为 0,因此只有 deny 与 gui 会真正生效。

仅仅尚未执行完的工具调用不会被报成 waiting;这类会话保持 running,并附带一个信息性的 pendingTool 字段。

安全

这个中继是一个控制面:能访问它的人,就能向该 DSH 实例的任意会话发送指令、读取其全部历史。

- 它只服务回环调用方,其余一律返回 403。这一点很重要,因为随附的 Web profile 绑定的是 0.0.0.0。
- 两端都设置 DSH_RELAY_TOKEN 可以额外要求 x-dsh-relay-token 头。
- 该路由直接注册在 DSH 的 HTTP 服务器上,不受 /api 的信任主机栅栏保护;上面的检查是唯一的关口。

已知限制

- MCP server 与 DSH 必须在同一台机器上,因为中继只服务回环。
- 单次委派轮次受 DSH_RELAY_TIMEOUT_MS 限制,且不流式返回中间输出。
- cancel 只停止当前活动轮次,DSH 里已排队的消息仍会继续执行。
- 插件通过 sessionController.page 读取会话尾部,因此超大会话只报告近期历史。

开发

开发这个插件不必反复重启 DSH:先把它作为动态 Cordis 插件加载,对着真实服务验证行为,再固化成 bundle 包安装。但最后一定要真重启一次——动态插件总是在它依赖的服务就绪之后才被激活,因此它无法暴露启动顺序类的缺陷(例如缺少 inject)。

中继的路由表带一个无依赖的测试:它用桩替换 Cordis 上下文,因此既不需要 DSH,也不需要重启:

npm test

许可证

MIT
预览分享卡片长什么样 ↓Nigheart/dsh-mcp-relay
分享 · DeepSeek Harness Hub
Nigheart/dsh-mcp-relay

把一个正在运行的 DeepSeek HarnessDSH实例通过

回到顶部