@max-null/dsh-skill-mcp-center 0.5.0 → 0.5.1
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/client.js +12 -1
- package/dist/rpc.d.ts +35 -3
- package/dist/rpc.js +109 -44
- package/package.json +5 -2
- package/release-notes.md +35 -0
package/client.js
CHANGED
|
@@ -924,7 +924,18 @@ var inject = ["slots", "connection", "locale"];
|
|
|
924
924
|
function apply(ctx) {
|
|
925
925
|
injectCss();
|
|
926
926
|
rpc = async (endpoint, payload = {}) => {
|
|
927
|
-
const
|
|
927
|
+
const response = await fetch("/api/skill-mcp", {
|
|
928
|
+
method: "POST",
|
|
929
|
+
headers: { "content-type": "application/json" },
|
|
930
|
+
body: JSON.stringify({ endpoint, payload })
|
|
931
|
+
});
|
|
932
|
+
if (response.status === 404) {
|
|
933
|
+
const legacy = await ctx.connection.rpc.call("/skill-mcp", endpoint, payload);
|
|
934
|
+
if (legacy.ok) return legacy.value;
|
|
935
|
+
throw new Error(legacy.error?.message ?? `skill-mcp-center: ${endpoint} failed`);
|
|
936
|
+
}
|
|
937
|
+
if (!response.ok) throw new Error(`skill-mcp-center: ${endpoint} failed (HTTP ${response.status})`);
|
|
938
|
+
const result = await response.json();
|
|
928
939
|
if (result.ok) return result.value;
|
|
929
940
|
throw new Error(result.error?.message ?? `skill-mcp-center: ${endpoint} failed`);
|
|
930
941
|
};
|
package/dist/rpc.d.ts
CHANGED
|
@@ -1,11 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `SkillMcpRpc` —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* `SkillMcpRpc` — the host half of the browser's private RPC entry point.
|
|
3
|
+
*
|
|
4
|
+
* Transport (2026-09-17). Two host shapes are supported because the two
|
|
5
|
+
* compositions this plugin runs under differ:
|
|
6
|
+
*
|
|
7
|
+
* - **Newer hosts (0.1.5 line, e.g. the web source build)** expose
|
|
8
|
+
* `connection.fetch.register(...)`. This route is used there because
|
|
9
|
+
* `connection.rpc.handle('/skill-mcp', …)` ends at `owner.webServer.register()`
|
|
10
|
+
* inside Connection (`client-connection/src/rpc-host.ts:179`) and the owning
|
|
11
|
+
* fiber declares no `webServer` injection, so it throws
|
|
12
|
+
* `cannot get property "webServer" without inject`; every channel registered
|
|
13
|
+
* that way fails and the browser sees `HTTP 405` from the static fallback.
|
|
14
|
+
* `fetch.register` only writes Connection's own route map, so one exact route
|
|
15
|
+
* under the shared `/api` channel serves the same endpoints.
|
|
16
|
+
*
|
|
17
|
+
* - **Older hosts (e.g. the packaged SSiD kernel)** expose only the logical
|
|
18
|
+
* channel registry, where `rpc.handle` works, so it stays as the fallback.
|
|
19
|
+
*
|
|
20
|
+
* `connection` is resolved with `ctx.inject` rather than a declared dependency:
|
|
21
|
+
* it activates after this plugin, and `ctx.plugin()` does not wait for
|
|
22
|
+
* dependencies.
|
|
5
23
|
*/
|
|
6
24
|
import { Service, type Context } from '@deepseek-ai/cordis';
|
|
25
|
+
/** Exact Fetch route serving this plugin's endpoints under the shared channel. */
|
|
26
|
+
export declare const ROUTE = "/api/skill-mcp";
|
|
27
|
+
/** Logical channel used on hosts without exact Fetch-route registration. */
|
|
28
|
+
export declare const LEGACY_CHANNEL = "/skill-mcp";
|
|
29
|
+
/** Body the browser half posts to {@link ROUTE}. */
|
|
30
|
+
export interface SkillMcpRequest {
|
|
31
|
+
/** Endpoint name, e.g. `listSkills`. */
|
|
32
|
+
readonly endpoint: string;
|
|
33
|
+
/** Endpoint arguments. */
|
|
34
|
+
readonly payload?: unknown;
|
|
35
|
+
}
|
|
7
36
|
export declare class SkillMcpRpc extends Service {
|
|
8
37
|
static inject: string[];
|
|
38
|
+
/**
|
|
39
|
+
* @param ctx - plugin context carrying the skill/MCP engine.
|
|
40
|
+
*/
|
|
9
41
|
constructor(ctx: Context);
|
|
10
42
|
}
|
|
11
43
|
export default SkillMcpRpc;
|
package/dist/rpc.js
CHANGED
|
@@ -1,58 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `SkillMcpRpc` —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* `SkillMcpRpc` — the host half of the browser's private RPC entry point.
|
|
3
|
+
*
|
|
4
|
+
* Transport (2026-09-17). Two host shapes are supported because the two
|
|
5
|
+
* compositions this plugin runs under differ:
|
|
6
|
+
*
|
|
7
|
+
* - **Newer hosts (0.1.5 line, e.g. the web source build)** expose
|
|
8
|
+
* `connection.fetch.register(...)`. This route is used there because
|
|
9
|
+
* `connection.rpc.handle('/skill-mcp', …)` ends at `owner.webServer.register()`
|
|
10
|
+
* inside Connection (`client-connection/src/rpc-host.ts:179`) and the owning
|
|
11
|
+
* fiber declares no `webServer` injection, so it throws
|
|
12
|
+
* `cannot get property "webServer" without inject`; every channel registered
|
|
13
|
+
* that way fails and the browser sees `HTTP 405` from the static fallback.
|
|
14
|
+
* `fetch.register` only writes Connection's own route map, so one exact route
|
|
15
|
+
* under the shared `/api` channel serves the same endpoints.
|
|
16
|
+
*
|
|
17
|
+
* - **Older hosts (e.g. the packaged SSiD kernel)** expose only the logical
|
|
18
|
+
* channel registry, where `rpc.handle` works, so it stays as the fallback.
|
|
19
|
+
*
|
|
20
|
+
* `connection` is resolved with `ctx.inject` rather than a declared dependency:
|
|
21
|
+
* it activates after this plugin, and `ctx.plugin()` does not wait for
|
|
22
|
+
* dependencies.
|
|
5
23
|
*/
|
|
6
24
|
import { Service } from '@deepseek-ai/cordis';
|
|
7
|
-
|
|
25
|
+
/** Exact Fetch route serving this plugin's endpoints under the shared channel. */
|
|
26
|
+
export const ROUTE = '/api/skill-mcp';
|
|
27
|
+
/** Logical channel used on hosts without exact Fetch-route registration. */
|
|
28
|
+
export const LEGACY_CHANNEL = '/skill-mcp';
|
|
8
29
|
function internal(message) {
|
|
9
30
|
return { ok: false, error: { code: 'internal', message, details: {} } };
|
|
10
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Wrap one endpoint result as a JSON response.
|
|
34
|
+
* @param value - RPC result envelope.
|
|
35
|
+
* @returns response carrying the envelope.
|
|
36
|
+
*/
|
|
37
|
+
function jsonResponse(value) {
|
|
38
|
+
return new Response(JSON.stringify(value), { headers: { 'content-type': 'application/json' } });
|
|
39
|
+
}
|
|
11
40
|
export class SkillMcpRpc extends Service {
|
|
12
|
-
static inject = ['skillMcp'
|
|
41
|
+
static inject = ['skillMcp'];
|
|
42
|
+
/**
|
|
43
|
+
* @param ctx - plugin context carrying the skill/MCP engine.
|
|
44
|
+
*/
|
|
13
45
|
constructor(ctx) {
|
|
14
46
|
super(ctx, 'skillMcpRpc');
|
|
15
|
-
ctx.connection
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
ctx.inject(['connection'], (connectionCtx) => {
|
|
48
|
+
// Both reads are attempted: the published peer types predate the
|
|
49
|
+
// `connection` declaration on Context, so neither is typed here.
|
|
50
|
+
const surface = connectionCtx;
|
|
51
|
+
const connection = surface.connection ?? surface.get?.('connection');
|
|
52
|
+
if (connection === undefined)
|
|
53
|
+
return;
|
|
54
|
+
if (typeof connection.fetch?.register === 'function') {
|
|
55
|
+
connection.fetch.register({
|
|
56
|
+
path: ROUTE,
|
|
57
|
+
methods: ['POST'],
|
|
58
|
+
requestBody: 'buffered',
|
|
59
|
+
fetch: async (request) => {
|
|
60
|
+
let body;
|
|
61
|
+
try {
|
|
62
|
+
body = await request.json();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return jsonResponse(internal('request body must be JSON'));
|
|
66
|
+
}
|
|
67
|
+
return jsonResponse(await dispatch(ctx, body));
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
connection.rpc.handle(LEGACY_CHANNEL, (endpoint, payload) => dispatch(ctx, { endpoint, payload }));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Run one endpoint against the engine.
|
|
78
|
+
* @param ctx - plugin context carrying the skill/MCP engine.
|
|
79
|
+
* @param body - decoded request body.
|
|
80
|
+
* @returns the endpoint result, or an `internal` failure.
|
|
81
|
+
*/
|
|
82
|
+
async function dispatch(ctx, body) {
|
|
83
|
+
try {
|
|
84
|
+
const p = (body.payload ?? {});
|
|
85
|
+
switch (body.endpoint) {
|
|
86
|
+
case 'listSkills': {
|
|
87
|
+
const cwd = p.cwd;
|
|
88
|
+
return { ok: true, value: await ctx.skillMcp.listSkills(typeof cwd === 'string' ? cwd : undefined) };
|
|
51
89
|
}
|
|
52
|
-
|
|
53
|
-
|
|
90
|
+
case 'toggleSkill': {
|
|
91
|
+
const path = p.path;
|
|
92
|
+
if (typeof path !== 'string' || path === '')
|
|
93
|
+
return internal('toggleSkill: path is required');
|
|
94
|
+
return { ok: true, value: await ctx.skillMcp.toggleSkill(path) };
|
|
54
95
|
}
|
|
55
|
-
|
|
96
|
+
case 'readSkill': {
|
|
97
|
+
const path = p.path;
|
|
98
|
+
if (typeof path !== 'string' || path === '')
|
|
99
|
+
return internal('readSkill: path is required');
|
|
100
|
+
const cwd = p.cwd;
|
|
101
|
+
return { ok: true, value: await ctx.skillMcp.readSkill(path, typeof cwd === 'string' ? cwd : undefined) };
|
|
102
|
+
}
|
|
103
|
+
case 'listMcpServers':
|
|
104
|
+
return { ok: true, value: await ctx.skillMcp.listMcpServers() };
|
|
105
|
+
case 'createMcpServer':
|
|
106
|
+
return { ok: true, value: await ctx.skillMcp.createMcpServer(p.config) };
|
|
107
|
+
case 'updateMcpServer':
|
|
108
|
+
return { ok: true, value: await ctx.skillMcp.updateMcpServer(String(p.id), p.config) };
|
|
109
|
+
case 'removeMcpServer':
|
|
110
|
+
return { ok: true, value: await ctx.skillMcp.removeMcpServer(String(p.id)) };
|
|
111
|
+
case 'setMcpServerEnabled':
|
|
112
|
+
return { ok: true, value: await ctx.skillMcp.setMcpServerEnabled(String(p.id), p.enabled === true) };
|
|
113
|
+
case 'mcpStatus':
|
|
114
|
+
return { ok: true, value: await ctx.skillMcp.mcpStatus() };
|
|
115
|
+
default:
|
|
116
|
+
return internal(`unknown endpoint "${body.endpoint}"`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
return internal(error instanceof Error ? error.message : String(error));
|
|
56
121
|
}
|
|
57
122
|
}
|
|
58
123
|
export default SkillMcpRpc;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@max-null/dsh-skill-mcp-center",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Skill & MCP management center for DeepSeek Harness — manage skills and MCP servers in Settings, live MCP status in the sidebar",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
23
|
"client.js",
|
|
24
|
-
"cordis.patch.yml",
|
|
24
|
+
"cordis.patch.yml",
|
|
25
|
+
"docs/shots",
|
|
26
|
+
"release-notes.md"
|
|
27
|
+
],
|
|
25
28
|
"dsh": {
|
|
26
29
|
"bundle": {
|
|
27
30
|
"patch": "./cordis.patch.yml"
|
package/release-notes.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Release Notes — @max-null/dsh-skill-mcp-center
|
|
2
|
+
|
|
3
|
+
## 0.5.1 (2026-09-17)
|
|
4
|
+
|
|
5
|
+
### 修复
|
|
6
|
+
|
|
7
|
+
- 修正 web 环境下设置面板的「加载失败:transport failure for /skill-mcp/listSkills: HTTP 405」。
|
|
8
|
+
根因在宿主侧:`ctx.connection.rpc.handle()` 注册逻辑通道时会访问 `client-connection`
|
|
9
|
+
未声明的 `webServer` 注入,cordis 属性代理抛错后注册静默失败(DSH 源码形态特有;
|
|
10
|
+
打包内核形态不受影响)。上游报告:deepseek-ai/deepseek-harness discussions #6880。
|
|
11
|
+
|
|
12
|
+
### 变更
|
|
13
|
+
|
|
14
|
+
- 传输层改为双路径自适应:
|
|
15
|
+
- 宿主提供 `connection.fetch.register` → 注册 `POST /api/skill-mcp` 精确路由
|
|
16
|
+
(只写 Connection 自己的路由表,不触碰 `owner.webServer`);
|
|
17
|
+
- 宿主只提供逻辑通道注册表(老打包内核)→ 自动回退 `/skill-mcp`;
|
|
18
|
+
- 浏览器侧先试新路由,**收到 404 才回退**(新路由未注册时 `/api/*` 是 404,根路径才是 405)。
|
|
19
|
+
- `connection` 服务改用 `ctx.inject(['connection'], cb)` 获取:它在插件 apply 时尚未就绪,
|
|
20
|
+
且 `ctx.plugin(P)` 不等待依赖。
|
|
21
|
+
|
|
22
|
+
### 兼容性
|
|
23
|
+
|
|
24
|
+
- 无 API 变更:端点集合、请求体、响应信封(RpcResult)与 peer 依赖均未变。
|
|
25
|
+
- 面板端与宿主端需同版本升级(浏览器侧的传输策略随之切换)。
|
|
26
|
+
|
|
27
|
+
### 测试
|
|
28
|
+
|
|
29
|
+
- 新增 `tests/rpc-transport.test.ts`(8 条):传输层选择、服务晚到时不抢先注册、
|
|
30
|
+
路由派发、JSON 解析失败与引擎异常收敛为 `internal` 信封。
|
|
31
|
+
- 全量 18 条通过;`npm run typecheck` 无错。
|
|
32
|
+
|
|
33
|
+
### 验证
|
|
34
|
+
|
|
35
|
+
- web(源码形态)实测:`POST /api/skill-mcp` → 200,面板恢复,控制台 0 errors。
|