DeepSeek Harness MCP 客户端问题:工具整族消失、会话失效不重连与分页游标
DeepSeek Harness(DSH)接 MCP 服务器时,故障几乎都发生在「连接之后的第二段流程」——注册与刷新,而不是连接本身。 本批讨论里有四种典型形态,而且它们各自有非常好认的日志判据:工具整族消失(注册代号切换时回滚不完整,加上「包含」语义吞掉错误,某个服务器本轮以 0 工具结束,旧代号又已被销毁,只能重启宿主);会话失效不重连(重连逻辑挂在传输关闭回调上,而「会话过期」不关闭传输,只走错误回调,于是每个调用都报 No valid session ID provided);新增工具看不到(自动刷新开关被写成字面量关闭,list_changed 通知白发);分页游标永远拿不到(不带游标的调用被转给 SDK 的自动聚合路径,它把多页合并并删掉 nextCursor)(#9864、#9740、#9625、#9374)。
DeepSeek Harness 工具整族消失:注册回滚不完整
一句话结论:代号切换时先销毁旧代号再注册新代号;一旦注册抛错,回滚只覆盖新代号,而配置又是「包含」语义——错误被吞掉、返回空集合,于是这个服务器本轮以零工具结束,旧代号却已经没了。 症状是「服务器进程活着、资源能列、裸握手能列 14 个工具,但 agent 工具面里 mcp__<server>__* 一个都没有」,只有重启宿主才恢复(#9864)。
DeepSeek Harness 三步定位:靠可搜索的日志行判阶段
- 先做裸握手对照:用 MCP 客户端直接连该服务器并列出工具——能列出说明服务器侧没问题,问题在宿主侧注册阶段(#9864)。
- 按日志行区分是哪个阶段。讨论中给出了可 grep 的两类日志:一类是「注册失败、未注册任何工具」(代号切换/注册阶段),另一类是「重新同步失败」(重连阶段)。两种对应的成因与处置不同(#9864)。
- 确认「包含」语义的后果:如果日志显示该 server 本轮注册了 0 个工具,而不是「少了几个」,即为本条(#9864)。
DeepSeek Harness 处置
- 即时恢复:重启宿主(唯一有效手段)(#9864)。
- 减少触发:让同一个服务器不要在两个工具面上同时提供同名工具,避免注册冲突(#9864)。
- 插件侧的做法与代价:可以给工具注册接口打补丁把冲突降级为非致命——但这会把「整代回滚」换成「只丢冲突的那一个名字」,属于用一致性换可用性的取舍(#9864)。
DeepSeek Harness 会话失效不重连:错误只走 onerror,不关传输
一句话结论:重连逻辑只挂在传输关闭事件上,而「会话失效」不会关闭传输——SDK 只通过错误回调上报,于是重连从未被触发,工具闭包永久绑定在死掉的那一代连接上。 症状是每个调用都报 Bad Request: No valid session ID provided 或 404 Session not found,只到重启 DSH 才恢复(#9740)。
DeepSeek Harness 判据与一个容易漏掉的触发条件
- 判据:报错里明确提到会话无效(400 或 404),而服务器进程可能已经重启过;重启 DSH 后立刻恢复(#9740)。
- 不要只盯着「服务器重启」:讨论中后来订正了触发条件——长时间空闲导致会话过期同样会触发(讨论中提到的量级是 12 小时空闲),所以「我什么都没重启」并不排除本条(#9740)。
DeepSeek Harness 三条处置
DeepSeek Harness 新增工具看不到:list_changed 的自动刷新被写死关闭
一句话结论:客户端构造时把「工具列表变化的自动刷新」硬编码成关闭(字面量,不是配置项);虽然「收到变化通知就刷新」的机器还在,但因为开关关着,它永远不会被触发。 症状是运行中的会话永远看不到服务器运行期新增的工具,报 unknown tool "mcp__<server>__<newTool>",而新建会话立刻可见(#9625)。
DeepSeek Harness 四步处置
- 确认判据:同一服务器,当前会话看不到新工具、新建会话能看到;这就是本条(#9625)。
- 区分两个开关:讨论中特别提醒「自动刷新」与「收到通知后的刷新回调」是两个开关——后者存在不代表前者开着;排查时不要因为看到回调就说「逻辑在、只是没触发」(#9625)。
- 规避:重启 DSH(重连路径会强制同步一次)(#9625)。
- 注意插件的边界:会话工具面发生变化时会发出开发者消息通知,但出树插件够不到闭包内部的那一代连接,所以没法从外部把它打开(#9625)。
DeepSeek Harness 分页游标:不带 cursor 的调用被自动聚合吞掉
一句话结论:调用方不带游标时,请求被转给 SDK 的自动聚合路径——它把所有页合并、并删掉 nextCursor 字段**,所以契约要求的「一页加游标」永远拿不到;页数超过内部上限 64 时直接报错。** 症状是 list_mcp_resources / list_mcp_resource_templates 从不返回游标,服务器页数多时报 exceeded listMaxPages (64); server pagination did not terminate(#9374)。
DeepSeek Harness 四步处置与一个要注意的契约变化
- 确认判据:返回结果里始终没有
nextCursor字段;页数多时报出上述错误(#9374)。 - 临时绕行:减少生效的数据量(缩小服务器侧单页容量或减少资源数),或改用能直接访问该服务器的客户端取资源列表(#9374)。
- 修法(讨论中给出):每次只请求一页并原样返回游标;作者提供了一条只改连接层的分支(#9374)。
- 契约变化要知情:改成「一页 + 游标」之后,调用方返回体积会从「全页」变成「一页」——这是行为变更,下游若有依赖需要同步调整(#9374)。
DSH插件 排查注意事项
MCP 问题的排查口诀是:先裸握手确认服务器侧,再按日志判「注册阶段还是重连阶段」,最后才怀疑服务器实现。 八条要点:
- 裸握手对照能一次排除一半可能,成本最低(#9864)。
- 「整族归零」与「少了几个」是两种成因:前者是回滚缺陷,后者多为冲突降级(#9864)。
- 会话失效不关传输,所以重连永远不发生(#9740)。
- 空闲也会让会话失效,不只服务器重启(#9740)。
- 「新建会话能看见、当前会话看不见」= 刷新开关问题,这是本条最省事的判据(#9625)。
- 两个开关别混为一谈:自动刷新 ≠ 变化回调(#9625)。
- 分页没有游标是中间层吃掉的,不是服务器不返回(#9374)。
- 修复状态如实:四条在讨论所指版本(
0.2.0-rc.2~0.2.1-alpha.*)里都未修;其中会话重连与分页游标有社区补丁/分支可参考,但都未合入,属用户侧绕行。
MCP 这类「工具时有时无」的问题,排查时最需要的是「当前到底连了哪些服务器、工具面长什么样」的清楚视图。与其在各处手敲命令,不如用 DSH Plugin Hub 这类可视化插件市场:已安装列表、装卸历史与系统日志集中在一个面板里,先把环境看清。

来源:Discussion #9864、Discussion #9740、Discussion #9625、Discussion #9374。
常见问题
在 DeepSeek Harness 里,这来自工具注册阶段的回滚缺陷:客户端在切换代号时会先销毁旧代号再注册新代号,一旦注册过程抛出冲突错误,回滚只回滚了新代号,而错误配置又是「包含」语义——它会吞掉错误并返回空集合,于是该服务器这一轮以零工具结束,而旧代号已经被销毁。判据是可搜索的日志行:注册失败且未注册任何工具与重新同步失败是两种不同阶段。
在 DeepSeek Harness 里,这是因为客户端只在传输关闭事件上重连,而「会话失效」不会关闭传输,SDK 只通过错误回调上报,于是重连逻辑根本没被触发,工具闭包永久绑在已经失效的那一代连接上。已知的触发条件不止服务器重启,长时间空闲导致会话过期也会触发。唯一即时可行的恢复是重启宿主,长期方向是订阅错误回调并按特定错误做窄判定后重连。
在 DeepSeek Harness 里,这是因为自动刷新开关被写成了字面量而不是配置项。客户端在构造时把工具列表变化的自动刷新硬编码为关闭,虽然「收到变化通知就刷新」的机器还在,但因为开关关着,它永远不会被触发。判据是同一服务器在当前会话看不到新工具、而新建会话立刻可见;规避办法是重启宿主,正解是把该开关改成可配置或默认开启。
在 DeepSeek Harness 里,这是因为不带游标的调用被转给了 SDK 的自动聚合路径:它会把所有页合并、并删掉游标字段,所以契约要求的「一页加游标」永远拿不到;页数超过内部上限 64 时就会报错。讨论中给出的修法是每次只请求一页并原样返回游标;在那之前,可以减少单页容量或改用能直接访问该服务器的客户端来取资源列表。
在 DeepSeek Harness 里,做一次裸握手对照最省事:用 MCP 客户端直接连该服务器并列出工具。如果裸握手能看到工具而在 DSH 里看不到,问题在宿主侧(注册或刷新);如果裸握手也列不到,那就是服务器自身没暴露。这一步能省掉大量猜测,尤其对「工具整族消失」那类只能靠日志判阶段的问题。
相关术语
- MCP(Model Context Protocol)
- MCP 是用于把外部工具与资源接入模型的开放协议,DeepSeek Harness 通过 mcp-client 连接 MCP 服务器,并把服务器暴露的工具注册进会话工具面。连接与注册是两个阶段,本族多数故障出在后者。— https://github.com/deepseek-ai/deepseek-harness/discussions/9864
- list_changed 通知
- list_changed 是 MCP 协议里服务器用于告知「我的工具列表变了」的通知。客户端需要显式打开自动刷新开关才会据此重新拉取,否则运行中的会话不会看到新增或移除的工具。— https://github.com/deepseek-ai/deepseek-harness/discussions/9625
- nextCursor
- nextCursor 是 MCP 分页返回里指向下一页的游标,客户端按「一页数据加一个游标」的契约逐页拉取。若中间层把多页合并并删除该字段,调用方就再也拿不到游标,也无法继续翻页。— https://github.com/deepseek-ai/deepseek-harness/discussions/9374
- generation.onclose
- generation.onclose 是 DeepSeek Harness 的 MCP 客户端里用于感知连接生命周期结束的回调,重连逻辑挂在它上面。会话失效这类错误不会关闭传输,因此不会触发它,重连也就不会发生。— https://github.com/deepseek-ai/deepseek-harness/discussions/9740
来源
- #9864 — mcp-client 工具注册静默失败:server 存活、资源可列,但整族工具消失(仅重启宿主可恢复)· deepseek-ai(GitHub Discussions)
- #9740 — mcp-client: stateful MCP server restart leaves every tool call failing until DSH restarts· deepseek-ai(GitHub Discussions)
- #9625 — 运行中的会话看不到 MCP 服务器运行期新增的工具(list_changed 被硬编码关闭)· deepseek-ai(GitHub Discussions)
- #9374 — list_mcp_resources 从不返回 nextCursor,超过 64 页直接报错· deepseek-ai(GitHub Discussions)