@huanlin/dsh-plugin-mcp-manager 0.1.0

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/index.js ADDED
@@ -0,0 +1,623 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@huanlin/dsh-plugin-mcp-manager",
3
+ factory: (require) => {
4
+ var exports = { exports: {} }.exports;
5
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ let react = require("react");
7
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ //#region src/client/Panel.tsx
10
+ /**
11
+ * MCP 管理面板(UI 对齐官方「模型」设置页设计语言):
12
+ * - 服务器列表:serverName + 传输标识 + 端点摘要 + 状态 Pill(connected/
13
+ * disconnected)+ 工具数;行内操作:编辑 / 删除
14
+ * - 新增/编辑表单:transport 切换(stdio ↔ streamable-http 字段组联动);
15
+ * serverName/command/args/env/url/headers/超时
16
+ * - 工具浏览:点击服务器展开其 mcp__ 工具列表(名称 + 描述),只读
17
+ * 全部 token 走 --dsw-alias-*;零 CSS 依赖(inline 样式)。
18
+ */
19
+ const sectionStyle = {
20
+ display: "flex",
21
+ flexDirection: "column",
22
+ gap: 12,
23
+ maxWidth: 720,
24
+ color: "var(--dsw-alias-label-primary)"
25
+ };
26
+ const titleStyle = {
27
+ margin: 0,
28
+ fontSize: 16,
29
+ lineHeight: "24px",
30
+ fontWeight: 500,
31
+ color: "var(--dsw-alias-label-primary)"
32
+ };
33
+ const introStyle = {
34
+ margin: 0,
35
+ fontSize: 14,
36
+ lineHeight: "22px",
37
+ color: "var(--dsw-alias-label-tertiary)"
38
+ };
39
+ const rowsStyle = {
40
+ margin: "12px 0 0",
41
+ padding: 0,
42
+ display: "flex",
43
+ flexDirection: "column",
44
+ gap: 8
45
+ };
46
+ const rowCardStyle = {
47
+ border: "1px solid var(--dsw-alias-border-l2)",
48
+ borderRadius: 12,
49
+ padding: "12px 14px",
50
+ display: "flex",
51
+ flexDirection: "column",
52
+ gap: 10
53
+ };
54
+ const rowHeadStyle = {
55
+ display: "flex",
56
+ alignItems: "center",
57
+ gap: 10,
58
+ minHeight: 28
59
+ };
60
+ const identityStyle = {
61
+ display: "inline-flex",
62
+ alignItems: "center",
63
+ gap: 6,
64
+ minWidth: 0,
65
+ flex: 1
66
+ };
67
+ const nameStyle = {
68
+ fontSize: 14,
69
+ lineHeight: "22px",
70
+ fontWeight: 500,
71
+ color: "var(--dsw-alias-label-primary)"
72
+ };
73
+ const metaStyle = {
74
+ fontSize: 11,
75
+ lineHeight: "16px",
76
+ color: "var(--dsw-alias-label-tertiary)",
77
+ fontFamily: "ui-monospace, monospace"
78
+ };
79
+ const actionsStyle = {
80
+ display: "inline-flex",
81
+ alignItems: "center",
82
+ gap: 4,
83
+ marginLeft: "auto"
84
+ };
85
+ const editorStyle = {
86
+ borderRadius: 12,
87
+ background: "var(--dsw-alias-bg-module-platform)",
88
+ padding: "14px 16px",
89
+ display: "flex",
90
+ flexDirection: "column",
91
+ gap: 14
92
+ };
93
+ const fieldStyle = {
94
+ display: "flex",
95
+ flexDirection: "column",
96
+ gap: 6
97
+ };
98
+ const fieldLabelStyle = {
99
+ fontSize: 12,
100
+ lineHeight: "18px",
101
+ fontWeight: 500,
102
+ color: "var(--dsw-alias-label-secondary)"
103
+ };
104
+ const editorActionsStyle = {
105
+ display: "flex",
106
+ justifyContent: "flex-end",
107
+ gap: 8
108
+ };
109
+ const errorStyle = {
110
+ margin: 0,
111
+ fontSize: 12,
112
+ lineHeight: "18px",
113
+ color: "var(--dsw-alias-state-error-primary)"
114
+ };
115
+ const toolListStyle = {
116
+ margin: "6px 0 0",
117
+ padding: "8px 12px",
118
+ display: "flex",
119
+ flexDirection: "column",
120
+ gap: 4,
121
+ background: "var(--dsw-alias-bg-module-platform)",
122
+ borderRadius: 8
123
+ };
124
+ const toolRowStyle = {
125
+ fontSize: 12,
126
+ lineHeight: "18px",
127
+ color: "var(--dsw-alias-label-secondary)"
128
+ };
129
+ const toolNameStyle = {
130
+ fontFamily: "ui-monospace, monospace",
131
+ color: "var(--dsw-alias-label-primary)"
132
+ };
133
+ const warnStyle = {
134
+ margin: "8px 0 0",
135
+ fontSize: 11,
136
+ lineHeight: "16px",
137
+ color: "var(--dsw-alias-state-warning-primary)"
138
+ };
139
+ /** 空表单默认值。 */
140
+ function emptyForm(transport = "stdio") {
141
+ return transport === "stdio" ? {
142
+ serverName: "",
143
+ transport: "stdio",
144
+ command: "",
145
+ args: [],
146
+ env: {},
147
+ cwd: "",
148
+ toolCallTimeoutMs: 6e4,
149
+ failOnStartupError: false
150
+ } : {
151
+ serverName: "",
152
+ transport: "streamable-http",
153
+ url: "",
154
+ headers: {},
155
+ toolCallTimeoutMs: 6e4,
156
+ failOnStartupError: false
157
+ };
158
+ }
159
+ /** 把键值对 Record 渲染为可编辑文本(每行 key=value)。 */
160
+ function recordToText(r) {
161
+ if (r === void 0) return "";
162
+ return Object.entries(r).map(([k, v]) => `${k}=${v}`).join("\n");
163
+ }
164
+ /** 把文本(每行 key=value)解析回 Record。 */
165
+ function textToRecord(text) {
166
+ const out = {};
167
+ for (const line of text.split("\n")) {
168
+ const trimmed = line.trim();
169
+ if (trimmed.length === 0) continue;
170
+ const eq = trimmed.indexOf("=");
171
+ if (eq === -1) {
172
+ out[trimmed] = "";
173
+ continue;
174
+ }
175
+ out[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
176
+ }
177
+ return out;
178
+ }
179
+ /** 设置页面板主体。 */
180
+ function McpPanel() {
181
+ const [servers, setServers] = (0, react.useState)([]);
182
+ const [toolGroups, setToolGroups] = (0, react.useState)({});
183
+ const [error, setError] = (0, react.useState)(void 0);
184
+ const [loading, setLoading] = (0, react.useState)(true);
185
+ const [busy, setBusy] = (0, react.useState)(false);
186
+ const [expanded, setExpanded] = (0, react.useState)(/* @__PURE__ */ new Set());
187
+ const [editing, setEditing] = (0, react.useState)(void 0);
188
+ const [envText, setEnvText] = (0, react.useState)("");
189
+ const [headersText, setHeadersText] = (0, react.useState)("");
190
+ const refresh = (0, react.useCallback)(async () => {
191
+ try {
192
+ const [serversRes, toolsRes] = await Promise.all([fetch("/api/mcp-manager/servers", { headers: { accept: "application/json" } }), fetch("/api/mcp-manager/tools", { headers: { accept: "application/json" } })]);
193
+ const serversBody = await serversRes.json();
194
+ const toolsBody = await toolsRes.json();
195
+ setServers(serversBody.servers ?? []);
196
+ setToolGroups(toolsBody.groups ?? {});
197
+ } catch (caught) {
198
+ setError(caught instanceof Error ? caught.message : String(caught));
199
+ } finally {
200
+ setLoading(false);
201
+ }
202
+ }, []);
203
+ (0, react.useEffect)(() => {
204
+ refresh();
205
+ }, [refresh]);
206
+ /**
207
+ * 自动轮询:存在「连接中」服务器时每 2s 刷新一次(mcp-client 挂载/握手有延迟,
208
+ * 工具注册后状态会变为 connected);全部稳定后停止轮询。避免面板长时间卡在
209
+ * 误导性的「未连接」——连接中显示「连接中…」并自动追上到「已连接」。
210
+ */
211
+ (0, react.useEffect)(() => {
212
+ if (!servers.some((s) => s.status === "connecting")) return;
213
+ const timer = setInterval(() => {
214
+ refresh();
215
+ }, 2e3);
216
+ return () => clearInterval(timer);
217
+ }, [servers, refresh]);
218
+ /** 打开新增表单。 */
219
+ const startAdd = (0, react.useCallback)(() => {
220
+ const config = emptyForm("stdio");
221
+ setEditing({
222
+ id: "",
223
+ config
224
+ });
225
+ setEnvText("");
226
+ setHeadersText("");
227
+ setError(void 0);
228
+ }, []);
229
+ /** 打开编辑表单。 */
230
+ const startEdit = (0, react.useCallback)((row) => {
231
+ setEditing({
232
+ id: row.id,
233
+ config: { ...row.config }
234
+ });
235
+ setEnvText(recordToText(row.config.env));
236
+ setHeadersText(recordToText(row.config.headers));
237
+ setError(void 0);
238
+ }, []);
239
+ /** 切换 transport(重置字段组)。 */
240
+ const switchTransport = (0, react.useCallback)((transport) => {
241
+ setEditing((prev) => prev === void 0 ? prev : {
242
+ ...prev,
243
+ config: emptyForm(transport)
244
+ });
245
+ setEnvText("");
246
+ setHeadersText("");
247
+ }, []);
248
+ /** 保存(新增或更新)。 */
249
+ const save = (0, react.useCallback)(async () => {
250
+ if (editing === void 0) return;
251
+ setBusy(true);
252
+ setError(void 0);
253
+ try {
254
+ const config = { ...editing.config };
255
+ if (config.transport === "stdio") config.env = textToRecord(envText);
256
+ else config.headers = textToRecord(headersText);
257
+ const isEdit = editing.id.length > 0;
258
+ const body = await (await fetch(isEdit ? `/api/mcp-manager/servers/${encodeURIComponent(editing.id)}` : "/api/mcp-manager/servers", {
259
+ method: isEdit ? "PUT" : "POST",
260
+ headers: { "content-type": "application/json" },
261
+ body: JSON.stringify(isEdit ? { config } : { config })
262
+ })).json();
263
+ if (body.ok !== true) throw new Error(body.message ?? "save failed");
264
+ setEditing(void 0);
265
+ await refresh();
266
+ } catch (caught) {
267
+ setError(caught instanceof Error ? caught.message : String(caught));
268
+ } finally {
269
+ setBusy(false);
270
+ }
271
+ }, [
272
+ editing,
273
+ envText,
274
+ headersText,
275
+ refresh
276
+ ]);
277
+ /** 删除。 */
278
+ const remove = (0, react.useCallback)(async (id) => {
279
+ setBusy(true);
280
+ setError(void 0);
281
+ try {
282
+ const body = await (await fetch(`/api/mcp-manager/servers/${encodeURIComponent(id)}`, { method: "DELETE" })).json();
283
+ if (body.ok !== true) throw new Error(body.message ?? "delete failed");
284
+ await refresh();
285
+ } catch (caught) {
286
+ setError(caught instanceof Error ? caught.message : String(caught));
287
+ } finally {
288
+ setBusy(false);
289
+ }
290
+ }, [refresh]);
291
+ /** 展开/折叠工具浏览。 */
292
+ const toggleExpand = (0, react.useCallback)((id) => {
293
+ setExpanded((prev) => {
294
+ const next = new Set(prev);
295
+ if (next.has(id)) next.delete(id);
296
+ else next.add(id);
297
+ return next;
298
+ });
299
+ }, []);
300
+ const c = editing?.config;
301
+ const isStdio = c?.transport === "stdio";
302
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
303
+ style: sectionStyle,
304
+ children: [
305
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
306
+ style: titleStyle,
307
+ children: "MCP 服务器"
308
+ }),
309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
310
+ style: introStyle,
311
+ children: [
312
+ "管理 MCP 服务器连接。每台服务器由官方 ",
313
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: "@deepseek-ai/dsh-mcp-client" }),
314
+ " 挂载, 工具以 ",
315
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: "mcp__<serverName>__*" }),
316
+ " 命名注册。配置写入 profile patch,HMR 实时生效。"
317
+ ]
318
+ }),
319
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
320
+ style: warnStyle,
321
+ children: "⚠ env / headers 以明文存入 profile cordis.patch.yml(与官方示例同形态)。勿放长期密钥。"
322
+ }),
323
+ error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
324
+ style: errorStyle,
325
+ children: error
326
+ }),
327
+ loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
328
+ style: introStyle,
329
+ children: "加载中…"
330
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
331
+ style: rowsStyle,
332
+ children: [servers.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
333
+ style: rowCardStyle,
334
+ children: [
335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
336
+ style: rowHeadStyle,
337
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
338
+ style: identityStyle,
339
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
340
+ style: nameStyle,
341
+ children: row.config.serverName
342
+ }), row.status === "connected" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
343
+ active: true,
344
+ children: [row.toolCount, " 工具"]
345
+ }) : row.status === "connecting" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, { children: "连接中…" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, { children: "未连接" })]
346
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
347
+ style: actionsStyle,
348
+ children: [
349
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
350
+ onClick: () => toggleExpand(row.id),
351
+ disabled: busy,
352
+ children: expanded.has(row.id) ? "收起" : "工具"
353
+ }),
354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
355
+ onClick: () => startEdit(row),
356
+ disabled: busy,
357
+ children: "编辑"
358
+ }),
359
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
360
+ onClick: () => void remove(row.id),
361
+ disabled: busy,
362
+ children: "删除"
363
+ })
364
+ ]
365
+ })]
366
+ }),
367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
368
+ style: metaStyle,
369
+ children: [
370
+ row.transport,
371
+ " · ",
372
+ row.endpoint,
373
+ " · id: ",
374
+ row.id
375
+ ]
376
+ }),
377
+ expanded.has(row.id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
378
+ style: toolListStyle,
379
+ children: (toolGroups[row.config.serverName] ?? []).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
380
+ style: toolRowStyle,
381
+ children: "(无已注册工具——服务器未连接或未同步)"
382
+ }) : toolGroups[row.config.serverName].map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
383
+ style: toolRowStyle,
384
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
385
+ style: toolNameStyle,
386
+ children: t.rawName
387
+ }), t.description !== void 0 && t.description.length > 0 ? ` — ${t.description}` : ""]
388
+ }, t.name))
389
+ })
390
+ ]
391
+ }, row.id)), servers.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
392
+ style: introStyle,
393
+ children: "尚无 MCP 服务器。点击「新增」添加。"
394
+ })]
395
+ }),
396
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
397
+ style: editorActionsStyle,
398
+ children: editing === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
399
+ onClick: startAdd,
400
+ disabled: busy,
401
+ children: "+ 新增服务器"
402
+ })
403
+ }),
404
+ editing !== void 0 && c !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
405
+ style: editorStyle,
406
+ children: [
407
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
408
+ style: fieldStyle,
409
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
410
+ style: fieldLabelStyle,
411
+ children: "传输方式"
412
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
413
+ style: {
414
+ display: "flex",
415
+ gap: 8
416
+ },
417
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
418
+ onClick: () => switchTransport("stdio"),
419
+ disabled: busy,
420
+ children: isStdio ? "✓ stdio" : "stdio"
421
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
422
+ onClick: () => switchTransport("streamable-http"),
423
+ disabled: busy,
424
+ children: !isStdio ? "✓ streamable-http" : "streamable-http"
425
+ })]
426
+ })]
427
+ }),
428
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
429
+ style: fieldStyle,
430
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
431
+ style: fieldLabelStyle,
432
+ children: "serverName(工具命名空间,唯一)"
433
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
434
+ value: c.serverName,
435
+ onChange: (e) => setEditing((p) => p === void 0 ? p : {
436
+ ...p,
437
+ config: {
438
+ ...p.config,
439
+ serverName: e.target.value
440
+ }
441
+ }),
442
+ disabled: busy,
443
+ placeholder: "github"
444
+ })]
445
+ }),
446
+ isStdio ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
447
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
448
+ style: fieldStyle,
449
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
450
+ style: fieldLabelStyle,
451
+ children: "command"
452
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
453
+ value: c.command ?? "",
454
+ onChange: (e) => setEditing((p) => p === void 0 ? p : {
455
+ ...p,
456
+ config: {
457
+ ...p.config,
458
+ command: e.target.value
459
+ }
460
+ }),
461
+ disabled: busy,
462
+ placeholder: "npx"
463
+ })]
464
+ }),
465
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
466
+ style: fieldStyle,
467
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
468
+ style: fieldLabelStyle,
469
+ children: "args(每行一个)"
470
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
471
+ value: (c.args ?? []).join("\n"),
472
+ onChange: (e) => setEditing((p) => p === void 0 ? p : {
473
+ ...p,
474
+ config: {
475
+ ...p.config,
476
+ args: e.target.value.split("\n")
477
+ }
478
+ }),
479
+ disabled: busy,
480
+ rows: 3,
481
+ style: {
482
+ fontSize: 12,
483
+ fontFamily: "ui-monospace, monospace",
484
+ borderRadius: 8,
485
+ border: "1px solid var(--dsw-alias-border-l2)",
486
+ padding: "8px 10px"
487
+ },
488
+ placeholder: "-y\n@modelcontextprotocol/server-github"
489
+ })]
490
+ }),
491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
492
+ style: fieldStyle,
493
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
494
+ style: fieldLabelStyle,
495
+ children: "env(每行 key=value,明文存储)"
496
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
497
+ value: envText,
498
+ onChange: (e) => setEnvText(e.target.value),
499
+ disabled: busy,
500
+ rows: 3,
501
+ style: {
502
+ fontSize: 12,
503
+ fontFamily: "ui-monospace, monospace",
504
+ borderRadius: 8,
505
+ border: "1px solid var(--dsw-alias-border-l2)",
506
+ padding: "8px 10px"
507
+ },
508
+ placeholder: "GITHUB_TOKEN=ghp_xxx"
509
+ })]
510
+ }),
511
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
512
+ style: fieldStyle,
513
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
514
+ style: fieldLabelStyle,
515
+ children: "cwd(可空)"
516
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
517
+ value: c.cwd ?? "",
518
+ onChange: (e) => setEditing((p) => p === void 0 ? p : {
519
+ ...p,
520
+ config: {
521
+ ...p.config,
522
+ cwd: e.target.value
523
+ }
524
+ }),
525
+ disabled: busy,
526
+ placeholder: ""
527
+ })]
528
+ })
529
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
530
+ style: fieldStyle,
531
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
532
+ style: fieldLabelStyle,
533
+ children: "url"
534
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
535
+ value: c.url ?? "",
536
+ onChange: (e) => setEditing((p) => p === void 0 ? p : {
537
+ ...p,
538
+ config: {
539
+ ...p.config,
540
+ url: e.target.value
541
+ }
542
+ }),
543
+ disabled: busy,
544
+ placeholder: "https://mcp.example.com/mcp"
545
+ })]
546
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
547
+ style: fieldStyle,
548
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
549
+ style: fieldLabelStyle,
550
+ children: "headers(每行 key=value,明文存储)"
551
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
552
+ value: headersText,
553
+ onChange: (e) => setHeadersText(e.target.value),
554
+ disabled: busy,
555
+ rows: 3,
556
+ style: {
557
+ fontSize: 12,
558
+ fontFamily: "ui-monospace, monospace",
559
+ borderRadius: 8,
560
+ border: "1px solid var(--dsw-alias-border-l2)",
561
+ padding: "8px 10px"
562
+ },
563
+ placeholder: "Authorization=Bearer xxx"
564
+ })]
565
+ })] }),
566
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
567
+ style: fieldStyle,
568
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
569
+ style: fieldLabelStyle,
570
+ children: "toolCallTimeoutMs"
571
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
572
+ type: "number",
573
+ value: String(c.toolCallTimeoutMs ?? 6e4),
574
+ onChange: (e) => setEditing((p) => p === void 0 ? p : {
575
+ ...p,
576
+ config: {
577
+ ...p.config,
578
+ toolCallTimeoutMs: Number(e.target.value) || 6e4
579
+ }
580
+ }),
581
+ disabled: busy
582
+ })]
583
+ }),
584
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
585
+ style: editorActionsStyle,
586
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
587
+ onClick: () => setEditing(void 0),
588
+ disabled: busy,
589
+ children: "取消"
590
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
591
+ onClick: () => void save(),
592
+ disabled: busy,
593
+ children: editing.id.length > 0 ? "保存" : "添加"
594
+ })]
595
+ })
596
+ ]
597
+ })
598
+ ]
599
+ });
600
+ }
601
+ //#endregion
602
+ //#region src/client/index.ts
603
+ /** Cordis 插件名。 */
604
+ const name = "dsh-mcp-manager-client";
605
+ /** 需要 slots(settings.section 插槽)。 */
606
+ const inject = ["slots"];
607
+ /** 注册设置页「MCP」面板。 */
608
+ function apply(ctx) {
609
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
610
+ name: "settings.section",
611
+ id: "dsh-mcp-manager",
612
+ order: 61,
613
+ label: () => "MCP",
614
+ inject: () => ({})
615
+ }, McpPanel));
616
+ }
617
+ //#endregion
618
+ exports.apply = apply;
619
+ exports.inject = inject;
620
+ exports.name = name;
621
+ return exports;
622
+ }
623
+ });