@moon16u/dsh-plugin-mcp-console 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/client.js ADDED
@@ -0,0 +1,954 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@moon16u/dsh-plugin-mcp-console",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ var React = require("react");
8
+
9
+ // ==== mcp-console client body (synced; keep identical to dsh-pouch/lib/client.js) ====
10
+
11
+ var McpIcons = require("@deepseek-ai/dsh-client-ui-primitives");
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // mcp-console client: an "MCP" settings section (mcp-manager-gui-spec.md)
15
+ // over /api/dsh-mcp-console/*. All identifiers carry an Mcp/mcp prefix so
16
+ // this body can be pasted verbatim into the dsh-pouch root client bundle.
17
+ // ---------------------------------------------------------------------------
18
+
19
+ var McpNS = "mcp-console";
20
+
21
+ var mcpZh = {
22
+ nav: "MCP 服务器",
23
+ title: "已安装的 MCP 服务器",
24
+ refresh: "同步配置并刷新状态",
25
+ add: "添加 MCP",
26
+ importAction: "导入 JSON",
27
+ empty: "还没有配置任何 MCP 服务器。",
28
+ toolsEnabled: "{n} 个工具已启用",
29
+ toolsAvailable: "0 个工具",
30
+ noTools: "该服务器未提供任何工具。",
31
+ clickToDisable: "点击禁用该工具",
32
+ clickToEnable: "点击启用该工具",
33
+ enable: "启用",
34
+ disable: "停用",
35
+ remove: "删除",
36
+ removeConfirm: "删除该服务器?其配置与连接将被移除。",
37
+ confirmYes: "确认删除",
38
+ confirmNo: "取消",
39
+ importTitle: "导入 mcpServers JSON",
40
+ importHelp: "支持粘贴标准 mcpServers JSON 配置(兼容 Claude/Cursor 等生态),同名服务自动跳过",
41
+ importPlaceholder: '{\n "mcpServers": {\n "github": {\n "command": "npx",\n "args": ["-y", "@modelcontextprotocol/server-github"]\n }\n }\n}',
42
+ importPreviewAdded: "将新增 {n} 台",
43
+ importConfirm: "确认导入",
44
+ importResult: "已导入 {added} 台,跳过 {skipped} 台",
45
+ addTitle: "添加 MCP 服务器",
46
+ editTitle: "编辑 MCP 服务器",
47
+ formName: "服务名称",
48
+ formNameHelp: "仅支持字母、数字、下划线及短横线,最多 32 个字符",
49
+ formNamePlaceholder: "例如: github, memory, local-tools",
50
+ formTransport: "传输协议",
51
+ transportStdio: "stdio (本地命令)",
52
+ transportHttp: "http / sse (远程服务)",
53
+ formCommand: "执行命令",
54
+ formCommandPlaceholder: "例如: npx, uvx, python",
55
+ formArgs: "启动参数",
56
+ formArgsPlaceholder: "每行一个参数,例如:\n-y\n@modelcontextprotocol/server-filesystem",
57
+ formEnv: "环境变量",
58
+ formEnvPlaceholder: "每行一个 KEY=VALUE,例如:\nGITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxxx",
59
+ formUrl: "服务 URL",
60
+ formUrlPlaceholder: "http://127.0.0.1:3001/mcp",
61
+ formHeaders: "自定义请求头 (Headers)",
62
+ formHeadersPlaceholder: "每行一个 KEY: VALUE,例如:\nAuthorization: Bearer my_api_key",
63
+ formTimeout: "调用超时 (毫秒)",
64
+ formFail: "启动失败时中断并标记错误",
65
+ formScope: "生效作用域",
66
+ scopeGlobal: "全局",
67
+ scopeProject: "项目",
68
+ scopeGlobalHelp: "所有项目生效",
69
+ scopeProjectHelp: "当前项目生效",
70
+ scopeProjectNoWorkspaceHelp: "所有项目生效(当前处于全局设置,项目级专属配置需在具体项目会话中配置)",
71
+ scopeProjectDisabledTip: "当前处于全局设置,无活跃项目工作区",
72
+ formAdvanced: "高级选项",
73
+ required: "必填",
74
+ saveConfig: "保存配置",
75
+ cancel: "取消",
76
+ loadingData: "加载中…",
77
+ apiError: "接口错误",
78
+ storeError: "配置文件告警",
79
+ profileReadOnly: "外部实例",
80
+ errorPrefix: "错误",
81
+ running: "运行中",
82
+ connecting: "连接中",
83
+ loading: "启动中",
84
+ disabled: "已停用",
85
+ failed: "失败",
86
+ toolDeniedNote: "禁用的工具仍保留在模型工具列表中,但调用会被拒绝。",
87
+ };
88
+
89
+ var mcpEn = {
90
+ nav: "MCP servers",
91
+ title: "Installed MCP Servers",
92
+ refresh: "Sync config & refresh status",
93
+ add: "Add MCP",
94
+ importAction: "Import JSON",
95
+ empty: "No MCP servers configured yet.",
96
+ toolsEnabled: "{n} tools enabled",
97
+ toolsAvailable: "0 tools",
98
+ noTools: "No tools provided by this server.",
99
+ clickToDisable: "Click to disable tool",
100
+ clickToEnable: "Click to enable tool",
101
+ enable: "Enable",
102
+ disable: "Disable",
103
+ remove: "Delete",
104
+ removeConfirm: "Delete this server? Its config and connection will be removed.",
105
+ confirmYes: "Delete",
106
+ confirmNo: "Cancel",
107
+ importTitle: "Import mcpServers JSON",
108
+ importHelp: "Paste standard mcpServers JSON (Claude/Cursor compatible); duplicate names are skipped",
109
+ importPlaceholder: '{\n "mcpServers": {\n "github": {\n "command": "npx",\n "args": ["-y", "@modelcontextprotocol/server-github"]\n }\n }\n}',
110
+ importPreviewAdded: "{n} to add",
111
+ importConfirm: "Import",
112
+ importResult: "Imported {added}, skipped {skipped}",
113
+ addTitle: "Add MCP server",
114
+ editTitle: "Edit MCP server",
115
+ formName: "Server name",
116
+ formNameHelp: "Letters, digits, underscore and hyphen only, up to 32 characters",
117
+ formNamePlaceholder: "e.g. github, memory, local-tools",
118
+ formTransport: "Transport",
119
+ transportStdio: "stdio (local command)",
120
+ transportHttp: "http / sse (remote service)",
121
+ formCommand: "Command",
122
+ formCommandPlaceholder: "e.g. npx, uvx, python",
123
+ formArgs: "Args",
124
+ formArgsPlaceholder: "One per line, e.g.:\n-y\n@modelcontextprotocol/server-filesystem",
125
+ formEnv: "Env vars",
126
+ formEnvPlaceholder: "One KEY=VALUE per line, e.g.:\nGITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxxx",
127
+ formUrl: "Service URL",
128
+ formUrlPlaceholder: "http://127.0.0.1:3001/mcp",
129
+ formHeaders: "Headers",
130
+ formHeadersPlaceholder: "One KEY: VALUE per line, e.g.:\nAuthorization: Bearer my_api_key",
131
+ formTimeout: "Call timeout (ms)",
132
+ formFail: "Abort and mark error on startup failure",
133
+ formScope: "Scope",
134
+ scopeGlobal: "Global",
135
+ scopeProject: "Project",
136
+ scopeGlobalHelp: "Applies to all projects",
137
+ scopeProjectHelp: "Applies to current project only",
138
+ scopeProjectNoWorkspaceHelp: "Applies to all projects (currently in global settings; configure project-scoped MCP within a project session)",
139
+ scopeProjectDisabledTip: "Currently in global settings, no active workspace",
140
+ formAdvanced: "Advanced",
141
+ required: "required",
142
+ saveConfig: "Save config",
143
+ cancel: "Cancel",
144
+ loadingData: "Loading…",
145
+ apiError: "API error",
146
+ storeError: "Config file warning",
147
+ profileReadOnly: "External instance",
148
+ errorPrefix: "Error",
149
+ running: "Running",
150
+ connecting: "Connecting",
151
+ loading: "Starting",
152
+ disabled: "Disabled",
153
+ failed: "Failed",
154
+ toolDeniedNote: "Disabled tools stay in the model tool list, but their calls are denied.",
155
+ };
156
+
157
+ var mcpCss = [
158
+ ".mcp-mgr{color:var(--dsw-alias-label-primary,#111827);font-family:var(--dsw-font-family);font-size:13px;line-height:20px;display:flex;flex-direction:column;gap:14px;max-width:760px;box-sizing:border-box}",
159
+ ".mcp-mgr *{box-sizing:border-box}",
160
+ ".mcp-mgr-head{display:flex;align-items:center;gap:10px}",
161
+ ".mcp-mgr-title{font-size:17px;font-weight:600;margin:0;flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
162
+ ".mcp-mgr-iconbtn{border:0;background:0 0;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;border-radius:8px;width:30px;height:30px;display:grid;place-items:center;flex:none;padding:0}",
163
+ ".mcp-mgr-iconbtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05));color:var(--dsw-alias-label-primary,#111827)}",
164
+ ".mcp-mgr-iconbtn:disabled{color:var(--dsw-alias-label-dimmed,#9ca3af);cursor:wait}",
165
+ ".mcp-mgr-btn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-base,#ffffff);color:var(--dsw-alias-label-primary,#111827);font-family:var(--dsw-font-family);cursor:pointer;border-radius:8px;padding:6px 14px;font-size:13px;line-height:20px;display:inline-flex;align-items:center;gap:6px;box-shadow:0 1px 2px rgba(0,0,0,.04)}",
166
+ ".mcp-mgr-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#f9fafb);border-color:#d1d5db}",
167
+ ".mcp-mgr-btn:disabled{color:var(--dsw-alias-label-dimmed,#9ca3af);cursor:default}",
168
+ ".mcp-mgr-list{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;background:var(--dsw-alias-bg-base,#ffffff);overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.05)}",
169
+ ".mcp-mgr-item{padding:14px 16px;display:flex;flex-direction:column;gap:8px}",
170
+ ".mcp-mgr-item + .mcp-mgr-item{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb)}",
171
+ ".mcp-mgr-itemhead{display:flex;align-items:center;gap:8px;min-width:0}",
172
+ ".mcp-mgr-name{font-size:15px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary,#111827)}",
173
+ ".mcp-dot{width:8px;height:8px;border-radius:999px;flex:none;background:var(--dsw-alias-label-tertiary,#9ca3af)}",
174
+ ".mcp-dot[data-status=running]{background:var(--dsw-alias-state-success-primary,#10b981)}",
175
+ ".mcp-dot[data-status=failed]{background:var(--dsw-alias-state-error-primary,#ef4444)}",
176
+ ".mcp-dot[data-status=connecting],.mcp-dot[data-status=loading]{background:var(--dsw-alias-state-warn-label,#f59e0b)}",
177
+ ".mcp-readonly-badge{flex:none;font-size:11px;line-height:16px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:6px;padding:1px 6px;color:var(--dsw-alias-label-secondary,#6b7280);background:var(--dsw-alias-interactive-bg-hover,#f3f4f6)}",
178
+ ".mcp-mgr-ops{margin-left:auto;display:flex;align-items:center;gap:10px;flex:none}",
179
+ ".mcp-mgr-del{border:0;background:0 0;color:var(--dsw-alias-label-secondary,#9ca3af);cursor:pointer;border-radius:8px;width:28px;height:28px;display:grid;place-items:center;padding:0}",
180
+ ".mcp-mgr-del:hover:not(:disabled){background:rgba(239,68,68,.1);color:var(--dsw-alias-state-error-primary,#ef4444)}",
181
+ ".mcp-mgr-del:disabled{color:var(--dsw-alias-label-dimmed,#9ca3af);cursor:default}",
182
+ ".mcp-switch{position:relative;display:inline-block;width:38px;height:22px;flex:none;cursor:pointer}",
183
+ ".mcp-switch input{position:absolute;opacity:0;inset:0;margin:0;cursor:inherit}",
184
+ ".mcp-switch-track{position:absolute;inset:0;background:#d1d5db;border-radius:999px;transition:background .15s ease;pointer-events:none}",
185
+ ".mcp-switch-thumb{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:999px;background:#ffffff;transition:transform .15s ease;pointer-events:none;box-shadow:0 1px 3px rgba(0,0,0,.25)}",
186
+ ".mcp-switch input:checked ~ .mcp-switch-track{background:#10b981}",
187
+ ".mcp-switch input:checked ~ .mcp-switch-thumb{transform:translateX(16px)}",
188
+ ".mcp-switch input:disabled{cursor:wait}",
189
+ ".mcp-switch input:disabled ~ .mcp-switch-track{opacity:.5}",
190
+ ".mcp-mgr-summary{display:inline-flex;align-items:center;gap:6px;background:0 0;border:0;padding:0;color:var(--dsw-alias-label-secondary,#6b7280);font-family:var(--dsw-font-family);font-size:13px;cursor:pointer;user-select:none;align-self:flex-start}",
191
+ ".mcp-mgr-summary:hover{color:var(--dsw-alias-label-primary,#111827)}",
192
+ ".mcp-mgr-chevron{display:inline-block;width:0;height:0;border-left:4px solid currentColor;border-top:5px solid transparent;border-bottom:5px solid transparent;transition:transform .15s ease;flex:none}",
193
+ ".mcp-mgr-summary[data-open=true] .mcp-mgr-chevron{transform:rotate(90deg)}",
194
+ ".mcp-mgr-name-btn{background:0 0;border:0;padding:0;color:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;cursor:pointer;text-align:left;min-width:0}",
195
+ ".mcp-mgr-name-btn:hover{text-decoration:underline;text-underline-offset:3px}",
196
+ ".mcp-mgr-err{color:var(--dsw-alias-state-error-primary,#ef4444);margin:0;font-size:12px;word-break:break-word}",
197
+ ".mcp-mgr-tools{display:flex;flex-wrap:wrap;gap:8px;padding-top:4px}",
198
+ ".mcp-pill-wrap{position:relative;display:inline-flex}",
199
+ ".mcp-pill{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-base,#ffffff);color:var(--dsw-alias-label-primary,#374151);border-radius:6px;padding:3px 10px;font-size:12px;line-height:18px;font-family:var(--ds-font-family-code,ui-monospace,monospace);cursor:pointer;transition:all .15s ease;user-select:none}",
200
+ ".mcp-pill:hover{background:var(--dsw-alias-interactive-bg-hover,#f3f4f6);border-color:#d1d5db}",
201
+ ".mcp-pill[data-enabled=false]{color:#4b5563 !important;text-decoration:none !important;opacity:1 !important;background:#f3f4f6 !important;border-color:#e5e7eb !important}",
202
+ ".mcp-pill[data-enabled=false]:hover{background:#e5e7eb !important;color:#111827 !important;border-color:#d1d5db !important}",
203
+ ".mcp-tip{display:none;position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%);background:rgba(17,24,39,.94);color:#fff;border-radius:6px;padding:3px 8px;font-size:11px;line-height:16px;white-space:nowrap;z-index:20;box-shadow:0 2px 8px rgba(0,0,0,.2);pointer-events:none}",
204
+ ".mcp-tip::after{content:'';position:absolute;top:100%;left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:rgba(17,24,39,.94)}",
205
+ ".mcp-pill-wrap:hover .mcp-tip{display:block}",
206
+ ".mcp-mgr-note{color:var(--dsw-alias-label-secondary,#6b7280);margin:0}",
207
+ ".mcp-mgr-warn{color:var(--dsw-alias-state-warn-label,#f59e0b);margin:0;font-size:12px}",
208
+ ".mcp-ext{color:var(--dsw-alias-label-tertiary,#9ca3af);font-size:12px;word-break:break-all;margin:0}",
209
+ ".mcp-ext-title{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;font-weight:500;margin:0}",
210
+ ".mcp-err{color:var(--dsw-alias-state-error-primary,#ef4444);margin:0;font-size:12px;word-break:break-word}",
211
+ ".mcp-modal{position:fixed;inset:0;z-index:9100;background:rgba(0,0,0,.45);backdrop-filter:blur(3px);display:flex;align-items:center;justify-content:center;padding:12px;box-sizing:border-box}",
212
+ ".mcp-modal *{box-sizing:border-box}",
213
+ ".mcp-modal-card{width:540px;max-width:calc(100vw - 24px);max-height:min(96vh,900px);display:flex;flex-direction:column;overflow:hidden;background:var(--dsw-alias-bg-base,#ffffff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 20px 48px -8px rgba(0,0,0,.25);color:var(--dsw-alias-label-primary,#111827);font-family:var(--dsw-font-family);font-size:13px;line-height:20px}",
214
+ ".mcp-modal-head{display:flex;align-items:center;justify-content:space-between;padding:12px 18px;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);flex:none;background:var(--dsw-alias-bg-base,#ffffff)}",
215
+ ".mcp-modal-title{font-size:15px;font-weight:600;flex:1 1 auto;min-width:0;margin:0;color:var(--dsw-alias-label-primary,#111827)}",
216
+ ".mcp-form-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:14px 18px;display:flex;flex-direction:column;gap:10px;box-sizing:border-box}",
217
+ ".mcp-form-footer{display:flex !important;justify-content:flex-end !important;align-items:center !important;gap:10px !important;padding:10px 18px !important;border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb) !important;background:#f9fafb !important;flex:none !important;box-sizing:border-box !important}",
218
+ ".mcp-form{display:flex;flex-direction:column;gap:10px}",
219
+ ".mcp-form-row{display:flex;flex-direction:column;gap:3px;min-width:0;width:100%}",
220
+ ".mcp-form-label{color:var(--dsw-alias-label-primary,#374151);font-size:12px;font-weight:600;margin:0;display:block}",
221
+ ".mcp-form-label .mcp-req{color:#ef4444}",
222
+ ".mcp-help{color:var(--dsw-alias-label-tertiary,#9ca3af);font-size:11px;line-height:15px;margin:1px 0 0 0}",
223
+ ".mcp-in{width:100% !important;max-width:100% !important;box-sizing:border-box !important;border:1px solid var(--dsw-alias-border-l2,#d1d5db) !important;background:var(--dsw-alias-bg-base,#ffffff) !important;color:var(--dsw-alias-label-primary,#111827) !important;font-family:var(--dsw-font-family);border-radius:7px !important;padding:6px 10px !important;font-size:13px !important;min-width:0;outline:none !important;transition:border-color .15s ease,box-shadow .15s ease !important}",
224
+ ".mcp-in:focus{border-color:#10b981 !important;box-shadow:0 0 0 3px rgba(16,185,129,.15) !important}",
225
+ ".mcp-in::placeholder{color:#9ca3af}",
226
+ ".mcp-in-mono{font-family:var(--ds-font-family-code,ui-monospace,monospace) !important;font-size:12px !important}",
227
+ ".mcp-in-area{resize:vertical;min-height:68px !important;line-height:18px;overflow-y:hidden}",
228
+ ".mcp-in-area:focus,.mcp-in-area:hover{overflow-y:auto}",
229
+ ".mcp-in-area-tall{min-height:180px !important;overflow-y:auto}",
230
+ ".mcp-seg{display:flex !important;width:100% !important;gap:4px !important;padding:3px !important;background:#f3f4f6 !important;border:1px solid #e5e7eb !important;border-radius:8px !important;box-sizing:border-box !important}",
231
+ ".mcp-seg-btn{flex:1 1 0% !important;appearance:none !important;-webkit-appearance:none !important;border:1px solid transparent !important;background:transparent !important;color:#6b7280 !important;cursor:pointer !important;border-radius:6px !important;padding:5px 12px !important;font-size:12px !important;line-height:18px !important;font-family:var(--dsw-font-family) !important;text-align:center !important;outline:none !important;transition:all .15s ease !important;box-shadow:none !important}",
232
+ ".mcp-seg-btn:hover:not([data-on=true]):not(:disabled){color:#111827 !important;background:rgba(0,0,0,.04) !important}",
233
+ ".mcp-seg-btn[data-on=true]{background:#ffffff !important;color:#111827 !important;font-weight:600 !important;border-color:#d1d5db !important;box-shadow:0 1px 3px rgba(0,0,0,.1),0 1px 2px rgba(0,0,0,.06) !important}",
234
+ ".mcp-seg-btn:disabled{opacity:.45 !important;cursor:not-allowed !important;background:0 0 !important;color:var(--dsw-alias-label-dimmed,#9ca3af) !important;box-shadow:none !important}",
235
+ ".mcp-check-row{display:flex !important;align-items:center !important;gap:8px !important;cursor:pointer !important;user-select:none !important;color:var(--dsw-alias-label-primary,#374151) !important;font-size:12px !important;margin:2px 0 !important}",
236
+ ".mcp-check-row input[type=checkbox]{appearance:none !important;-webkit-appearance:none !important;width:16px !important;height:16px !important;border:1.5px solid #d1d5db !important;border-radius:4px !important;background:#ffffff !important;cursor:pointer !important;display:inline-grid !important;place-content:center !important;margin:0 !important;outline:none !important;transition:all .15s ease !important;flex:none !important}",
237
+ ".mcp-check-row input[type=checkbox]:hover{border-color:#10b981 !important}",
238
+ ".mcp-check-row input[type=checkbox]:checked{background:#10b981 !important;border-color:#10b981 !important}",
239
+ ".mcp-check-row input[type=checkbox]:checked::after{content:'' !important;width:4px !important;height:8px !important;border:solid #ffffff !important;border-width:0 2px 2px 0 !important;transform:rotate(45deg) !important;margin-top:-2px !important}",
240
+ ".mcp-adv-toggle{appearance:none !important;-webkit-appearance:none !important;display:inline-flex !important;align-items:center !important;gap:6px !important;background:transparent !important;border:0 !important;outline:none !important;color:var(--dsw-alias-label-secondary,#4b5563) !important;font-size:12px !important;font-weight:600 !important;cursor:pointer !important;padding:4px 0 !important;margin-top:2px !important;margin-bottom:1px !important;font-family:var(--dsw-font-family) !important;user-select:none !important}",
241
+ ".mcp-adv-toggle:hover{color:var(--dsw-alias-label-primary,#111827) !important}",
242
+ ".mcp-adv-box{display:flex !important;flex-direction:column !important;gap:10px !important;background:#f9fafb !important;border:1px solid #e5e7eb !important;border-radius:9px !important;padding:10px 12px !important;margin-top:3px !important;margin-bottom:1px !important;box-sizing:border-box !important}",
243
+ ".mcp-btn-secondary{appearance:none !important;-webkit-appearance:none !important;border:1px solid #d1d5db !important;background:#ffffff !important;color:#374151 !important;font-family:var(--dsw-font-family) !important;font-weight:500 !important;cursor:pointer !important;border-radius:7px !important;padding:6px 16px !important;font-size:12px !important;line-height:18px !important;transition:all .15s ease !important;outline:none !important;box-shadow:0 1px 2px rgba(0,0,0,.05) !important}",
244
+ ".mcp-btn-secondary:hover:not(:disabled){background:#f3f4f6 !important;border-color:#9ca3af !important;color:#111827 !important}",
245
+ ".mcp-btn-primary{appearance:none !important;-webkit-appearance:none !important;border:1px solid #059669 !important;background:#10b981 !important;color:#ffffff !important;font-family:var(--dsw-font-family) !important;font-weight:600 !important;cursor:pointer !important;border-radius:7px !important;padding:6px 16px !important;font-size:12px !important;line-height:18px !important;transition:all .15s ease !important;outline:none !important;box-shadow:0 1px 2px rgba(0,0,0,.05) !important}",
246
+ ".mcp-btn-primary:hover:not(:disabled){background:#059669 !important;border-color:#047857 !important}",
247
+ ".mcp-btn-primary:disabled{background:#e5e7eb !important;border-color:#d1d5db !important;color:#9ca3af !important;cursor:not-allowed !important;box-shadow:none !important}",
248
+ ".mcp-btn-danger{appearance:none !important;-webkit-appearance:none !important;border:1px solid #dc2626 !important;background:#ef4444 !important;color:#ffffff !important;font-family:var(--dsw-font-family) !important;font-weight:600 !important;cursor:pointer !important;border-radius:7px !important;padding:6px 16px !important;font-size:12px !important;line-height:18px !important;transition:all .15s ease !important;outline:none !important}",
249
+ ".mcp-btn-danger:hover:not(:disabled){background:#dc2626 !important}",
250
+ ".mcp-card-tools{color:var(--dsw-alias-label-tertiary,#9ca3af);font-size:12px;cursor:pointer;user-select:none}",
251
+ ".mcp-card-tools:hover{color:var(--dsw-alias-label-secondary,#6b7280)}",
252
+ ".mcp-card-ops{display:flex;gap:8px;flex-wrap:wrap}",
253
+ ].join("");
254
+
255
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@moon16u/dsh-plugin-mcp-console/McpSection.module.css\"]") === null) {
256
+ var mcpStyleTag = document.createElement("style");
257
+ mcpStyleTag.dataset.plugin = "@moon16u/dsh-plugin-mcp-console";
258
+ mcpStyleTag.dataset.pluginCss = "@moon16u/dsh-plugin-mcp-console/McpSection.module.css";
259
+ mcpStyleTag.textContent = mcpCss;
260
+ document.head.appendChild(mcpStyleTag);
261
+ }
262
+
263
+ var McpH = React.createElement;
264
+
265
+ function mcpApi(path, options) {
266
+ return fetch("/api/dsh-mcp-console" + path, {
267
+ headers: { "content-type": "application/json" },
268
+ ...options,
269
+ }).then(async function (response) {
270
+ var body = null;
271
+ try {
272
+ body = await response.json();
273
+ } catch {}
274
+ if (!response.ok) {
275
+ var message = body && body.error && body.error.message ? body.error.message : response.status + " " + response.statusText;
276
+ throw new Error(message);
277
+ }
278
+ return body;
279
+ });
280
+ }
281
+
282
+ function mcpFill(template, values) {
283
+ return String(template).replace(/\{(\w+)\}/g, function (_, key) {
284
+ return Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : "{" + key + "}";
285
+ });
286
+ }
287
+
288
+ /** Parse "KEY=VALUE" / "KEY:VALUE" lines into a record. */
289
+ function mcpParseLines(text, separator) {
290
+ var out = {};
291
+ String(text ?? "").split(/\r?\n/).forEach(function (line) {
292
+ var trimmed = line.trim();
293
+ if (trimmed.length === 0) return;
294
+ var at = trimmed.indexOf(separator);
295
+ if (at <= 0) return;
296
+ var key = trimmed.slice(0, at).trim();
297
+ var value = trimmed.slice(at + separator.length).trim();
298
+ if (key.length > 0) out[key] = value;
299
+ });
300
+ return out;
301
+ }
302
+
303
+ function mcpFormatLines(record, separator) {
304
+ return Object.keys(record ?? {}).map(function (key) {
305
+ return key + separator + record[key];
306
+ }).join("\n");
307
+ }
308
+
309
+ /** The per-server master toggle (mcp-manager-gui-spec §3.3-4). */
310
+ function McpSwitch(props) {
311
+ var t = props.t;
312
+ return McpH("label", {
313
+ className: "mcp-switch",
314
+ title: props.enabled ? t("disable") : t("enable"),
315
+ "aria-label": props.enabled ? t("disable") : t("enable"),
316
+ },
317
+ McpH("input", {
318
+ type: "checkbox",
319
+ checked: props.enabled,
320
+ disabled: props.disabled,
321
+ onChange: function (event) { props.onChange(event.target.checked); },
322
+ }),
323
+ McpH("span", { className: "mcp-switch-track" }),
324
+ McpH("span", { className: "mcp-switch-thumb" }));
325
+ }
326
+
327
+ /** One clickable tool pill with hover tooltip (spec §3.4; readOnly: none). */
328
+ function McpToolPill(props) {
329
+ var t = props.t;
330
+ var tool = props.tool;
331
+ return McpH("span", { className: "mcp-pill-wrap" },
332
+ McpH("button", {
333
+ type: "button",
334
+ className: "mcp-pill",
335
+ "data-enabled": tool.enabled ? "true" : "false",
336
+ "aria-pressed": tool.enabled ? "true" : "false",
337
+ disabled: props.disabled,
338
+ onClick: function () { if (props.onToggle) props.onToggle(tool); },
339
+ }, tool.label),
340
+ props.readOnly ? null : McpH("span", { className: "mcp-tip" }, tool.enabled ? t("clickToDisable") : t("clickToEnable")));
341
+ }
342
+
343
+ /**
344
+ * One server item: header row (name + status + badge | delete + master
345
+ * switch), summary row (chevron + count, expand trigger), tool pills.
346
+ * Read-only entries (profile-declared, review §2-4) hide delete, disable
347
+ * switch and pills, and skip tooltips. Editing opens by clicking the
348
+ * server name (the explicit edit button is gone per review §2-2).
349
+ */
350
+ function McpServerItem(props) {
351
+ var t = props.t;
352
+ var server = props.server;
353
+ var open = props.open;
354
+ var readOnly = server.readOnly === true;
355
+ var busy = !readOnly && props.busy === server.name;
356
+ var enabled = server.enabled !== false;
357
+ var summary = enabled
358
+ ? (server.toolCount > 0
359
+ ? mcpFill(t("toolsEnabled"), { n: server.enabledToolCount })
360
+ : t("toolsAvailable"))
361
+ : t("disabled");
362
+ var statusLabel = t(server.status);
363
+ var nameElement = readOnly
364
+ ? McpH("span", { className: "mcp-mgr-name", title: server.name }, server.name)
365
+ : McpH("button", {
366
+ type: "button", className: "mcp-mgr-name mcp-mgr-name-btn",
367
+ title: t("editTitle"), "aria-label": t("editTitle"),
368
+ onClick: function () { props.onEdit(server); },
369
+ }, server.name);
370
+ return McpH("div", { className: "mcp-mgr-item", "data-mcp-server": server.name },
371
+ McpH("div", { className: "mcp-mgr-itemhead" },
372
+ nameElement,
373
+ McpH("span", { className: "mcp-dot", "data-status": server.status, title: statusLabel, key: "dot" }),
374
+ readOnly ? McpH("span", { className: "mcp-readonly-badge", title: t("profileReadOnly") }, t("profileReadOnly")) : null,
375
+ McpH("span", { className: "mcp-mgr-ops" },
376
+ readOnly ? null : McpH("button", {
377
+ type: "button", className: "mcp-mgr-del", disabled: busy,
378
+ title: t("remove"), "aria-label": t("remove"),
379
+ onClick: function () { props.onRemove(server); },
380
+ }, McpH(McpIcons.IconTrashOutline16, { size: 14 })),
381
+ McpH(McpSwitch, {
382
+ t: t, enabled: enabled, disabled: busy || readOnly,
383
+ onChange: function (next) { if (!readOnly) props.onToggleServer(server, next); },
384
+ }))),
385
+ McpH("div", { style: { display: "flex", alignItems: "center", gap: "8px" } },
386
+ McpH("button", {
387
+ type: "button", className: "mcp-mgr-summary", "data-open": open ? "true" : "false",
388
+ "aria-expanded": open ? "true" : "false",
389
+ onClick: function () { props.onToggleOpen(server.name); },
390
+ },
391
+ McpH("span", { className: "mcp-mgr-chevron" }),
392
+ McpH("span", null, summary))),
393
+ server.error ? McpH("p", { className: "mcp-mgr-err" }, t("errorPrefix") + ": " + server.error) : null,
394
+ open
395
+ ? (server.toolCount === 0
396
+ ? McpH("p", { className: "mcp-mgr-note" }, t("noTools"))
397
+ : McpH("div", { className: "mcp-mgr-tools" }, server.tools.map(function (tool) {
398
+ return McpH(McpToolPill, {
399
+ key: tool.name,
400
+ tool: tool,
401
+ t: t,
402
+ readOnly: readOnly,
403
+ disabled: busy || !enabled || readOnly,
404
+ onToggle: readOnly ? null : function (target) { props.onToggleTool(server, target); },
405
+ });
406
+ })))
407
+ : null);
408
+ }
409
+
410
+ /**
411
+ * Add/edit form (mcp-manager-modal-spec.md): scrollable body + fixed
412
+ * footer with a right-aligned primary "保存配置" button, segmented
413
+ * transport/scope controls, inline checkbox, natural-language labels with
414
+ * placeholders and helper text instead of bare regex/variable names.
415
+ * Renders as [scrollable body, footer] so the modal card stays flex-column.
416
+ */
417
+ function McpServerForm(props) {
418
+ var t = props.t;
419
+ var initial = props.initial ?? {};
420
+ var isEdit = initial.name !== undefined;
421
+ var useState = React.useState;
422
+ var transportState = useState(initial.transport === "streamable-http" ? "streamable-http" : "stdio");
423
+ var transport = transportState[0], setTransport = transportState[1];
424
+ var nameState = useState(initial.name ?? "");
425
+ var name = nameState[0], setName = nameState[1];
426
+ var commandState = useState(initial.command ?? "");
427
+ var command = commandState[0], setCommand = commandState[1];
428
+ var argsState = useState(Array.isArray(initial.args) ? initial.args.join("\n") : "");
429
+ var args = argsState[0], setArgs = argsState[1];
430
+ var envState = useState(mcpFormatLines(initial.env, "="));
431
+ var env = envState[0], setEnv = envState[1];
432
+ var urlState = useState(initial.url ?? "");
433
+ var url = urlState[0], setUrl = urlState[1];
434
+ var headersState = useState(mcpFormatLines(initial.headers, ":"));
435
+ var headers = headersState[0], setHeaders = headersState[1];
436
+ var timeoutState = useState(String(initial.toolCallTimeoutMs ?? 60000));
437
+ var timeout = timeoutState[0], setTimeoutMs = timeoutState[1];
438
+ var failState = useState(initial.failOnStartupError === true);
439
+ var fail = failState[0], setFail = failState[1];
440
+ var scopeState = useState(initial.scope === "project" ? "project" : "global");
441
+ var scope = scopeState[0], setScope = scopeState[1];
442
+ var advancedState = useState(false);
443
+ var advancedOpen = advancedState[0], setAdvancedOpen = advancedState[1];
444
+ var errorState = useState(null);
445
+ var error = errorState[0], setError = errorState[1];
446
+ var busyState = useState(false);
447
+ var busy = busyState[0], setBusy = busyState[1];
448
+
449
+ function collect() {
450
+ var config = {
451
+ name: name.trim(),
452
+ transport: transport,
453
+ enabled: initial.enabled === false ? false : true,
454
+ scope: scope,
455
+ toolCallTimeoutMs: Number(timeout) > 0 ? Number(timeout) : 60000,
456
+ failOnStartupError: fail,
457
+ disabledTools: Array.isArray(initial.disabledTools) ? initial.disabledTools : [],
458
+ };
459
+ if (transport === "stdio") {
460
+ config.command = command.trim();
461
+ config.args = args.split(/\r?\n/).map(function (line) { return line.trim(); }).filter(function (line) { return line.length > 0; });
462
+ config.env = mcpParseLines(env, "=");
463
+ } else {
464
+ config.url = url.trim();
465
+ config.headers = mcpParseLines(headers, ":");
466
+ }
467
+ return config;
468
+ }
469
+
470
+ function submit() {
471
+ setError(null);
472
+ setBusy(true);
473
+ Promise.resolve(props.onSubmit(collect())).then(function () {
474
+ setBusy(false);
475
+ }, function (failure) {
476
+ setError(failure instanceof Error ? failure.message : String(failure));
477
+ setBusy(false);
478
+ });
479
+ }
480
+
481
+ function fieldRow(key, label, required, control, help) {
482
+ return McpH("div", { className: "mcp-form-row", key: key },
483
+ McpH("label", { className: "mcp-form-label" },
484
+ label,
485
+ required ? McpH("span", { className: "mcp-req", key: "req" }, " *") : null),
486
+ control,
487
+ help ? McpH("p", { className: "mcp-help", key: "help" }, help) : null);
488
+ }
489
+
490
+ function seg(options, value, onChange) {
491
+ return McpH("div", { className: "mcp-seg", role: "radiogroup" },
492
+ options.map(function (option) {
493
+ var isOptionDisabled = option.disabled === true;
494
+ return McpH("button", {
495
+ type: "button",
496
+ key: option.value,
497
+ className: "mcp-seg-btn",
498
+ "data-on": value === option.value ? "true" : "false",
499
+ role: "radio",
500
+ "aria-checked": value === option.value ? "true" : "false",
501
+ disabled: isOptionDisabled,
502
+ title: isOptionDisabled ? (option.title || "") : "",
503
+ onClick: function () {
504
+ if (!isOptionDisabled) onChange(option.value);
505
+ },
506
+ }, option.label);
507
+ }));
508
+ }
509
+
510
+ var hasProject = props.hasProjectWorkspace === true;
511
+
512
+ var body = [
513
+ fieldRow("name", t("formName"), true,
514
+ McpH("input", {
515
+ className: "mcp-in", type: "text", value: name, "aria-label": t("formName"),
516
+ placeholder: t("formNamePlaceholder"),
517
+ disabled: isEdit,
518
+ onChange: function (event) { setName(event.target.value); },
519
+ }),
520
+ t("formNameHelp")),
521
+ fieldRow("transport", t("formTransport"), false,
522
+ seg([
523
+ { value: "stdio", label: t("transportStdio") },
524
+ { value: "streamable-http", label: t("transportHttp") },
525
+ ], transport, setTransport)),
526
+ ];
527
+
528
+ if (transport === "stdio") {
529
+ body.push(fieldRow("command", t("formCommand"), true,
530
+ McpH("input", {
531
+ className: "mcp-in mcp-in-mono", type: "text", value: command, "aria-label": t("formCommand"),
532
+ placeholder: t("formCommandPlaceholder"),
533
+ onChange: function (event) { setCommand(event.target.value); },
534
+ })));
535
+ body.push(fieldRow("args", t("formArgs"), false,
536
+ McpH("textarea", {
537
+ className: "mcp-in mcp-in-area", value: args, "aria-label": t("formArgs"), rows: 3,
538
+ placeholder: t("formArgsPlaceholder"),
539
+ onChange: function (event) { setArgs(event.target.value); },
540
+ })));
541
+ body.push(fieldRow("env", t("formEnv"), false,
542
+ McpH("textarea", {
543
+ className: "mcp-in mcp-in-area", value: env, "aria-label": t("formEnv"), rows: 3,
544
+ placeholder: t("formEnvPlaceholder"),
545
+ onChange: function (event) { setEnv(event.target.value); },
546
+ })));
547
+ } else {
548
+ body.push(fieldRow("url", t("formUrl"), true,
549
+ McpH("input", {
550
+ className: "mcp-in mcp-in-mono", type: "text", value: url, "aria-label": t("formUrl"),
551
+ placeholder: t("formUrlPlaceholder"),
552
+ onChange: function (event) { setUrl(event.target.value); },
553
+ })));
554
+ body.push(fieldRow("headers", t("formHeaders"), false,
555
+ McpH("textarea", {
556
+ className: "mcp-in mcp-in-area", value: headers, "aria-label": t("formHeaders"), rows: 3,
557
+ placeholder: t("formHeadersPlaceholder"),
558
+ onChange: function (event) { setHeaders(event.target.value); },
559
+ })));
560
+ }
561
+
562
+ body.push(fieldRow("scope", t("formScope"), false,
563
+ seg([
564
+ { value: "global", label: t("scopeGlobal") },
565
+ { value: "project", label: t("scopeProject"), disabled: !hasProject, title: t("scopeProjectDisabledTip") },
566
+ ], hasProject ? scope : "global", setScope),
567
+ !hasProject
568
+ ? t("scopeProjectNoWorkspaceHelp")
569
+ : (scope === "global" ? t("scopeGlobalHelp") : t("scopeProjectHelp"))));
570
+
571
+ var advanced = McpH("div", { key: "advanced" },
572
+ McpH("button", {
573
+ type: "button", className: "mcp-adv-toggle",
574
+ "aria-expanded": advancedOpen ? "true" : "false",
575
+ onClick: function () { setAdvancedOpen(!advancedOpen); },
576
+ },
577
+ McpH(McpIcons.IconChevronRightOutline14, { size: 14, style: { transform: advancedOpen ? "rotate(90deg)" : "none", transition: "transform .15s ease" } }),
578
+ t("formAdvanced")),
579
+ advancedOpen
580
+ ? McpH("div", { className: "mcp-adv-box" },
581
+ fieldRow("timeout", t("formTimeout"), false,
582
+ McpH("input", {
583
+ className: "mcp-in", type: "number", min: "1", value: timeout, "aria-label": t("formTimeout"),
584
+ onChange: function (event) { setTimeoutMs(event.target.value); },
585
+ })),
586
+ McpH("label", { className: "mcp-check-row" },
587
+ McpH("input", {
588
+ type: "checkbox", checked: fail, "aria-label": t("formFail"),
589
+ onChange: function (event) { setFail(event.target.checked); },
590
+ }),
591
+ McpH("span", null, t("formFail"))))
592
+ : null);
593
+
594
+ return McpH(React.Fragment, null,
595
+ McpH("div", { className: "mcp-form-body" },
596
+ McpH("div", { className: "mcp-form" }, body),
597
+ advanced,
598
+ error ? McpH("p", { className: "mcp-err" }, error) : null),
599
+ McpH("div", { className: "mcp-form-footer" },
600
+ McpH("button", {
601
+ type: "button", className: "mcp-btn-secondary", disabled: busy, onClick: props.onCancel,
602
+ }, t("cancel")),
603
+ McpH("button", {
604
+ type: "button", className: "mcp-btn-primary", disabled: busy, onClick: submit,
605
+ }, t("saveConfig"))));
606
+ }
607
+
608
+ /** mcpServers JSON paste-import dialog (plan §5.5), modal-spec styling. */
609
+ function McpImportDialog(props) {
610
+ var t = props.t;
611
+ var useState = React.useState;
612
+ var textState = useState("");
613
+ var text = textState[0], setText = textState[1];
614
+ var previewState = useState(null);
615
+ var preview = previewState[0], setPreview = previewState[1];
616
+ var errorState = useState(null);
617
+ var error = errorState[0], setError = errorState[1];
618
+ var busyState = useState(false);
619
+ var busy = busyState[0], setBusy = busyState[1];
620
+
621
+ function reparse(value) {
622
+ setText(value);
623
+ setError(null);
624
+ if (value.trim().length === 0) {
625
+ setPreview(null);
626
+ return;
627
+ }
628
+ try {
629
+ var parsed = JSON.parse(value);
630
+ var map = parsed && typeof parsed === "object" && parsed.mcpServers && typeof parsed.mcpServers === "object" ? parsed.mcpServers : parsed;
631
+ if (!map || typeof map !== "object" || Array.isArray(map)) throw new Error("expected an object");
632
+ setPreview({ count: Object.keys(map).length });
633
+ } catch (failure) {
634
+ setPreview(null);
635
+ setError(failure instanceof Error ? failure.message : String(failure));
636
+ }
637
+ }
638
+
639
+ function run() {
640
+ setBusy(true);
641
+ setError(null);
642
+ mcpApi("/import", { method: "POST", body: JSON.stringify({ json: text }) }).then(function (result) {
643
+ setBusy(false);
644
+ props.onDone(result);
645
+ }, function (failure) {
646
+ setBusy(false);
647
+ setError(failure instanceof Error ? failure.message : String(failure));
648
+ });
649
+ }
650
+
651
+ return McpH(React.Fragment, null,
652
+ McpH("div", { className: "mcp-form-body" },
653
+ McpH("p", { className: "mcp-help", style: { color: "var(--dsw-alias-label-secondary,#4b5563)", fontSize: "12px", lineHeight: "18px" } }, t("importHelp")),
654
+ McpH("textarea", {
655
+ className: "mcp-in mcp-in-area mcp-in-area-tall mcp-in-mono", rows: 10, value: text,
656
+ "aria-label": t("importTitle"),
657
+ placeholder: t("importPlaceholder"),
658
+ onChange: function (event) { reparse(event.target.value); },
659
+ }),
660
+ preview ? McpH("p", { className: "mcp-mgr-note", style: { color: "#10b981", fontWeight: 500 } }, mcpFill(t("importPreviewAdded"), { n: preview.count })) : null,
661
+ error ? McpH("p", { className: "mcp-err" }, error) : null),
662
+ McpH("div", { className: "mcp-form-footer" },
663
+ McpH("button", {
664
+ type: "button", className: "mcp-btn-secondary", disabled: busy, onClick: props.onCancel,
665
+ }, t("cancel")),
666
+ McpH("button", {
667
+ type: "button", className: "mcp-btn-primary",
668
+ disabled: busy || preview === null, onClick: run,
669
+ }, t("importConfirm"))));
670
+ }
671
+
672
+ /**
673
+ * The settings section (mcp-manager-gui-spec.md §1–§3): header bar with
674
+ * refresh + add + import, the server list card, and modals.
675
+ */
676
+ function McpSection(props) {
677
+ var t = props.t;
678
+ var useState = React.useState;
679
+ var useEffect = React.useEffect;
680
+
681
+ var snapshotState = useState(null);
682
+ var snapshot = snapshotState[0], setSnapshot = snapshotState[1];
683
+ var modeState = useState(null);
684
+ var mode = modeState[0], setMode = modeState[1]; // null | "add" | "edit" | "import"
685
+ var editingState = useState(null);
686
+ var editing = editingState[0], setEditing = editingState[1];
687
+ var busyState = useState(null);
688
+ var busy = busyState[0], setBusy = busyState[1];
689
+ var errorState = useState(null);
690
+ var error = errorState[0], setError = errorState[1];
691
+ var confirmState = useState(null);
692
+ var confirmName = confirmState[0], setConfirm = confirmState[1];
693
+ var expandedState = useState({});
694
+ var expanded = expandedState[0], setExpanded = expandedState[1];
695
+ var refreshingState = useState(false);
696
+ var refreshing = refreshingState[0], setRefreshing = refreshingState[1];
697
+
698
+ function load() {
699
+ return mcpApi("/servers").then(setSnapshot, function (failure) {
700
+ setError(failure instanceof Error ? failure.message : String(failure));
701
+ });
702
+ }
703
+
704
+ useEffect(function () {
705
+ var stale = false;
706
+ mcpApi("/servers").then(function (body) {
707
+ if (!stale) setSnapshot(body);
708
+ }, function (failure) {
709
+ if (!stale) setError(failure instanceof Error ? failure.message : String(failure));
710
+ });
711
+ return function () { stale = true; };
712
+ }, []);
713
+
714
+ // Live updates while the settings page is open (plan §5.4 /events).
715
+ useEffect(function () {
716
+ var source = new EventSource("/api/dsh-mcp-console/events");
717
+ source.addEventListener("status_changed", function (event) {
718
+ try {
719
+ setSnapshot(JSON.parse(event.data));
720
+ } catch {}
721
+ });
722
+ return function () { source.close(); };
723
+ }, []);
724
+
725
+ useEffect(function () {
726
+ function onKey(event) {
727
+ if (event.key === "Escape" && mode !== null) setMode(null);
728
+ }
729
+ window.addEventListener("keydown", onKey);
730
+ return function () { window.removeEventListener("keydown", onKey); };
731
+ }, [mode]);
732
+
733
+ function withBusy(name, action) {
734
+ setBusy(name);
735
+ setError(null);
736
+ return Promise.resolve(action()).then(function (value) {
737
+ setBusy(null);
738
+ return value;
739
+ }, function (failure) {
740
+ setBusy(null);
741
+ setError(failure instanceof Error ? failure.message : String(failure));
742
+ });
743
+ }
744
+
745
+ function refresh() {
746
+ setRefreshing(true);
747
+ withBusy(null, function () {
748
+ return mcpApi("/refresh", { method: "POST" }).then(function () { return load(); });
749
+ }).then(function () { setRefreshing(false); }, function () { setRefreshing(false); });
750
+ }
751
+
752
+ function toggleServer(server, next) {
753
+ return withBusy(server.name, function () {
754
+ return mcpApi("/servers/" + encodeURIComponent(server.name) + "/" + (next ? "enable" : "disable"), { method: "POST" });
755
+ });
756
+ }
757
+
758
+ function toggleTool(server, tool) {
759
+ var disabled = server.tools.filter(function (item) { return !item.enabled; }).map(function (item) { return item.name; });
760
+ var next = tool.enabled ? disabled.concat([tool.name]) : disabled.filter(function (name) { return name !== tool.name; });
761
+ return withBusy(server.name, function () {
762
+ return mcpApi("/servers/" + encodeURIComponent(server.name), {
763
+ method: "PATCH",
764
+ body: JSON.stringify({ disabledTools: next }),
765
+ });
766
+ });
767
+ }
768
+
769
+ function removeServer(server) {
770
+ setConfirm(server.name);
771
+ }
772
+
773
+ function confirmRemove() {
774
+ var name = confirmName;
775
+ setConfirm(null);
776
+ withBusy(name, function () {
777
+ return mcpApi("/servers/" + encodeURIComponent(name), { method: "DELETE" });
778
+ });
779
+ }
780
+
781
+ function submitAdd(config) {
782
+ return mcpApi("/servers", { method: "POST", body: JSON.stringify(config) }).then(function () {
783
+ setMode(null);
784
+ setExpanded(function (previous) {
785
+ var next = { ...previous };
786
+ next[config.name] = false;
787
+ return next;
788
+ });
789
+ });
790
+ }
791
+
792
+ function submitEdit(config) {
793
+ var body = { ...config };
794
+ delete body.name;
795
+ return mcpApi("/servers/" + encodeURIComponent(config.name), { method: "PATCH", body: JSON.stringify(body) }).then(function () {
796
+ setMode(null);
797
+ setEditing(null);
798
+ });
799
+ }
800
+
801
+ var servers = snapshot && snapshot.servers ? snapshot.servers : [];
802
+ var readOnlyServers = snapshot && snapshot.externalServers ? snapshot.externalServers : [];
803
+ var allServers = servers.concat(readOnlyServers);
804
+ var busyAny = busy !== null || refreshing;
805
+
806
+ var listChildren = [];
807
+ if (allServers.length === 0) {
808
+ listChildren.push(McpH("div", { className: "mcp-mgr-item", key: "empty" },
809
+ McpH("p", { className: "mcp-mgr-note" }, t("empty"))));
810
+ }
811
+ allServers.forEach(function (server) {
812
+ listChildren.push(McpH(McpServerItem, {
813
+ key: server.name,
814
+ t: t,
815
+ server: server,
816
+ busy: busy,
817
+ open: expanded[server.name] === true,
818
+ onToggleOpen: function (name) {
819
+ setExpanded(function (previous) {
820
+ var next = { ...previous };
821
+ next[name] = !previous[name];
822
+ return next;
823
+ });
824
+ },
825
+ onToggleServer: toggleServer,
826
+ onToggleTool: toggleTool,
827
+ onRemove: removeServer,
828
+ onEdit: function (target) { setEditing(target); setMode("edit"); },
829
+ }));
830
+ });
831
+
832
+ var notes = [];
833
+ if (error) notes.push(McpH("p", { className: "mcp-err", key: "api-error" }, t("apiError") + ": " + error));
834
+ (snapshot && snapshot.storeErrors ? snapshot.storeErrors : []).forEach(function (message, index) {
835
+ notes.push(McpH("p", { className: "mcp-mgr-warn", key: "store-" + index }, t("storeError") + ": " + message));
836
+ });
837
+ if (snapshot && snapshot.clientError) {
838
+ notes.push(McpH("p", { className: "mcp-err", key: "client-error" }, t("errorPrefix") + ": " + snapshot.clientError));
839
+ }
840
+
841
+ var modal = null;
842
+ if (mode === "add" || mode === "edit") {
843
+ modal = McpH("div", { className: "mcp-modal", key: "modal", onClick: function () { setMode(null); setEditing(null); } },
844
+ McpH("div", { className: "mcp-modal-card", onClick: function (event) { event.stopPropagation(); } },
845
+ McpH("div", { className: "mcp-modal-head" },
846
+ McpH("h3", { className: "mcp-modal-title" }, mode === "add" ? t("addTitle") : t("editTitle")),
847
+ McpH("button", {
848
+ type: "button", className: "mcp-mgr-iconbtn", "aria-label": t("cancel"),
849
+ onClick: function () { setMode(null); setEditing(null); },
850
+ }, McpH(McpIcons.IconCloseOutline16, { size: 14 }))),
851
+ McpH(McpServerForm, {
852
+ t: t,
853
+ initial: editing,
854
+ hasProjectWorkspace: Boolean(snapshot && snapshot.hasProjectWorkspace),
855
+ onSubmit: mode === "add" ? submitAdd : submitEdit,
856
+ onCancel: function () { setMode(null); setEditing(null); },
857
+ })));
858
+ } else if (mode === "import") {
859
+ modal = McpH("div", { className: "mcp-modal", key: "modal", onClick: function () { setMode(null); } },
860
+ McpH("div", { className: "mcp-modal-card", onClick: function (event) { event.stopPropagation(); } },
861
+ McpH("div", { className: "mcp-modal-head" },
862
+ McpH("h3", { className: "mcp-modal-title" }, t("importTitle")),
863
+ McpH("button", {
864
+ type: "button", className: "mcp-mgr-iconbtn", "aria-label": t("cancel"),
865
+ onClick: function () { setMode(null); },
866
+ }, McpH(McpIcons.IconCloseOutline16, { size: 14 }))),
867
+ McpH(McpImportDialog, {
868
+ t: t,
869
+ onCancel: function () { setMode(null); },
870
+ onDone: function (result) {
871
+ setMode(null);
872
+ if (result && result.skipped && result.skipped.length > 0 && result.added.length === 0) {
873
+ setError(result.skipped.map(function (item) { return item.name + ": " + item.reason; }).join("; "));
874
+ } else {
875
+ setError(null);
876
+ }
877
+ },
878
+ })));
879
+ } else if (confirmName !== null) {
880
+ modal = McpH("div", { className: "mcp-modal", key: "confirm", onClick: function () { setConfirm(null); } },
881
+ McpH("div", { className: "mcp-modal-card", style: { width: "420px" }, onClick: function (event) { event.stopPropagation(); } },
882
+ McpH("div", { className: "mcp-modal-head" },
883
+ McpH("h3", { className: "mcp-modal-title" }, t("remove")),
884
+ McpH("button", {
885
+ type: "button", className: "mcp-mgr-iconbtn", "aria-label": t("cancel"),
886
+ onClick: function () { setConfirm(null); },
887
+ }, McpH(McpIcons.IconCloseOutline16, { size: 14 }))),
888
+ McpH("div", { className: "mcp-form-body" },
889
+ McpH("p", { className: "mcp-mgr-note" }, t("removeConfirm"))),
890
+ McpH("div", { className: "mcp-form-footer" },
891
+ McpH("button", {
892
+ type: "button", className: "mcp-btn-secondary",
893
+ onClick: function () { setConfirm(null); },
894
+ }, t("confirmNo")),
895
+ McpH("button", {
896
+ type: "button", className: "mcp-btn-danger",
897
+ onClick: confirmRemove,
898
+ }, t("confirmYes")))));
899
+ }
900
+
901
+ return McpH("div", { className: "mcp-mgr", "data-mcp-console-section": "" },
902
+ McpH("div", { className: "mcp-mgr-head" },
903
+ McpH("h2", { className: "mcp-mgr-title" }, t("title")),
904
+ McpH("button", {
905
+ type: "button", className: "mcp-mgr-iconbtn", disabled: busyAny,
906
+ title: t("refresh"), "aria-label": t("refresh"),
907
+ onClick: refresh,
908
+ }, McpH(McpIcons.IconRefreshOutline16, { size: 15 })),
909
+ McpH("button", {
910
+ type: "button", className: "mcp-mgr-btn", disabled: busyAny,
911
+ onClick: function () { setEditing(null); setMode("add"); },
912
+ }, McpH(McpIcons.IconPlusOutline16, { size: 14 }), t("add")),
913
+ McpH("button", {
914
+ type: "button", className: "mcp-mgr-btn", disabled: busyAny,
915
+ onClick: function () { setMode("import"); },
916
+ }, t("importAction"))),
917
+ snapshot
918
+ ? McpH("div", { className: "mcp-mgr-list" }, listChildren)
919
+ : McpH("p", { className: "mcp-mgr-note" }, t("loadingData")),
920
+ notes.length > 0 ? McpH("div", { style: { display: "flex", flexDirection: "column", gap: "6px" } }, notes) : null,
921
+ servers.some(function (server) { return server.tools && server.tools.some(function (tool) { return !tool.enabled; }); })
922
+ ? McpH("p", { className: "mcp-ext" }, t("toolDeniedNote"))
923
+ : null,
924
+ modal);
925
+ }
926
+
927
+ var mcpInject = ["slots", "locale"];
928
+
929
+ function mcpConsoleApply(ctx) {
930
+ ctx.effect(function () {
931
+ return ctx.locale.register(McpNS, { zh: mcpZh, en: mcpEn });
932
+ }, "mcp-console: dictionaries");
933
+
934
+ var mcpT = ctx.locale.bind(McpNS);
935
+
936
+ ctx.slots.inject("settings.section", function () {
937
+ return ctx.slots.register({
938
+ name: "settings.section",
939
+ id: "mcp-console",
940
+ order: 12,
941
+ label: function () { return mcpT("nav"); },
942
+ inject: function () { return { t: mcpT }; },
943
+ }, McpSection);
944
+ });
945
+ }
946
+
947
+ exports.McpSection = McpSection;
948
+ exports.mcpConsoleApply = mcpConsoleApply;
949
+ exports.mcpInject = mcpInject;
950
+
951
+ // ==== end mcp-console client body ====
952
+ return module.exports;
953
+ }
954
+ });