Repository navigation
[Grok/dsh 35] 本地 MCP 发现与工具调用 #167
Description
Activity
Ticket 35 / #167 已合入
rewrite/grok-dsh-20260920的1f7de23。这次是快进推送(e2b56a8..1f7de23),没有强推。提交已 rebase 到 #151(e2b56a8)和 #172(7f927ee)之上,冲突两边都保留了(acp.rs、main.rs、rust.mjs、overlay 和 mock LLM)。本地 MCP 由 dsh 执行:codsh 负责发现和配置 MCP 服务器,每个服务器包一层 stdio 代理,再交给 dsh 挂载。工具调用走 dsh 原有的权限、Hook 和取消链路。
- 发现来源:
- 用户配置
[mcp],以及项目.grok/config.toml(需要信任) .mcp.json- Claude / Cursor 兼容配置(通过
compat或GROK_CLAUDE_MCPS_ENABLED/GROK_CURSOR_MCPS_ENABLED开关) - 编辑器 ACP 传入的
mcpServers
- 用户配置
- 生效规则:
disabled_mcp_servers和enabled=false生效。撤销项目信任后,项目里的服务器会被移除。 - CLI:
codsh --rust mcp list|add|remove|enable|disable|doctor(支持--json)。doctor会真实启动服务器,报告初始化结果和工具数量。
- TUI:
/mcps(别名/mcp)可列出状态,以及执行 enable、disable、restart、refresh,操作后重新挂载并续接当前会话。一轮进行中不允许操作。- 服务器缺失、崩溃、初始化失败会显示启动提示;plain 模式写到 stderr。
- 代理:
- 有启动超时
startup_timeout_sec/GROK_MCP_STARTUP_TIMEOUT_SECS/MCP_TIMEOUT,也有工具超时tool_timeout_sec。 - 状态和 stderr 写在
$DSH_HOME/mcp/下。 - 初始化错误记为
initialize failed (code): msg。 - 挂载失败的服务器会被剔除后重试,其余服务器照常可用。
- 有启动超时
- dsh 插件
rust-acp-mcp.mjs:- 提供工具目录,以及
search_tool(BM25)和use_tool(嵌套ctx.tools.execute,父调用即当前工具调用,因此权限审批、Hook 和取消都同链)。 - 输出上限用
max_output_bytes/GROK_MAX_MCP_OUTPUT_BYTES/MAX_MCP_OUTPUT_BYTES控制,超出部分写到$DSH_HOME/mcp/output/<callId>,并附参考实现的截断提示。
- 提供工具目录,以及
- 权限和 Hook:规则里的工具名会去掉
mcp__前缀后匹配,search_tool和use_tool按只读处理(嵌套的实际工具仍要单独审批)。Hook 看到的是去掉前缀后的真实工具名。 - 文档(README 中英文、CONTEXT、CONTRIBUTING、packages/cli/README)已更新。变更集是
.changeset/rust-dsh-mcp-local.md(patch)。
验证(Linux x86_64,Node 22.19,干净环境):
-
pnpm typecheck:通过。 -
pnpm test:86 个文件,2332 通过、1 失败。失败的是reference-evidence.spec.mjs的 "drives interactive help":高负载下跑了 8.1s,超过默认 5s 超时。单独用--testTimeout=60000跑通过,和本改动无关。 -
新增
scripts/rust-mcp.spec.mjs:10/10 通过,其中 3 个单元测试、7 个用例使用真实 dsh 加真实 MCP 测试进程(e2e/fixtures/rust-mcp-fixture.mjs,无需密钥)。覆盖完整链路(配置 → dsh → MCP 副作用 → 界面/ACP):- ACP 批准和拒绝,文件副作用按预期发生或不发生
use_tool、search_tool保留名字、schema、参数和结果- 工具报错、取消
- deny 规则和 Hook 阻断
- 程序缺失、崩溃、初始化失败,
doctor报告,崩溃后 restart 恢复 - 通过配置和环境变量设置输出上限(溢出文件已写出)
tool_timeout_sec- CLI 的 enable、disable、add、remove(禁用后工具从目录中消失)
- 项目信任和撤销
与
scripts/rust-acp-protocol.spec.mjs一起跑共 48/48 通过。 -
cargo fmt --check:通过。 -
cargo clippy -D warnings:失败,原因只有filesystem_sandbox.rs里已有的两处问题(tail_marker未使用、多余的return)。加-A dead_code -A clippy::needless_return后全部通过,本改动的代码没有警告。 -
cargo test --locked --workspace --no-fail-fast:503 + 498 + 42 + 61 + 2 + 379 通过,4 个失败,都不是本改动引入的:filesystem_sandbox的 2 个用例(strict_requests_network_isolation...、trusted_project_custom_applies...)是已有问题。privacy::drafts_stay_local...、voice::substitute_route...是已有的不稳定用例。我在未改动的基线e2b56a8上单独跑,同样会失败。- 新增的 12 个 mcp、mcp_proxy 和 permission 单测全部通过。
-
TUI 的
/mcps列表、disable、enable 和启动提示,是在这台 Linux 机器上用临时 Python PTY 脚本手动验证的,脚本未提交。
未验证:
- macOS 专用的
scripts/rust-*-pty-test.py和已安装产物(installed-artifact)脚本:没有在这台 Linux 机器上运行。这些脚本要求 macOS 环境或证据。 - macOS、Windows,
test:e2e,真实编辑器或图形界面,付费模型。
已知限制和留给后续的部分:
- 每次调用受 dsh 60s 上限约束。
structuredContent不单独渲染,use_tool只在没有文本时返回它。- 超过 64 字符的工具名会被哈希缩短。
- dsh 原生的
mcp__*工具仍然可见。 - 只支持 stdio,不启动 SSE/HTTP,远程 MCP 认证和多类型内容留给 [Grok/dsh 36] 远程 MCP 认证与多类型内容 #168,插件 MCP 贡献留给 [Grok/dsh 72] 插件 MCP 贡献接入 #204。
- managed 的 MCP allow/deny 和
disabled_mcp_tools还没有应用。 - 启动器只放行白名单里的环境变量。
$DSH_HOME/mcp/output里的溢出文件不会自动清理。- 挂载失败重试时,可能多留一条 dsh 会话记录。
main、npm 发布和默认版本都没有改动,PR #213 也没有动。
- 发现来源:
Parent
#132
Ticket: 35
Status: ready-for-agent
User Stories: 39, 41(见父规格)
What to build
用户配置并信任本地 MCP 服务,查看初始化状态,通过 dsh 查找和调用实际工具,并能重启或停用服务。
Acceptance criteria
Blocked by
Delivery contract
用户于2026-09-20批准当前测试与任务拆分。按父规格的已安装产物/真实终端和公开协议测试验收;实际实现须带适用的文档、Changeset及必要检查。ready-for-agent表示任务已定义,不表示前置任务已完成,也不授权自动开始本轮实施、购买付费资源或发布软件版本。