dsh-skill-mcp 0.4.0 → 0.4.2

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/lib/client.js CHANGED
@@ -10,8 +10,7 @@
10
10
  * - MCP 管理(v0.3):添加/编辑(表单 + JSON 切换)/删除(二次确认),
11
11
  * 从其他 agent(Claude Code / Codex / Cursor / Gemini CLI)扫描导入,
12
12
  * 写入作用域显式选择(全局 / 当前项目)。
13
- * - 挂载点:设置页 settings.section + 侧边栏 footer 快捷入口(全屏浮层),
14
- * 两处均渲染同一 CenterPanel。
13
+ * - 挂载点:设置页 settings.section 槽位,渲染 CenterPanel。
15
14
  */
16
15
  window.__ModuleLoader__.load({
17
16
  id: "dsh-skill-mcp",
@@ -23,8 +22,8 @@ window.__ModuleLoader__.load({
23
22
 
24
23
  const INJECT = ["slots", "locale", "workspaces", "connection"];
25
24
 
26
- /** connection RPC 通道(与 host 半一致)。 */
27
- const RPC_CHANNEL = "/skill-mcp";
25
+ /** dsh v0.1.5:浏览器侧统一走 /api 通道,endpoint 带插件前缀。 */
26
+ const RPC_PREFIX = "skill-mcp";
28
27
 
29
28
  let hostCtx = null;
30
29
 
@@ -33,7 +32,7 @@ window.__ModuleLoader__.load({
33
32
  * 返回的 value 保留业务形状({ok, message, ...})。
34
33
  */
35
34
  async function callApi(endpoint, body = {}) {
36
- const result = await hostCtx.connection.rpc.call(RPC_CHANNEL, endpoint, body);
35
+ const result = await hostCtx.connection.rpc.call("/api", `${RPC_PREFIX}/${endpoint}`, body);
37
36
  if (result.ok) return result.value;
38
37
  throw new Error(result.error?.message || `rpc ${endpoint} failed`);
39
38
  }
@@ -43,14 +42,6 @@ window.__ModuleLoader__.load({
43
42
  // ------------------------------------------------------------------
44
43
 
45
44
  const css = `
46
- .dsh-ccs-overlay { position: fixed; inset: 0; z-index: 1000; display: flex;
47
- align-items: center; justify-content: center;
48
- background: var(--dsw-alias-scrim, rgba(0,0,0,0.45)); }
49
- .dsh-ccs-overlay-panel { width: 640px; max-width: 92vw; height: min(640px, 86vh);
50
- background: var(--dsw-alias-bg-primary, #fff); color: var(--dsw-alias-text-primary, #222);
51
- border-radius: 12px; padding: 20px 22px;
52
- box-shadow: 0 8px 32px rgba(0,0,0,0.2);
53
- display: flex; flex-direction: column; }
54
45
  .dsh-ccs-page { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
55
46
  .dsh-ccs-title { margin: 0 0 6px; font-size: 16px; font-weight: 600; flex-shrink: 0; }
56
47
  .dsh-ccs-tabs { display: flex; gap: 2px; margin: 8px 0 14px; flex-shrink: 0;
@@ -103,9 +94,6 @@ window.__ModuleLoader__.load({
103
94
  .dsh-ccs-body-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; max-height: 60vh; }
104
95
  .dsh-ccs-body-scroll::-webkit-scrollbar { width: 8px; }
105
96
  .dsh-ccs-body-scroll::-webkit-scrollbar-thumb { background: var(--dsw-alias-scrollbar-bg-l2, rgba(127,127,127,0.35)); border-radius: 4px; }
106
- /* 浮层高度固定,列表区撑满剩余空间,去掉兜底上限 */
107
- .dsh-ccs-overlay-panel .dsh-ccs-list-scroll,
108
- .dsh-ccs-overlay-panel .dsh-ccs-body-scroll { max-height: none; }
109
97
  /* 设置页上下文:区块撑满设置内容区剩余高度,消除底部大片留白 */
110
98
  .dsh-ccs-page-fill { flex: 1 0 auto; min-height: calc(100vh - 180px); }
111
99
  .dsh-ccs-page-fill .dsh-ccs-list-scroll,
@@ -139,8 +127,7 @@ window.__ModuleLoader__.load({
139
127
  border: 1px solid var(--dsw-alias-border-primary, rgba(127,127,127,0.35));
140
128
  background: var(--dsw-alias-bg-primary, transparent); color: var(--dsw-alias-text-primary, #222); }
141
129
  .dsh-ccs-input-row { display: flex; gap: 8px; align-items: center; margin: 12px 0; }
142
- /* 弹框:添加/编辑 MCP(遮罩用 bg-mask-1,表面用实底 bg-base,避免透出变暗)。
143
- z-index 取 1100,高于全屏浮层 .dsh-ccs-overlay 的 1000。 */
130
+ /* 弹框:添加/编辑 MCP(遮罩用 bg-mask-1,表面用实底 bg-base,避免透出变暗) */
144
131
  .dsh-ccs-modal-mask { position: fixed; inset: 0; z-index: 1100; display: flex;
145
132
  align-items: center; justify-content: center; padding: 24px;
146
133
  background: var(--dsw-alias-bg-mask-1, rgba(0,0,0,0.45)); }
@@ -274,22 +261,6 @@ window.__ModuleLoader__.load({
274
261
  .dsh-ccs-check-sub { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #8a8a8a);
275
262
  font-family: var(--ds-font-family-code, ui-monospace, Consolas, monospace);
276
263
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
277
- /* 侧栏 footer 入口:整行按钮(图标 + 文字),对齐原生 Settings 行。 */
278
- div:has(> [data-slot='sidebar.footer.action']) { flex-wrap: wrap; }
279
- .dsh-ccs-launcher { flex: 0 0 100%; min-width: 0; display: flex; align-items: center;
280
- height: 40px; margin: 0; position: relative; }
281
- .dsh-ccs-launcher-btn { width: 100%; min-width: 0; height: 40px; cursor: pointer;
282
- color: var(--dsw-alias-label-primary, #222); background: none; border: none; border-radius: 0;
283
- display: inline-flex; align-items: center; gap: 6px; padding: 0 8px 0 6px;
284
- font-family: inherit; font-size: 14px; overflow: hidden; }
285
- .dsh-ccs-launcher-btn:hover { background: var(--dsw-alias-interactive-bg-hover-solid, rgba(127,127,127,0.12)); }
286
- .dsh-ccs-launcher-icon { flex: none; display: inline-flex; align-items: center; }
287
- .dsh-ccs-launcher-label { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
288
- /* 侧栏收起成 56px rail:入口缩成 36px 圆形,文字隐藏。 */
289
- .dsh-ccs-launcher.dsh-ccs-rail { flex: none; width: 36px; height: 36px; margin: 0; }
290
- .dsh-ccs-launcher.dsh-ccs-rail .dsh-ccs-launcher-btn { border-radius: 50%; justify-content: center;
291
- gap: 0; width: 36px; height: 36px; padding: 0; }
292
- .dsh-ccs-launcher.dsh-ccs-rail .dsh-ccs-launcher-label { display: none; }
293
264
  `;
294
265
 
295
266
  function ensureStyles() {
@@ -1222,44 +1193,6 @@ window.__ModuleLoader__.load({
1222
1193
  });
1223
1194
  }
1224
1195
 
1225
- /**
1226
- * 侧边栏 footer 快捷入口:整行「⚡ 技能与MCP」按钮,点击打开全屏浮层。
1227
- * 插槽只传 `wide`(侧栏是否展开);收起时缩成 36px 圆形、隐藏文字。
1228
- * 注意:宿主不渲染描述里的 label,文字必须由组件自绘。
1229
- */
1230
- function SidebarLauncher({ wide }) {
1231
- const [open, setOpen] = React.useState(false);
1232
- return jsx(React.Fragment, {
1233
- children: [
1234
- jsx('div', {
1235
- key: 'seat', className: `dsh-ccs-launcher${wide === false ? ' dsh-ccs-rail' : ''}`,
1236
- children: jsx('button', {
1237
- key: 'btn', type: 'button', className: 'dsh-ccs-launcher-btn',
1238
- title: '技能与MCP', 'aria-label': '技能与MCP', onClick: () => setOpen(true),
1239
- children: [
1240
- jsx('span', { key: 'icon', className: 'dsh-ccs-launcher-icon', children: '⚡' }),
1241
- jsx('span', { key: 'label', className: 'dsh-ccs-launcher-label', children: '技能与MCP' }),
1242
- ],
1243
- }),
1244
- }),
1245
- open
1246
- ? jsx('div', {
1247
- key: 'overlay', className: 'dsh-ccs-overlay',
1248
- onClick: (e) => { if (e.target === e.currentTarget) setOpen(false); },
1249
- children: jsx('div', {
1250
- className: 'dsh-ccs-overlay-panel',
1251
- children: jsx(CenterPanel, {
1252
- pickDirectory: hostCtx?.workspaces
1253
- ? () => hostCtx.workspaces.pickDirectory()
1254
- : undefined,
1255
- }),
1256
- }),
1257
- })
1258
- : null,
1259
- ],
1260
- });
1261
- }
1262
-
1263
1196
  // ------------------------------------------------------------------
1264
1197
  // apply
1265
1198
  // ------------------------------------------------------------------
@@ -1283,14 +1216,6 @@ window.__ModuleLoader__.load({
1283
1216
  // 注意要包一层箭头函数:方法内部依赖 this,摘下来传会丢绑定。
1284
1217
  pickDirectory: ctx.workspaces ? () => ctx.workspaces.pickDirectory() : undefined,
1285
1218
  })));
1286
-
1287
- // 侧边栏 footer ⚡ 快捷入口(sidebar.footer.action 槽位)
1288
- ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({
1289
- name: 'sidebar.footer.action',
1290
- id: 'dsh-skill-mcp',
1291
- order: 30,
1292
- label: () => '技能与MCP',
1293
- }, SidebarLauncher));
1294
1219
  };
1295
1220
 
1296
1221
  return { apply, inject: INJECT };
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(() => () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-skill-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
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",