@civilization/deepseek-harness-skill-mcp 0.3.1 → 0.3.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 +9 -5
- package/icon.svg +5 -0
- package/lib/client.js +105 -15
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +19 -11
- package/src/client/index.jsx +75 -13
- package/src/client/locales.js +12 -0
- package/src/client/styles.css +22 -0
- package/src/mcp-sse.js +86 -21
- package/src/store.js +45 -9
- package/src/web.js +12 -2
package/README.md
CHANGED
|
@@ -145,7 +145,11 @@ MCP 工具是提供给模型的工具 schema,不是用户 `/` 命令,因此
|
|
|
145
145
|
|
|
146
146
|
当前版本不接受 `env`、认证 header、带凭据或 token 查询参数的 URL。请勿把密钥放进工具参数、MCP 地址或提交到仓库。
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
### MCP 自动重连与周期配置
|
|
149
|
+
|
|
150
|
+
- **重连周期滑块(3–60 秒,默认 7 秒)**:用户在添加或编辑 MCP 服务器时,可使用滑块指定最大重连等待阈值(`reconnectInterval`)。
|
|
151
|
+
- **无限长效重试保底**:解除底层连续 10 次失败后彻底放弃重试的硬限制,改为长效保底探测模式。当服务端离线或重启时,客户端从 500ms 开始指数退避,并在达到设定的阈值后持续周期探测;只要 MCP 服务端恢复,客户端将在设定的阈值时间内自动重新建立连接并热重载工具。
|
|
152
|
+
- **全协议统一管理**:`stdio`、`streamable-http` 与旧版 `sse` 均统一纳入监督重连体系,端点离线或初次发现失败绝不阻塞 DSH 正常启动。
|
|
149
153
|
|
|
150
154
|
## Agent 工具与 Skill
|
|
151
155
|
|
|
@@ -196,12 +200,12 @@ pnpm run web
|
|
|
196
200
|
|
|
197
201
|
浏览器端 `/` 候选缓存失效兼容逻辑会使用公开的 `connection/reset` 客户端事件;服务端通过注入 `webServer` 与 `connection` 双重挂载 `/extensions` 路由并由 Cordis effect 托管生命周期,防止动态请求掉入前端静态资源的 405 fallback。自动测试验证 Host/RPC、HTTP 桥接和浏览器 bundle 构建,但没有在本仓库内启动完整 Harness Web 做端到端菜单点击验证。外部文件改动仍依赖 filesystem provider 先完成 watcher 失效,必要时可在变更后点击管理页“刷新”。
|
|
198
202
|
|
|
199
|
-
## Harness 版本兼容
|
|
203
|
+
## Harness / Cordis 版本兼容
|
|
200
204
|
|
|
201
|
-
当前版本在 DeepSeek Harness **0.2.
|
|
205
|
+
当前版本在 DeepSeek Harness **0.2.1-rc.2** 及 Cordis **4.0.5** 上验证:`pnpm test`(35 项全部通过)与 `pnpm run build` 通过,并逐项核对了所用公开接口——`tools.register/get/schemas/guard`、`tools/pre-execute` 的 `deny` 决策、`skills.register/snapshot`、`webServer.register(kind: 'prefix')`、服务端 `connection.rpc.handle` 与浏览器端 `connection.rpc.call`、`settings.section` 槽位、`locale.register/bind`、`connection/reset` 事件、客户端 bundle 的 `window.__ModuleLoader__.load({ id, factory })` 格式。
|
|
202
206
|
|
|
203
|
-
- `peerDependencies` 只声明**下界**(`@deepseek-ai/dsh-tools: >=0.1.5-rc.2`,无上界),以便跟随 Harness 的快速迭代:Harness
|
|
204
|
-
- `@deepseek-ai/dsh-mcp-client` 与 `@deepseek-ai/dsh-skill-filesystem` 作为 `dependencies`
|
|
207
|
+
- `peerDependencies` 只声明**下界**(`@deepseek-ai/cordis: ^4.0.2`,`@deepseek-ai/dsh-tools: >=0.1.5-rc.2`,无上界),以便跟随 Harness 的快速迭代:Harness 的插件兼容性判定用运行时版本比对这些范围。
|
|
208
|
+
- `@deepseek-ai/dsh-mcp-client` 与 `@deepseek-ai/dsh-skill-filesystem` 作为 `dependencies` 对齐至 `0.2.1-rc.2`,并对齐 `@deepseek-ai/schemastery: ~3.18.5`。
|
|
205
209
|
- 工具目录的核心工具名单同样是版本相关数据,见上文“名单维护”。
|
|
206
210
|
|
|
207
211
|
## 发布
|
package/icon.svg
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none">
|
|
2
|
+
<rect x="1" y="1" width="30" height="30" rx="8" fill="#7C3AED"/>
|
|
3
|
+
<path d="M11 6v5M21 6v5M8 11h16v6a8 8 0 0 1-8 8h0a8 8 0 0 1-8-8v-6z" stroke="#FFFFFF" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
<path d="M16 25v3" stroke="#FFFFFF" stroke-width="2.2" stroke-linecap="round"/>
|
|
5
|
+
</svg>
|
package/lib/client.js
CHANGED
|
@@ -23,6 +23,8 @@ var index_exports = {};
|
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
CatalogSection: () => CatalogSection,
|
|
25
25
|
McpSection: () => McpSection,
|
|
26
|
+
SkillMcpIcon: () => SkillMcpIcon,
|
|
27
|
+
SkillMcpPage: () => SkillMcpPage,
|
|
26
28
|
SkillSection: () => SkillSection,
|
|
27
29
|
apply: () => apply,
|
|
28
30
|
inject: () => inject
|
|
@@ -93,6 +95,9 @@ var ExtensionsController = class {
|
|
|
93
95
|
|
|
94
96
|
// src/client/locales.js
|
|
95
97
|
var zh = {
|
|
98
|
+
panel: "\u6280\u80FD\u4E0E MCP",
|
|
99
|
+
panelTitle: "\u6280\u80FD\u4E0E\u5916\u90E8\u6269\u5C55",
|
|
100
|
+
panelIntro: "\u7BA1\u7406\u81EA\u5B9A\u4E49 Skill \u6280\u80FD\u5E93\u3001MCP \u5DE5\u5177\u670D\u52A1\u4E0E\u7CFB\u7EDF\u5DE5\u5177\u6743\u9650\u3002",
|
|
96
101
|
navSkill: "Skill \u7BA1\u7406",
|
|
97
102
|
navMcp: "MCP \u7BA1\u7406",
|
|
98
103
|
skillIntro: "\u914D\u7F6E\u4E00\u4E2A\u6216\u591A\u4E2A Skill \u8DEF\u5F84\uFF1B\u6BCF\u4E2A\u8DEF\u5F84\u4E0B\u53EF\u4EE5\u5305\u542B\u591A\u4E2A\u6280\u80FD\u3002",
|
|
@@ -216,9 +221,15 @@ var zh = {
|
|
|
216
221
|
noCatalogResults: "\u672A\u627E\u5230\u5339\u914D\u7684\u5DE5\u5177",
|
|
217
222
|
noCatalogResultsHint: "\u8BF7\u5C1D\u8BD5\u4FEE\u6539\u641C\u7D22\u8BCD\u6216\u9009\u62E9\u5176\u4ED6\u63D2\u4EF6\u5206\u7EC4\u3002",
|
|
218
223
|
toolProtected: "\u6838\u5FC3\u4FDD\u7559",
|
|
219
|
-
toolTakeoverHint: "\u6B64\u5DE5\u5177\u53D7 skill-catalog \u7EDF\u4E00\u7BA1\u7406\u3002"
|
|
224
|
+
toolTakeoverHint: "\u6B64\u5DE5\u5177\u53D7 skill-catalog \u7EDF\u4E00\u7BA1\u7406\u3002",
|
|
225
|
+
reconnectInterval: "\u91CD\u8FDE\u5468\u671F\u9608\u503C",
|
|
226
|
+
reconnectIntervalDesc: "\u670D\u52A1\u7AEF\u65AD\u5F00\u540E\u81EA\u52A8\u63A2\u6D4B\u5E76\u6062\u590D\u8FDE\u63A5\u7684\u6700\u5927\u7B49\u5F85\u65F6\u95F4\uFF08\u8303\u56F4 3\u201360 \u79D2\uFF0C\u9ED8\u8BA4 7 \u79D2\uFF09\u3002",
|
|
227
|
+
seconds: "\u79D2"
|
|
220
228
|
};
|
|
221
229
|
var en = {
|
|
230
|
+
panel: "Skills & MCP",
|
|
231
|
+
panelTitle: "Skills & External Extensions",
|
|
232
|
+
panelIntro: "Manage custom Skill repositories, MCP tool servers, and tool permissions.",
|
|
222
233
|
navSkill: "Skills",
|
|
223
234
|
navMcp: "MCP",
|
|
224
235
|
skillIntro: "Configure one or more Skill paths; each path can contain multiple skills.",
|
|
@@ -342,11 +353,14 @@ var en = {
|
|
|
342
353
|
noCatalogResults: "No matching tools found",
|
|
343
354
|
noCatalogResultsHint: "Try adjusting your search terms or filter.",
|
|
344
355
|
toolProtected: "Protected",
|
|
345
|
-
toolTakeoverHint: "This tool is managed under skill-catalog."
|
|
356
|
+
toolTakeoverHint: "This tool is managed under skill-catalog.",
|
|
357
|
+
reconnectInterval: "Reconnect Interval",
|
|
358
|
+
reconnectIntervalDesc: "Maximum wait time before retrying dropped connections (range 3\u201360s, default 7s).",
|
|
359
|
+
seconds: "s"
|
|
346
360
|
};
|
|
347
361
|
|
|
348
362
|
// src/client/styles.css
|
|
349
|
-
var styles_default = '.dsh-ext{color:var(--dsw-alias-label-primary);max-width:840px;padding:4px 0 28px;font:inherit}\n.dsh-ext-heading,.dsh-ext-card>header{display:flex;justify-content:space-between;align-items:center;gap:16px}\n.dsh-ext h2{font-size:22px;letter-spacing:-.4px;margin:0 0 8px}.dsh-ext h3{font-size:15px;margin:0;display:flex;align-items:center;gap:8px}\n.dsh-ext p,.dsh-ext small{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.6}.dsh-ext-heading p{margin:0}\n.dsh-ext button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:8px 13px;white-space:nowrap}\n.dsh-ext button:hover{background:var(--dsw-alias-bg-layer-3)}.dsh-ext button:disabled{opacity:.5;cursor:default}\n.dsh-ext :is(button,input,select,textarea):focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}\n.dsh-ext-card{border:1px solid var(--dsw-alias-border-l4);border-radius:16px;margin-top:20px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}\n.dsh-ext-card>header{padding:18px 20px;border-bottom:1px solid var(--dsw-alias-border-l4)}.dsh-ext-count{font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary)}\n.dsh-ext-empty{padding:32px 20px}.dsh-ext-empty strong{font-size:14px;font-weight:500}.dsh-ext-empty p{margin:6px 0 0}\n.dsh-ext ul{list-style:none;padding:0;margin:0}.dsh-ext li{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:16px 20px;border-bottom:1px solid var(--dsw-alias-border-l4)}.dsh-ext li:last-child{border:0}\n.dsh-ext-details{display:flex;flex-direction:column;gap:5px;min-width:0}.dsh-ext-details strong{font-size:14px}.dsh-ext-details code{font-size:12px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}\n.dsh-ext-runtime{display:flex;align-items:center;flex-wrap:wrap;gap:7px;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsh-ext-status{width:7px;height:7px;border-radius:50%;background:#858585;box-shadow:0 0 0 3px color-mix(in srgb,#858585 16%,transparent)}\n.dsh-ext-status-available{background:#38a169;box-shadow:0 0 0 3px color-mix(in srgb,#38a169 18%,transparent)}.dsh-ext-status-waiting{background:#d69e2e;box-shadow:0 0 0 3px color-mix(in srgb,#d69e2e 18%,transparent)}\n.dsh-ext-runtime details{width:100%;margin-top:2px}.dsh-ext-runtime summary{width:max-content;cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none}.dsh-ext-runtime summary:hover{color:var(--dsw-alias-label-primary)}\n.dsh-ext-runtime .dsh-ext-tools{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.dsh-ext-runtime .dsh-ext-tools li{display:block;border:0;padding:4px 8px;border-radius:6px;background:var(--dsw-alias-bg-layer-3)}\n.dsh-ext-form{padding:20px;border-top:1px solid var(--dsw-alias-border-l4)}.dsh-ext fieldset{border:0;padding:0;margin:0;display:grid;grid-template-columns:1fr 1fr;gap:16px}.dsh-ext label{display:flex;flex-direction:column;gap:7px;font-size:13px}\n.dsh-ext input,.dsh-ext select,.dsh-ext textarea{box-sizing:border-box;min-width:0;width:100%;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l4);border-radius:8px;padding:10px}.dsh-ext textarea{resize:vertical}\n.dsh-ext-actions{display:flex;justify-content:flex-end;gap:8px;grid-column:1/-1}.dsh-ext fieldset>p{grid-column:1/-1;margin:0}.dsh-ext-notice{padding:12px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:10px}.dsh-ext-footnote{padding:0 4px}\n.dsh-ext-row-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end}.dsh-ext .dsh-ext-danger{color:#e98282}.dsh-ext-inline-form{width:100%;padding:14px 0 2px;border:0}.dsh-skill-edit-form{margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}\n.dsh-confirm{box-sizing:border-box;width:min(430px,calc(100vw - 32px));padding:22px;border:1px solid var(--dsw-alias-border-l4);border-radius:16px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);box-shadow:0 20px 60px rgba(0,0,0,.28);font:inherit}.dsh-confirm::backdrop{background:rgba(10,12,16,.48);backdrop-filter:blur(3px)}\n.dsh-confirm-icon{display:grid;place-items:center;width:34px;height:34px;margin-bottom:16px;border-radius:10px;background:color-mix(in srgb,#e05252 14%,transparent);color:#e98282;font-size:19px;font-weight:700}.dsh-confirm-copy h3{margin:0;font-size:16px}.dsh-confirm-copy p{margin:8px 0 0;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.65}.dsh-confirm-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:22px}.dsh-confirm button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:8px 14px}.dsh-confirm button:hover{background:var(--dsw-alias-bg-layer-3)}.dsh-confirm button:disabled{opacity:.5;cursor:default}.dsh-confirm button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.dsh-confirm .dsh-confirm-delete{border-color:color-mix(in srgb,#e05252 45%,var(--dsw-alias-border-l4));background:#b83232;color:#fff}.dsh-confirm .dsh-confirm-delete:hover{background:#c63b3b}\n.dsh-ext-heading-actions{display:flex;gap:8px;align-items:center}.dsh-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.dsh-skill-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px}.dsh-skill-stats>div{display:flex;flex-direction:column;gap:4px;padding:14px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:12px;background:var(--dsw-alias-bg-layer-2)}.dsh-skill-stats strong{font-size:20px}.dsh-skill-stats span{font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsh-skill-search{margin-top:14px}.dsh-skill-search input{padding-left:13px}.dsh-skill-manager>.dsh-ext-form{margin-top:14px;border:1px solid var(--dsw-alias-border-l4);border-radius:14px;background:var(--dsw-alias-bg-layer-2)}\n.dsh-skill-filters{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:10px}.dsh-skill-filters label{gap:5px;font-size:11px;color:var(--dsw-alias-label-tertiary)}.dsh-skill-filters select{padding:8px 10px;color:var(--dsw-alias-label-primary)}\n.dsh-skill-source-manager{margin-top:14px}.dsh-skill-source-manager>summary{cursor:pointer;padding:14px 18px;font-size:14px;font-weight:600;user-select:none}.dsh-skill-source-manager[open]>summary{border-bottom:1px solid var(--dsw-alias-border-l4)}\n.dsh-skill-source-manager li{padding:11px 18px;flex-wrap:wrap}.dsh-skill-source-title{display:flex;align-items:flex-start;gap:11px;min-width:0}.dsh-skill-source-title>.dsh-ext-status{margin-top:6px;flex:0 0 auto}.dsh-skill-source-title>div{display:flex;min-width:0;flex-direction:column;gap:3px}.dsh-skill-source-title code{font-size:11px;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}.dsh-skill-source-title small{font-size:11px;color:#e98282}\n.dsh-skill-library{margin-top:14px}.dsh-skill-group-editor{display:flex;align-items:flex-end;gap:8px}.dsh-skill-group-editor label{flex:1}.dsh-skill-group-editor input{padding:7px 9px}.dsh-skill-group-editor button{padding:7px 10px}\n.dsh-skill-row{display:block!important;padding:15px 18px!important}.dsh-skill-row-main{min-width:0}.dsh-skill-name{display:flex;align-items:center;gap:9px}.dsh-skill-name strong{font-size:14px}.dsh-skill-row-main>p{margin:6px 0 3px}.dsh-skill-row-main>code{font-size:11px;color:var(--dsw-alias-label-tertiary)}\n.dsh-skill-badge{font-size:11px;padding:2px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary)}.dsh-skill-badge-group{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}.dsh-skill-badge-invalid{color:#e98282}.dsh-skill-badge-shadowed{color:#a98af8}.dsh-ext-status-invalid{background:#e05252;box-shadow:0 0 0 3px color-mix(in srgb,#e05252 18%,transparent)}.dsh-ext-status-shadowed{background:#8b5cf6;box-shadow:0 0 0 3px color-mix(in srgb,#8b5cf6 18%,transparent)}.dsh-ext-status-manual{background:#d69e2e;box-shadow:0 0 0 3px color-mix(in srgb,#d69e2e 18%,transparent)}\n.dsh-skill-row details{margin-top:9px}.dsh-skill-row summary{width:max-content;cursor:pointer;font-size:12px;color:var(--dsw-alias-label-secondary)}.dsh-skill-detail{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}.dsh-skill-detail>p{grid-column:1/-1;margin:0}.dsh-skill-detail p strong,.dsh-skill-detail>div>strong{display:block;margin-bottom:5px;color:var(--dsw-alias-label-secondary);font-size:12px}\n.dsh-skill-invocation{display:flex;flex-direction:column;gap:7px}.dsh-skill-invocation label,.dsh-skill-create-flags label{display:flex;flex-direction:row;align-items:center;gap:5px}.dsh-ext input[type=checkbox]{width:auto;min-width:auto}.dsh-skill-detail ul,.dsh-skill-detail li{display:block!important;margin:0;padding:0!important;border:0!important}.dsh-skill-resources{display:flex!important;flex-wrap:wrap!important;gap:5px}.dsh-skill-resources li{padding:3px 6px!important;border-radius:5px!important;background:var(--dsw-alias-bg-layer-3)!important}\n.dsh-ext-span{grid-column:1/-1}.dsh-skill-create-flags{display:flex;flex-direction:column;gap:7px;font-size:13px}.dsh-skill-create-flags>span{margin-bottom:1px}.dsh-skill-source>.dsh-ext-notice{margin:12px 18px}.dsh-skill-source>.dsh-ext-empty{padding:18px}\n@media(max-width:600px){.dsh-ext fieldset,.dsh-skill-detail,.dsh-skill-filters{grid-template-columns:1fr}.dsh-ext-heading{align-items:flex-start;flex-direction:column}.dsh-ext-heading-actions{flex-wrap:wrap}.dsh-ext-card>header{padding:14px}.dsh-ext li{padding:14px}.dsh-skill-stats{grid-template-columns:repeat(2,1fr)}.dsh-skill-group-editor{align-items:stretch;flex-direction:column}}\n\n/* Skill Catalog Takeover Styles */\n.dsh-catalog-section{margin-top:14px}\n.dsh-catalog-header-actions{display:flex;align-items:center;gap:8px}\n.dsh-catalog-group-card{margin-top:16px;border:1px solid var(--dsw-alias-border-l4);border-radius:14px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}\n.dsh-catalog-group-header{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-3) 50%,transparent);border-bottom:1px solid var(--dsw-alias-border-l4);cursor:pointer;user-select:none}\n.dsh-catalog-group-header::-webkit-details-marker{display:none}\n.dsh-catalog-group-header::marker{content:""}\n.dsh-catalog-group-card:not([open]) .dsh-catalog-group-header{border-bottom:0}\n.dsh-catalog-group-title{display:flex;align-items:center;gap:10px}\n.dsh-catalog-group-title::before{content:"\u25B6";display:inline-block;font-size:11px;color:var(--dsw-alias-label-tertiary);transition:transform .2s ease}\n.dsh-catalog-group-card[open] .dsh-catalog-group-title::before{transform:rotate(90deg)}\n.dsh-catalog-group-title h4{margin:0;font-size:14px;font-weight:600}\n.dsh-catalog-group-actions{display:flex;align-items:center;gap:6px}\n.dsh-catalog-group-actions button{padding:4px 10px;font-size:12px;border-radius:7px}\n.dsh-catalog-tool-item{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:14px 18px;border-bottom:1px solid var(--dsw-alias-border-l4);transition:background .15s ease}\n.dsh-catalog-tool-item:last-child{border-bottom:0}\n.dsh-catalog-tool-item:hover{background:color-mix(in srgb,var(--dsw-alias-bg-layer-3) 40%,transparent)}\n.dsh-catalog-tool-info{display:flex;flex-direction:column;gap:5px;min-width:0;flex:1}\n.dsh-catalog-tool-header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-catalog-tool-header code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:13px;font-weight:600;color:var(--dsw-alias-brand-primary)}\n.dsh-catalog-tool-info p{margin:0;font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.5}\n.dsh-catalog-switch-wrap{display:flex;align-items:center;gap:10px;flex-shrink:0}\n.dsh-catalog-switch-label{font-size:12px;font-weight:500}\n.dsh-catalog-switch{position:relative;display:inline-block;width:40px;height:22px;cursor:pointer}\n.dsh-catalog-switch input{opacity:0;width:0;height:0;position:absolute}\n.dsh-catalog-slider{position:absolute;cursor:pointer;inset:0;background:var(--dsw-alias-bg-layer-4,#3b3b3b);border:1px solid var(--dsw-alias-border-l4);border-radius:22px;transition:.2s}\n.dsh-catalog-slider:before{position:absolute;content:"";height:16px;width:16px;left:2px;bottom:2px;background:#fff;border-radius:50%;transition:.2s}\n.dsh-catalog-switch input:checked + .dsh-catalog-slider{background:var(--dsw-alias-brand-primary,#3182ce);border-color:var(--dsw-alias-brand-primary,#3182ce)}\n.dsh-catalog-switch input:checked + .dsh-catalog-slider:before{transform:translateX(18px)}\n.dsh-catalog-switch input:disabled + .dsh-catalog-slider{opacity:.4;cursor:not-allowed}\n.dsh-catalog-tool-details{margin-top:4px;width:100%}\n.dsh-catalog-tool-details summary{width:max-content;cursor:pointer;font-size:12px;color:var(--dsw-alias-label-secondary);user-select:none;transition:color .15s}\n.dsh-catalog-tool-details summary:hover{color:var(--dsw-alias-label-primary)}\n.dsh-catalog-tool-details p{margin:6px 0 2px;font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.55;background:var(--dsw-alias-bg-layer-1);padding:8px 12px;border-radius:8px;border:1px solid var(--dsw-alias-border-l4)}\n\n';
|
|
363
|
+
var styles_default = '.dsh-ext{color:var(--dsw-alias-label-primary);max-width:840px;padding:4px 0 28px;font:inherit}\n.dsh-ext-heading,.dsh-ext-card>header{display:flex;justify-content:space-between;align-items:center;gap:16px}\n.dsh-ext h2{font-size:22px;letter-spacing:-.4px;margin:0 0 8px}.dsh-ext h3{font-size:15px;margin:0;display:flex;align-items:center;gap:8px}\n.dsh-ext p,.dsh-ext small{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.6}.dsh-ext-heading p{margin:0}\n.dsh-ext button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:8px 13px;white-space:nowrap}\n.dsh-ext button:hover{background:var(--dsw-alias-bg-layer-3)}.dsh-ext button:disabled{opacity:.5;cursor:default}\n.dsh-ext :is(button,input,select,textarea):focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}\n.dsh-ext-card{border:1px solid var(--dsw-alias-border-l4);border-radius:16px;margin-top:20px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}\n.dsh-ext-card>header{padding:18px 20px;border-bottom:1px solid var(--dsw-alias-border-l4)}.dsh-ext-count{font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary)}\n.dsh-ext-empty{padding:32px 20px}.dsh-ext-empty strong{font-size:14px;font-weight:500}.dsh-ext-empty p{margin:6px 0 0}\n.dsh-ext ul{list-style:none;padding:0;margin:0}.dsh-ext li{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:16px 20px;border-bottom:1px solid var(--dsw-alias-border-l4)}.dsh-ext li:last-child{border:0}\n.dsh-ext-details{display:flex;flex-direction:column;gap:5px;min-width:0}.dsh-ext-details strong{font-size:14px}.dsh-ext-details code{font-size:12px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}\n.dsh-ext-runtime{display:flex;align-items:center;flex-wrap:wrap;gap:7px;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsh-ext-status{width:7px;height:7px;border-radius:50%;background:#858585;box-shadow:0 0 0 3px color-mix(in srgb,#858585 16%,transparent)}\n.dsh-ext-status-available{background:#38a169;box-shadow:0 0 0 3px color-mix(in srgb,#38a169 18%,transparent)}.dsh-ext-status-waiting{background:#d69e2e;box-shadow:0 0 0 3px color-mix(in srgb,#d69e2e 18%,transparent)}\n.dsh-ext-runtime details{width:100%;margin-top:2px}.dsh-ext-runtime summary{width:max-content;cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none}.dsh-ext-runtime summary:hover{color:var(--dsw-alias-label-primary)}\n.dsh-ext-runtime .dsh-ext-tools{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.dsh-ext-runtime .dsh-ext-tools li{display:block;border:0;padding:4px 8px;border-radius:6px;background:var(--dsw-alias-bg-layer-3)}\n.dsh-ext-form{padding:20px;border-top:1px solid var(--dsw-alias-border-l4)}.dsh-ext fieldset{border:0;padding:0;margin:0;display:grid;grid-template-columns:1fr 1fr;gap:16px}.dsh-ext label{display:flex;flex-direction:column;gap:7px;font-size:13px}\n.dsh-ext input,.dsh-ext select,.dsh-ext textarea{box-sizing:border-box;min-width:0;width:100%;font:inherit;color:inherit;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l4);border-radius:8px;padding:10px}.dsh-ext textarea{resize:vertical}\n.dsh-ext-actions{display:flex;justify-content:flex-end;gap:8px;grid-column:1/-1}.dsh-ext fieldset>p{grid-column:1/-1;margin:0}.dsh-ext-notice{padding:12px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:10px}.dsh-ext-footnote{padding:0 4px}\n.dsh-ext-row-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end}.dsh-ext .dsh-ext-danger{color:#e98282}.dsh-ext-inline-form{width:100%;padding:14px 0 2px;border:0}.dsh-skill-edit-form{margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}\n.dsh-confirm{box-sizing:border-box;width:min(430px,calc(100vw - 32px));padding:22px;border:1px solid var(--dsw-alias-border-l4);border-radius:16px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);box-shadow:0 20px 60px rgba(0,0,0,.28);font:inherit}.dsh-confirm::backdrop{background:rgba(10,12,16,.48);backdrop-filter:blur(3px)}\n.dsh-confirm-icon{display:grid;place-items:center;width:34px;height:34px;margin-bottom:16px;border-radius:10px;background:color-mix(in srgb,#e05252 14%,transparent);color:#e98282;font-size:19px;font-weight:700}.dsh-confirm-copy h3{margin:0;font-size:16px}.dsh-confirm-copy p{margin:8px 0 0;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.65}.dsh-confirm-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:22px}.dsh-confirm button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:8px 14px}.dsh-confirm button:hover{background:var(--dsw-alias-bg-layer-3)}.dsh-confirm button:disabled{opacity:.5;cursor:default}.dsh-confirm button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.dsh-confirm .dsh-confirm-delete{border-color:color-mix(in srgb,#e05252 45%,var(--dsw-alias-border-l4));background:#b83232;color:#fff}.dsh-confirm .dsh-confirm-delete:hover{background:#c63b3b}\n.dsh-ext-heading-actions{display:flex;gap:8px;align-items:center}.dsh-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.dsh-skill-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px}.dsh-skill-stats>div{display:flex;flex-direction:column;gap:4px;padding:14px 16px;border:1px solid var(--dsw-alias-border-l4);border-radius:12px;background:var(--dsw-alias-bg-layer-2)}.dsh-skill-stats strong{font-size:20px}.dsh-skill-stats span{font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsh-skill-search{margin-top:14px}.dsh-skill-search input{padding-left:13px}.dsh-skill-manager>.dsh-ext-form{margin-top:14px;border:1px solid var(--dsw-alias-border-l4);border-radius:14px;background:var(--dsw-alias-bg-layer-2)}\n.dsh-skill-filters{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:10px}.dsh-skill-filters label{gap:5px;font-size:11px;color:var(--dsw-alias-label-tertiary)}.dsh-skill-filters select{padding:8px 10px;color:var(--dsw-alias-label-primary)}\n.dsh-skill-source-manager{margin-top:14px}.dsh-skill-source-manager>summary{cursor:pointer;padding:14px 18px;font-size:14px;font-weight:600;user-select:none}.dsh-skill-source-manager[open]>summary{border-bottom:1px solid var(--dsw-alias-border-l4)}\n.dsh-skill-source-manager li{padding:11px 18px;flex-wrap:wrap}.dsh-skill-source-title{display:flex;align-items:flex-start;gap:11px;min-width:0}.dsh-skill-source-title>.dsh-ext-status{margin-top:6px;flex:0 0 auto}.dsh-skill-source-title>div{display:flex;min-width:0;flex-direction:column;gap:3px}.dsh-skill-source-title code{font-size:11px;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}.dsh-skill-source-title small{font-size:11px;color:#e98282}\n.dsh-skill-library{margin-top:14px}.dsh-skill-group-editor{display:flex;align-items:flex-end;gap:8px}.dsh-skill-group-editor label{flex:1}.dsh-skill-group-editor input{padding:7px 9px}.dsh-skill-group-editor button{padding:7px 10px}\n.dsh-skill-row{display:block!important;padding:15px 18px!important}.dsh-skill-row-main{min-width:0}.dsh-skill-name{display:flex;align-items:center;gap:9px}.dsh-skill-name strong{font-size:14px}.dsh-skill-row-main>p{margin:6px 0 3px}.dsh-skill-row-main>code{font-size:11px;color:var(--dsw-alias-label-tertiary)}\n.dsh-skill-badge{font-size:11px;padding:2px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary)}.dsh-skill-badge-group{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}.dsh-skill-badge-invalid{color:#e98282}.dsh-skill-badge-shadowed{color:#a98af8}.dsh-ext-status-invalid{background:#e05252;box-shadow:0 0 0 3px color-mix(in srgb,#e05252 18%,transparent)}.dsh-ext-status-shadowed{background:#8b5cf6;box-shadow:0 0 0 3px color-mix(in srgb,#8b5cf6 18%,transparent)}.dsh-ext-status-manual{background:#d69e2e;box-shadow:0 0 0 3px color-mix(in srgb,#d69e2e 18%,transparent)}\n.dsh-skill-row details{margin-top:9px}.dsh-skill-row summary{width:max-content;cursor:pointer;font-size:12px;color:var(--dsw-alias-label-secondary)}.dsh-skill-detail{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:12px;padding:14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1)}.dsh-skill-detail>p{grid-column:1/-1;margin:0}.dsh-skill-detail p strong,.dsh-skill-detail>div>strong{display:block;margin-bottom:5px;color:var(--dsw-alias-label-secondary);font-size:12px}\n.dsh-skill-invocation{display:flex;flex-direction:column;gap:7px}.dsh-skill-invocation label,.dsh-skill-create-flags label{display:flex;flex-direction:row;align-items:center;gap:5px}.dsh-ext input[type=checkbox]{width:auto;min-width:auto}.dsh-skill-detail ul,.dsh-skill-detail li{display:block!important;margin:0;padding:0!important;border:0!important}.dsh-skill-resources{display:flex!important;flex-wrap:wrap!important;gap:5px}.dsh-skill-resources li{padding:3px 6px!important;border-radius:5px!important;background:var(--dsw-alias-bg-layer-3)!important}\n.dsh-ext-span{grid-column:1/-1}.dsh-skill-create-flags{display:flex;flex-direction:column;gap:7px;font-size:13px}.dsh-skill-create-flags>span{margin-bottom:1px}.dsh-skill-source>.dsh-ext-notice{margin:12px 18px}.dsh-skill-source>.dsh-ext-empty{padding:18px}\n@media(max-width:600px){.dsh-ext fieldset,.dsh-skill-detail,.dsh-skill-filters{grid-template-columns:1fr}.dsh-ext-heading{align-items:flex-start;flex-direction:column}.dsh-ext-heading-actions{flex-wrap:wrap}.dsh-ext-card>header{padding:14px}.dsh-ext li{padding:14px}.dsh-skill-stats{grid-template-columns:repeat(2,1fr)}.dsh-skill-group-editor{align-items:stretch;flex-direction:column}}\n\n/* Skill Catalog Takeover Styles */\n.dsh-catalog-section{margin-top:14px}\n.dsh-catalog-header-actions{display:flex;align-items:center;gap:8px}\n.dsh-catalog-group-card{margin-top:16px;border:1px solid var(--dsw-alias-border-l4);border-radius:14px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}\n.dsh-catalog-group-header{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;background:color-mix(in srgb,var(--dsw-alias-bg-layer-3) 50%,transparent);border-bottom:1px solid var(--dsw-alias-border-l4);cursor:pointer;user-select:none}\n.dsh-catalog-group-header::-webkit-details-marker{display:none}\n.dsh-catalog-group-header::marker{content:""}\n.dsh-catalog-group-card:not([open]) .dsh-catalog-group-header{border-bottom:0}\n.dsh-catalog-group-title{display:flex;align-items:center;gap:10px}\n.dsh-catalog-group-title::before{content:"\u25B6";display:inline-block;font-size:11px;color:var(--dsw-alias-label-tertiary);transition:transform .2s ease}\n.dsh-catalog-group-card[open] .dsh-catalog-group-title::before{transform:rotate(90deg)}\n.dsh-catalog-group-title h4{margin:0;font-size:14px;font-weight:600}\n.dsh-catalog-group-actions{display:flex;align-items:center;gap:6px}\n.dsh-catalog-group-actions button{padding:4px 10px;font-size:12px;border-radius:7px}\n.dsh-catalog-tool-item{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:14px 18px;border-bottom:1px solid var(--dsw-alias-border-l4);transition:background .15s ease}\n.dsh-catalog-tool-item:last-child{border-bottom:0}\n.dsh-catalog-tool-item:hover{background:color-mix(in srgb,var(--dsw-alias-bg-layer-3) 40%,transparent)}\n.dsh-catalog-tool-info{display:flex;flex-direction:column;gap:5px;min-width:0;flex:1}\n.dsh-catalog-tool-header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.dsh-catalog-tool-header code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:13px;font-weight:600;color:var(--dsw-alias-brand-primary)}\n.dsh-catalog-tool-info p{margin:0;font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.5}\n.dsh-catalog-switch-wrap{display:flex;align-items:center;gap:10px;flex-shrink:0}\n.dsh-catalog-switch-label{font-size:12px;font-weight:500}\n.dsh-catalog-switch{position:relative;display:inline-block;width:40px;height:22px;cursor:pointer}\n.dsh-catalog-switch input{opacity:0;width:0;height:0;position:absolute}\n.dsh-catalog-slider{position:absolute;cursor:pointer;inset:0;background:var(--dsw-alias-bg-layer-4,#3b3b3b);border:1px solid var(--dsw-alias-border-l4);border-radius:22px;transition:.2s}\n.dsh-catalog-slider:before{position:absolute;content:"";height:16px;width:16px;left:2px;bottom:2px;background:#fff;border-radius:50%;transition:.2s}\n.dsh-catalog-switch input:checked + .dsh-catalog-slider{background:var(--dsw-alias-brand-primary,#3182ce);border-color:var(--dsw-alias-brand-primary,#3182ce)}\n.dsh-catalog-switch input:checked + .dsh-catalog-slider:before{transform:translateX(18px)}\n.dsh-catalog-switch input:disabled + .dsh-catalog-slider{opacity:.4;cursor:not-allowed}\n.dsh-catalog-tool-details{margin-top:4px;width:100%}\n.dsh-catalog-tool-details summary{width:max-content;cursor:pointer;font-size:12px;color:var(--dsw-alias-label-secondary);user-select:none;transition:color .15s}\n.dsh-catalog-tool-details summary:hover{color:var(--dsw-alias-label-primary)}\n.dsh-catalog-tool-details p{margin:6px 0 2px;font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.55;background:var(--dsw-alias-bg-layer-1);padding:8px 12px;border-radius:8px;border:1px solid var(--dsw-alias-border-l4)}\n\n/* MCP Reconnect Slider */\n.dsh-mcp-slider-group{grid-column:1/-1;display:flex;flex-direction:column;gap:8px;padding:12px 14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l4)}\n.dsh-mcp-slider-header{display:flex;justify-content:space-between;align-items:center}\n.dsh-mcp-slider-header label{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}\n.dsh-mcp-slider-badge{font-size:12px;font-weight:700;padding:2px 8px;border-radius:999px;color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 14%,transparent)}\n.dsh-mcp-slider-group input[type=range]{width:100%;cursor:pointer;accent-color:var(--dsw-alias-brand-primary);margin:4px 0}\n.dsh-mcp-slider-hint{margin:0;font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.4}\n\n\n\n/* Main Panel Page Layout */\n.dsh-ext-page{box-sizing:border-box;height:100%;display:flex;flex-direction:column;align-items:center;overflow:auto;padding:0 clamp(24px,4vw,48px) 48px;color:var(--dsw-alias-label-primary)}\n.dsh-ext-page>*{width:100%;max-width:960px}\n.dsh-ext-page-head{box-sizing:border-box;padding-top:28px}\n[data-platform=darwin] .dsh-ext-page-head{padding-top:calc(28px + var(--dsh-frame-top-clearance,0px))}\n.dsh-ext-page-title{margin:0;font-size:20px;font-weight:500;line-height:28px}\n.dsh-ext-page-intro{margin:4px 0 0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}\n.dsh-ext-page-tabs{display:flex;gap:8px;margin-top:20px;border-bottom:1px solid var(--dsw-alias-border-l4);padding-bottom:12px}\n.dsh-ext-page-tabs button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);padding:6px 14px;transition:all .15s ease}\n.dsh-ext-page-tabs button:hover{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary)}\n.dsh-ext-page-tabs button.is-active{background:var(--dsw-alias-brand-primary);color:#fff;border-color:var(--dsw-alias-brand-primary);font-weight:500}\n.dsh-ext-page-body{width:100%;max-width:960px}\n';
|
|
350
364
|
|
|
351
365
|
// src/client/index.jsx
|
|
352
366
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -381,17 +395,20 @@ function apply(ctx) {
|
|
|
381
395
|
return ok;
|
|
382
396
|
};
|
|
383
397
|
const face = { hooks: { manager: controller }, request };
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
label: () => t(kind === "skill" ? "navSkill" : kind === "mcp" ? "navMcp" : "navCatalog"),
|
|
398
|
+
const PANEL_ID = "skill-mcp";
|
|
399
|
+
ctx.slots.inject("main", () => ctx.slots.register({
|
|
400
|
+
name: "main",
|
|
401
|
+
key: PANEL_ID,
|
|
389
402
|
locale: namespace,
|
|
390
403
|
inject: () => face
|
|
391
|
-
},
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
404
|
+
}, SkillMcpPage));
|
|
405
|
+
ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
|
|
406
|
+
name: "sidebar.panellist",
|
|
407
|
+
id: PANEL_ID,
|
|
408
|
+
order: 21,
|
|
409
|
+
locale: namespace,
|
|
410
|
+
label: () => t("panel")
|
|
411
|
+
}, SkillMcpIcon));
|
|
395
412
|
}
|
|
396
413
|
function SkillSection({ t, useManager, request }) {
|
|
397
414
|
const state = useManager((value) => value);
|
|
@@ -962,8 +979,9 @@ function SkillSourceForm({ source, t, busy, onSave, onCancel }) {
|
|
|
962
979
|
}
|
|
963
980
|
function McpForm({ row, t, busy, onSave, onCancel }) {
|
|
964
981
|
const prefix = (0, import_react.useId)();
|
|
965
|
-
const initial = row?.configuration ?? { transport: "stdio", command: "", args: [], cwd: "" };
|
|
982
|
+
const initial = row?.configuration ?? { transport: "stdio", command: "", args: [], cwd: "", reconnectInterval: 7 };
|
|
966
983
|
const [transport, setTransport] = (0, import_react.useState)(initial.transport);
|
|
984
|
+
const [reconnectInterval, setReconnectInterval] = (0, import_react.useState)(initial.reconnectInterval ?? 7);
|
|
967
985
|
const field = (name, required = true, multiline = false, value = "") => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: `${prefix}-${name}`, children: [
|
|
968
986
|
t(name),
|
|
969
987
|
multiline ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { id: `${prefix}-${name}`, name, rows: 3, defaultValue: value }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { id: `${prefix}-${name}`, name, required, autoComplete: "off", defaultValue: value, ...name === "id" ? { pattern: "[A-Za-z0-9_\\-]{1,32}", maxLength: 32 } : {} })
|
|
@@ -971,7 +989,7 @@ function McpForm({ row, t, busy, onSave, onCancel }) {
|
|
|
971
989
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { className: row ? "dsh-ext-inline-form" : "dsh-ext-form", onSubmit: (event) => {
|
|
972
990
|
event.preventDefault();
|
|
973
991
|
const data = Object.fromEntries(new FormData(event.currentTarget));
|
|
974
|
-
const configuration = JSON.stringify(transport === "stdio" ? { transport, command: data.command, args: data.args.split(/\r?\n/).filter(Boolean), cwd: data.cwd } : { transport, url: data.url });
|
|
992
|
+
const configuration = JSON.stringify(transport === "stdio" ? { transport, command: data.command, args: data.args.split(/\r?\n/).filter(Boolean), cwd: data.cwd, reconnectInterval } : { transport, url: data.url, reconnectInterval });
|
|
975
993
|
void onSave({ id: row?.id ?? data.id, configuration });
|
|
976
994
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { disabled: busy, children: [
|
|
977
995
|
!row && field("id"),
|
|
@@ -988,6 +1006,29 @@ function McpForm({ row, t, busy, onSave, onCancel }) {
|
|
|
988
1006
|
field("args", false, true, (initial.args ?? []).join("\n")),
|
|
989
1007
|
field("cwd", false, false, initial.cwd)
|
|
990
1008
|
] }) : field("url", true, false, initial.url),
|
|
1009
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mcp-slider-group", children: [
|
|
1010
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mcp-slider-header", children: [
|
|
1011
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: `${prefix}-reconnect`, children: t("reconnectInterval") }),
|
|
1012
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-mcp-slider-badge", children: [
|
|
1013
|
+
reconnectInterval,
|
|
1014
|
+
" ",
|
|
1015
|
+
t("seconds")
|
|
1016
|
+
] })
|
|
1017
|
+
] }),
|
|
1018
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1019
|
+
"input",
|
|
1020
|
+
{
|
|
1021
|
+
id: `${prefix}-reconnect`,
|
|
1022
|
+
type: "range",
|
|
1023
|
+
min: "3",
|
|
1024
|
+
max: "60",
|
|
1025
|
+
step: "1",
|
|
1026
|
+
value: reconnectInterval,
|
|
1027
|
+
onChange: (event) => setReconnectInterval(Number(event.target.value))
|
|
1028
|
+
}
|
|
1029
|
+
),
|
|
1030
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-mcp-slider-hint", children: t("reconnectIntervalDesc") })
|
|
1031
|
+
] }),
|
|
991
1032
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("secret") }),
|
|
992
1033
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-ext-actions", children: [
|
|
993
1034
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: onCancel, children: t("cancel") }),
|
|
@@ -998,6 +1039,7 @@ function McpForm({ row, t, busy, onSave, onCancel }) {
|
|
|
998
1039
|
function AddForm({ kind, t, busy, onSave, onCancel }) {
|
|
999
1040
|
const prefix = (0, import_react.useId)();
|
|
1000
1041
|
const [transport, setTransport] = (0, import_react.useState)("stdio");
|
|
1042
|
+
const [reconnectInterval, setReconnectInterval] = (0, import_react.useState)(7);
|
|
1001
1043
|
const field = (name, required = true, multiline = false) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: `${prefix}-${name}`, children: [
|
|
1002
1044
|
t(name),
|
|
1003
1045
|
multiline ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { id: `${prefix}-${name}`, name, rows: 3 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { id: `${prefix}-${name}`, name, required, autoComplete: "off", ...name === "id" ? { pattern: "[A-Za-z0-9_\\-]{1,32}", maxLength: 32 } : {} })
|
|
@@ -1007,7 +1049,7 @@ function AddForm({ kind, t, busy, onSave, onCancel }) {
|
|
|
1007
1049
|
const data = Object.fromEntries(new FormData(event.currentTarget));
|
|
1008
1050
|
const args = kind === "skill" ? { directory: data.directory } : {
|
|
1009
1051
|
id: data.id,
|
|
1010
|
-
configuration: JSON.stringify(transport === "stdio" ? { transport, command: data.command, args: data.args.split(/\r?\n/).filter(Boolean), cwd: data.cwd } : { transport, url: data.url })
|
|
1052
|
+
configuration: JSON.stringify(transport === "stdio" ? { transport, command: data.command, args: data.args.split(/\r?\n/).filter(Boolean), cwd: data.cwd, reconnectInterval } : { transport, url: data.url, reconnectInterval })
|
|
1011
1053
|
};
|
|
1012
1054
|
void onSave(args);
|
|
1013
1055
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { disabled: busy, children: [
|
|
@@ -1026,6 +1068,29 @@ function AddForm({ kind, t, busy, onSave, onCancel }) {
|
|
|
1026
1068
|
field("args", false, true),
|
|
1027
1069
|
field("cwd", false)
|
|
1028
1070
|
] }) : field("url"),
|
|
1071
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mcp-slider-group", children: [
|
|
1072
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-mcp-slider-header", children: [
|
|
1073
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: `${prefix}-reconnect`, children: t("reconnectInterval") }),
|
|
1074
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-mcp-slider-badge", children: [
|
|
1075
|
+
reconnectInterval,
|
|
1076
|
+
" ",
|
|
1077
|
+
t("seconds")
|
|
1078
|
+
] })
|
|
1079
|
+
] }),
|
|
1080
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1081
|
+
"input",
|
|
1082
|
+
{
|
|
1083
|
+
id: `${prefix}-reconnect`,
|
|
1084
|
+
type: "range",
|
|
1085
|
+
min: "3",
|
|
1086
|
+
max: "60",
|
|
1087
|
+
step: "1",
|
|
1088
|
+
value: reconnectInterval,
|
|
1089
|
+
onChange: (event) => setReconnectInterval(Number(event.target.value))
|
|
1090
|
+
}
|
|
1091
|
+
),
|
|
1092
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-mcp-slider-hint", children: t("reconnectIntervalDesc") })
|
|
1093
|
+
] }),
|
|
1029
1094
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("secret") })
|
|
1030
1095
|
] }),
|
|
1031
1096
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-ext-actions", children: [
|
|
@@ -1181,5 +1246,30 @@ function CatalogSection({ t, useManager, request }) {
|
|
|
1181
1246
|
] }, group.id))
|
|
1182
1247
|
] });
|
|
1183
1248
|
}
|
|
1249
|
+
function SkillMcpIcon({ size = 16 }) {
|
|
1250
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 16 16", width: size, height: size, fill: "none", "aria-hidden": "true", style: { display: "block" }, children: [
|
|
1251
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 2v3M10.5 2v3M3.5 5h9v3.8c0 2.2-1.8 4-4 4h-1c-2.2 0-4-1.8-4-4V5z", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1252
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 12.8v1.7", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
|
|
1253
|
+
] });
|
|
1254
|
+
}
|
|
1255
|
+
function SkillMcpPage({ t, useManager, request }) {
|
|
1256
|
+
const [activeTab, setActiveTab] = (0, import_react.useState)("skills");
|
|
1257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-ext-page", children: [
|
|
1258
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "dsh-ext-page-head", children: [
|
|
1259
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { className: "dsh-ext-page-title", children: t("panelTitle") }),
|
|
1260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-ext-page-intro", children: t("panelIntro") }),
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-ext-page-tabs", role: "tablist", children: [
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": activeTab === "skills", className: activeTab === "skills" ? "is-active" : "", onClick: () => setActiveTab("skills"), children: t("navSkill") }),
|
|
1263
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": activeTab === "mcp", className: activeTab === "mcp" ? "is-active" : "", onClick: () => setActiveTab("mcp"), children: t("navMcp") }),
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": activeTab === "catalog", className: activeTab === "catalog" ? "is-active" : "", onClick: () => setActiveTab("catalog"), children: t("navCatalog") })
|
|
1265
|
+
] })
|
|
1266
|
+
] }),
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { className: "dsh-ext-page-body", children: [
|
|
1268
|
+
activeTab === "skills" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkillSection, { t, useManager, request }),
|
|
1269
|
+
activeTab === "mcp" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(McpSection, { t, useManager, request }),
|
|
1270
|
+
activeTab === "catalog" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CatalogSection, { t, useManager, request })
|
|
1271
|
+
] })
|
|
1272
|
+
] });
|
|
1273
|
+
}
|
|
1184
1274
|
|
|
1185
1275
|
return module.exports;}});
|
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@civilization/deepseek-harness-skill-mcp",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Skill
|
|
3
|
+
"version": "0.3.3",
|
|
4
|
+
"description": "管理外部 Skill 技能路径与 MCP 服务连接,赋予模型外部工具调用能力。",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"exports": {
|
|
19
19
|
".": "./src/index.js",
|
|
20
20
|
"./client": "./lib/client.js",
|
|
21
|
+
"./locale/*.json": "./locale/*.json",
|
|
22
|
+
"./icon": "./icon.svg",
|
|
21
23
|
"./mcp-sse": "./src/mcp-sse.js",
|
|
22
24
|
"./package.json": "./package.json"
|
|
23
25
|
},
|
|
@@ -25,6 +27,8 @@
|
|
|
25
27
|
"src",
|
|
26
28
|
"lib",
|
|
27
29
|
"skills",
|
|
30
|
+
"locale/*.json",
|
|
31
|
+
"icon.svg",
|
|
28
32
|
"cordis.patch.yml",
|
|
29
33
|
"README.md",
|
|
30
34
|
"LICENSE"
|
|
@@ -35,8 +39,11 @@
|
|
|
35
39
|
},
|
|
36
40
|
"client": {
|
|
37
41
|
"platform": "web",
|
|
42
|
+
"immediately": true,
|
|
38
43
|
"inject": [
|
|
39
|
-
"@deepseek-ai/dsh-client-
|
|
44
|
+
"@deepseek-ai/dsh-client-locale",
|
|
45
|
+
"@deepseek-ai/dsh-client-ui-layout",
|
|
46
|
+
"@deepseek-ai/dsh-client-ui-sidebar"
|
|
40
47
|
]
|
|
41
48
|
}
|
|
42
49
|
},
|
|
@@ -44,16 +51,17 @@
|
|
|
44
51
|
"test": "node --test tests/*.test.js",
|
|
45
52
|
"build": "node scripts/build.js",
|
|
46
53
|
"web": "node scripts/web.js",
|
|
47
|
-
"setup": "node scripts/setup.js"
|
|
54
|
+
"setup": "node scripts/setup.js",
|
|
55
|
+
"prepack": "pnpm build"
|
|
48
56
|
},
|
|
49
57
|
"engines": {
|
|
50
58
|
"node": ">=22.19.0"
|
|
51
59
|
},
|
|
52
60
|
"packageManager": "pnpm@10.33.0",
|
|
53
61
|
"dependencies": {
|
|
54
|
-
"@deepseek-ai/dsh-mcp-client": "0.2.
|
|
55
|
-
"@deepseek-ai/dsh-skill-filesystem": "0.2.
|
|
56
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
62
|
+
"@deepseek-ai/dsh-mcp-client": ">=0.2.1-rc.2 <0.3.0",
|
|
63
|
+
"@deepseek-ai/dsh-skill-filesystem": ">=0.2.1-rc.2 <0.3.0",
|
|
64
|
+
"@deepseek-ai/schemastery": "~3.18.5",
|
|
57
65
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
58
66
|
"yaml": "^2.9.0"
|
|
59
67
|
},
|
|
@@ -62,10 +70,10 @@
|
|
|
62
70
|
"@deepseek-ai/dsh-tools": ">=0.1.5-rc.2"
|
|
63
71
|
},
|
|
64
72
|
"devDependencies": {
|
|
65
|
-
"@deepseek-ai/cordis": "4.0.
|
|
66
|
-
"@deepseek-ai/dsh-skill": "0.2.
|
|
67
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.
|
|
68
|
-
"@deepseek-ai/dsh-tools": "0.2.
|
|
73
|
+
"@deepseek-ai/cordis": "4.0.5",
|
|
74
|
+
"@deepseek-ai/dsh-skill": "0.2.1-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-tools": "0.2.1-rc.2",
|
|
69
77
|
"esbuild": "^0.28.2",
|
|
70
78
|
"react": "^19.2.8"
|
|
71
79
|
}
|
package/src/client/index.jsx
CHANGED
|
@@ -31,15 +31,15 @@ export function apply(ctx) {
|
|
|
31
31
|
return ok
|
|
32
32
|
}
|
|
33
33
|
const face = { hooks: { manager: controller }, request }
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
name: '
|
|
37
|
-
label: () => t(kind === 'skill' ? 'navSkill' : kind === 'mcp' ? 'navMcp' : 'navCatalog'), locale: namespace,
|
|
34
|
+
const PANEL_ID = 'skill-mcp'
|
|
35
|
+
ctx.slots.inject('main', () => ctx.slots.register({
|
|
36
|
+
name: 'main', key: PANEL_ID, locale: namespace,
|
|
38
37
|
inject: () => face,
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
}, SkillMcpPage))
|
|
39
|
+
ctx.slots.inject('sidebar.panellist', () => ctx.slots.register({
|
|
40
|
+
name: 'sidebar.panellist', id: PANEL_ID, order: 21, locale: namespace,
|
|
41
|
+
label: () => t('panel'),
|
|
42
|
+
}, SkillMcpIcon))
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function SkillSection({ t, useManager, request }) {
|
|
@@ -343,8 +343,9 @@ function SkillSourceForm({ source, t, busy, onSave, onCancel }) {
|
|
|
343
343
|
|
|
344
344
|
function McpForm({ row, t, busy, onSave, onCancel }) {
|
|
345
345
|
const prefix = useId()
|
|
346
|
-
const initial = row?.configuration ?? { transport: 'stdio', command: '', args: [], cwd: '' }
|
|
346
|
+
const initial = row?.configuration ?? { transport: 'stdio', command: '', args: [], cwd: '', reconnectInterval: 7 }
|
|
347
347
|
const [transport, setTransport] = useState(initial.transport)
|
|
348
|
+
const [reconnectInterval, setReconnectInterval] = useState(initial.reconnectInterval ?? 7)
|
|
348
349
|
const field = (name, required = true, multiline = false, value = '') => <label htmlFor={`${prefix}-${name}`}>{t(name)}{multiline
|
|
349
350
|
? <textarea id={`${prefix}-${name}`} name={name} rows={3} defaultValue={value} />
|
|
350
351
|
: <input id={`${prefix}-${name}`} name={name} required={required} autoComplete="off" defaultValue={value} {...(name === 'id' ? { pattern: '[A-Za-z0-9_\\-]{1,32}', maxLength: 32 } : {})} />}</label>
|
|
@@ -352,13 +353,29 @@ function McpForm({ row, t, busy, onSave, onCancel }) {
|
|
|
352
353
|
event.preventDefault()
|
|
353
354
|
const data = Object.fromEntries(new FormData(event.currentTarget))
|
|
354
355
|
const configuration = JSON.stringify(transport === 'stdio'
|
|
355
|
-
? { transport, command: data.command, args: data.args.split(/\r?\n/).filter(Boolean), cwd: data.cwd }
|
|
356
|
-
: { transport, url: data.url })
|
|
356
|
+
? { transport, command: data.command, args: data.args.split(/\r?\n/).filter(Boolean), cwd: data.cwd, reconnectInterval }
|
|
357
|
+
: { transport, url: data.url, reconnectInterval })
|
|
357
358
|
void onSave({ id: row?.id ?? data.id, configuration })
|
|
358
359
|
}}><fieldset disabled={busy}>{!row && field('id')}
|
|
359
360
|
<label htmlFor={`${prefix}-transport`}>{t('transport')}<select id={`${prefix}-transport`} value={transport} onChange={event => setTransport(event.target.value)}>
|
|
360
361
|
<option value="stdio">{t('stdio')}</option><option value="streamable-http">{t('http')}</option><option value="sse">{t('sse')}</option></select></label>
|
|
361
362
|
{transport === 'stdio' ? <>{field('command', true, false, initial.command)}{field('args', false, true, (initial.args ?? []).join('\n'))}{field('cwd', false, false, initial.cwd)}</> : field('url', true, false, initial.url)}
|
|
363
|
+
<div className="dsh-mcp-slider-group">
|
|
364
|
+
<div className="dsh-mcp-slider-header">
|
|
365
|
+
<label htmlFor={`${prefix}-reconnect`}>{t('reconnectInterval')}</label>
|
|
366
|
+
<span className="dsh-mcp-slider-badge">{reconnectInterval} {t('seconds')}</span>
|
|
367
|
+
</div>
|
|
368
|
+
<input
|
|
369
|
+
id={`${prefix}-reconnect`}
|
|
370
|
+
type="range"
|
|
371
|
+
min="3"
|
|
372
|
+
max="60"
|
|
373
|
+
step="1"
|
|
374
|
+
value={reconnectInterval}
|
|
375
|
+
onChange={event => setReconnectInterval(Number(event.target.value))}
|
|
376
|
+
/>
|
|
377
|
+
<p className="dsh-mcp-slider-hint">{t('reconnectIntervalDesc')}</p>
|
|
378
|
+
</div>
|
|
362
379
|
<p>{t('secret')}</p>
|
|
363
380
|
<div className="dsh-ext-actions"><button type="button" onClick={onCancel}>{t('cancel')}</button><button type="submit">{t(busy ? 'saving' : row ? 'saveChanges' : 'save')}</button></div>
|
|
364
381
|
</fieldset></form>
|
|
@@ -367,6 +384,7 @@ function McpForm({ row, t, busy, onSave, onCancel }) {
|
|
|
367
384
|
function AddForm({ kind, t, busy, onSave, onCancel }) {
|
|
368
385
|
const prefix = useId()
|
|
369
386
|
const [transport, setTransport] = useState('stdio')
|
|
387
|
+
const [reconnectInterval, setReconnectInterval] = useState(7)
|
|
370
388
|
const field = (name, required = true, multiline = false) => <label htmlFor={`${prefix}-${name}`}>{t(name)}{multiline
|
|
371
389
|
? <textarea id={`${prefix}-${name}`} name={name} rows={3} />
|
|
372
390
|
: <input id={`${prefix}-${name}`} name={name} required={required} autoComplete="off" {...(name === 'id' ? { pattern: '[A-Za-z0-9_\\-]{1,32}', maxLength: 32 } : {})} />}</label>
|
|
@@ -375,14 +393,30 @@ function AddForm({ kind, t, busy, onSave, onCancel }) {
|
|
|
375
393
|
const data = Object.fromEntries(new FormData(event.currentTarget))
|
|
376
394
|
const args = kind === 'skill' ? { directory: data.directory } : {
|
|
377
395
|
id: data.id, configuration: JSON.stringify(transport === 'stdio'
|
|
378
|
-
? { transport, command: data.command, args: data.args.split(/\r?\n/).filter(Boolean), cwd: data.cwd }
|
|
379
|
-
: { transport, url: data.url }),
|
|
396
|
+
? { transport, command: data.command, args: data.args.split(/\r?\n/).filter(Boolean), cwd: data.cwd, reconnectInterval }
|
|
397
|
+
: { transport, url: data.url, reconnectInterval }),
|
|
380
398
|
}
|
|
381
399
|
void onSave(args)
|
|
382
400
|
}}><fieldset disabled={busy}>{kind === 'skill' ? field('directory') : <>{field('id')}
|
|
383
401
|
<label htmlFor={`${prefix}-transport`}>{t('transport')}<select id={`${prefix}-transport`} value={transport} onChange={event => setTransport(event.target.value)}>
|
|
384
402
|
<option value="stdio">{t('stdio')}</option><option value="streamable-http">{t('http')}</option><option value="sse">{t('sse')}</option></select></label>
|
|
385
403
|
{transport === 'stdio' ? <>{field('command')}{field('args', false, true)}{field('cwd', false)}</> : field('url')}
|
|
404
|
+
<div className="dsh-mcp-slider-group">
|
|
405
|
+
<div className="dsh-mcp-slider-header">
|
|
406
|
+
<label htmlFor={`${prefix}-reconnect`}>{t('reconnectInterval')}</label>
|
|
407
|
+
<span className="dsh-mcp-slider-badge">{reconnectInterval} {t('seconds')}</span>
|
|
408
|
+
</div>
|
|
409
|
+
<input
|
|
410
|
+
id={`${prefix}-reconnect`}
|
|
411
|
+
type="range"
|
|
412
|
+
min="3"
|
|
413
|
+
max="60"
|
|
414
|
+
step="1"
|
|
415
|
+
value={reconnectInterval}
|
|
416
|
+
onChange={event => setReconnectInterval(Number(event.target.value))}
|
|
417
|
+
/>
|
|
418
|
+
<p className="dsh-mcp-slider-hint">{t('reconnectIntervalDesc')}</p>
|
|
419
|
+
</div>
|
|
386
420
|
<p>{t('secret')}</p>
|
|
387
421
|
</>}
|
|
388
422
|
<div className="dsh-ext-actions"><button type="button" onClick={onCancel}>{t('cancel')}</button><button type="submit">{t(busy ? 'saving' : 'save')}</button></div>
|
|
@@ -532,3 +566,31 @@ export function CatalogSection({ t, useManager, request }) {
|
|
|
532
566
|
</section>
|
|
533
567
|
}
|
|
534
568
|
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
export function SkillMcpIcon({ size = 16 }) {
|
|
572
|
+
return <svg viewBox="0 0 16 16" width={size} height={size} fill="none" aria-hidden="true" style={{ display: "block" }}>
|
|
573
|
+
<path d="M5.5 2v3M10.5 2v3M3.5 5h9v3.8c0 2.2-1.8 4-4 4h-1c-2.2 0-4-1.8-4-4V5z" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round" />
|
|
574
|
+
<path d="M8 12.8v1.7" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" />
|
|
575
|
+
</svg>
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export function SkillMcpPage({ t, useManager, request }) {
|
|
579
|
+
const [activeTab, setActiveTab] = useState("skills")
|
|
580
|
+
return <div className="dsh-ext-page">
|
|
581
|
+
<header className="dsh-ext-page-head">
|
|
582
|
+
<h1 className="dsh-ext-page-title">{t("panelTitle")}</h1>
|
|
583
|
+
<p className="dsh-ext-page-intro">{t("panelIntro")}</p>
|
|
584
|
+
<div className="dsh-ext-page-tabs" role="tablist">
|
|
585
|
+
<button type="button" role="tab" aria-selected={activeTab === "skills"} className={activeTab === "skills" ? "is-active" : ""} onClick={() => setActiveTab("skills")}>{t("navSkill")}</button>
|
|
586
|
+
<button type="button" role="tab" aria-selected={activeTab === "mcp"} className={activeTab === "mcp" ? "is-active" : ""} onClick={() => setActiveTab("mcp")}>{t("navMcp")}</button>
|
|
587
|
+
<button type="button" role="tab" aria-selected={activeTab === "catalog"} className={activeTab === "catalog" ? "is-active" : ""} onClick={() => setActiveTab("catalog")}>{t("navCatalog")}</button>
|
|
588
|
+
</div>
|
|
589
|
+
</header>
|
|
590
|
+
<main className="dsh-ext-page-body">
|
|
591
|
+
{activeTab === "skills" && <SkillSection t={t} useManager={useManager} request={request} />}
|
|
592
|
+
{activeTab === "mcp" && <McpSection t={t} useManager={useManager} request={request} />}
|
|
593
|
+
{activeTab === "catalog" && <CatalogSection t={t} useManager={useManager} request={request} />}
|
|
594
|
+
</main>
|
|
595
|
+
</div>
|
|
596
|
+
}
|
package/src/client/locales.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/** Both dictionaries use the same key vocabulary; checked by tests. */
|
|
2
2
|
export const zh = {
|
|
3
|
+
panel: '技能与 MCP',
|
|
4
|
+
panelTitle: '技能与外部扩展',
|
|
5
|
+
panelIntro: '管理自定义 Skill 技能库、MCP 工具服务与系统工具权限。',
|
|
3
6
|
navSkill: 'Skill 管理', navMcp: 'MCP 管理',
|
|
4
7
|
skillIntro: '配置一个或多个 Skill 路径;每个路径下可以包含多个技能。', mcpIntro: '管理外部工具服务器,让它们在当前配置中可用。',
|
|
5
8
|
skills: 'Skill 路径', mcp: 'MCP 服务器', refresh: '刷新', add: '添加路径', addMcp: '添加服务器', newSkill: '新建 Skill',
|
|
@@ -51,8 +54,14 @@ export const zh = {
|
|
|
51
54
|
noCatalogResultsHint: '请尝试修改搜索词或选择其他插件分组。',
|
|
52
55
|
toolProtected: '核心保留',
|
|
53
56
|
toolTakeoverHint: '此工具受 skill-catalog 统一管理。',
|
|
57
|
+
reconnectInterval: '重连周期阈值',
|
|
58
|
+
reconnectIntervalDesc: '服务端断开后自动探测并恢复连接的最大等待时间(范围 3–60 秒,默认 7 秒)。',
|
|
59
|
+
seconds: '秒',
|
|
54
60
|
}
|
|
55
61
|
export const en = {
|
|
62
|
+
panel: 'Skills & MCP',
|
|
63
|
+
panelTitle: 'Skills & External Extensions',
|
|
64
|
+
panelIntro: 'Manage custom Skill repositories, MCP tool servers, and tool permissions.',
|
|
56
65
|
navSkill: 'Skills', navMcp: 'MCP',
|
|
57
66
|
skillIntro: 'Configure one or more Skill paths; each path can contain multiple skills.', mcpIntro: 'Manage external tool servers for this configuration.',
|
|
58
67
|
skills: 'Skill paths', mcp: 'MCP servers', refresh: 'Refresh', add: 'Add path', addMcp: 'Add server', newSkill: 'New Skill',
|
|
@@ -104,4 +113,7 @@ export const en = {
|
|
|
104
113
|
noCatalogResultsHint: 'Try adjusting your search terms or filter.',
|
|
105
114
|
toolProtected: 'Protected',
|
|
106
115
|
toolTakeoverHint: 'This tool is managed under skill-catalog.',
|
|
116
|
+
reconnectInterval: 'Reconnect Interval',
|
|
117
|
+
reconnectIntervalDesc: 'Maximum wait time before retrying dropped connections (range 3–60s, default 7s).',
|
|
118
|
+
seconds: 's',
|
|
107
119
|
}
|
package/src/client/styles.css
CHANGED
|
@@ -70,3 +70,25 @@
|
|
|
70
70
|
.dsh-catalog-tool-details summary:hover{color:var(--dsw-alias-label-primary)}
|
|
71
71
|
.dsh-catalog-tool-details p{margin:6px 0 2px;font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.55;background:var(--dsw-alias-bg-layer-1);padding:8px 12px;border-radius:8px;border:1px solid var(--dsw-alias-border-l4)}
|
|
72
72
|
|
|
73
|
+
/* MCP Reconnect Slider */
|
|
74
|
+
.dsh-mcp-slider-group{grid-column:1/-1;display:flex;flex-direction:column;gap:8px;padding:12px 14px;border-radius:10px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l4)}
|
|
75
|
+
.dsh-mcp-slider-header{display:flex;justify-content:space-between;align-items:center}
|
|
76
|
+
.dsh-mcp-slider-header label{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
77
|
+
.dsh-mcp-slider-badge{font-size:12px;font-weight:700;padding:2px 8px;border-radius:999px;color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 14%,transparent)}
|
|
78
|
+
.dsh-mcp-slider-group input[type=range]{width:100%;cursor:pointer;accent-color:var(--dsw-alias-brand-primary);margin:4px 0}
|
|
79
|
+
.dsh-mcp-slider-hint{margin:0;font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.4}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
/* Main Panel Page Layout */
|
|
84
|
+
.dsh-ext-page{box-sizing:border-box;height:100%;display:flex;flex-direction:column;align-items:center;overflow:auto;padding:0 clamp(24px,4vw,48px) 48px;color:var(--dsw-alias-label-primary)}
|
|
85
|
+
.dsh-ext-page>*{width:100%;max-width:960px}
|
|
86
|
+
.dsh-ext-page-head{box-sizing:border-box;padding-top:28px}
|
|
87
|
+
[data-platform=darwin] .dsh-ext-page-head{padding-top:calc(28px + var(--dsh-frame-top-clearance,0px))}
|
|
88
|
+
.dsh-ext-page-title{margin:0;font-size:20px;font-weight:500;line-height:28px}
|
|
89
|
+
.dsh-ext-page-intro{margin:4px 0 0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}
|
|
90
|
+
.dsh-ext-page-tabs{display:flex;gap:8px;margin-top:20px;border-bottom:1px solid var(--dsw-alias-border-l4);padding-bottom:12px}
|
|
91
|
+
.dsh-ext-page-tabs button{font:inherit;font-size:13px;cursor:pointer;border:1px solid var(--dsw-alias-border-l4);border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);padding:6px 14px;transition:all .15s ease}
|
|
92
|
+
.dsh-ext-page-tabs button:hover{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary)}
|
|
93
|
+
.dsh-ext-page-tabs button.is-active{background:var(--dsw-alias-brand-primary);color:#fff;border-color:var(--dsw-alias-brand-primary);font-weight:500}
|
|
94
|
+
.dsh-ext-page-body{width:100%;max-width:960px}
|
package/src/mcp-sse.js
CHANGED
|
@@ -18,12 +18,26 @@ export const Config = Schema.object({
|
|
|
18
18
|
url: Schema.string().required(),
|
|
19
19
|
toolCallTimeoutMs: Schema.number().default(60_000),
|
|
20
20
|
failOnStartupError: Schema.boolean().default(false),
|
|
21
|
+
reconnect: Schema.object({
|
|
22
|
+
enabled: Schema.boolean().default(true),
|
|
23
|
+
initialDelayMs: Schema.number().default(500),
|
|
24
|
+
maxDelayMs: Schema.number().default(7000),
|
|
25
|
+
maxAttempts: Schema.number().default(Number.MAX_SAFE_INTEGER),
|
|
26
|
+
}).default({}),
|
|
21
27
|
})
|
|
22
28
|
|
|
23
29
|
export async function apply(ctx, config) {
|
|
24
|
-
const
|
|
25
|
-
|
|
30
|
+
const reconnectPolicy = {
|
|
31
|
+
enabled: config.reconnect?.enabled ?? true,
|
|
32
|
+
initialDelayMs: config.reconnect?.initialDelayMs ?? 500,
|
|
33
|
+
maxDelayMs: config.reconnect?.maxDelayMs ?? 7000,
|
|
34
|
+
maxAttempts: config.reconnect?.maxAttempts ?? Number.MAX_SAFE_INTEGER,
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
let disposed = false
|
|
38
|
+
let reconnectTimer
|
|
39
|
+
let failedAttempts = 0
|
|
40
|
+
let currentGeneration
|
|
27
41
|
let registrations = new Map()
|
|
28
42
|
let synchronization = Promise.resolve()
|
|
29
43
|
|
|
@@ -31,13 +45,25 @@ export async function apply(ctx, config) {
|
|
|
31
45
|
for (const dispose of registrations.values()) dispose()
|
|
32
46
|
registrations = new Map()
|
|
33
47
|
}
|
|
34
|
-
ctx.effect(() => () => {
|
|
35
|
-
disposed = true
|
|
36
|
-
clearTools()
|
|
37
|
-
void client.close().catch(() => {})
|
|
38
|
-
}, 'mcp-sse-client.connection')
|
|
39
48
|
|
|
40
|
-
const
|
|
49
|
+
const scheduleReconnect = (cause) => {
|
|
50
|
+
if (disposed || !reconnectPolicy.enabled) return
|
|
51
|
+
if (reconnectTimer !== undefined) return
|
|
52
|
+
failedAttempts++
|
|
53
|
+
if (failedAttempts > reconnectPolicy.maxAttempts) {
|
|
54
|
+
ctx.logger.error(`mcp-sse-client(${config.serverName}): giving up after ${reconnectPolicy.maxAttempts} failed reconnect attempts`)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
const delayMs = Math.min(reconnectPolicy.maxDelayMs, reconnectPolicy.initialDelayMs * (2 ** Math.min(failedAttempts - 1, 30)))
|
|
58
|
+
ctx.logger.warn(`mcp-sse-client(${config.serverName}): connection lost or failed (${messageOf(cause)}); reconnecting in ${delayMs}ms (attempt ${failedAttempts})`)
|
|
59
|
+
reconnectTimer = setTimeout(() => {
|
|
60
|
+
reconnectTimer = undefined
|
|
61
|
+
void connectGeneration(false)
|
|
62
|
+
}, delayMs)
|
|
63
|
+
reconnectTimer.unref?.()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const synchronizeTools = async (client, generation) => {
|
|
41
67
|
const definitions = new Map()
|
|
42
68
|
let cursor
|
|
43
69
|
do {
|
|
@@ -52,7 +78,7 @@ export async function apply(ctx, config) {
|
|
|
52
78
|
}
|
|
53
79
|
cursor = response.nextCursor
|
|
54
80
|
} while (cursor)
|
|
55
|
-
if (disposed) return
|
|
81
|
+
if (disposed || generation.closed) return
|
|
56
82
|
clearTools()
|
|
57
83
|
const next = new Map()
|
|
58
84
|
try {
|
|
@@ -64,21 +90,60 @@ export async function apply(ctx, config) {
|
|
|
64
90
|
}
|
|
65
91
|
}
|
|
66
92
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
async function connectGeneration(startup) {
|
|
94
|
+
if (disposed) return
|
|
95
|
+
const client = new Client({ name: `dsh-sse-${config.serverName}`, version: '1.0.0' }, { capabilities: {} })
|
|
96
|
+
const transport = new SSEClientTransport(new URL(config.url))
|
|
97
|
+
const generation = { client, transport, closed: false }
|
|
98
|
+
currentGeneration = generation
|
|
99
|
+
|
|
100
|
+
const onDisconnect = (err) => {
|
|
101
|
+
if (generation.closed || disposed) return
|
|
102
|
+
generation.closed = true
|
|
103
|
+
clearTools()
|
|
104
|
+
void client.close().catch(() => {})
|
|
105
|
+
if (currentGeneration === generation) {
|
|
106
|
+
scheduleReconnect(err ?? new Error('connection closed'))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
client.onclose = () => onDisconnect()
|
|
111
|
+
client.onerror = (err) => onDisconnect(err)
|
|
112
|
+
|
|
113
|
+
client.setNotificationHandler(ToolListChangedNotificationSchema, () => {
|
|
114
|
+
if (generation.closed || disposed) return
|
|
115
|
+
synchronization = synchronization.then(() => synchronizeTools(client, generation)).catch(error => {
|
|
116
|
+
ctx.logger.warn(`mcp-sse-client(${config.serverName}): tool refresh failed: ${messageOf(error)}`)
|
|
117
|
+
})
|
|
70
118
|
})
|
|
71
|
-
})
|
|
72
119
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
try {
|
|
121
|
+
await client.connect(transport)
|
|
122
|
+
await synchronizeTools(client, generation)
|
|
123
|
+
failedAttempts = 0
|
|
124
|
+
ctx.logger.info(`mcp-sse-client(${config.serverName}): connected and synchronized tools`)
|
|
125
|
+
} catch (error) {
|
|
126
|
+
onDisconnect(error)
|
|
127
|
+
if (startup && config.failOnStartupError) {
|
|
128
|
+
throw new Error(`mcp-sse-client(${config.serverName}): initial connection failed: ${messageOf(error)}`, { cause: error })
|
|
129
|
+
}
|
|
130
|
+
}
|
|
81
131
|
}
|
|
132
|
+
|
|
133
|
+
ctx.effect(() => () => {
|
|
134
|
+
disposed = true
|
|
135
|
+
if (reconnectTimer !== undefined) {
|
|
136
|
+
clearTimeout(reconnectTimer)
|
|
137
|
+
reconnectTimer = undefined
|
|
138
|
+
}
|
|
139
|
+
clearTools()
|
|
140
|
+
if (currentGeneration && !currentGeneration.closed) {
|
|
141
|
+
currentGeneration.closed = true
|
|
142
|
+
void currentGeneration.client.close().catch(() => {})
|
|
143
|
+
}
|
|
144
|
+
}, 'mcp-sse-client.connection')
|
|
145
|
+
|
|
146
|
+
await connectGeneration(true)
|
|
82
147
|
}
|
|
83
148
|
|
|
84
149
|
function createTool(client, publicName, tool, timeout) {
|
package/src/store.js
CHANGED
|
@@ -39,12 +39,13 @@ function validateId(id) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function parsePatch(value) {
|
|
42
|
-
if (!Array.isArray(value)
|
|
43
|
-
|
|
42
|
+
if (!Array.isArray(value)) throw new Error('Expected patch array.')
|
|
43
|
+
const insertOp = value.find(row => row && typeof row === 'object' && Array.isArray(row.insert))
|
|
44
|
+
if (!insertOp) throw new Error('Expected one managed insert operation.')
|
|
44
45
|
const ids = new Set()
|
|
45
46
|
const managed = []
|
|
46
47
|
const preserved = []
|
|
47
|
-
for (const row of
|
|
48
|
+
for (const row of insertOp.insert) {
|
|
48
49
|
if (row?.id === 'extension-manager' && row.name === managerModule) {
|
|
49
50
|
if (ids.has(row.id)) throw new Error('Duplicate extension id.')
|
|
50
51
|
ids.add(row.id)
|
|
@@ -69,7 +70,8 @@ function parsePatch(value) {
|
|
|
69
70
|
} else throw new Error('Unknown managed plugin module.')
|
|
70
71
|
managed.push(row)
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
+
const unmanagedRows = value.filter(row => row !== insertOp)
|
|
74
|
+
return { managed, preserved, unmanagedRows }
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
/** Validate the patch and return only extension rows owned by the manager. */
|
|
@@ -84,7 +86,8 @@ function markedRows(content) {
|
|
|
84
86
|
if (start < 0 || end < start || content.indexOf(blockStart, start + blockStart.length) >= 0
|
|
85
87
|
|| content.indexOf(blockEnd, end + blockEnd.length) >= 0) throw new Error('Invalid dsh-skill-mcp managed block.')
|
|
86
88
|
const bodyStart = start + blockStart.length
|
|
87
|
-
|
|
89
|
+
const parsed = parsePatch(parse(content.slice(bodyStart, end)))
|
|
90
|
+
return { start, end: end + blockEnd.length, rows: parsed.managed, unmanagedRows: parsed.unmanagedRows }
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
function legacyPatch(content) {
|
|
@@ -99,7 +102,10 @@ function renderBlock(rows) {
|
|
|
99
102
|
function writeManagedRows(content, rows) {
|
|
100
103
|
const marked = markedRows(content)
|
|
101
104
|
const block = renderBlock(rows)
|
|
102
|
-
if (marked)
|
|
105
|
+
if (marked) {
|
|
106
|
+
const unmanaged = marked.unmanagedRows?.length ? `\n${stringify(marked.unmanagedRows).trimEnd()}\n` : ''
|
|
107
|
+
return `${content.slice(0, marked.start)}${block}${unmanaged}${content.slice(marked.end)}`
|
|
108
|
+
}
|
|
103
109
|
const legacy = legacyPatch(content)
|
|
104
110
|
if (legacy) return `${JSON.stringify([{ insert: [...legacy.preserved, ...rows] }], null, 2)}\n`
|
|
105
111
|
if (/^[ \t]*\[\][ \t]*$/m.test(content)) return content.replace(/^[ \t]*\[\][ \t]*$/m, block)
|
|
@@ -121,10 +127,28 @@ export class ExtensionStore {
|
|
|
121
127
|
/** Migrate managed MCP rows so an unavailable server cannot reject profile startup. */
|
|
122
128
|
async ensureNonFatalMcpStartup() {
|
|
123
129
|
const rows = await this.read()
|
|
124
|
-
|
|
130
|
+
let needsUpdate = false
|
|
131
|
+
for (const row of rows) {
|
|
132
|
+
if (row.config?.serverName) {
|
|
133
|
+
if (row.config.failOnStartupError !== false || !row.config.reconnect || row.config.reconnect.maxAttempts !== Number.MAX_SAFE_INTEGER) {
|
|
134
|
+
needsUpdate = true
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!needsUpdate) return
|
|
125
140
|
await this.update(current => {
|
|
126
141
|
for (const row of current) {
|
|
127
|
-
if (row.config
|
|
142
|
+
if (row.config?.serverName) {
|
|
143
|
+
row.config.failOnStartupError = false
|
|
144
|
+
const maxDelay = row.config.reconnect?.maxDelayMs ?? 7000
|
|
145
|
+
row.config.reconnect = {
|
|
146
|
+
enabled: true,
|
|
147
|
+
initialDelayMs: 500,
|
|
148
|
+
maxDelayMs: Math.min(60_000, Math.max(3_000, maxDelay)),
|
|
149
|
+
maxAttempts: Number.MAX_SAFE_INTEGER,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
128
152
|
}
|
|
129
153
|
})
|
|
130
154
|
}
|
|
@@ -195,11 +219,23 @@ export class ExtensionStore {
|
|
|
195
219
|
normalizeMcp(id, input) {
|
|
196
220
|
// Authentication values belong in the host credential setup, not tool arguments or logs.
|
|
197
221
|
if (input.env || input.headers) throw new Error('This version does not accept environment values or authentication headers.')
|
|
222
|
+
const reconnectIntervalSec = Math.min(60, Math.max(3, Math.round(Number(input.reconnectInterval ?? 7)) || 7))
|
|
223
|
+
const reconnect = {
|
|
224
|
+
enabled: true,
|
|
225
|
+
initialDelayMs: 500,
|
|
226
|
+
maxDelayMs: reconnectIntervalSec * 1000,
|
|
227
|
+
maxAttempts: Number.MAX_SAFE_INTEGER,
|
|
228
|
+
}
|
|
198
229
|
const normalized = input.transport === 'stdio' ? {
|
|
199
230
|
...input,
|
|
200
231
|
command: typeof input.command === 'string' ? normalizeWindowsPath(input.command) : input.command,
|
|
201
232
|
cwd: typeof input.cwd === 'string' && input.cwd ? normalizeWindowsPath(input.cwd) : input.cwd,
|
|
202
|
-
|
|
233
|
+
reconnect,
|
|
234
|
+
} : {
|
|
235
|
+
...input,
|
|
236
|
+
reconnect,
|
|
237
|
+
}
|
|
238
|
+
delete normalized.reconnectInterval
|
|
203
239
|
const config = input.transport === 'sse'
|
|
204
240
|
? SseMcpConfig({ ...normalized, serverName: id, failOnStartupError: false })
|
|
205
241
|
: McpConfig({ ...normalized, serverName: id, failOnStartupError: false })
|
package/src/web.js
CHANGED
|
@@ -55,8 +55,18 @@ export async function describeExtensions(store, ctx, options = {}) {
|
|
|
55
55
|
: row.config.customSkillDirs[0],
|
|
56
56
|
transport: row.config.transport ?? '',
|
|
57
57
|
configuration: row.config.serverName ? (row.config.transport === 'stdio'
|
|
58
|
-
? {
|
|
59
|
-
|
|
58
|
+
? {
|
|
59
|
+
transport: row.config.transport,
|
|
60
|
+
command: row.config.command,
|
|
61
|
+
args: row.config.args ?? [],
|
|
62
|
+
cwd: row.config.cwd ?? '',
|
|
63
|
+
reconnectInterval: row.config.reconnect?.maxDelayMs ? Math.round(row.config.reconnect.maxDelayMs / 1000) : 7,
|
|
64
|
+
}
|
|
65
|
+
: {
|
|
66
|
+
transport: row.config.transport,
|
|
67
|
+
url: row.config.url,
|
|
68
|
+
reconnectInterval: row.config.reconnect?.maxDelayMs ? Math.round(row.config.reconnect.maxDelayMs / 1000) : 7,
|
|
69
|
+
}) : undefined,
|
|
60
70
|
tools: row.config.serverName
|
|
61
71
|
? toolNames.filter(name => name.startsWith(`mcp__${row.id}__`)).sort() : [],
|
|
62
72
|
})),
|