跳到主要内容
路径文档

搭一个带 MCP + 子 Agent 的助手

端到端实战:把 DSH 的 MCP 集成 + 子 Agent 组合,搭一个"能查外部数据、能并行干活"的助手。机制见对应页面,这里照做。

一、目标

我们要做:

一个能"查 GitHub / 数据库"的助手(走 MCP)
+ 一个能"接收后续消息持续研究"的常驻子 Agent(走 subagent)
+ 一个把这些串起来的入口

二、接一个 MCP 服务器

MCP 集成。把 MCP 客户端挂进 profile:

# 在你的 profile 目录,用 patch 加一个 github MCP server

~/.dsh/profiles/web/cordis.patch.yml:

- insert:
- id: mcp-github
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: github
transport: stdio
command: npx
args: ['-y', '@modelcontextprotocol/server-github']
env:
GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN

重启 dsh web。现在模型能调 mcp__github__* 工具(GitHub 数据)。

@deepseek-ai/dsh-mcp-client 在核心里,直接挂;否则先 dsh plugin --profile web add。一个实例一个 server。

HTTP 型 server 用 streamable-http 传输(远程/本地 HTTP 端点):

- insert:
- id: mcp-web
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: web
transport: streamable-http
url: http://localhost:3000/mcp
headers:
Authorization: !!js '`Bearer ${process.env.MCP_TOKEN}`'

两种传输的关键字段:

字段传输说明
transport两者stdio(子进程)或 streamable-http(HTTP 端点),必填
serverName两者工具命名空间,[A-Za-z0-9_-]{1,32},实例间唯一
command / args / env / cwdstdio拉起子进程的命令、参数、环境、工作目录
url / headershttpserver 地址与附加头(如鉴权 token)
toolCallTimeoutMs两者单次 callTool 超时,默认 60000
failOnStartupError两者初始连接/同步失败是否让插件激活失败,默认 false
reconnect.*两者断线重连:指数退避(500ms 起翻倍,上限 30000ms),maxAttempts 默认 10 次后放弃

三、工具如何被发现与命名

启动时 mcp-client 等 listTools() 回来,把每个工具用 ctx.tools.register() 注册成 mcp__<serverName>__<rawName> 形式——Claude Code / Codex 同款命名。要点:

  • 命名空间隔离:两个 server 各自发布一个 search 也没冲突,分别挂在 mcp__github__searchmcp__web__search 下。
  • 重名 serverName 冲突:同一 serverName 出现两次,后加载的实例直接 load 失败。
  • 热更新同步:servernotifications/tools/list_changed 会自动 re-sync;断线重连成功后也重新发现,恢复的这代工具替换上一代,不重复也不泄漏。
  • 名字规范化:公开名限制 64 字符、[A-Za-z0-9_-];被替换/截断时追加 12 位十六进制 hash(serverName,rawName 的确定性函数),不同工具不会塌成同名。

改完 patch 后 HMR 热切:断开 + 重连,serverName 不变则工具名不变。

四、配一个常驻子 Agent

子 Agent。我们建一个可续子会话,让它持续吃消息再研究:

用 subagent startContinuable 建一个常驻"研究助手",
label: research-assistant,
initialPrompt: 你是常驻研究助手,随时收我的任务,用 MCP git 工具查数据。

之后随时 followup(childId, '再查一下这个 repo 的 issue') 喂新任务。子 Agent 有自己作用域、独立 header,不污染主会话。

五、派发任务与回报

常驻子 Agent 是"先建、再喂、最后回收"三段:

  • :subagent startContinuable 返回 { childId, messageId },prompt 已入子会话 inbox,不等它开跑。
  • :之后用 send_message(模型侧工具)或 followup(childId, ...)(服务 API)发后续,每条消息成为子 Agent 的下一个 FIFO 回合;它还在跑时,消息排队等当前回合结束,不能改正在跑的那回合。
  • 回报:子 Agent 可用子会话专属的 report 工具把结论回报父 Agent(帧成 Background subagent <child-id> reported:);即便它没调 report,结算时父会话也会收到 Background subagent <child-id> finished... 通知,带停止原因和最后一段话,不会静默丢结果。
  • 看/停:list_agents 列出常驻子 Agent(含 running/idle/ready 状态),interrupt_agent 只停当前回合、不清空已排队消息、不销毁子会话。

子 Agent 是独立会话、独立作用域;spawn 子会话默认看不到父会话历史,派发时给足上下文,而不是指望它记得上文。

六、组合:一条入口

把"问一句 → 子 agent 并行查/研究 → 汇总"做成一次调用:

我用 research-assistant 去查:最近 3 个 issue 里,哪些和构建有关?
把 mcp__github__ 能查的都查一遍,给我一个带链接的清单。

主 agent 把任务交给子 Agent,子 Agent 调 MCP 工具查 GitHub、回报清单。

七、常见失败排查

现象原因与处理
一个工具都没出现默认 failOnStartupError: false,初始连接失败时插件激活但不带任何工具;看启动日志,必要时设 true 让失败显式化
断线后工具还在但调用一直失败重连用指数退避;maxAttempts(默认 10)耗尽会注销该 server 全部工具,需 HMR 重载或重启 Host
serverName 重复报错后加载的实例 load 失败;给每个实例起唯一 serverName
工具名被外部注册抢占整代回滚(不会只注册一半),日志报冲突
send_message 发了没回音它只返回"已入队"确认,不返回子 Agent 回复;详情看子 Agent 自己的 transcript,或让子 Agent 用 report 回报
interrupt_agent 报未授权只有目标子 Agent 的祖先(含跨代)能停它;自己/兄弟/过期调用都拒绝
HTTP server 连不上streamable-http 的失败按每请求重试,不触发 supervisor 重启;确认 URL/headers 正确
report 收不到需要父 Agent 仍是"活的直系父会话";父已结算/卸载则回报无主

更完整的字段与行为见 MCP 集成子 Agent

八、验证与权限

dsh web --dump-config | grep -E "mcp|subagent" # 看 MCP + subagent 是否装载
# 会话里看 mcp 工具调用
zstdcat ~/.dsh/sessions/*/*/session.jsonl.zstd | grep -oE 'mcp__[a-z_]+' | sort -u

子 Agent 有自己的作用域;想限制它用什么工具,在 start/startContinuabletoolFilter

小结

现在你有一个工作台:外部数据(MCP)+ 并行研究(子 Agent)。再加 技能 把常用流程固化、或 工作流 自动化,就能当一个稳定助手用了。

想看完整机制:MCPMCP 集成;子 Agent → 子 Agent;worker 编排 → 工作流