dsh-skill-mcp 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # dsh-skill-mcp
2
2
 
3
- DSH 技能与MCP插件(v0.2),两大功能:
3
+ DSH 技能与MCP插件(v0.4),两大功能:
4
4
 
5
5
  1. **技能软链接管理**:把任意源目录下的技能目录**以目录联接(Windows 上是 junction,macOS/Linux 上是 symlink)批量链接进用户技能根目录 `~/.dsh/skills`**,让 DSH 直接加载它们;同时提供技能生命周期管理:启用/停用、取消链接、删除。
6
- 2. **按项目自动加载 MCP**(v0.2 新增):监测当前会话工作区,读取用户级 `~/.dsh/mcp.json` 与项目级 `<项目根>/.dsh/mcp.json`(回退 `<项目根>/.mcp.json`,Claude 格式),通过 loader **热加载 / 热卸载**该项目声明的 MCP 服务——每个项目自动带上自己的 MCP(如 quick-java 项目里的 codegraph)。
6
+ 2. **MCP 管理与按项目自动加载**:面板内可直接添加/编辑/删除 MCP 服务,或从其他 agent(Claude Code / Codex / Cursor / Gemini CLI)扫描导入;同时监测当前会话工作区,读取用户级 `~/.dsh/mcp.json` 与项目级 `<项目根>/.dsh/mcp.json`(回退 `<项目根>/.mcp.json`,Claude 格式),通过 loader **热加载 / 热卸载**该项目声明的 MCP 服务——每个项目自动带上自己的 MCP(如 quick-java 项目里的 codegraph)。
7
7
 
8
- 设置弹窗里有 **技能与MCP** 页面(内部 tab 切换「技能 / MCP」),侧栏底部还有 **⚡ 技能与MCP** 快捷入口,两者打开的是同一个面板。
8
+ 入口在设置弹窗的 **技能与MCP** 页面,面板内部 tab 切换「技能 / MCP」。
9
9
 
10
10
  ## 安装
11
11
 
@@ -19,7 +19,7 @@ DSH 技能与MCP插件(v0.2),两大功能:
19
19
  dsh plugin --profile web add dsh-skill-mcp@latest
20
20
  ```
21
21
 
22
- 装完硬刷新浏览器(Cmd/Ctrl+Shift+R)即可看到「技能与MCP」设置页与侧栏底部 ⚡ 入口(DSH 对 client 改动热加载;仅 host 半更新时需要重启 `dsh web`)。
22
+ 装完硬刷新浏览器(Cmd/Ctrl+Shift+R)即可在设置弹窗看到「技能与MCP」页面(DSH 对 client 改动热加载;host 半有更新时需重启 `dsh web`)。
23
23
 
24
24
  ### 方式二:让 DSH 自己装——把下面这段提示词发给任意一个 DSH 会话
25
25
 
@@ -40,7 +40,8 @@ dsh plugin --profile web add dsh-skill-mcp@latest
40
40
  | --- | --- |
41
41
  | 安装时 pnpm 拦截 `node-pty` 构建脚本(仅旧版 dshmarket ≤1.37 的依赖) | 在 `~/.dsh/profiles/web` 执行 `pnpm approve-builds --all` 放行后重跑安装 |
42
42
  | 装完看不到入口 | 硬刷新浏览器(Cmd/Ctrl+Shift+R);host 半有改动时需重启 `dsh web` |
43
- | 设置页在但侧栏没有 ⚡ 按钮 | 检查宿主是否加载了客户端半边:package.json 的 `exports` 必须包含 `"./package.json"`,缺了会被静默丢弃 |
43
+ | 设置弹窗里没有「技能与MCP」页 | 先硬刷新;仍没有则检查客户端半边是否被加载——package.json 的 `exports` 必须包含 `"./package.json"`,缺了会被静默丢弃 |
44
+ | 宿主启动报 `loader entry failed` / `RPC_CHANNEL is not defined` | 运行副本(`~/.dsh/profiles/web/node_modules/dsh-skill-mcp`)版本过旧,与 dsh ≥ 0.1.5 不匹配;升级到 0.4.x 后重启 `dsh web` |
44
45
 
45
46
  ### 从源码安装(可选,替代 npm 方式)
46
47
 
@@ -126,15 +127,33 @@ dsh 原生格式(`{ "servers": { … } }` 或顶层直接是 name→config 映
126
127
  - 配置文件改动约 0.5 秒内自动生效(文件监听),无需重启;
127
128
  - tab 里可对每个服务热启停(写入 `~/.dsh/project-mcp-state.json`,重启后保持),状态点显示连接情况(绿=已连接、黄=加载中、红=失败、灰=停用)与工具数。
128
129
 
130
+ ### MCP 服务管理与跨 agent 导入(v0.3 新增)
131
+
132
+ MCP tab 工具栏提供 **添加 / 导入 / 重新加载**:
133
+
134
+ - **添加 / 编辑**:表单与 JSON 两种编辑方式(stdio / http 传输,支持 env、headers、args 等字段),写入作用域可选「全局」(`~/.dsh/mcp.json`)或「当前项目」(`<项目根>/.dsh/mcp.json`);保存后立即 reconcile 热生效;
135
+ - **删除**:二次确认后从所在配置文件移除并热卸载,按项目自动加载的条目不受影响;
136
+ - **从其他 agent 导入**:扫描本机已有 agent 的 MCP 配置(纯读取,不改动原文件),按 agent 分组勾选导入,已存在的条目自动跳过:
137
+
138
+ | agent | 扫描路径 |
139
+ | --- | --- |
140
+ | Claude Code | `~/.claude/settings.json`、`~/.claude.json`(后者覆盖同名) |
141
+ | Cursor | `~/.cursor/mcp.json` |
142
+ | Gemini CLI | `~/.gemini/settings.json`(`httpUrl` 归一为 http 传输) |
143
+ | Codex | `~/.codex/config.toml` 的 `[mcp_servers.*]`(内置零依赖 TOML 子集解析) |
144
+
145
+ 导入时同样选择写入作用域(全局 / 当前项目);SSE 等不支持的条目形状会被跳过。
146
+
129
147
  ## 界面
130
148
 
131
- - 设置页与侧栏快捷入口打开同一「技能与MCP」面板,顶部 tab 切换「技能 / MCP」;
149
+ - 入口在设置弹窗的「技能与MCP」页,面板顶部 tab 切换「技能 / MCP」(无侧栏入口);
132
150
  - 技能 tab:可搜索的技能列表(按名称/描述过滤;仅列表区滚动,头部固定),每行技能卡片带状态徽章(`linked`、`pinned`、`已停用`、`agent`);
151
+ - MCP tab:服务状态列表 + 工具栏(添加 / 导入 / 重新加载),添加/编辑表单与导入面板均为内联展开卡片,下拉菜单不嵌套滚动、编辑时不自动弹出;
133
152
  - 操作按颜色区分:启停 switch(绿=启用、灰=停用)、取消链接(蓝)、删除(红色悬停)、MCP 状态点(绿=已连接、黄=加载中、红=失败、灰=停用)。
134
153
 
135
154
  ## 技术说明
136
155
 
137
- - 宿主半边与客户端半边走 **connection RPC**(通道 `/skill-mcp`,`authority: 'loopback'`,官方推荐的 Client→Host 私有通道接缝),端点覆盖 `skills/*` 与 `mcp/*`;
156
+ - 宿主半边与客户端半边走 **dsh v0.1.5 的 `/api` 传输模型**:宿主用 `connection.fetch.register` 注册 15 个精确 POST 路由 `/api/skill-mcp/<endpoint>`(fetchRoutes 优先于网关 interceptor,注册过程只需 inject `connection`),浏览器侧固定 POST 调用;信封为 client-request `{rpcId, method, payload}` → server-response `{rpcId, result:{ok, value | error}}`,端点覆盖 `skills/*` 与 `mcp/*`;
138
157
  - 按项目 MCP 通过 `ctx.loader` 把条目热加载为 `@deepseek-ai/dsh-mcp-client`(id 前缀 `pmcp-`),运行状态由 loader fiber 阶段 + 工具目录派生;
139
- - 客户端半边通过 `settings.section` 扩展点注册设置页,通过 `sidebar.footer.action` 插槽注册侧栏快捷按钮;
158
+ - 客户端半边通过 `settings.section` 扩展点注册设置页(单一面板,不再注册侧栏入口);
140
159
  - package.json 的 `exports` 必须包含 `"./package.json"`——宿主用 `require.resolve('<pkg>/package.json')` 定位客户端 bundle,缺了这一项客户端半边会被静默丢弃。
package/lib/client.js CHANGED
@@ -22,8 +22,8 @@ window.__ModuleLoader__.load({
22
22
 
23
23
  const INJECT = ["slots", "locale", "workspaces", "connection"];
24
24
 
25
- /** connection RPC 通道(与 host 半一致)。 */
26
- const RPC_CHANNEL = "/skill-mcp";
25
+ /** dsh v0.1.5:浏览器侧统一走 /api 通道,endpoint 带插件前缀。 */
26
+ const RPC_PREFIX = "skill-mcp";
27
27
 
28
28
  let hostCtx = null;
29
29
 
@@ -32,7 +32,7 @@ window.__ModuleLoader__.load({
32
32
  * 返回的 value 保留业务形状({ok, message, ...})。
33
33
  */
34
34
  async function callApi(endpoint, body = {}) {
35
- const result = await hostCtx.connection.rpc.call(RPC_CHANNEL, endpoint, body);
35
+ const result = await hostCtx.connection.rpc.call("/api", `${RPC_PREFIX}/${endpoint}`, body);
36
36
  if (result.ok) return result.value;
37
37
  throw new Error(result.error?.message || `rpc ${endpoint} failed`);
38
38
  }
package/lib/index.js CHANGED
@@ -109,7 +109,7 @@ const USER_INVOCABLE_KEY = 'user-invocable'
109
109
  const PIN_KEY = 'pinned'
110
110
 
111
111
  // RPC 通道与按项目 MCP 的命名空间。
112
- const RPC_CHANNEL = '/skill-mcp'
112
+ const RPC_PREFIX = 'skill-mcp' // 挂在 /api/<prefix>/<endpoint> 下的精确 POST 路由
113
113
  const MCP_ENTRY_PREFIX = 'pmcp-'
114
114
  const MCP_CLIENT_NAME = '@deepseek-ai/dsh-mcp-client'
115
115
 
@@ -879,7 +879,9 @@ async function saveMcpDisabled(disabled) {
879
879
  // ---------------------------------------------------------------------------
880
880
 
881
881
  export const name = 'dsh-skill-mcp'
882
- export const inject = ['connection', 'loader', 'tools']
882
+ // connection.rpc.handle 内部会通过本插件 ctx 访问 webServer 注册路由,
883
+ // 新版 cordis 对未声明 inject 的服务访问直接抛错(旧版静默 undefined)
884
+ export const inject = ['connection', 'loader', 'tools', 'webServer']
883
885
 
884
886
  export function apply(ctx) {
885
887
  // ---- MCP engine state ----
@@ -1225,15 +1227,36 @@ export function apply(ctx) {
1225
1227
  },
1226
1228
  }
1227
1229
 
1228
- ctx.connection.rpc.handle(RPC_CHANNEL, async (endpoint, payload) => {
1229
- try {
1230
- const action = endpoints[endpoint]
1231
- if (action === undefined) return rpcFail(`unknown endpoint "${endpoint}"`)
1232
- return rpcOk(await action.run(payload ?? {}))
1233
- } catch (e) {
1234
- return rpcFail(e?.message || String(e))
1230
+ // ---- RPC(dsh v0.1.5 新传输模型)----
1231
+ // 浏览器侧固定 POST /api/<endpoint>;/api 前缀路由由 core 挂载。插件用
1232
+ // connection.fetch.register 注册精确 POST 路由(优先于网关 interceptor,
1233
+ // 注册过程不碰 webServer,只需 inject connection)。信封:client-request
1234
+ // {rpcId,method,payload} → server-response {rpcId,result:{ok,value|error}}。
1235
+ const okResponse = (rpcId, result) => new Response(
1236
+ JSON.stringify({ type: 'server-response', rpcId, result }),
1237
+ { status: 200, headers: { 'content-type': 'application/json' } },
1238
+ )
1239
+ ctx.inject(['connection'], (c) => {
1240
+ for (const [name, action] of Object.entries(endpoints)) {
1241
+ c.connection.fetch.register({
1242
+ path: `/api/${RPC_PREFIX}/${name}`,
1243
+ methods: ['POST'],
1244
+ requestBody: 'buffered',
1245
+ fetch: async (request) => {
1246
+ let rpcId = ''
1247
+ try {
1248
+ const msg = await request.json()
1249
+ rpcId = typeof msg?.rpcId === 'string' ? msg.rpcId : ''
1250
+ const value = await action.run(msg?.payload ?? {})
1251
+ return okResponse(rpcId, rpcOk(value))
1252
+ } catch (e) {
1253
+ return okResponse(rpcId, rpcFail(e?.message || String(e)))
1254
+ }
1255
+ },
1256
+ })
1235
1257
  }
1236
- }, { authority: 'loopback' })
1258
+ console.log(`[dsh-skill-mcp] rpc routes ready under /api/${RPC_PREFIX} (${Object.keys(endpoints).length} endpoints)`)
1259
+ })
1237
1260
 
1238
1261
  // 文件监听随插件卸载关闭。
1239
1262
  ctx.effect(() => () => {
@@ -1241,5 +1264,4 @@ export function apply(ctx) {
1241
1264
  watchers.clear()
1242
1265
  })
1243
1266
 
1244
- console.log(`[dsh-skill-mcp] host ready: connection RPC channel '${RPC_CHANNEL}' (skills + project MCP)`)
1245
1267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-skill-mcp",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Management center: link skill directories into the user skills root (enable/disable/unlink/delete) and manage MCP servers — add/edit/delete, import from other agents (Claude Code/Codex/Cursor/Gemini CLI), auto-load each project's own MCP servers via the loader",
5
5
  "license": "BSD-3-Clause",
6
6
  "homepage": "https://github.com/LvanLa/deepseek-harness-ex/tree/master/plugins/dsh-skill-mcp",