DeepSeek Harness Hub
← 返回列表

MCP 工具懒加载MikotoMyWife/dsh-mcp-loader

MCP兼容 / 相关生态spec-screened在 GitHub 查看 ↗
未验证

按会话惰性加载 MCP 工具,精简上下文工具列表

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/10 · 已提供中文文档

为 DeepSeek Harness(DSH)实现 MCP 工具的懒加载:每个多工具 MCP 服务器一个加载器工具、按代理进行工具屏蔽、描述预设

综合分
29.4
GitHub 分
29.4
用户评分
★ Stars
0
周下载量
兼容 / 相关生态插件(非 dsh 原生,请按其对应运行时安装)
git clone https://github.com/MikotoMyWife/dsh-mcp-loader.git
数据截至 2026/9/16(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-mcp-loader

为 DeepSeek Harness(DSH)提供的惰性加载 MCP 工具:
每个多工具 MCP 服务器由一个加载器工具表示(默认为 mcp_),服务器的真实工具只有在调用加载器之后才会进入模型上下文。当安装了许多 MCP 服务器时,这消除了每个工具 schema 都出现在每个请求中的固定开销——并使可见工具列表保持足够小,以便模型能选对工具。加载是按会话进行的:调用加载器的会话(及其子代理)能看到这些工具,而进程中的其他所有会话——包括之后创建的会话——仍然只能看到加载器。

工作原理

初始工具列表:   mcp_notes, mcp_browser, mcp_desktop, ...        (每个服务器一个加载器)
↓ 模型调用 mcp_notes({})
→ "ok"                     下一个请求会暴露 mcp__notes__search、mcp__notes__create 等
↓ 模型再次调用 mcp_notes({})
→ "ok (20 tool(s) hidden)" 这些工具离开上下文
↓ 第三次调用会再次加载它们

加载器是一个切换开关,作用域限定在调用它的会话:调用一次加载,再调用一次隐藏,第三次调用再次加载——不需要单独的“卸载工具”占用一个位置。第二个会话切换同一个服务器时拥有自己的状态:它永远不会继承第一个会话的披露状态,在一个会话中隐藏也永远不会从另一个会话中撤回这些工具。底层生成只有在最后一个持有者释放它时才会被释放。工具数量 ≤ singleToolThreshold 的服务器会在启动时被检测到,并保持常驻而无需加载器——而且由于再没有任何东西能重新暴露它们,它们也永远不会按会话被屏蔽(singleToolThreshold: 0 会让每个服务器都有一个加载器,因此每个服务器都是按会话的)。

注册模型

- 工具 schema 在组装提示词时(ctx.systemPrompt.tools(...))被投影,因此在一个工具调用内部注册的工具会出现在同一轮中模型的下一个步骤里——无需额外的往返。
- 启动时只注册加载器(不启动任何进程)。一次性探测可能会连接 auto/eager 服务器以统计其工具数量:工具数量 ≤ singleToolThreshold 的服务器变为常驻,其加载器被移除;mode: lazy 服务器从不参与探测,也不会在启动时被启动。
- 注册是部署范围(全局)的,与官方 dsh-mcp-client 一致,因为 ctx.tools.restrict() 只过滤继承的工具,并拒绝未全局注册的名称——按代理隐藏(hiddenTools)建立在全局注册之上。
- 可见性是按会话的。全局注册会把工具放入每个代理都会继承的层中,因此一次没有会话作用域的加载会泄漏到其他所有会话;相反,加载器会把调用会话记录为一个持有者,而每个不是持有者(也不是持有者的子代理)的代理都会收到一个 restrict({ deny }) 掩码
在该代的公共名称之上。一个完全没有持有者加载的代——一次没有 agent 的调用,例如一个
驱动 ctx.tools.execute 的测试或脚本——保持部署范围的 v0.5.0 行为。
子代理会继承:当子代理是 subagent 时,遍历会沿着 parentSession 进行,并在
分叉处(isSeeded,无 origin)有意停止,因为这是一个新会话,会以未展开状态开始。

功能

- 按服务器加载器开关——加载、隐藏、重新加载,并带有可见的结果消息(ok、ok (N tool(s) hidden)),
且按会话生效:一个会话的加载对其他所有会话不可见(其子代理会继承它)。
- 模式——auto(启动时探测一次)、lazy(始终位于加载器之后,从不探测)、eager(始终常驻,无加载器)。
- hiddenTools——在服务器加载后按 agent 屏蔽特定工具(restrict({ deny })),对每个现有
agent 以及之后创建的 agent 都生效。条目是规则——精确原始名称(ping)、精确公共名称
(mcp__solo__ping)或 glob(e、mcp__solo__、? 匹配一个字符)——会在加载时根据服务器
实际暴露的工具展开。屏蔽仅影响按 agent 的可见性;这些工具仍保持全局注册。
- disabledTools——完全不注册匹配的工具(注册轴,全局):这些工具在注册前被丢弃,
不计入任何已加载工具总数,并且对每个 agent 都不可见。规则语法与
hiddenTools 相同。加载器工具永远不会受影响(它不是被发现的 MCP 工具)。
- 描述工程——按服务器的 description、descriptionPreset、按工具的 toolDescriptions
覆盖,以及参数描述截断(maxParameterDescriptionChars),使面向模型的文本
说明工具的作用以及何时使用它。
- 韧性——原子注册(任何失败都会将服务器回滚为零工具)、共享并发
尝试、list_changed 重新同步(整代替换)、原始 tools/call(跳过对
structuredContent 的 outputSchema 验证,与官方客户端相同)、传输失败时丢弃并在下次调用时重连。
- 发现硬上限——按服务器,一次真实发现受 maxToolListPages 页、maxToolsPerServer
个原始工具和 discoveryTimeoutMs 截止时间限制;超过其中任何一项都会使加载/重新同步失败,并给出指明
服务器和原因的错误,保留加载器工具,且永不重试。
- 有序重新同步——按服务器的单调发现代次确保两个并发的 list_changed
重新同步(或一次重新同步与一次加载竞争)绝不会让较旧的快照在较新的快照之后落地——并覆盖它:
过期的发现结果会被丢弃,而不是被应用。
- 传输——stdio(生成 command/args,env 合并到 SDK 默认环境)和
streamable-http(url/headers)。
- 配置快速失败——未知预设或无效服务器名称([A-Za-z0-9_-]{1,32})会使插件挂载失败,并给出
命名字段。

安装

bash
npm install            # 本地构建/测试
npm run build          # tsc → lib/
npm test               # 真实链路端到端测试:真实 cordis ctx + dsh-tools ToolRuntime + 真实 MCP stdio 子进程

然后在你的配置文件中注册该插件(id 为 mcp-loader,包名为 dsh-mcp-loader)并添加你的服务器:
yaml
- id: mcp-loader
name: dsh-mcp-loader
config:
servers:
notes:
description: "搜索、读取、创建并整理个人笔记。当任务涉及用户笔记时使用。"
command: npx
args: ['-y', 'mcp-remote', 'https://example.invalid/mcp']
desktop:
description: "控制真实桌面……"
mode: lazy
command: npx
args: ['-y', 'example-desktop-mcp']

配置

| 字段 | 默认值 | 含义 |
|---|---|---|
| servers | {} | 服务器表;键是 mcp____ 的命名空间 |
| servers..description | — | 加载器描述正文:它能做什么 + 何时使用它 |
| servers..mode | auto | auto / lazy(始终为加载器)/ eager(始终常驻) |
| servers..loaderName | mcp_ | 模型可见的加载器名称 |
| servers..hiddenTools | — | 工具规则(原始/公开的精确名称或 glob),在加载后按 agent 屏蔽;glob  匹配任意连续字符,? 匹配单个字符 |
| servers..disabledTools | — | 永不注册的工具规则(语法同 hiddenTools) |
| servers..toolDescriptions | — | 按工具覆盖描述 |
| servers..descriptionPreset | — | 内置描述表(desktop-touch) |
| servers..maxParameterDescriptionChars | 0 | 截断参数描述(预设可能隐含一个值) |
| servers..transport | stdio | stdio 或 streamable-http |
| servers..command/args/env/cwd | — | 要启动的 stdio 进程 |
| servers..url/headers | — | streamable-http 端点及额外请求头 |
| servers..toolCallTimeoutMs | 60000 | 每次 tools/call 的超时时间 |
| servers..reconnectAttempts | 1 | 每次用户可见操作(一次加载器加载、一次工具调用、一次启动探测):连接/发现失败后的重试次数——连接与发现共享一个预算,即 reconnectAttempts + 1 次尝试(0 = 仅尝试一次,v0.5.0 行为) |
| servers..reconnectBackoffMs | 500 | 基础重试延迟;每次重试翻倍(×2ⁿ),上限 30 秒 |
| servers..idleDisconnectMs | 0 | 在空闲该时长后关闭未加载服务器的 MCP 连接(0 = 始终保持热连接);下次加载时重新连接 |
| servers..maxToolListPages | 100 | 发现硬上限:每次真实发现的最大 tools/list 页数;超出则加载/重新同步失败并指明 pages(永不重试,永不截断) |
| servers..maxToolsPerServer | 500 | 发现硬上限:一个服务器可暴露的最大原始工具数;超出则失败并指明 tools(在 disabledTools 之前计数) |
| servers..discoveryTimeoutMs | 60000 | 单次真实分页的发现截止时间;超时会以 timeout 命名失败并断开连接,因此挂起的服务器永远不会阻塞后续调用 |
| connectTimeoutMs | 30000 | 连接握手超时 |
| singleToolThreshold | 1 | auto 模式:工具数量 ≤ 此值的服务器保持常驻 |
| loaderHint | Call to load this MCP server's tools into this session; call again to hide them. | 追加到每个加载器描述中 |
| probeAtStartup | true | 在启动时探测 auto/eager 服务器(lazy 永不探测) |

隐藏与禁用工具

有两个选项可将工具从模型视野中移除;它们作用于不同的轴,绝不能混为一谈。
两者接受相同的规则语法:

- 一个精确原始名称,例如 ping —— 对 MCP 工具的原始名称匹配 ^ping$;
- 一个精确公开名称,例如 mcp__inkstone__search —— 对公开名称 mcp____ 进行匹配;
- 任一拼写形式的通配符,例如 note_、search_?、mcp__inkstone__ ——  匹配任意字符序列
(包括空序列),? 恰好匹配一个字符;模式是锚定的(^…$)。

规则会在服务器加载时实际暴露的工具列表上展开(并在重新同步时重新展开),因此
模式只会隐藏实际存在的工具,且多条规则命中同一工具时只拒绝一次。精确名称保持
其 v0.5.0 行为不变。

| 配置 | 机制 | 作用范围 | 生效时机 |
|---|---|---|---|
| 加载器被调用两次 / mode: lazy 从未加载 | 注册表移除(卸载 / 未注册) | 所有代理 | 调用时 |
| 加载时未持有加载器(默认) | 可见性移除 —— 按代理执行 restrict({ deny }),当会话自身选择加入时解除 | 仅非持有者会话 | 加载后,按代理应用 |
| disabledTools | 注册表移除 —— 匹配的工具在注册之前被过滤掉 | 所有代理(全局禁用) | 加载 / 重新同步时 |
| hiddenTools | 可见性移除 —— 按代理执行 restrict({ deny }) | 每个代理(按代理掩码) | 加载后,按代理应用 |

这三个轴相互独立:在注册表中 ≠ 可见 ≠ 可调用。disabledTools 属于
注册表轴(从未注册的工具不可达),而按会话的默认掩码和
hiddenTools 则留在可见性轴上(工具保持全局注册,仅按代理掩码)。两个掩码
按代理合并,因此一个服务器可以既被配置隐藏,又未被相关代理持有。
这里刻意没有执行轴:此插件不添加任何 pre-execute 拒绝监听器 —— 一个已
注册且可见的工具就是可调用的。(只有当未来某种模式在保持工具注册的同时将其投影移除时,才值得添加执行轴拒绝。)

锁定保护。 服务器自身的加载器工具永远不会被其自身的规则隐藏或禁用:
- 与加载器名称文本完全相同的规则(loaderName 或默认的 mcp_)会在插件挂载时被拒绝,并给出指明服务器和加载器的消息;
- 一个同时覆盖加载器名称的 glob 会从拒绝掩码中移除,并在加载时记录一次性警告;加载器保持可见,以便服务器始终可以再次切换。

disabledTools 完全无法禁用加载器:加载器是插件注册的工具,绝不是被发现的 MCP 工具。

当一切都被禁用时。 一个所有被发现的工具都匹配 disabledTools 的服务器会被视为空服务器:加载器调用返回 ok 且不注册任何内容(记录为 loaded 0 tool(s) ... (N suppressed by disabledTools)),并且在 auto/eager 下,启动探测会完全移除加载器——此后该服务器只有在你编辑其配置并重启后才会返回(如果你希望在不重启的情况下重新启用工具,请保持 mode: lazy)。

与发现上限的交互。 每服务器发现上限位于连接层,统计的是原始发现的工具(页数 / 工具数 / 截止时间),而 disabledTools 稍后在加载器层进行过滤。因此,一个报告的原始工具数超过上限的服务器会在 disabledTools 能够发挥作用之前就发现失败——这是有意为之:上限防范的是失控服务器自身的目录,而不是你对它的配置。如果你需要禁用庞大目录中的大部分内容,请提高上限或移除该服务器。上限失败是确定性的,且绝不会重试。

已知限制

- 粒度是按服务器而非按工具(每个服务器一个加载器)。
- 工具规则在某一代加载时展开,并在重新同步时重新展开,因此 glob 看到的是当时的工具集。掩码对每个服务器在每个 agent 上应用一次(每 agent 的 restrict 不会在重新同步时重放),因此一个在后续重新同步中才首次出现的工具,会对该次重新同步之后创建的 agent 生效掩码,而不会对在较早展开下已被掩码的 agent 生效。
- 启动探测是一个快照:之后增长超过阈值的服务器会保持已揭示状态直到重启(用 mode: lazy 将其固定)。
- 图像/音频会变成 [image image/png] 文本占位符;只有工具会被桥接——不支持 MCP 资源/提示/进度以及任务类型的工具(与 dsh-mcp-client 一致)。
- 重连是有界且惰性的:一个用户可见操作(加载器加载、工具调用、启动探测)从 reconnectAttempts + 1 次连接+发现尝试的单一预算中支取(reconnectAttempts/reconnectBackoffMs);失败的 tools/call 会立即暴露且绝不重放。没有急切的后台保活/重连——一个 idleDisconnectMs: 0(默认值)的空闲服务器会无限期保持热连接,而 idleDisconnectMs > 0 的服务器仅在没有已加载工具时断开连接,并在下次加载时重连。
- 已知的生命周期边界情况(目前接受;针对这些情况的连接“代”方案留待后续切片处理):
- 当一个调用方正在连接时,另一个调用方断开连接,在很窄的时间窗口内可能留下两个已生成的子进程,并使失败的那个成为孤儿。
- 空闲断开连接会等待已在进行的连接尝试(最多 connectTimeoutMs)完成后再关闭;仅被调度的重试会被取消,而不是延迟断开连接。
- 在默认的 reconnectAttempts: 1 下,确定性的发现错误(例如重复的工具名称)会在暴露前重试一次。发现硬性上限是例外:DiscoveryLimitError(页面/工具上限或截止时间)是确定性的,会立即暴露,绝不重试。

生态系统定位

dsh-mcp-loader 是若干 DSH 插件之一,这些插件避免让庞大的 MCP 工具目录出现在每个请求中
(可与 dsh-mcp-lazy、dsh-capability-menu、dsh-tool-folder、dsh-mcp-lens、dsh-tool-search 比较)。
它的差异化特点:按服务器的加载器开关、按代理的 hiddenTools 屏蔽,以及面向模型的描述
预设——外加自有的连接(stdio 和 streamable-http),而不是包装另一个 MCP 客户端。

许可证

MIT

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群