Skip to main content
Glama
zeneone

dsh-mcp

by zeneone
README.md
# dsh-mcp — MCP 服务器管理插件(MCP Server Manager for DSH)

为 DSH Web GUI 提供 MCP(Model Context Protocol)服务器管理能力:

- **服务器列表**:在「设置 → MCP」页中新增 / 编辑 / 删除 MCP 服务器,支持 stdio(子进程)、
  Streamable HTTP 与 SSE(服务器发送事件)三种传输,也可**从 JSON 批量添加**;配置存
  `~/.dsh/dsh-mcp.json`(0600、原子写入)。
- **启用 / 禁用**:每个服务器有全局开关;禁用后断开连接,其工具不再注册。
- **会话级连接选择**:会话栏(标题行)新增「MCP」选择器,可勾选本次会话连接哪些
  MCP 服务器(默认全部已启用,也可显式选择「不连接任何」),未勾选的服务器不在本会话
  使用;「设置 → MCP」页的「会话连接」页签同样可调整,勾选即时生效。
- **工具注册**:已连接服务器的工具以 `mcp__<服务器id>__<工具名>` 注册为**会话作用域**
  工具(经 `agent.ctx.tools.register`),只对选中的会话可见;调用通过 MCP 协议转发。
- **连接管理**:断线自动按指数退避重连(最多 10 次),支持 `notifications/tools/list_changed`
  工具列表热同步;GUI 提供一键连接测试;点击服务器「工具数」可罗列该服务器提供的工具。
- **OAuth 浏览器授权**:对需要 Bearer 授权的 HTTP/SSE 服务器(401 挑战),点「授权」弹出
  授权面板,在外部浏览器完成登录(RFC 9728/8414 发现 + 动态客户端注册 + PKCE);令牌持久化
  到 `~/.dsh/dsh-mcp-oauth/<id>.json`(0600)后自动携带,断线重连自动刷新;回调监听固定
  端口 3085(占用则回退随机,并自动清除失效注册重注册),授权完成后自动重连,删除服务器时
  一并清除令牌。
- **会话栏 MCP 选择器**:标题行的「MCP n」按钮下拉面板**自适应展开方向**——会话名再短、
  窗口再窄,面板都完整可见,不被窗口边缘裁剪。
- **Agent 工具**:`mcp_list` 列出已配置服务器与运行状态;系统提示词自动向模型宣告本插件。

## 架构

- 宿主端(Host,`src/index.ts`):cordis 插件 `name = 'mcp'`,挂载
  `/api/dsh-mcp/*` 路由(loopback 围栏)、`mcp_list` 工具、系统提示词段落,并监听
  `agent/created` / `agent/disposed` 维护每个会话的作用域工具注册。
- 引擎(`src/engine.ts` + `src/engine/connection.ts` + `src/engine/bridge.ts`):
  每服务器一个连接(@modelcontextprotocol/sdk),共享连接、按会话注册。
- 存储(`src/store.ts`):服务器 CRUD + 会话选择,纯文件 I/O,无 cordis 依赖。
- 客户端(`src/client/*`):官方插槽挂载——「设置」侧边栏的 `settings.section`(id `mcp`,
  服务器 / 会话 两个页签)+ 会话栏 `conversation.session.header.actions` 的 MCP 选择器;
  颜色走 `--dsw-alias-*` 主题变量(自动适配明暗主题)。

## 安装

```sh
# 在 dsh web profile(默认 web)中安装(link 方式,随源码热更新)
dsh plugin --profile web add "link:<本包绝对路径>"
```

安装后重启 dsh(`dsh --profile web`)生效:宿主加载节点端,Web GUI 加载浏览器端
(`/plugins/dsh-mcp/client.js`)。

### 移交给他人安装

把整个 `dsh-mcp` 文件夹交给对方即可(**无需 node_modules**,但要保留 `lib/` 构建产物——
它包含宿主端 `lib/index.js` 与浏览器端 `lib/client.js`,是 dsh 实际加载的内容)。对方步骤:

```sh
# 1. 放到任意位置(如 ~/plugins/dsh-mcp)
# 2. 安装依赖(需 pnpm;lock 文件保证版本一致)
cd dsh-mcp
pnpm install

# 3. 若 lib/ 缺失(例如通过 git 交付且 .gitignore 忽略了它),先构建
pnpm run build

# 4. 可选:验证(34 个测试,含真实 stdio/SSE MCP 服务器与 OAuth 端到端)
pnpm run typecheck && pnpm test

# 5. 安装到 dsh web profile(link 方式,源码改动即热更新)
dsh plugin --profile web add "link:C:/path/to/dsh-mcp"

# 6. 重启 dsh 生效
dsh --profile web
```

**Windows 注意事项**:
- 若 `pnpm install` 报 `ERR_PNPM_UNEXPECTED_STORE`(pnpm store 路径冲突),在 profile 的
  `~/.dsh/profiles/web/.npmrc` 写入 `store-dir=你的 pnpm store 路径`(如
  `C:/Users/<你>/AppData/Local/pnpm/store/v11`)。
- 若 `dsh plugin add` 安装失败,可手工安装:在 `~/.dsh/profiles/web` 下执行
  `pnpm add --store-dir <同上的 store 路径> link:C:/path/to/dsh-mcp`,并把 `dsh-mcp`
  加进该 profile 的 `dsh.profile.bundles` 清单后重启。
- 运行环境要求:Node ≥ 22.19(package.json engines)。

## 使用

1. 打开「设置」,侧边栏选择「MCP」进入服务器管理页。
2. 「服务器」页签:新增服务器(表单或「从 JSON 添加」,stdio 填写启动命令/参数/环境变量,
   或 Streamable HTTP / SSE 填写 URL/请求头),点击「测试」验证连通;点击「工具数」罗列工具;
   编辑时表单自动预填已保存的参数/环境变量,也可切到「查看 JSON」查看完整配置并复制。
3. 会话栏标题行点击「MCP」选择器,勾选本会话要连接的服务器(或点「全部已启用」/「不连接任何」),
   即时生效——该会话的模型即可调用对应 `mcp__…` 工具;「设置 → MCP → 会话连接」页签同样可调。
4. 服务器需要 Bearer 授权时:编辑服务器,在 HTTP/SSE 表单中打开「启用 OAuth 浏览器授权」,
   保存后在列表点「授权」——浏览器会打开授权页,完成后自动连接(令牌存
   `~/.dsh/dsh-mcp-oauth/<id>.json`);「清除授权」可重新授权。
5. 在会话中可直接询问 agent「有哪些 MCP 服务器」——它会用 `mcp_list` 回答。

## 安全模型

- 配置(stdio 环境变量、HTTP 头如 Authorization)以明文存于本机私有文件
  `~/.dsh/dsh-mcp.json`——与 dsh-ssh 密码存储同一信任模型。列表接口不返回密钥值;
  仅「编辑详情」接口(loopback 限定)返回完整配置供表单预填与「查看 JSON」,避免
  编辑时误清空已保存的参数与环境变量。
- stdio 传输会以宿主进程权限启动用户配置的命令;MCP 工具输出**原样返回**,可能含敏感信息。
- OAuth 令牌(access/refresh token 与 PKCE verifier)以明文存于
  `~/.dsh/dsh-mcp-oauth/<serverId>.json`(0600)——与配置文件同一信任模型;授权回调用
  127.0.0.1 回环端口监听,仅本机可访问。回调监听固定端口 `3085`(被占用时回退随机
  端口),保证 dsh 重启后已注册客户端仍匹配,不会出现 `invalid redirect_uri`;
  若检测到端口变化会自动清除旧注册并重新注册。
- 所有 `/api/dsh-mcp/*` 路由仅限 loopback(防 LAN 暴露)。

## 开发

```sh
pnpm install
pnpm run typecheck   # tsc --noEmit
pnpm test            # vitest(store / bridge / engine 端到端)
pnpm run build       # tsc 声明 + tsdown(lib/index.js + lib/client.js)
```

引擎端到端测试会真实启动一个 stdio MCP 服务器子进程验证连接、发现、作用域注册与调用转发。

## 已知限制

- 不支持需要 `taskSupport: required` 的 MCP 工具(桥接层拒绝并报错)。
- 图片类工具结果降级为文本占位(不接入附件存储)。
- 服务器重命名不影响工具名(工具名以 `id` 为命名空间);删除服务器会断开连接并从所有
  会话选择中移除。

## 版本记录

- **0.2.0**:OAuth 浏览器授权完善——授权面板(外部浏览器完成、自动检测)、回调固定端口 3085、
  端口变化自动重注册(修 `invalid redirect_uri`)、删除服务器同步清令牌、授权按钮始终可见(可重新授权)、
  授权完成自动重连(verifier 快照免疫陈旧连接覆盖、连接 401 不再自动发起授权流干扰用户流程)、
  会话栏选择器下拉自适应防裁剪、`mcp_list` 输出与 schema 对齐。
- **0.1.0**:基础版本——服务器列表增删改/启用禁用/测试、会话级连接选择、每会话作用域工具注册、
  断线重连、工具列表热同步、SSE 传输支持、JSON 批量添加、编辑预填/查看 JSON。