dsh-mcp 1.3.0 → 1.5.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 CHANGED
@@ -14572,7 +14572,8 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_list_result$schema = external_export
14572
14572
  "env": external_exports.array(external_exports.object({
14573
14573
  "name": external_exports.string().readonly(),
14574
14574
  "secret": external_exports.boolean().readonly(),
14575
- "configured": external_exports.boolean().readonly()
14575
+ "configured": external_exports.boolean().readonly(),
14576
+ "value": external_exports.string().readonly().optional()
14576
14577
  })).readonly(),
14577
14578
  "toolCallTimeoutMs": external_exports.number().readonly(),
14578
14579
  "failOnStartupError": external_exports.boolean().readonly(),
@@ -14604,7 +14605,7 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_test_parameter_0$schema = external_e
14604
14605
  "name": external_exports.string().readonly(),
14605
14606
  "secret": external_exports.boolean().readonly(),
14606
14607
  "value": external_exports.string().readonly().optional()
14607
- })).readonly()
14608
+ })).readonly().optional()
14608
14609
  });
14609
14610
  var _deepseek_ai_dsh_mcp_manager_mcpManager_test_result$schema = external_exports.object({
14610
14611
  "ok": external_exports.literal(true).readonly(),
@@ -14641,7 +14642,7 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_parameter_0$schema = external
14641
14642
  "name": external_exports.string().readonly(),
14642
14643
  "secret": external_exports.boolean().readonly(),
14643
14644
  "value": external_exports.string().readonly().optional()
14644
- })).readonly()
14645
+ })).readonly().optional()
14645
14646
  });
14646
14647
  var _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema = external_exports.union([external_exports.object({
14647
14648
  "ok": external_exports.literal(true).readonly(),
@@ -14661,7 +14662,8 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema = external_expo
14661
14662
  "env": external_exports.array(external_exports.object({
14662
14663
  "name": external_exports.string().readonly(),
14663
14664
  "secret": external_exports.boolean().readonly(),
14664
- "configured": external_exports.boolean().readonly()
14665
+ "configured": external_exports.boolean().readonly(),
14666
+ "value": external_exports.string().readonly().optional()
14665
14667
  })).readonly(),
14666
14668
  "toolCallTimeoutMs": external_exports.number().readonly(),
14667
14669
  "failOnStartupError": external_exports.boolean().readonly(),
@@ -14678,6 +14680,69 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema = external_expo
14678
14680
  "message": external_exports.string().readonly()
14679
14681
  }).readonly()
14680
14682
  })]);
14683
+ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema = external_exports.object({
14684
+ "servers": external_exports.array(external_exports.object({
14685
+ "server": external_exports.object({
14686
+ "serverName": external_exports.string().readonly(),
14687
+ "transport": external_exports.union([external_exports.literal("stdio"), external_exports.literal("streamable-http")]).readonly(),
14688
+ "enabled": external_exports.boolean().readonly().optional(),
14689
+ "command": external_exports.string().readonly().optional(),
14690
+ "args": external_exports.array(external_exports.string()).readonly().optional(),
14691
+ "cwd": external_exports.string().readonly().optional(),
14692
+ "url": external_exports.string().readonly().optional(),
14693
+ "headers": external_exports.union([external_exports.record(external_exports.string(), external_exports.string()), external_exports.array(external_exports.object({
14694
+ "name": external_exports.string().readonly(),
14695
+ "value": external_exports.string().readonly()
14696
+ })).readonly()]).readonly().optional(),
14697
+ "toolCallTimeoutMs": external_exports.number().readonly().optional(),
14698
+ "failOnStartupError": external_exports.boolean().readonly().optional()
14699
+ }).readonly(),
14700
+ "env": external_exports.array(external_exports.object({
14701
+ "name": external_exports.string().readonly(),
14702
+ "secret": external_exports.boolean().readonly().optional(),
14703
+ "value": external_exports.string().readonly().optional()
14704
+ })).readonly().optional()
14705
+ })).readonly()
14706
+ });
14707
+ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema = external_exports.union([external_exports.object({
14708
+ "ok": external_exports.literal(true).readonly(),
14709
+ "added": external_exports.number().readonly(),
14710
+ "updated": external_exports.number().readonly(),
14711
+ "removed": external_exports.number().readonly(),
14712
+ "servers": external_exports.array(external_exports.object({
14713
+ "id": external_exports.intersection(external_exports.string(), external_exports.unknown()).readonly(),
14714
+ "serverName": external_exports.string().readonly(),
14715
+ "transport": external_exports.union([external_exports.literal("stdio"), external_exports.literal("streamable-http")]).readonly(),
14716
+ "enabled": external_exports.boolean().readonly(),
14717
+ "command": external_exports.string().readonly(),
14718
+ "args": external_exports.array(external_exports.string()).readonly(),
14719
+ "cwd": external_exports.string().readonly(),
14720
+ "url": external_exports.string().readonly(),
14721
+ "headers": external_exports.array(external_exports.object({
14722
+ "name": external_exports.string().readonly(),
14723
+ "value": external_exports.string().readonly()
14724
+ })).readonly(),
14725
+ "env": external_exports.array(external_exports.object({
14726
+ "name": external_exports.string().readonly(),
14727
+ "secret": external_exports.boolean().readonly(),
14728
+ "configured": external_exports.boolean().readonly(),
14729
+ "value": external_exports.string().readonly().optional()
14730
+ })).readonly(),
14731
+ "toolCallTimeoutMs": external_exports.number().readonly(),
14732
+ "failOnStartupError": external_exports.boolean().readonly(),
14733
+ "status": external_exports.object({
14734
+ "phase": external_exports.union([external_exports.literal("failed"), external_exports.literal("stopped"), external_exports.literal("mounting"), external_exports.literal("live")]).readonly(),
14735
+ "tools": external_exports.array(external_exports.string()).readonly(),
14736
+ "error": external_exports.string().readonly().optional()
14737
+ }).readonly()
14738
+ })).readonly()
14739
+ }), external_exports.object({
14740
+ "ok": external_exports.literal(false).readonly(),
14741
+ "error": external_exports.object({
14742
+ "code": external_exports.union([external_exports.literal("MCP_SERVER_NOT_FOUND"), external_exports.literal("MCP_SERVER_NAME_CONFLICT"), external_exports.literal("MCP_INVALID_SPEC"), external_exports.literal("MCP_MOUNT_FAILED")]).readonly(),
14743
+ "message": external_exports.string().readonly()
14744
+ }).readonly()
14745
+ })]);
14681
14746
  var _deepseek_ai_dsh_mcp_manager_mcpManager_toolsList_result$schema = external_exports.object({
14682
14747
  "ok": external_exports.literal(true).readonly(),
14683
14748
  "tools": external_exports.array(external_exports.object({
@@ -14715,6 +14780,37 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_result$schema = external_e
14715
14780
  "message": external_exports.string().readonly()
14716
14781
  }).readonly()
14717
14782
  })]);
14783
+ var _deepseek_ai_dsh_mcp_manager_mcpManager_envList_result$schema = external_exports.object({
14784
+ "ok": external_exports.literal(true).readonly(),
14785
+ "vars": external_exports.array(external_exports.object({
14786
+ "name": external_exports.string().readonly(),
14787
+ "secret": external_exports.boolean().readonly(),
14788
+ "configured": external_exports.boolean().readonly(),
14789
+ "value": external_exports.string().readonly().optional()
14790
+ })).readonly()
14791
+ });
14792
+ var _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_parameter_0$schema = external_exports.object({
14793
+ "vars": external_exports.array(external_exports.object({
14794
+ "name": external_exports.string().readonly(),
14795
+ "secret": external_exports.boolean().readonly().optional(),
14796
+ "value": external_exports.string().readonly().optional()
14797
+ })).readonly()
14798
+ });
14799
+ var _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_result$schema = external_exports.union([external_exports.object({
14800
+ "ok": external_exports.literal(true).readonly(),
14801
+ "vars": external_exports.array(external_exports.object({
14802
+ "name": external_exports.string().readonly(),
14803
+ "secret": external_exports.boolean().readonly(),
14804
+ "configured": external_exports.boolean().readonly(),
14805
+ "value": external_exports.string().readonly().optional()
14806
+ })).readonly()
14807
+ }), external_exports.object({
14808
+ "ok": external_exports.literal(false).readonly(),
14809
+ "error": external_exports.object({
14810
+ "code": external_exports.union([external_exports.literal("MCP_INVALID_SPEC")]).readonly(),
14811
+ "message": external_exports.string().readonly()
14812
+ }).readonly()
14813
+ })]);
14718
14814
  var TYPERT_REMOTE = {
14719
14815
  package: "@deepseek-ai/dsh-mcp-manager",
14720
14816
  descriptors: [
@@ -14807,6 +14903,31 @@ var TYPERT_REMOTE = {
14807
14903
  },
14808
14904
  sourceLocation: { "file": "packages/mcp/mcp-manager/src/index.ts", "line": 329, "column": 9 }
14809
14905
  },
14906
+ {
14907
+ id: "@deepseek-ai/dsh-mcp-manager#mcpManager/upsertJson",
14908
+ service: "mcpManager",
14909
+ namespace: "mcpManager",
14910
+ method: "upsertJson",
14911
+ invocation: { kind: "direct" },
14912
+ parameters: [
14913
+ {
14914
+ name: "request",
14915
+ wire: "request",
14916
+ source: "json",
14917
+ codec: {
14918
+ mode: "strict",
14919
+ typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertJsonRequest",
14920
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema
14921
+ }
14922
+ }
14923
+ ],
14924
+ result: {
14925
+ mode: "strict",
14926
+ typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertJsonResult",
14927
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema
14928
+ },
14929
+ sourceLocation: { "file": "lib/index.js" }
14930
+ },
14810
14931
  {
14811
14932
  id: "@deepseek-ai/dsh-mcp-manager#mcpManager/toolsList",
14812
14933
  service: "mcpManager",
@@ -14870,154 +14991,56 @@ var TYPERT_REMOTE = {
14870
14991
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_result$schema
14871
14992
  },
14872
14993
  sourceLocation: { "file": "lib/index.js" }
14994
+ },
14995
+ {
14996
+ id: "@deepseek-ai/dsh-mcp-manager#mcpManager/envList",
14997
+ service: "mcpManager",
14998
+ namespace: "mcpManager",
14999
+ method: "envList",
15000
+ invocation: { kind: "direct" },
15001
+ parameters: [],
15002
+ result: {
15003
+ mode: "strict",
15004
+ typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvListResult",
15005
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envList_result$schema
15006
+ },
15007
+ sourceLocation: { "file": "lib/index.js" }
15008
+ },
15009
+ {
15010
+ id: "@deepseek-ai/dsh-mcp-manager#mcpManager/envSet",
15011
+ service: "mcpManager",
15012
+ namespace: "mcpManager",
15013
+ method: "envSet",
15014
+ invocation: { kind: "direct" },
15015
+ parameters: [
15016
+ {
15017
+ name: "request",
15018
+ wire: "request",
15019
+ source: "json",
15020
+ codec: {
15021
+ mode: "strict",
15022
+ typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvSetRequest",
15023
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_parameter_0$schema
15024
+ }
15025
+ }
15026
+ ],
15027
+ result: {
15028
+ mode: "strict",
15029
+ typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvSetResult",
15030
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_result$schema
15031
+ },
15032
+ sourceLocation: { "file": "lib/index.js" }
14873
15033
  }
14874
15034
  ]
14875
15035
  };
14876
15036
  var remote_contribution_default = TYPERT_REMOTE;
14877
15037
 
14878
15038
  // src/client/McpSettingsSection.tsx
14879
- var import_react2 = require("react");
15039
+ var import_react4 = require("react");
14880
15040
 
14881
15041
  // src/client/ServerForm.tsx
14882
15042
  var import_react = require("react");
14883
15043
 
14884
- // src/client/mcp-store.ts
14885
- var import_client = require("@deepseek-ai/dsh-client-runtime/client");
14886
- var DEFAULT_TOOL_CALL_TIMEOUT_MS = 6e4;
14887
- var envRowSeq = 0;
14888
- function nextEnvRowKey() {
14889
- envRowSeq += 1;
14890
- return `env-${envRowSeq}`;
14891
- }
14892
- function createEnvRowDraft() {
14893
- return { key: nextEnvRowKey(), name: "", secret: false, value: "", configured: false };
14894
- }
14895
- function emptyDraft() {
14896
- return {
14897
- id: null,
14898
- serverName: "",
14899
- transport: "stdio",
14900
- enabled: true,
14901
- command: "",
14902
- argsText: "",
14903
- cwd: "",
14904
- url: "",
14905
- headersText: "",
14906
- env: [],
14907
- toolCallTimeoutMs: String(DEFAULT_TOOL_CALL_TIMEOUT_MS),
14908
- // Reject the mount by default when the startup connection fails, so a
14909
- // broken server never registers stale tools.
14910
- failOnStartupError: true
14911
- };
14912
- }
14913
- function draftFromServer(server) {
14914
- return {
14915
- id: server.id,
14916
- serverName: server.serverName,
14917
- transport: server.transport,
14918
- enabled: server.enabled,
14919
- command: server.command,
14920
- argsText: server.args.join("\n"),
14921
- cwd: server.cwd,
14922
- url: server.url,
14923
- headersText: server.headers.map((header) => `${header.name}: ${header.value}`).join("\n"),
14924
- env: server.env.map((entry) => ({
14925
- key: nextEnvRowKey(),
14926
- name: entry.name,
14927
- secret: entry.secret,
14928
- value: "",
14929
- configured: entry.configured
14930
- })),
14931
- toolCallTimeoutMs: String(server.toolCallTimeoutMs),
14932
- failOnStartupError: server.failOnStartupError
14933
- };
14934
- }
14935
- function parseLines(text) {
14936
- return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
14937
- }
14938
- function parseHeaders(text) {
14939
- const headers = [];
14940
- for (const line of parseLines(text)) {
14941
- const separator = line.indexOf(":");
14942
- if (separator <= 0) continue;
14943
- const name = line.slice(0, separator).trim();
14944
- const value = line.slice(separator + 1).trim();
14945
- if (name.length > 0) headers.push({ name, value });
14946
- }
14947
- return headers;
14948
- }
14949
- function parseTimeout(text) {
14950
- const value = Number(text);
14951
- return Number.isInteger(value) && value > 0 ? value : DEFAULT_TOOL_CALL_TIMEOUT_MS;
14952
- }
14953
- function draftToSubmission(draft) {
14954
- return {
14955
- server: {
14956
- serverName: draft.serverName.trim(),
14957
- transport: draft.transport,
14958
- enabled: draft.enabled,
14959
- command: draft.command.trim(),
14960
- args: parseLines(draft.argsText),
14961
- cwd: draft.cwd.trim(),
14962
- url: draft.url.trim(),
14963
- headers: parseHeaders(draft.headersText),
14964
- toolCallTimeoutMs: parseTimeout(draft.toolCallTimeoutMs),
14965
- failOnStartupError: draft.failOnStartupError
14966
- },
14967
- env: draft.env.map((row) => ({
14968
- name: row.name.trim(),
14969
- secret: row.secret,
14970
- ...row.secret && row.value.trim().length === 0 ? {} : { value: row.value }
14971
- }))
14972
- };
14973
- }
14974
- function createMcpManagerStore() {
14975
- return (0, import_client.defineStore)({
14976
- init: () => ({
14977
- loadState: "loading",
14978
- servers: [],
14979
- draft: null,
14980
- busy: null,
14981
- testRunning: false,
14982
- test: null
14983
- }),
14984
- actions: {
14985
- setLoadState: (d, state) => {
14986
- d.loadState = state;
14987
- },
14988
- setServers: (d, servers) => {
14989
- d.servers = servers;
14990
- },
14991
- beginCreate: (d) => {
14992
- d.draft = emptyDraft();
14993
- d.test = null;
14994
- },
14995
- beginEdit: (d, server) => {
14996
- d.draft = draftFromServer(server);
14997
- d.test = null;
14998
- },
14999
- cancelEdit: (d) => {
15000
- d.draft = null;
15001
- d.test = null;
15002
- d.busy = null;
15003
- },
15004
- updateDraft: (d, patch) => {
15005
- if (d.draft === null) return;
15006
- d.draft = { ...d.draft, ...patch };
15007
- },
15008
- setBusy: (d, busy) => {
15009
- d.busy = busy;
15010
- },
15011
- setTestRunning: (d, running) => {
15012
- d.testRunning = running;
15013
- },
15014
- setTest: (d, test) => {
15015
- d.test = test;
15016
- }
15017
- }
15018
- });
15019
- }
15020
-
15021
15044
  // src/client/ServerForm.module.css
15022
15045
  (function() {
15023
15046
  if (typeof document !== "undefined") {
@@ -15026,12 +15049,12 @@ function createMcpManagerStore() {
15026
15049
  const style = document.createElement("style");
15027
15050
  style.setAttribute("data-plugin", "dsh-mcp");
15028
15051
  style.setAttribute("data-file", "ServerForm.module.css");
15029
- style.textContent = ".form {\n display: flex;\n flex-direction: column;\n gap: 14px;\n width: 100%;\n max-width: 760px;\n color: var(--dsw-alias-label-primary);\n}\n\n.titleRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.title {\n margin: 0;\n font-size: 15px;\n line-height: 22px;\n font-weight: 600;\n}\n\n.titleActions {\n display: flex;\n gap: 8px;\n}\n\nbutton {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 5px 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n cursor: pointer;\n}\n\nbutton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\nbutton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\nbutton:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n.primary {\n border-color: var(--dsw-alias-button-primary-fill);\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.primary:hover {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n.danger {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.error {\n margin: 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.field input,\n.field select,\n.field textarea {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 6px 10px;\n outline: none;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n resize: vertical;\n}\n\n.field input::placeholder,\n.field textarea::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.field input:focus-visible,\n.field select:focus-visible,\n.field textarea:focus-visible {\n border-color: var(--dsw-alias-brand-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent);\n}\n\n.envBlock {\n display: flex;\n flex-direction: column;\n gap: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 10px 12px 12px;\n margin: 0;\n}\n\n.envBlock legend {\n padding: 0 4px;\n font-size: 13px;\n line-height: 20px;\n font-weight: 600;\n}\n\n.hint,\n.muted {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.envRow {\n display: grid;\n grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.6fr) auto auto;\n align-items: center;\n gap: 8px;\n}\n\n.envRow input {\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 5px 8px;\n outline: none;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n}\n\n.secretToggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 18px;\n white-space: nowrap;\n cursor: pointer;\n}\n\n.checkRow {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n.testResult {\n margin: 0;\n font-size: 13px;\n line-height: 20px;\n}\n\n.testOk {\n color: var(--dsw-alias-state-success-primary);\n}\n\n.testFail {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n";
15052
+ style.textContent = ".form {\n display: flex;\n flex-direction: column;\n gap: 14px;\n width: 100%;\n max-width: 760px;\n color: var(--dsw-alias-label-primary);\n}\n\n.titleRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.title {\n margin: 0;\n font-size: 15px;\n line-height: 22px;\n font-weight: 600;\n}\n\n.titleActions {\n display: flex;\n gap: 8px;\n}\n\nbutton {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 5px 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n cursor: pointer;\n}\n\nbutton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\nbutton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\nbutton:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n.primary {\n border-color: var(--dsw-alias-button-primary-fill);\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.primary:hover {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n.danger {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.error {\n margin: 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.field input,\n.field select,\n.field textarea {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 6px 10px;\n outline: none;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n resize: vertical;\n}\n\n.field input::placeholder,\n.field textarea::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.field input:focus-visible,\n.field select:focus-visible,\n.field textarea:focus-visible {\n border-color: var(--dsw-alias-brand-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent);\n}\n\n.hint,\n.muted {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.checkRow {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n.testResult {\n margin: 0;\n font-size: 13px;\n line-height: 20px;\n}\n\n.testOk {\n color: var(--dsw-alias-state-success-primary);\n}\n\n.testFail {\n color: var(--dsw-alias-state-error-primary);\n}\n\n.actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n";
15030
15053
  document.head.appendChild(style);
15031
15054
  }
15032
15055
  }
15033
15056
  })();
15034
- var ServerForm_default = { "form": "form", "titleRow": "titleRow", "title": "title", "titleActions": "titleActions", "primary": "primary", "danger": "danger", "error": "error", "field": "field", "envBlock": "envBlock", "hint": "hint", "muted": "muted", "envRow": "envRow", "secretToggle": "secretToggle", "checkRow": "checkRow", "testResult": "testResult", "testOk": "testOk", "testFail": "testFail", "actions": "actions" };
15057
+ var ServerForm_default = { "form": "form", "titleRow": "titleRow", "title": "title", "titleActions": "titleActions", "primary": "primary", "danger": "danger", "error": "error", "field": "field", "hint": "hint", "muted": "muted", "checkRow": "checkRow", "testResult": "testResult", "testOk": "testOk", "testFail": "testFail", "actions": "actions" };
15035
15058
 
15036
15059
  // src/client/ServerForm.tsx
15037
15060
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -15042,15 +15065,6 @@ function McpServerForm(props) {
15042
15065
  setSaveError(null);
15043
15066
  actions.updateDraft(patch);
15044
15067
  };
15045
- const updateEnvRow = (key, patch) => {
15046
- update({ env: draft.env.map((row) => row.key === key ? { ...row, ...patch } : row) });
15047
- };
15048
- const removeEnvRow = (key) => {
15049
- update({ env: draft.env.filter((row) => row.key !== key) });
15050
- };
15051
- const addEnvRow = () => {
15052
- update({ env: [...draft.env, createEnvRowDraft()] });
15053
- };
15054
15068
  const errorText = (error51) => error51 instanceof Error ? error51.message : String(error51);
15055
15069
  const runTest = async () => {
15056
15070
  setSaveError(null);
@@ -15216,52 +15230,6 @@ function McpServerForm(props) {
15216
15230
  )
15217
15231
  ] })
15218
15232
  ] }),
15219
- stdio ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className: ServerForm_default.envBlock, children: [
15220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("legend", { children: t("envVars") }),
15221
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: ServerForm_default.hint, children: t("envHint") }),
15222
- draft.env.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: ServerForm_default.muted, children: [
15223
- t("envVars"),
15224
- ": 0"
15225
- ] }) : null,
15226
- draft.env.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: ServerForm_default.envRow, children: [
15227
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
15228
- "input",
15229
- {
15230
- type: "text",
15231
- className: ServerForm_default.envName,
15232
- value: row.name,
15233
- placeholder: t("envName"),
15234
- "aria-label": t("envName"),
15235
- onChange: (event) => updateEnvRow(row.key, { name: event.currentTarget.value })
15236
- }
15237
- ),
15238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
15239
- "input",
15240
- {
15241
- type: row.secret ? "password" : "text",
15242
- className: ServerForm_default.envValue,
15243
- value: row.value,
15244
- placeholder: row.secret && row.configured ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : t("envValue"),
15245
- "aria-label": t("envValue"),
15246
- onChange: (event) => updateEnvRow(row.key, { value: event.currentTarget.value })
15247
- }
15248
- ),
15249
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: ServerForm_default.secretToggle, children: [
15250
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
15251
- "input",
15252
- {
15253
- type: "checkbox",
15254
- checked: row.secret,
15255
- title: t("envSecretHint"),
15256
- onChange: (event) => updateEnvRow(row.key, { secret: event.currentTarget.checked })
15257
- }
15258
- ),
15259
- t("envSecret")
15260
- ] }),
15261
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: ServerForm_default.danger, "aria-label": t("removeEnvVar"), onClick: () => removeEnvRow(row.key), children: "\u2715" })
15262
- ] }, row.key)),
15263
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: addEnvRow, children: t("addEnvVar") })
15264
- ] }) : null,
15265
15233
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: ServerForm_default.field, children: [
15266
15234
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("toolCallTimeoutMs") }),
15267
15235
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -15306,6 +15274,419 @@ function McpServerForm(props) {
15306
15274
  ] });
15307
15275
  }
15308
15276
 
15277
+ // src/client/ServersJsonEditor.tsx
15278
+ var import_react2 = require("react");
15279
+
15280
+ // src/client/ServersJsonEditor.module.css
15281
+ (function() {
15282
+ if (typeof document !== "undefined") {
15283
+ const existing = document.querySelector('style[data-plugin="dsh-mcp"][data-file="ServersJsonEditor.module.css"]');
15284
+ if (!existing) {
15285
+ const style = document.createElement("style");
15286
+ style.setAttribute("data-plugin", "dsh-mcp");
15287
+ style.setAttribute("data-file", "ServersJsonEditor.module.css");
15288
+ style.textContent = ".panel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 10px 14px;\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.editor {\n width: 100%;\n box-sizing: border-box;\n min-height: 260px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 8px 10px;\n outline: none;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n resize: vertical;\n}\n\n.editor:focus-visible {\n border-color: var(--dsw-alias-brand-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent);\n}\n\n.hint {\n margin: 0;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.error {\n margin: 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 12px;\n line-height: 18px;\n word-break: break-all;\n}\n\n.notice {\n margin: 0;\n color: var(--dsw-alias-state-success-primary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n\n.actions button {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 5px 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n cursor: pointer;\n}\n\n.actions button:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.actions button:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n.actions .primary {\n border-color: var(--dsw-alias-button-primary-fill);\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.actions .primary:hover {\n background: var(--dsw-alias-button-primary-hover);\n}\n";
15289
+ document.head.appendChild(style);
15290
+ }
15291
+ }
15292
+ })();
15293
+ var ServersJsonEditor_default = { "panel": "panel", "editor": "editor", "hint": "hint", "error": "error", "notice": "notice", "actions": "actions", "primary": "primary" };
15294
+
15295
+ // src/client/ServersJsonEditor.tsx
15296
+ var import_jsx_runtime2 = require("react/jsx-runtime");
15297
+ function serversToJsonText(servers) {
15298
+ const document2 = servers.map((server) => {
15299
+ const entry = {
15300
+ serverName: server.serverName,
15301
+ transport: server.transport,
15302
+ enabled: server.enabled
15303
+ };
15304
+ if (server.transport === "stdio") {
15305
+ entry.command = server.command;
15306
+ entry.args = [...server.args];
15307
+ if (server.cwd.length > 0) entry.cwd = server.cwd;
15308
+ } else {
15309
+ entry.url = server.url;
15310
+ }
15311
+ if (server.headers.length > 0) {
15312
+ entry.headers = Object.fromEntries(server.headers.map((header) => [header.name, header.value]));
15313
+ }
15314
+ entry.toolCallTimeoutMs = server.toolCallTimeoutMs;
15315
+ entry.failOnStartupError = server.failOnStartupError;
15316
+ entry.env = server.env.map((row) => row.secret ? { name: row.name, secret: true, configured: row.configured } : { name: row.name, secret: false, ...row.value !== void 0 && row.value.length > 0 ? { value: row.value } : {} });
15317
+ return entry;
15318
+ });
15319
+ return `${JSON.stringify(document2, null, 2)}
15320
+ `;
15321
+ }
15322
+ function parseServersJson(text) {
15323
+ let value;
15324
+ try {
15325
+ value = JSON.parse(text);
15326
+ } catch (error51) {
15327
+ return { ok: false, error: error51 instanceof Error ? error51.message : String(error51) };
15328
+ }
15329
+ if (!Array.isArray(value)) {
15330
+ return { ok: false, error: "JSON \u9876\u5C42\u5FC5\u987B\u662F\u670D\u52A1\u5668\u914D\u7F6E\u6570\u7EC4\uFF1A[ { serverName, transport, ... }, ... ]" };
15331
+ }
15332
+ if (value.length === 0) {
15333
+ return { ok: false, error: "\u914D\u7F6E\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A" };
15334
+ }
15335
+ const servers = [];
15336
+ const seen = /* @__PURE__ */ new Set();
15337
+ for (const item of value) {
15338
+ if (item === null || typeof item !== "object" || Array.isArray(item)) {
15339
+ return { ok: false, error: "\u6BCF\u4E2A\u6761\u76EE\u5FC5\u987B\u662F\u5BF9\u8C61\uFF1A{ serverName, transport, ... }" };
15340
+ }
15341
+ const server = item;
15342
+ const serverName = typeof server.serverName === "string" ? server.serverName.trim() : "";
15343
+ if (serverName.length === 0) return { ok: false, error: "serverName \u4E0D\u80FD\u4E3A\u7A7A" };
15344
+ if (seen.has(serverName)) return { ok: false, error: `serverName "${serverName}" \u91CD\u590D` };
15345
+ seen.add(serverName);
15346
+ const transport = server.transport;
15347
+ if (transport !== "stdio" && transport !== "streamable-http") {
15348
+ return { ok: false, error: `\u670D\u52A1\u5668 "${serverName}" \u7684 transport \u5FC5\u987B\u662F stdio \u6216 streamable-http` };
15349
+ }
15350
+ if (transport === "stdio" && (typeof server.command !== "string" || server.command.trim().length === 0)) {
15351
+ return { ok: false, error: `\u670D\u52A1\u5668 "${serverName}"\uFF08stdio\uFF09\u7F3A\u5C11 command` };
15352
+ }
15353
+ if (transport === "streamable-http" && typeof server.url !== "string") {
15354
+ return { ok: false, error: `\u670D\u52A1\u5668 "${serverName}"\uFF08streamable-http\uFF09\u7F3A\u5C11 url` };
15355
+ }
15356
+ if (server.headers !== void 0) {
15357
+ const headers = server.headers;
15358
+ const okHeaders = headers !== null && typeof headers === "object" && !Array.isArray(headers) || Array.isArray(headers) && headers.every((h) => h !== null && typeof h === "object" && typeof h.name === "string");
15359
+ if (!okHeaders) return { ok: false, error: `\u670D\u52A1\u5668 "${serverName}" \u7684 headers \u5FC5\u987B\u662F { "\u540D\u79F0": "\u503C" } \u5BF9\u8C61\u6216 { name, value } \u6570\u7EC4` };
15360
+ }
15361
+ const envValue = server.env;
15362
+ const env = Array.isArray(envValue) ? envValue : [];
15363
+ for (const row of env) {
15364
+ const r = row;
15365
+ if (r === null || typeof r !== "object" || typeof r.name !== "string" || r.name.trim().length === 0) {
15366
+ return { ok: false, error: `\u670D\u52A1\u5668 "${serverName}" \u7684 env \u6761\u76EE\u7F3A\u5C11\u5408\u6CD5\u7684 name` };
15367
+ }
15368
+ if (r.secret !== void 0 && typeof r.secret !== "boolean") {
15369
+ return { ok: false, error: `\u670D\u52A1\u5668 "${serverName}" \u7684 env \u6761\u76EE "${r.name}" \u7684 secret \u5FC5\u987B\u662F\u5E03\u5C14\u503C` };
15370
+ }
15371
+ if (r.value !== void 0 && typeof r.value !== "string") {
15372
+ return { ok: false, error: `\u670D\u52A1\u5668 "${serverName}" \u7684 env \u6761\u76EE "${r.name}" \u7684 value \u5FC5\u987B\u662F\u5B57\u7B26\u4E32` };
15373
+ }
15374
+ }
15375
+ servers.push({ server, env });
15376
+ }
15377
+ return { ok: true, servers };
15378
+ }
15379
+ function ServersJsonEditor({ injected, t, onApplied }) {
15380
+ const [text, setText] = (0, import_react2.useState)("");
15381
+ const [error51, setError] = (0, import_react2.useState)(null);
15382
+ const [notice, setNotice] = (0, import_react2.useState)(null);
15383
+ const [busy, setBusy] = (0, import_react2.useState)(false);
15384
+ const [loaded, setLoaded] = (0, import_react2.useState)(false);
15385
+ const seqRef = (0, import_react2.useRef)(0);
15386
+ (0, import_react2.useEffect)(() => {
15387
+ if (loaded) return;
15388
+ let current = true;
15389
+ void injected.list().then(
15390
+ (servers) => {
15391
+ if (!current) return;
15392
+ setText(serversToJsonText(servers));
15393
+ setLoaded(true);
15394
+ setError(null);
15395
+ },
15396
+ () => {
15397
+ if (current) setError("\u65E0\u6CD5\u8BFB\u53D6\u670D\u52A1\u5668\u5217\u8868");
15398
+ }
15399
+ );
15400
+ return () => {
15401
+ current = false;
15402
+ };
15403
+ }, [injected, loaded]);
15404
+ const reseed = () => {
15405
+ const seq = ++seqRef.current;
15406
+ setBusy(true);
15407
+ setNotice(null);
15408
+ void injected.list().then(
15409
+ (servers) => {
15410
+ if (seq !== seqRef.current) return;
15411
+ setText(serversToJsonText(servers));
15412
+ setError(null);
15413
+ },
15414
+ () => {
15415
+ if (seq === seqRef.current) setError("\u65E0\u6CD5\u8BFB\u53D6\u670D\u52A1\u5668\u5217\u8868");
15416
+ }
15417
+ ).finally(() => {
15418
+ if (seq === seqRef.current) setBusy(false);
15419
+ });
15420
+ };
15421
+ const apply2 = () => {
15422
+ const parsed = parseServersJson(text);
15423
+ if (!parsed.ok) {
15424
+ setError(parsed.error);
15425
+ return;
15426
+ }
15427
+ const seq = ++seqRef.current;
15428
+ setBusy(true);
15429
+ setError(null);
15430
+ setNotice(null);
15431
+ void injected.upsertJson(parsed.servers).then(
15432
+ (result) => {
15433
+ if (seq !== seqRef.current) return;
15434
+ setNotice(`${t("serversJsonDone")}: +${result.added} / ~${result.updated} / -${result.removed}`);
15435
+ onApplied();
15436
+ },
15437
+ (error52) => {
15438
+ if (seq !== seqRef.current) return;
15439
+ setError(error52 instanceof Error ? error52.message : String(error52));
15440
+ }
15441
+ ).finally(() => {
15442
+ if (seq === seqRef.current) setBusy(false);
15443
+ });
15444
+ };
15445
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: ServersJsonEditor_default.panel, children: [
15446
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
15447
+ "textarea",
15448
+ {
15449
+ className: ServersJsonEditor_default.editor,
15450
+ rows: 16,
15451
+ spellCheck: false,
15452
+ "aria-label": t("serversJsonLabel"),
15453
+ value: text,
15454
+ onChange: (event) => setText(event.currentTarget.value)
15455
+ }
15456
+ ),
15457
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: ServersJsonEditor_default.hint, children: t("serversJsonHint") }),
15458
+ error51 !== null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: ServersJsonEditor_default.error, role: "alert", children: error51 }) : null,
15459
+ notice !== null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: ServersJsonEditor_default.notice, role: "status", children: notice }) : null,
15460
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: ServersJsonEditor_default.actions, children: [
15461
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: busy, onClick: reseed, children: t("serversJsonReseed") }),
15462
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: ServersJsonEditor_default.primary, disabled: busy, onClick: apply2, children: busy ? t("serversJsonSaving") : t("serversJsonApply") })
15463
+ ] })
15464
+ ] });
15465
+ }
15466
+
15467
+ // src/client/GlobalEnvEditor.tsx
15468
+ var import_react3 = require("react");
15469
+
15470
+ // src/client/GlobalEnvEditor.module.css
15471
+ (function() {
15472
+ if (typeof document !== "undefined") {
15473
+ const existing = document.querySelector('style[data-plugin="dsh-mcp"][data-file="GlobalEnvEditor.module.css"]');
15474
+ if (!existing) {
15475
+ const style = document.createElement("style");
15476
+ style.setAttribute("data-plugin", "dsh-mcp");
15477
+ style.setAttribute("data-file", "GlobalEnvEditor.module.css");
15478
+ style.textContent = ".panel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 10px 14px;\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.hint,\n.muted {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.row {\n display: grid;\n grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.8fr) auto auto;\n align-items: center;\n gap: 8px;\n}\n\n.row input {\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 5px 8px;\n outline: none;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n}\n\n.row input:focus-visible {\n border-color: var(--dsw-alias-brand-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent);\n}\n\n.name {\n width: 100%;\n}\n\n.value {\n width: 100%;\n}\n\n.secretToggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 18px;\n white-space: nowrap;\n cursor: pointer;\n user-select: none;\n}\n\n.secretToggle input {\n cursor: pointer;\n}\n\n.secretToggle.secretOn {\n color: var(--dsw-alias-brand-primary);\n font-weight: 600;\n}\n\n.danger {\n border: 1px solid transparent !important;\n background: transparent !important;\n color: var(--dsw-alias-state-error-primary) !important;\n cursor: pointer;\n}\n\n.overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.4);\n}\n\n.dialog {\n display: flex;\n flex-direction: column;\n gap: 8px;\n width: min(520px, calc(100vw - 48px));\n max-height: 80vh;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n padding: 16px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);\n}\n\n.dialogTitle {\n margin: 0;\n font-size: 15px;\n line-height: 22px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.bulkText {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 8px 10px;\n outline: none;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n resize: vertical;\n}\n\n.bulkText:focus-visible {\n border-color: var(--dsw-alias-brand-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent);\n}\n\n.retryRow {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.retryRow button {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 3px 10px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n}\n\n.retryRow button:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.error {\n margin: 0;\n color: var(--dsw-alias-state-error-primary);\n font-size: 12px;\n line-height: 18px;\n word-break: break-all;\n}\n\n.notice {\n margin: 0;\n color: var(--dsw-alias-state-success-primary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n\n.actions button {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 5px 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n cursor: pointer;\n}\n\n.actions button:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.actions button:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n.actions .primary {\n border-color: var(--dsw-alias-button-primary-fill);\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.actions .primary:hover {\n background: var(--dsw-alias-button-primary-hover);\n}\n";
15479
+ document.head.appendChild(style);
15480
+ }
15481
+ }
15482
+ })();
15483
+ var GlobalEnvEditor_default = { "panel": "panel", "hint": "hint", "muted": "muted", "row": "row", "name": "name", "value": "value", "secretToggle": "secretToggle", "secretOn": "secretOn", "danger": "danger", "overlay": "overlay", "dialog": "dialog", "dialogTitle": "dialogTitle", "bulkText": "bulkText", "retryRow": "retryRow", "error": "error", "notice": "notice", "actions": "actions", "primary": "primary" };
15484
+
15485
+ // src/client/GlobalEnvEditor.tsx
15486
+ var import_jsx_runtime3 = require("react/jsx-runtime");
15487
+ var rowSeq = 0;
15488
+ function nextKey() {
15489
+ rowSeq += 1;
15490
+ return `genv-${rowSeq}`;
15491
+ }
15492
+ function emptyRow() {
15493
+ return { key: nextKey(), name: "", secret: true, value: "", configured: false };
15494
+ }
15495
+ function GlobalEnvEditor({ injected, t, onApplied }) {
15496
+ const [rows, setRows] = (0, import_react3.useState)([]);
15497
+ const [error51, setError] = (0, import_react3.useState)(null);
15498
+ const [notice, setNotice] = (0, import_react3.useState)(null);
15499
+ const [busy, setBusy] = (0, import_react3.useState)(false);
15500
+ const [loaded, setLoaded] = (0, import_react3.useState)(false);
15501
+ const [loadFailed, setLoadFailed] = (0, import_react3.useState)(false);
15502
+ const [bulkOpen, setBulkOpen] = (0, import_react3.useState)(false);
15503
+ const [bulkText, setBulkText] = (0, import_react3.useState)("");
15504
+ const seqRef = (0, import_react3.useRef)(0);
15505
+ const reload = () => {
15506
+ const seq = ++seqRef.current;
15507
+ setLoadFailed(false);
15508
+ void injected.envList().then(
15509
+ (state) => {
15510
+ if (seq !== seqRef.current) return;
15511
+ setRows(state.vars.map((row) => ({
15512
+ key: nextKey(),
15513
+ name: row.name,
15514
+ secret: row.secret,
15515
+ // 展示 process.env 优先返回的值(非 secret);secret 值不展示
15516
+ value: row.secret ? "" : row.value ?? "",
15517
+ configured: row.configured
15518
+ })));
15519
+ setLoaded(true);
15520
+ setError(null);
15521
+ },
15522
+ (error52) => {
15523
+ if (seq !== seqRef.current) return;
15524
+ setLoadFailed(true);
15525
+ setError(error52 instanceof Error ? error52.message : String(error52));
15526
+ }
15527
+ );
15528
+ };
15529
+ (0, import_react3.useEffect)(() => {
15530
+ if (loaded) return;
15531
+ reload();
15532
+ }, [injected, loaded]);
15533
+ const update = (key, patch) => {
15534
+ setRows((prev) => prev.map((row) => row.key === key ? { ...row, ...patch } : row));
15535
+ };
15536
+ const remove = (key) => {
15537
+ setRows((prev) => prev.filter((row) => row.key !== key));
15538
+ };
15539
+ const add = () => {
15540
+ setRows((prev) => [...prev, emptyRow()]);
15541
+ };
15542
+ const applyBulk = () => {
15543
+ const parsed = bulkText.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
15544
+ if (parsed.length === 0) {
15545
+ setBulkOpen(false);
15546
+ return;
15547
+ }
15548
+ const next = [];
15549
+ for (const line of parsed) {
15550
+ const eq = line.indexOf("=");
15551
+ if (eq <= 0) {
15552
+ next.push({ key: nextKey(), name: line, secret: false, value: "", configured: false });
15553
+ continue;
15554
+ }
15555
+ const name = line.slice(0, eq).trim();
15556
+ const value = line.slice(eq + 1);
15557
+ if (name.length === 0) continue;
15558
+ next.push({ key: nextKey(), name, secret: false, value, configured: false });
15559
+ }
15560
+ if (next.length > 0) setRows((prev) => [...prev, ...next]);
15561
+ setBulkOpen(false);
15562
+ setBulkText("");
15563
+ };
15564
+ const save = () => {
15565
+ const vars = [];
15566
+ const seen = /* @__PURE__ */ new Set();
15567
+ for (const row of rows) {
15568
+ const name = row.name.trim();
15569
+ if (name.length === 0) {
15570
+ if (row.value.trim().length > 0) {
15571
+ setError("\u5B58\u5728\u672A\u586B\u5199\u53D8\u91CF\u540D\u7684\u884C");
15572
+ return;
15573
+ }
15574
+ continue;
15575
+ }
15576
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
15577
+ setError(`\u53D8\u91CF\u540D "${name}" \u5FC5\u987B\u662F\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF\uFF0C\u4E14\u4EE5\u5B57\u6BCD\u6216\u4E0B\u5212\u7EBF\u5F00\u5934`);
15578
+ return;
15579
+ }
15580
+ if (seen.has(name)) {
15581
+ setError(`\u53D8\u91CF\u540D "${name}" \u91CD\u590D`);
15582
+ return;
15583
+ }
15584
+ seen.add(name);
15585
+ vars.push({
15586
+ name,
15587
+ secret: row.secret,
15588
+ // 填了值就提交(secret 也会写入凭据文档);留空不提交(secret 保留原值)
15589
+ ...row.value.trim().length > 0 ? { value: row.value } : {}
15590
+ });
15591
+ }
15592
+ const seq = ++seqRef.current;
15593
+ setBusy(true);
15594
+ setError(null);
15595
+ setNotice(null);
15596
+ void injected.envSet(vars).then(
15597
+ (state) => {
15598
+ if (seq !== seqRef.current) return;
15599
+ setRows(state.vars.map((row) => ({
15600
+ key: nextKey(),
15601
+ name: row.name,
15602
+ secret: row.secret,
15603
+ value: row.secret ? "" : row.value ?? "",
15604
+ configured: row.configured
15605
+ })));
15606
+ setNotice(t("globalEnvDone"));
15607
+ onApplied();
15608
+ },
15609
+ (error52) => {
15610
+ if (seq !== seqRef.current) return;
15611
+ setError(error52 instanceof Error ? error52.message : String(error52));
15612
+ }
15613
+ ).finally(() => {
15614
+ if (seq === seqRef.current) setBusy(false);
15615
+ });
15616
+ };
15617
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: GlobalEnvEditor_default.panel, children: [
15618
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: GlobalEnvEditor_default.hint, children: t("globalEnvHint") }),
15619
+ rows.length === 0 && !loadFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: GlobalEnvEditor_default.muted, children: t("globalEnvEmpty") }) : null,
15620
+ rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: GlobalEnvEditor_default.row, children: [
15621
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
15622
+ "input",
15623
+ {
15624
+ type: "text",
15625
+ className: GlobalEnvEditor_default.name,
15626
+ value: row.name,
15627
+ placeholder: t("globalEnvName"),
15628
+ "aria-label": t("globalEnvName"),
15629
+ onChange: (event) => update(row.key, { name: event.currentTarget.value })
15630
+ }
15631
+ ),
15632
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
15633
+ "input",
15634
+ {
15635
+ type: row.secret ? "password" : "text",
15636
+ className: GlobalEnvEditor_default.value,
15637
+ value: row.value,
15638
+ placeholder: row.secret && row.configured ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : t("globalEnvValue"),
15639
+ "aria-label": t("globalEnvValue"),
15640
+ onChange: (event) => update(row.key, { value: event.currentTarget.value })
15641
+ }
15642
+ ),
15643
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: `${GlobalEnvEditor_default.secretToggle} ${row.secret ? GlobalEnvEditor_default.secretOn : ""}`, title: t("globalEnvSecretHint"), children: [
15644
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
15645
+ "input",
15646
+ {
15647
+ type: "checkbox",
15648
+ checked: row.secret,
15649
+ "aria-label": t("globalEnvSecret"),
15650
+ onChange: (event) => update(row.key, { secret: event.currentTarget.checked })
15651
+ }
15652
+ ),
15653
+ t("globalEnvSecret")
15654
+ ] }),
15655
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: GlobalEnvEditor_default.danger, "aria-label": t("globalEnvRemove"), onClick: () => remove(row.key), children: "\u2715" })
15656
+ ] }, row.key)),
15657
+ loadFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: GlobalEnvEditor_default.retryRow, children: [
15658
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: GlobalEnvEditor_default.error, role: "alert", children: t("globalEnvLoadFailed") }),
15659
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: reload, children: t("globalEnvRetry") })
15660
+ ] }) : null,
15661
+ error51 !== null && !loadFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: GlobalEnvEditor_default.error, role: "alert", children: error51 }) : null,
15662
+ notice !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: GlobalEnvEditor_default.notice, role: "status", children: notice }) : null,
15663
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: GlobalEnvEditor_default.actions, children: [
15664
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", disabled: busy, onClick: add, children: t("globalEnvAdd") }),
15665
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", disabled: busy, onClick: () => setBulkOpen(true), children: t("globalEnvBulk") }),
15666
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: GlobalEnvEditor_default.primary, disabled: busy, onClick: save, children: busy ? t("globalEnvSaving") : t("globalEnvSave") })
15667
+ ] }),
15668
+ bulkOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: GlobalEnvEditor_default.overlay, onClick: () => setBulkOpen(false), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: GlobalEnvEditor_default.dialog, role: "dialog", "aria-label": t("globalEnvBulk"), onClick: (event) => event.stopPropagation(), children: [
15669
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: GlobalEnvEditor_default.dialogTitle, children: t("globalEnvBulk") }),
15670
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: GlobalEnvEditor_default.hint, children: t("globalEnvBulkPrompt") }),
15671
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
15672
+ "textarea",
15673
+ {
15674
+ className: GlobalEnvEditor_default.bulkText,
15675
+ rows: 8,
15676
+ autoFocus: true,
15677
+ spellCheck: false,
15678
+ value: bulkText,
15679
+ onChange: (event) => setBulkText(event.currentTarget.value)
15680
+ }
15681
+ ),
15682
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: GlobalEnvEditor_default.actions, children: [
15683
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => setBulkOpen(false), children: t("cancel") }),
15684
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: GlobalEnvEditor_default.primary, onClick: applyBulk, children: t("globalEnvBulkOk") })
15685
+ ] })
15686
+ ] }) }) : null
15687
+ ] });
15688
+ }
15689
+
15309
15690
  // src/client/McpSettingsSection.module.css
15310
15691
  (function() {
15311
15692
  if (typeof document !== "undefined") {
@@ -15314,15 +15695,15 @@ function McpServerForm(props) {
15314
15695
  const style = document.createElement("style");
15315
15696
  style.setAttribute("data-plugin", "dsh-mcp");
15316
15697
  style.setAttribute("data-file", "McpSettingsSection.module.css");
15317
- style.textContent = ".section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n width: 100%;\n max-width: 760px;\n color: var(--dsw-alias-label-primary);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n}\n\n.status,\n.failure p {\n margin: 0;\n}\n\n.status,\n.failure {\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.failure {\n display: flex;\n align-items: center;\n gap: 10px;\n color: var(--dsw-alias-state-error-primary);\n}\n\nbutton {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 5px 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n cursor: pointer;\n}\n\nbutton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\nbutton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\nbutton:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n.primary {\n border-color: var(--dsw-alias-button-primary-fill);\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.primary:hover {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 10px 14px;\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.rowMain {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.rowTitle {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n.serverName {\n overflow: hidden;\n font-size: 14px;\n line-height: 20px;\n font-weight: 600;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.badge {\n display: inline-flex;\n align-items: center;\n min-height: 18px;\n border-radius: 5px;\n padding: 1px 6px;\n font-size: 11px;\n line-height: 16px;\n white-space: nowrap;\n}\n\n.badge.mounting {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n}\n\n.badge.live {\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent);\n color: var(--dsw-alias-state-success-primary);\n}\n\n.badge.failed {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent);\n color: var(--dsw-alias-state-error-primary);\n}\n\n.badge.stopped {\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-tertiary);\n}\n\n.muted {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.rowMeta {\n display: flex;\n gap: 14px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n font-variant-numeric: tabular-nums;\n}\n\n.rowActions {\n flex: none;\n}\n\n.card {\n display: flex;\n flex-direction: column;\n gap: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.card > .row {\n border: 0;\n border-radius: 0;\n background: transparent;\n}\n\n.modeRow {\n display: flex;\n align-items: center;\n gap: 18px;\n flex-wrap: wrap;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 8px 14px;\n background: var(--dsw-alias-bg-layer-3);\n font-size: 13px;\n line-height: 20px;\n}\n\n.modeLabel {\n font-weight: 600;\n}\n\n.modeOption {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n}\n\n.modeHint {\n margin: 0;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.toolPanel {\n border-top: 1px solid var(--dsw-alias-border-l2);\n padding: 10px 14px;\n}\n\n.toolGrid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));\n gap: 4px 14px;\n max-height: 320px;\n overflow-y: auto;\n}\n\n.toolRow {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n padding: 2px 0;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n}\n\n.toolName {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n";
15698
+ style.textContent = ".section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n width: 100%;\n max-width: 760px;\n color: var(--dsw-alias-label-primary);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n}\n\n.status,\n.failure p {\n margin: 0;\n}\n\n.status,\n.failure {\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.failure {\n display: flex;\n align-items: center;\n gap: 10px;\n color: var(--dsw-alias-state-error-primary);\n}\n\nbutton {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 5px 12px;\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n cursor: pointer;\n}\n\nbutton:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\nbutton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\nbutton:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n.primary {\n border-color: var(--dsw-alias-button-primary-fill);\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.primary:hover {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n.active {\n border-color: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-brand-primary);\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent);\n}\n\n.module {\n display: flex;\n flex-direction: column;\n gap: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 10px 14px;\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.moduleTitle {\n display: inline-flex !important;\n align-items: center;\n gap: 6px;\n width: fit-content;\n border: none !important;\n background: transparent !important;\n padding: 0 !important;\n font-weight: 600;\n font-size: 14px;\n line-height: 20px;\n color: var(--dsw-alias-label-primary) !important;\n}\n\n.moduleTitle:hover {\n background: transparent !important;\n}\n\n.caret {\n display: inline-block;\n width: 14px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.configHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.configActions {\n display: flex;\n gap: 8px;\n}\n\n.inlineForm {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 12px 14px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.inlineFormItem {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 12px 14px;\n background: var(--dsw-alias-bg-layer-2);\n list-style: none;\n}\n\n.list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 10px 14px;\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.rowMain {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.rowTitle {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n.serverName {\n overflow: hidden;\n font-size: 14px;\n line-height: 20px;\n font-weight: 600;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.badge {\n display: inline-flex;\n align-items: center;\n min-height: 18px;\n border-radius: 5px;\n padding: 1px 6px;\n font-size: 11px;\n line-height: 16px;\n white-space: nowrap;\n}\n\n.badge.mounting {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n}\n\n.badge.live {\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent);\n color: var(--dsw-alias-state-success-primary);\n}\n\n.badge.failed {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent);\n color: var(--dsw-alias-state-error-primary);\n}\n\n.badge.stopped {\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-tertiary);\n}\n\n.muted {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.rowMeta {\n display: flex;\n gap: 14px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n font-variant-numeric: tabular-nums;\n}\n\n.rowActions {\n flex: none;\n}\n\n.card {\n display: flex;\n flex-direction: column;\n gap: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.card > .row {\n border: 0;\n border-radius: 0;\n background: transparent;\n}\n\n.modeRow {\n display: flex;\n align-items: center;\n gap: 18px;\n flex-wrap: wrap;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n padding: 8px 14px;\n background: var(--dsw-alias-bg-layer-3);\n font-size: 13px;\n line-height: 20px;\n}\n\n.modeLabel {\n font-weight: 600;\n}\n\n.modeOption {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n}\n\n.modeHint {\n margin: 0;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.toolPanel {\n border-top: 1px solid var(--dsw-alias-border-l2);\n padding: 10px 14px;\n}\n\n.toolGrid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));\n gap: 4px 14px;\n max-height: 320px;\n overflow-y: auto;\n}\n\n.toolRow {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n padding: 2px 0;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n}\n\n.toolName {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n";
15318
15699
  document.head.appendChild(style);
15319
15700
  }
15320
15701
  }
15321
15702
  })();
15322
- var McpSettingsSection_default = { "section": "section", "header": "header", "status": "status", "failure": "failure", "primary": "primary", "list": "list", "row": "row", "rowMain": "rowMain", "rowTitle": "rowTitle", "serverName": "serverName", "badge": "badge", "mounting": "mounting", "live": "live", "failed": "failed", "stopped": "stopped", "muted": "muted", "rowMeta": "rowMeta", "rowActions": "rowActions", "card": "card", "modeRow": "modeRow", "modeLabel": "modeLabel", "modeOption": "modeOption", "modeHint": "modeHint", "toolPanel": "toolPanel", "toolGrid": "toolGrid", "toolRow": "toolRow", "toolName": "toolName" };
15703
+ var McpSettingsSection_default = { "section": "section", "header": "header", "status": "status", "failure": "failure", "primary": "primary", "active": "active", "module": "module", "moduleTitle": "moduleTitle", "caret": "caret", "configHeader": "configHeader", "configActions": "configActions", "inlineForm": "inlineForm", "inlineFormItem": "inlineFormItem", "list": "list", "row": "row", "rowMain": "rowMain", "rowTitle": "rowTitle", "serverName": "serverName", "badge": "badge", "mounting": "mounting", "live": "live", "failed": "failed", "stopped": "stopped", "muted": "muted", "rowMeta": "rowMeta", "rowActions": "rowActions", "card": "card", "modeRow": "modeRow", "modeLabel": "modeLabel", "modeOption": "modeOption", "modeHint": "modeHint", "toolPanel": "toolPanel", "toolGrid": "toolGrid", "toolRow": "toolRow", "toolName": "toolName" };
15323
15704
 
15324
15705
  // src/client/McpSettingsSection.tsx
15325
- var import_jsx_runtime2 = require("react/jsx-runtime");
15706
+ var import_jsx_runtime4 = require("react/jsx-runtime");
15326
15707
  function phaseKey(phase) {
15327
15708
  switch (phase) {
15328
15709
  case "mounting":
@@ -15351,13 +15732,6 @@ function viewToDraft(server) {
15351
15732
  cwd: server.cwd,
15352
15733
  url: server.url,
15353
15734
  headersText: server.headers.map((header) => `${header.name}: ${header.value}`).join("\n"),
15354
- env: server.env.map((entry, index) => ({
15355
- key: `view-${index}`,
15356
- name: entry.name,
15357
- secret: entry.secret,
15358
- value: "",
15359
- configured: entry.configured
15360
- })),
15361
15735
  toolCallTimeoutMs: String(server.toolCallTimeoutMs),
15362
15736
  failOnStartupError: server.failOnStartupError
15363
15737
  };
@@ -15367,13 +15741,16 @@ function McpSettingsSection(props) {
15367
15741
  const state = props.useStore((snapshot) => snapshot);
15368
15742
  const { setLoadState, setServers, beginCreate, beginEdit, cancelEdit, updateDraft, setBusy, setTestRunning, setTest } = props.actions;
15369
15743
  const t = props.t;
15370
- const [loadErrorDetail, setLoadErrorDetail] = (0, import_react2.useState)(null);
15371
- const [tools, setTools] = (0, import_react2.useState)(null);
15372
- const [toolsError, setToolsError] = (0, import_react2.useState)(null);
15373
- const [expanded, setExpanded] = (0, import_react2.useState)(/* @__PURE__ */ new Set());
15374
- const [refreshing, setRefreshing] = (0, import_react2.useState)(/* @__PURE__ */ new Set());
15375
- const timersRef = (0, import_react2.useRef)([]);
15376
- (0, import_react2.useEffect)(() => () => {
15744
+ const [loadErrorDetail, setLoadErrorDetail] = (0, import_react4.useState)(null);
15745
+ const [tools, setTools] = (0, import_react4.useState)(null);
15746
+ const [toolsError, setToolsError] = (0, import_react4.useState)(null);
15747
+ const [expanded, setExpanded] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
15748
+ const [refreshing, setRefreshing] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
15749
+ const [jsonOpen, setJsonOpen] = (0, import_react4.useState)(false);
15750
+ const [envOpen, setEnvOpen] = (0, import_react4.useState)(true);
15751
+ const timersRef = (0, import_react4.useRef)([]);
15752
+ let inlineForm = null;
15753
+ (0, import_react4.useEffect)(() => () => {
15377
15754
  for (const id of timersRef.current) window.clearTimeout(id);
15378
15755
  }, []);
15379
15756
  const refreshTools = () => {
@@ -15405,7 +15782,7 @@ function McpSettingsSection(props) {
15405
15782
  );
15406
15783
  refreshTools();
15407
15784
  };
15408
- (0, import_react2.useEffect)(() => {
15785
+ (0, import_react4.useEffect)(() => {
15409
15786
  let current = true;
15410
15787
  void list().then(
15411
15788
  (servers) => {
@@ -15500,13 +15877,13 @@ function McpSettingsSection(props) {
15500
15877
  const serverTools = (serverName) => (tools?.tools ?? []).filter((tool) => tool.server === serverName);
15501
15878
  const mode = tools?.mode ?? "search";
15502
15879
  if (state.loadState === "loading") {
15503
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: McpSettingsSection_default.status, "aria-busy": "true", children: t("loading") });
15880
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: McpSettingsSection_default.status, "aria-busy": "true", children: t("loading") });
15504
15881
  }
15505
15882
  if (state.loadState === "error") {
15506
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: McpSettingsSection_default.failure, children: [
15507
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "alert", children: t("loadError") }),
15508
- loadErrorDetail !== null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { className: McpSettingsSection_default.muted, children: loadErrorDetail }) : null,
15509
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", onClick: () => {
15883
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.failure, children: [
15884
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "alert", children: t("loadError") }),
15885
+ loadErrorDetail !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { className: McpSettingsSection_default.muted, children: loadErrorDetail }) : null,
15886
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: () => {
15510
15887
  setLoadState("loading");
15511
15888
  load();
15512
15889
  }, children: t("retry") })
@@ -15521,7 +15898,7 @@ function McpSettingsSection(props) {
15521
15898
  setTest,
15522
15899
  setServers
15523
15900
  };
15524
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
15901
+ const form = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
15525
15902
  McpServerForm,
15526
15903
  {
15527
15904
  draft: state.draft,
@@ -15538,13 +15915,13 @@ function McpSettingsSection(props) {
15538
15915
  }
15539
15916
  }
15540
15917
  );
15918
+ inlineForm = form;
15541
15919
  }
15542
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: McpSettingsSection_default.section, children: [
15543
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: McpSettingsSection_default.header, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: McpSettingsSection_default.primary, onClick: beginCreate, children: t("addServer") }) }),
15544
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: McpSettingsSection_default.modeRow, children: [
15545
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: McpSettingsSection_default.modeLabel, children: t("toolsModeLabel") }),
15546
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: McpSettingsSection_default.modeOption, children: [
15547
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
15920
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.section, children: [
15921
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.modeRow, children: [
15922
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: McpSettingsSection_default.modeLabel, children: t("toolsModeLabel") }),
15923
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: McpSettingsSection_default.modeOption, children: [
15924
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
15548
15925
  "input",
15549
15926
  {
15550
15927
  type: "radio",
@@ -15555,8 +15932,8 @@ function McpSettingsSection(props) {
15555
15932
  ),
15556
15933
  t("toolsModeSearch")
15557
15934
  ] }),
15558
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: McpSettingsSection_default.modeOption, children: [
15559
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
15935
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: McpSettingsSection_default.modeOption, children: [
15936
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
15560
15937
  "input",
15561
15938
  {
15562
15939
  type: "radio",
@@ -15568,53 +15945,115 @@ function McpSettingsSection(props) {
15568
15945
  t("toolsModeFull")
15569
15946
  ] })
15570
15947
  ] }),
15571
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: McpSettingsSection_default.modeHint, children: toolsError !== null ? toolsError : tools === null ? t("toolsLoading") : mode === "search" ? t("toolsHintSearch") : t("toolsHintFull") }),
15572
- state.servers.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: McpSettingsSection_default.status, children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: McpSettingsSection_default.list, children: state.servers.map((server) => {
15573
- const isExpanded = expanded.has(server.serverName);
15574
- const isRefreshing = refreshing.has(server.serverName);
15575
- const serverToolList = serverTools(server.serverName);
15576
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: McpSettingsSection_default.card, children: [
15577
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: McpSettingsSection_default.row, children: [
15578
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: McpSettingsSection_default.rowMain, children: [
15579
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: McpSettingsSection_default.rowTitle, children: [
15580
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: McpSettingsSection_default.serverName, children: server.serverName }),
15581
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `${McpSettingsSection_default.badge} ${McpSettingsSection_default[server.status.phase]}`, children: t(phaseKey(server.status.phase)) }),
15582
- !server.enabled && server.status.phase !== "stopped" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: McpSettingsSection_default.muted, children: t("statusStopped") }) : null
15583
- ] }),
15584
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: McpSettingsSection_default.rowMeta, children: [
15585
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: server.transport }),
15586
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
15587
- t("toolCount"),
15588
- ": ",
15589
- server.status.tools.length
15948
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: McpSettingsSection_default.modeHint, children: toolsError !== null ? toolsError : tools === null ? t("toolsLoading") : mode === "search" ? t("toolsHintSearch") : t("toolsHintFull") }),
15949
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.module, children: [
15950
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
15951
+ "button",
15952
+ {
15953
+ type: "button",
15954
+ className: McpSettingsSection_default.moduleTitle,
15955
+ "aria-expanded": envOpen,
15956
+ onClick: () => setEnvOpen((open) => !open),
15957
+ children: [
15958
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: McpSettingsSection_default.caret, children: envOpen ? "\u25BE" : "\u25B8" }),
15959
+ t("globalEnv")
15960
+ ]
15961
+ }
15962
+ ),
15963
+ envOpen ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
15964
+ GlobalEnvEditor,
15965
+ {
15966
+ injected: { envList: props.envList, envSet: props.envSet },
15967
+ t,
15968
+ onApplied: () => {
15969
+ refreshServer("");
15970
+ }
15971
+ }
15972
+ ) : null
15973
+ ] }),
15974
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.module, children: [
15975
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.configHeader, children: [
15976
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: McpSettingsSection_default.moduleTitle, children: t("configTitle") }),
15977
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.configActions, children: [
15978
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
15979
+ "button",
15980
+ {
15981
+ type: "button",
15982
+ className: jsonOpen ? McpSettingsSection_default.active : void 0,
15983
+ "aria-expanded": jsonOpen,
15984
+ onClick: () => setJsonOpen((open) => !open),
15985
+ children: t("serversJson")
15986
+ }
15987
+ ),
15988
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: McpSettingsSection_default.primary, onClick: beginCreate, children: t("addServer") })
15989
+ ] })
15990
+ ] }),
15991
+ jsonOpen ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
15992
+ ServersJsonEditor,
15993
+ {
15994
+ injected: { list: props.list, upsertJson: props.upsertJson },
15995
+ t,
15996
+ onApplied: () => {
15997
+ refreshServer("");
15998
+ refreshTools();
15999
+ setJsonOpen(false);
16000
+ timersRef.current.push(window.setTimeout(() => refreshServer(""), 2e3));
16001
+ timersRef.current.push(window.setTimeout(() => refreshServer(""), 6e3));
16002
+ }
16003
+ }
16004
+ ) : null,
16005
+ state.draft !== null && state.draft.id === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: McpSettingsSection_default.inlineForm, children: inlineForm }) : null,
16006
+ jsonOpen ? null : state.servers.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: McpSettingsSection_default.status, children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: McpSettingsSection_default.list, children: state.servers.flatMap((server) => {
16007
+ const isExpanded = expanded.has(server.serverName);
16008
+ const isRefreshing = refreshing.has(server.serverName);
16009
+ const serverToolList = serverTools(server.serverName);
16010
+ const card = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: McpSettingsSection_default.card, children: [
16011
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.row, children: [
16012
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.rowMain, children: [
16013
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.rowTitle, children: [
16014
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: McpSettingsSection_default.serverName, children: server.serverName }),
16015
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `${McpSettingsSection_default.badge} ${McpSettingsSection_default[server.status.phase]}`, children: t(phaseKey(server.status.phase)) }),
16016
+ !server.enabled && server.status.phase !== "stopped" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: McpSettingsSection_default.muted, children: t("statusStopped") }) : null
15590
16017
  ] }),
15591
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
15592
- t("envVars"),
15593
- ": ",
15594
- server.env.length
16018
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.rowMeta, children: [
16019
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: server.transport }),
16020
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
16021
+ t("toolCount"),
16022
+ ": ",
16023
+ server.status.tools.length
16024
+ ] }),
16025
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
16026
+ t("envVars"),
16027
+ ": ",
16028
+ server.env.length
16029
+ ] })
15595
16030
  ] })
16031
+ ] }),
16032
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: McpSettingsSection_default.rowActions, children: [
16033
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", disabled: isRefreshing, onClick: () => void toggleEnabled(server), children: server.enabled ? t("disable") : t("enabled") }),
16034
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", disabled: isRefreshing, onClick: () => refreshServer(server.serverName), children: isRefreshing ? t("refreshing") : t("refresh") }),
16035
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: () => toggleExpand(server.serverName), children: isExpanded ? t("toolsCollapse") : t("toolsExpand") }),
16036
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: () => beginEdit(server), children: t("edit") })
15596
16037
  ] })
15597
16038
  ] }),
15598
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: McpSettingsSection_default.rowActions, children: [
15599
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: isRefreshing, onClick: () => void toggleEnabled(server), children: server.enabled ? t("disable") : t("enabled") }),
15600
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: isRefreshing, onClick: () => refreshServer(server.serverName), children: isRefreshing ? t("refreshing") : t("refresh") }),
15601
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", onClick: () => toggleExpand(server.serverName), children: isExpanded ? t("toolsCollapse") : t("toolsExpand") }),
15602
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", onClick: () => beginEdit(server), children: t("edit") })
15603
- ] })
15604
- ] }),
15605
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: McpSettingsSection_default.toolPanel, children: tools === null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: McpSettingsSection_default.muted, children: toolsError ?? t("toolsLoading") }) : serverToolList.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: McpSettingsSection_default.muted, children: t("toolsEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: McpSettingsSection_default.toolGrid, children: serverToolList.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: McpSettingsSection_default.toolRow, title: tool.name, children: [
15606
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
15607
- "input",
15608
- {
15609
- type: "checkbox",
15610
- checked: tool.enabled,
15611
- onChange: () => toggleTool(tool)
15612
- }
15613
- ),
15614
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: McpSettingsSection_default.toolName, children: rawOf(tool.name) })
15615
- ] }, tool.name)) }) }) : null
15616
- ] }, server.id);
15617
- }) })
16039
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: McpSettingsSection_default.toolPanel, children: tools === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: McpSettingsSection_default.muted, children: toolsError ?? t("toolsLoading") }) : serverToolList.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: McpSettingsSection_default.muted, children: t("toolsEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: McpSettingsSection_default.toolGrid, children: serverToolList.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: McpSettingsSection_default.toolRow, title: tool.name, children: [
16040
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
16041
+ "input",
16042
+ {
16043
+ type: "checkbox",
16044
+ checked: tool.enabled,
16045
+ onChange: () => toggleTool(tool)
16046
+ }
16047
+ ),
16048
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: McpSettingsSection_default.toolName, children: rawOf(tool.name) })
16049
+ ] }, tool.name)) }) }) : null
16050
+ ] }, server.id);
16051
+ if (state.draft !== null && state.draft.id === server.id) {
16052
+ return [card, /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { className: McpSettingsSection_default.inlineFormItem, children: inlineForm }, `${server.id}-form`)];
16053
+ }
16054
+ return [card];
16055
+ }) })
16056
+ ] })
15618
16057
  ] });
15619
16058
  }
15620
16059
 
@@ -15642,9 +16081,9 @@ var zh = {
15642
16081
  url: "\u670D\u52A1\u5668 URL",
15643
16082
  urlPlaceholder: "\u4F8B\u5982 http://localhost:3000/mcp",
15644
16083
  headers: "\u8BF7\u6C42\u5934",
15645
- headersPlaceholder: '\u6BCF\u884C\u4E00\u4E2A "\u540D\u79F0: \u503C"',
16084
+ headersPlaceholder: '\u6BCF\u884C\u4E00\u4E2A "\u540D\u79F0: \u503C"\uFF1B\u503C\u652F\u6301 ${ENV} \u73AF\u5883\u53D8\u91CF\u66FF\u6362',
15646
16085
  envVars: "\u73AF\u5883\u53D8\u91CF",
15647
- envHint: "\u6CE8\u5165\u5230 stdio \u5B50\u8FDB\u7A0B\uFF1B\u6807\u8BB0\u4E3A secret \u7684\u503C\u5199\u5165\u51ED\u636E\u6587\u6863\uFF0C\u4E0D\u5728\u6B64\u660E\u6587\u4FDD\u5B58",
16086
+ envHint: "stdio\uFF1A\u6CE8\u5165\u5B50\u8FDB\u7A0B\uFF1BHTTP\uFF1A\u4F9B\u8BF7\u6C42\u5934 ${\u53D8\u91CF} \u66FF\u6362\uFF08\u5982 Authorization: Bearer ${TOKEN}\uFF09\u3002\u6807\u8BB0\u4E3A secret \u7684\u503C\u5199\u5165\u51ED\u636E\u6587\u6863\uFF0C\u4E0D\u5728\u6B64\u660E\u6587\u4FDD\u5B58",
15648
16087
  envName: "\u53D8\u91CF\u540D",
15649
16088
  envValue: "\u503C",
15650
16089
  envSecret: "secret",
@@ -15690,7 +16129,32 @@ var zh = {
15690
16129
  refresh: "\u5237\u65B0",
15691
16130
  refreshing: "\u5237\u65B0\u4E2D\u2026",
15692
16131
  toolsExpand: "\u5C55\u5F00\u5DE5\u5177",
15693
- toolsCollapse: "\u6536\u8D77\u5DE5\u5177"
16132
+ toolsCollapse: "\u6536\u8D77\u5DE5\u5177",
16133
+ serversJson: "JSON \u7EF4\u62A4\u914D\u7F6E",
16134
+ serversJsonLabel: "\u670D\u52A1\u5668\u914D\u7F6E JSON",
16135
+ serversJsonHint: "\u6574\u4E2A MCP \u670D\u52A1\u5668\u914D\u7F6E\u5217\u8868\uFF08\u6570\u7EC4\uFF09\uFF1A\u5E94\u7528\u540E\u5C06\u6309\u6B64\u5217\u8868\u66FF\u6362\u2014\u2014\u5DF2\u5217\u51FA\u7684\u670D\u52A1\u5668\u521B\u5EFA/\u66F4\u65B0\uFF0C\u672A\u5217\u51FA\u7684\u5C06\u88AB\u5220\u9664\uFF1Bsecret \u73AF\u5883\u53D8\u91CF\u503C\u4E0D\u4F1A\u5BFC\u51FA\uFF08configured \u6807\u8BB0\uFF09\uFF0C\u7559\u7A7A\u5219\u4FDD\u7559\u539F\u503C\u3002",
16136
+ serversJsonReseed: "\u91CD\u65B0\u5BFC\u51FA",
16137
+ serversJsonApply: "\u5E94\u7528",
16138
+ serversJsonSaving: "\u5E94\u7528\u4E2D\u2026",
16139
+ serversJsonDone: "\u5DF2\u5E94\u7528",
16140
+ globalEnv: "\u8FDB\u7A0B\u73AF\u5883\u53D8\u91CF",
16141
+ globalEnvHint: "\u8FDB\u7A0B\u7EA7\u73AF\u5883\u53D8\u91CF\uFF0C\u4F9B\u6240\u6709 MCP \u670D\u52A1\u5668\u7684\u8BF7\u6C42\u5934\u66FF\u6362\u5F15\u7528\uFF08headers \u503C\u5199 ${\u53D8\u91CF\u540D} \u6216\u76F4\u63A5\u5199\u53D8\u91CF\u540D\uFF09\u3002secret \u503C\u5199\u5165\u51ED\u636E\u6587\u6863\uFF0C\u4E0D\u5728\u6B64\u660E\u6587\u663E\u793A\uFF1B\u7559\u7A7A\u4FDD\u7559\u539F\u503C\u3002",
16142
+ globalEnvName: "\u53D8\u91CF\u540D",
16143
+ globalEnvValue: "\u503C",
16144
+ globalEnvSecret: "\u79C1\u5BC6",
16145
+ globalEnvSecretHint: "\u503C\u53EA\u5199\uFF0C\u4FDD\u5B58\u540E\u4E0D\u53EF\u89C1\uFF1B\u7559\u7A7A\u8868\u793A\u4FDD\u7559\u539F\u503C",
16146
+ globalEnvAdd: "\u6DFB\u52A0\u53D8\u91CF",
16147
+ globalEnvBulk: "\u6279\u91CF\u6DFB\u52A0",
16148
+ globalEnvBulkPrompt: "\u6BCF\u884C\u4E00\u4E2A\u53D8\u91CF\uFF0C\u683C\u5F0F NAME=value\uFF1B\u53EA\u586B NAME \u7684\u884C\u5C06\u4F5C\u4E3A\u4EC5\u53D8\u91CF\u540D\u884C\u6DFB\u52A0\u3002",
16149
+ globalEnvBulkOk: "\u6DFB\u52A0",
16150
+ globalEnvSave: "\u4FDD\u5B58",
16151
+ globalEnvSaving: "\u4FDD\u5B58\u4E2D\u2026",
16152
+ globalEnvRemove: "\u79FB\u9664\u8BE5\u53D8\u91CF",
16153
+ globalEnvEmpty: "\uFF08\u6682\u65E0\u8FDB\u7A0B\u73AF\u5883\u53D8\u91CF\uFF09",
16154
+ globalEnvLoadFailed: "\u8BFB\u53D6\u73AF\u5883\u53D8\u91CF\u5931\u8D25",
16155
+ globalEnvRetry: "\u91CD\u8BD5",
16156
+ globalEnvDone: "\u5DF2\u4FDD\u5B58",
16157
+ configTitle: "MCP \u914D\u7F6E"
15694
16158
  };
15695
16159
  var en = {
15696
16160
  nav: "MCP",
@@ -15715,9 +16179,9 @@ var en = {
15715
16179
  url: "Server URL",
15716
16180
  urlPlaceholder: "e.g. http://localhost:3000/mcp",
15717
16181
  headers: "Headers",
15718
- headersPlaceholder: 'One "Name: value" per line',
16182
+ headersPlaceholder: 'One "Name: value" per line; values support ${ENV} substitution',
15719
16183
  envVars: "Environment variables",
15720
- envHint: "Injected into the stdio child; secret values are stored in the credentials document, never shown here",
16184
+ envHint: "stdio: injected into the child process; HTTP: used for ${VAR} substitution in headers (e.g. Authorization: Bearer ${TOKEN}). Secret values are stored in the credentials document, never shown here",
15721
16185
  envName: "Name",
15722
16186
  envValue: "Value",
15723
16187
  envSecret: "secret",
@@ -15763,9 +16227,150 @@ var en = {
15763
16227
  refresh: "Refresh",
15764
16228
  refreshing: "Refreshing\u2026",
15765
16229
  toolsExpand: "Expand tools",
15766
- toolsCollapse: "Collapse tools"
16230
+ toolsCollapse: "Collapse tools",
16231
+ serversJson: "JSON config editor",
16232
+ serversJsonLabel: "Server-config JSON",
16233
+ serversJsonHint: "The whole MCP server list (array): applying replaces the list \u2014 listed servers are created/updated, existing servers absent from the document are removed; secret env values are never exported (a configured flag marks them), a blank value keeps the stored one.",
16234
+ serversJsonReseed: "Re-export",
16235
+ serversJsonApply: "Apply",
16236
+ serversJsonSaving: "Applying\u2026",
16237
+ serversJsonDone: "Applied",
16238
+ globalEnv: "Process env vars",
16239
+ globalEnvHint: "Process-level environment variables referenced by every server's header substitution (write ${NAME} or a bare name in a header value). Secret values are stored in the credentials document, never shown here; blank keeps the stored value.",
16240
+ globalEnvName: "Name",
16241
+ globalEnvValue: "Value",
16242
+ globalEnvSecret: "secret",
16243
+ globalEnvSecretHint: "Write-only; a blank value keeps the stored one",
16244
+ globalEnvAdd: "Add variable",
16245
+ globalEnvBulk: "Add many",
16246
+ globalEnvBulkPrompt: "One variable per line, as NAME=value; a bare NAME adds a name-only row.",
16247
+ globalEnvBulkOk: "Add",
16248
+ globalEnvSave: "Save",
16249
+ globalEnvSaving: "Saving\u2026",
16250
+ globalEnvRemove: "Remove this variable",
16251
+ globalEnvEmpty: "(no process env vars)",
16252
+ globalEnvLoadFailed: "Failed to read environment variables",
16253
+ globalEnvRetry: "Retry",
16254
+ globalEnvDone: "Saved",
16255
+ configTitle: "MCP config"
15767
16256
  };
15768
16257
 
16258
+ // src/client/mcp-store.ts
16259
+ var import_client = require("@deepseek-ai/dsh-client-runtime/client");
16260
+ var DEFAULT_TOOL_CALL_TIMEOUT_MS = 6e4;
16261
+ function emptyDraft() {
16262
+ return {
16263
+ id: null,
16264
+ serverName: "",
16265
+ transport: "stdio",
16266
+ enabled: true,
16267
+ command: "",
16268
+ argsText: "",
16269
+ cwd: "",
16270
+ url: "",
16271
+ headersText: "",
16272
+ toolCallTimeoutMs: String(DEFAULT_TOOL_CALL_TIMEOUT_MS),
16273
+ // Reject the mount by default when the startup connection fails, so a
16274
+ // broken server never registers stale tools.
16275
+ failOnStartupError: true
16276
+ };
16277
+ }
16278
+ function draftFromServer(server) {
16279
+ return {
16280
+ id: server.id,
16281
+ serverName: server.serverName,
16282
+ transport: server.transport,
16283
+ enabled: server.enabled,
16284
+ command: server.command,
16285
+ argsText: server.args.join("\n"),
16286
+ cwd: server.cwd,
16287
+ url: server.url,
16288
+ headersText: server.headers.map((header) => `${header.name}: ${header.value}`).join("\n"),
16289
+ toolCallTimeoutMs: String(server.toolCallTimeoutMs),
16290
+ failOnStartupError: server.failOnStartupError
16291
+ };
16292
+ }
16293
+ function parseLines(text) {
16294
+ return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
16295
+ }
16296
+ function parseHeaders(text) {
16297
+ const headers = [];
16298
+ for (const line of parseLines(text)) {
16299
+ const separator = line.indexOf(":");
16300
+ if (separator <= 0) continue;
16301
+ const name = line.slice(0, separator).trim();
16302
+ const value = line.slice(separator + 1).trim();
16303
+ if (name.length > 0) headers.push({ name, value });
16304
+ }
16305
+ return headers;
16306
+ }
16307
+ function parseTimeout(text) {
16308
+ const value = Number(text);
16309
+ return Number.isInteger(value) && value > 0 ? value : DEFAULT_TOOL_CALL_TIMEOUT_MS;
16310
+ }
16311
+ function draftToSubmission(draft) {
16312
+ return {
16313
+ server: {
16314
+ serverName: draft.serverName.trim(),
16315
+ transport: draft.transport,
16316
+ enabled: draft.enabled,
16317
+ command: draft.command.trim(),
16318
+ args: parseLines(draft.argsText),
16319
+ cwd: draft.cwd.trim(),
16320
+ url: draft.url.trim(),
16321
+ headers: parseHeaders(draft.headersText),
16322
+ toolCallTimeoutMs: parseTimeout(draft.toolCallTimeoutMs),
16323
+ failOnStartupError: draft.failOnStartupError
16324
+ }
16325
+ };
16326
+ }
16327
+ function createMcpManagerStore() {
16328
+ return (0, import_client.defineStore)({
16329
+ init: () => ({
16330
+ loadState: "loading",
16331
+ servers: [],
16332
+ draft: null,
16333
+ busy: null,
16334
+ testRunning: false,
16335
+ test: null
16336
+ }),
16337
+ actions: {
16338
+ setLoadState: (d, state) => {
16339
+ d.loadState = state;
16340
+ },
16341
+ setServers: (d, servers) => {
16342
+ d.servers = servers;
16343
+ },
16344
+ beginCreate: (d) => {
16345
+ d.draft = emptyDraft();
16346
+ d.test = null;
16347
+ },
16348
+ beginEdit: (d, server) => {
16349
+ d.draft = draftFromServer(server);
16350
+ d.test = null;
16351
+ },
16352
+ cancelEdit: (d) => {
16353
+ d.draft = null;
16354
+ d.test = null;
16355
+ d.busy = null;
16356
+ },
16357
+ updateDraft: (d, patch) => {
16358
+ if (d.draft === null) return;
16359
+ d.draft = { ...d.draft, ...patch };
16360
+ },
16361
+ setBusy: (d, busy) => {
16362
+ d.busy = busy;
16363
+ },
16364
+ setTestRunning: (d, running) => {
16365
+ d.testRunning = running;
16366
+ },
16367
+ setTest: (d, test) => {
16368
+ d.test = test;
16369
+ }
16370
+ }
16371
+ });
16372
+ }
16373
+
15769
16374
  // src/client/index.ts
15770
16375
  var NS = "settings.mcp";
15771
16376
  var inject = ["slots", "locale", "remote"];
@@ -15794,8 +16399,7 @@ async function apply(ctx) {
15794
16399
  const submission = draftToSubmission(draft);
15795
16400
  const result = await manager.upsert({
15796
16401
  ...draft.id === null ? {} : { id: draft.id },
15797
- server: submission.server,
15798
- env: submission.env
16402
+ server: submission.server
15799
16403
  });
15800
16404
  if (result.ok) return null;
15801
16405
  return failureOf(result.error);
@@ -15805,12 +16409,15 @@ async function apply(ctx) {
15805
16409
  if (result.ok) return null;
15806
16410
  return failureOf(result.error);
15807
16411
  },
16412
+ upsertJson: async (servers) => {
16413
+ const result = await unwrap(() => manager.upsertJson({ servers }));
16414
+ return { added: result.added, updated: result.updated, removed: result.removed, servers: result.servers };
16415
+ },
15808
16416
  test: async (draft) => {
15809
16417
  const submission = draftToSubmission(draft);
15810
16418
  const result = await unwrap(() => manager.test({
15811
16419
  ...draft.id === null ? {} : { id: draft.id },
15812
- server: submission.server,
15813
- env: submission.env
16420
+ server: submission.server
15814
16421
  }));
15815
16422
  return { probe: result.probe, elapsedMs: result.elapsedMs };
15816
16423
  },
@@ -15825,6 +16432,14 @@ async function apply(ctx) {
15825
16432
  toolsMode: async (request) => {
15826
16433
  const result = await unwrap(() => manager.toolsMode({ mode: request.mode }));
15827
16434
  return { ok: result.ok };
16435
+ },
16436
+ envList: async () => {
16437
+ const result = await unwrap(() => manager.envList());
16438
+ return { vars: result.vars };
16439
+ },
16440
+ envSet: async (vars) => {
16441
+ const result = await unwrap(() => manager.envSet({ vars }));
16442
+ return { vars: result.vars };
15828
16443
  }
15829
16444
  });
15830
16445
  ctx.slots.inject("settings.section", () => ctx.slots.register({