@wuyax/mcps 0.1.0-beta.1 → 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -34,13 +34,23 @@ __export(src_exports, {
34
34
  DEFAULT_REMOTE_TRANSPORT: () => DEFAULT_REMOTE_TRANSPORT,
35
35
  NPX_COMMAND: () => NPX_COMMAND,
36
36
  NPX_DASH_Y: () => NPX_DASH_Y,
37
+ SECRET_HEADER_PATTERN: () => SECRET_HEADER_PATTERN,
38
+ SECRET_KEY_PATTERN: () => SECRET_KEY_PATTERN,
37
39
  add: () => installMcpServer,
38
40
  agentConfigStore: () => agentConfigStore,
41
+ buildLinkedAgentChoices: () => buildLinkedAgentChoices,
39
42
  buildMcpServerConfig: () => buildMcpServerConfig,
40
43
  createAgentTransform: () => createAgentTransform,
41
44
  detectGloballyInstalledMcpAgents: () => detectGloballyInstalledMcpAgents,
42
45
  detectProjectInstalledMcpAgents: () => detectProjectInstalledMcpAgents,
46
+ detectUpdateTransition: () => detectUpdateTransition,
47
+ displayServerDetails: () => displayServerDetails,
43
48
  extractPackageName: () => extractPackageName,
49
+ formatArgsString: () => formatArgsString,
50
+ formatEnvText: () => formatEnvText,
51
+ formatHeadersText: () => formatHeadersText,
52
+ getCandidateAgentsForScope: () => getCandidateAgentsForScope,
53
+ getCoHostedAgents: () => getCoHostedAgents,
44
54
  getMcpAgentConfig: () => getMcpAgentConfig,
45
55
  getMcpAgentTypes: () => getMcpAgentTypes,
46
56
  getMcpAgentsSupportingProjectScope: () => getMcpAgentsSupportingProjectScope,
@@ -49,17 +59,22 @@ __export(src_exports, {
49
59
  installMcpServer: () => installMcpServer,
50
60
  installMcpServerForAgent: () => installMcpServerForAgent,
51
61
  installMcpServerForAgents: () => installMcpServerForAgents,
62
+ installToCompatibleAgents: () => installToCompatibleAgents,
52
63
  isMcpAgentType: () => isMcpAgentType,
53
64
  isMcpTransportSupported: () => isMcpTransportSupported,
54
65
  isRemoteMcpSource: () => isRemoteMcpSource,
55
66
  isRemoteServerConfig: () => isRemoteServerConfig,
56
67
  isStdioServerConfig: () => isStdioServerConfig,
68
+ linkedCheckbox: () => linkedCheckbox,
57
69
  list: () => listInstalledMcpServers,
58
70
  listInstalledMcpServers: () => listInstalledMcpServers,
59
71
  listServersInConfigFile: () => listServersInConfigFile,
60
72
  mainMenu: () => mainMenu,
73
+ maskSecretHeader: () => maskSecretHeader,
74
+ maskSecretValue: () => maskSecretValue,
61
75
  mcpAgentAliases: () => mcpAgentAliases,
62
76
  mcpAgents: () => mcpAgents,
77
+ mcpManageCommand: () => mcpManageCommand,
63
78
  normalizeServerConfig: () => normalizeServerConfig,
64
79
  parseArgsString: () => parseArgsString,
65
80
  parseEnvText: () => parseEnvText,
@@ -68,20 +83,33 @@ __export(src_exports, {
68
83
  parseServerConfig: () => parseServerConfig,
69
84
  parseSource: () => parseMcpSource,
70
85
  promptArgsConfig: () => promptArgsConfig,
86
+ promptEditArgs: () => promptEditArgs,
87
+ promptEditEnvConfig: () => promptEditEnvConfig,
88
+ promptEditHeadersConfig: () => promptEditHeadersConfig,
89
+ promptEditKeyValueConfig: () => promptEditKeyValueConfig,
71
90
  promptEnvConfig: () => promptEnvConfig,
72
91
  promptHeadersConfig: () => promptHeadersConfig,
73
92
  promptScope: () => promptScope,
74
93
  promptScopeAndAgents: () => promptScopeAndAgents,
94
+ promptSwitchServerType: () => promptSwitchServerType,
75
95
  readConfigFile: () => readConfigFile,
76
96
  remove: () => removeMcpServer,
77
97
  removeMcpServer: () => removeMcpServer,
78
98
  removeMcpServerFromAgent: () => removeMcpServerFromAgent,
79
99
  removeServerFromConfigFile: () => removeServerFromConfigFile,
100
+ resolveConfigClusters: () => resolveConfigClusters,
80
101
  resolveMcpAgentAlias: () => resolveMcpAgentAlias,
81
102
  resolveMcpConfigTarget: () => resolveMcpConfigTarget,
82
103
  resolveTargetAgents: () => resolveTargetAgents,
104
+ resolveTransport: () => resolveTransport,
105
+ sanitizeUpdatedServerConfig: () => sanitizeUpdatedServerConfig,
106
+ sortAgentsWithClusters: () => sortAgentsWithClusters,
107
+ toRemoteServerConfig: () => toRemoteServerConfig,
108
+ toStdioServerConfig: () => toStdioServerConfig,
83
109
  transformServerConfig: () => transformServerConfig,
84
110
  transformServerConfigForAgent: () => transformServerConfigForAgent,
111
+ update: () => updateMcpServer,
112
+ updateMcpServer: () => updateMcpServer,
85
113
  wizardAdd: () => wizardAdd,
86
114
  wizardManage: () => wizardManage,
87
115
  wizardRemove: () => wizardRemove,
@@ -543,9 +571,9 @@ var mcpAgentAliases = {
543
571
  var getMcpAgentConfig = (agentType) => mcpAgents[agentType];
544
572
  var getMcpAgentTypes = () => Object.values(mcpAgents).map((config) => config.name);
545
573
  var isMcpAgentType = (value) => value in mcpAgents;
546
- var resolveMcpAgentAlias = (input5) => {
547
- if (isMcpAgentType(input5)) return input5;
548
- return mcpAgentAliases[input5] ?? null;
574
+ var resolveMcpAgentAlias = (input7) => {
575
+ if (isMcpAgentType(input7)) return input7;
576
+ return mcpAgentAliases[input7] ?? null;
549
577
  };
550
578
  var isMcpTransportSupported = (agent, transport) => agent.supportedTransports.includes(transport);
551
579
  var detectProjectInstalledMcpAgents = (cwd) => getMcpAgentTypes().filter(
@@ -1056,6 +1084,74 @@ var agentConfigStore = new AgentConfigStore();
1056
1084
  // src/utils/to-error-message.ts
1057
1085
  var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
1058
1086
 
1087
+ // src/resolve-config-clusters.ts
1088
+ var getCandidateAgentsForScope = (options = {}) => {
1089
+ return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
1090
+ };
1091
+ var getCoHostedAgents = (agentType, options = {}) => {
1092
+ const currentAgent = getMcpAgentConfig(agentType);
1093
+ const currentTarget = resolveMcpConfigTarget(currentAgent, options);
1094
+ const candidates = getCandidateAgentsForScope(options);
1095
+ const coHosted = [];
1096
+ for (const candidateType of candidates) {
1097
+ if (candidateType === agentType) continue;
1098
+ const candidateConfig = getMcpAgentConfig(candidateType);
1099
+ const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
1100
+ if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
1101
+ coHosted.push(candidateType);
1102
+ }
1103
+ }
1104
+ return coHosted;
1105
+ };
1106
+ var resolveConfigClusters = (agentTypes, options = {}) => {
1107
+ const clustersByPath = /* @__PURE__ */ new Map();
1108
+ for (const agentType of agentTypes) {
1109
+ const agentConfig = getMcpAgentConfig(agentType);
1110
+ const target = resolveMcpConfigTarget(agentConfig, options);
1111
+ let keyMap = clustersByPath.get(target.configPath);
1112
+ if (!keyMap) {
1113
+ keyMap = /* @__PURE__ */ new Map();
1114
+ clustersByPath.set(target.configPath, keyMap);
1115
+ }
1116
+ let cluster = keyMap.get(target.configKey);
1117
+ if (!cluster) {
1118
+ const allCoHosted = getCoHostedAgents(agentType, options);
1119
+ cluster = {
1120
+ configPath: target.configPath,
1121
+ configKey: target.configKey,
1122
+ targetAgents: [],
1123
+ coHostedAgents: allCoHosted
1124
+ };
1125
+ keyMap.set(target.configKey, cluster);
1126
+ }
1127
+ if (!cluster.targetAgents.includes(agentType)) {
1128
+ cluster.targetAgents.push(agentType);
1129
+ }
1130
+ }
1131
+ const clusters = [];
1132
+ for (const keyMap of clustersByPath.values()) {
1133
+ for (const cluster of keyMap.values()) {
1134
+ cluster.coHostedAgents = cluster.coHostedAgents.filter(
1135
+ (co) => !cluster.targetAgents.includes(co)
1136
+ );
1137
+ clusters.push(cluster);
1138
+ }
1139
+ }
1140
+ return clusters;
1141
+ };
1142
+ var sortAgentsWithClusters = (agentTypes, options = {}) => {
1143
+ const clusters = resolveConfigClusters(agentTypes, options);
1144
+ const sorted = [];
1145
+ for (const cluster of clusters) {
1146
+ for (const agent of cluster.targetAgents) {
1147
+ if (!sorted.includes(agent)) {
1148
+ sorted.push(agent);
1149
+ }
1150
+ }
1151
+ }
1152
+ return sorted;
1153
+ };
1154
+
1059
1155
  // src/transforms/index.ts
1060
1156
  var DIALECT_PRESETS = {
1061
1157
  vscode: {
@@ -1271,12 +1367,18 @@ var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {
1271
1367
  const agent = getMcpAgentConfig(agentType);
1272
1368
  const isGlobal = options.global ?? false;
1273
1369
  const { target } = agentConfigStore.resolveTarget(agent, options);
1370
+ const coHosted = getCoHostedAgents(agentType, options);
1274
1371
  try {
1275
1372
  const transformed = transformServerConfigForAgent(agent, serverName, serverConfig, {
1276
1373
  global: isGlobal
1277
1374
  });
1278
1375
  agentConfigStore.writeServer(agent, serverName, transformed, options);
1279
- return { agent: agentType, success: true, path: target.configPath };
1376
+ return {
1377
+ agent: agentType,
1378
+ success: true,
1379
+ path: target.configPath,
1380
+ coConfiguredAgents: coHosted.length > 0 ? coHosted : void 0
1381
+ };
1280
1382
  } catch (error) {
1281
1383
  return {
1282
1384
  agent: agentType,
@@ -1286,16 +1388,69 @@ var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {
1286
1388
  };
1287
1389
  }
1288
1390
  };
1289
- var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => agentTypes.map(
1290
- (agentType) => installMcpServerForAgent(serverName, serverConfig, agentType, options)
1291
- );
1391
+ var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => {
1392
+ const clusters = resolveConfigClusters(agentTypes, options);
1393
+ const resultsByAgent = /* @__PURE__ */ new Map();
1394
+ const isGlobal = options.global ?? false;
1395
+ for (const cluster of clusters) {
1396
+ const primaryAgentType = cluster.targetAgents[0];
1397
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1398
+ try {
1399
+ const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
1400
+ global: isGlobal
1401
+ });
1402
+ agentConfigStore.writeServer(primaryAgent, serverName, transformed, options);
1403
+ for (const agentType of cluster.targetAgents) {
1404
+ resultsByAgent.set(agentType, {
1405
+ agent: agentType,
1406
+ success: true,
1407
+ path: cluster.configPath,
1408
+ coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1409
+ });
1410
+ }
1411
+ } catch (error) {
1412
+ const errorMsg = toErrorMessage(error);
1413
+ for (const agentType of cluster.targetAgents) {
1414
+ resultsByAgent.set(agentType, {
1415
+ agent: agentType,
1416
+ success: false,
1417
+ path: cluster.configPath,
1418
+ error: errorMsg
1419
+ });
1420
+ }
1421
+ }
1422
+ }
1423
+ return agentTypes.map((agentType) => resultsByAgent.get(agentType));
1424
+ };
1425
+ var installToCompatibleAgents = (serverName, serverConfig, options) => {
1426
+ const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
1427
+ const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1428
+ const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
1429
+ const installedResults = installMcpServerForAgents(serverName, serverConfig, compatibleAgents, {
1430
+ global: isGlobal,
1431
+ cwd
1432
+ });
1433
+ const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
1434
+ return allAgents.map((agentType) => {
1435
+ const incompatibleReason = incompatibleMap.get(agentType);
1436
+ if (incompatibleReason) {
1437
+ return {
1438
+ agent: agentType,
1439
+ success: false,
1440
+ path: "",
1441
+ error: incompatibleReason
1442
+ };
1443
+ }
1444
+ return installedMap.get(agentType);
1445
+ });
1446
+ };
1292
1447
 
1293
1448
  // src/utils/parse-mcp-agent-list.ts
1294
- var parseMcpAgentList = (input5) => {
1295
- if (!input5 || input5.length === 0) return void 0;
1296
- if (input5.includes("*")) return getMcpAgentTypes();
1449
+ var parseMcpAgentList = (input7) => {
1450
+ if (!input7 || input7.length === 0) return void 0;
1451
+ if (input7.includes("*")) return getMcpAgentTypes();
1297
1452
  const resolved = [];
1298
- for (const value of input5) {
1453
+ for (const value of input7) {
1299
1454
  const agentType = resolveMcpAgentAlias(value);
1300
1455
  if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
1301
1456
  resolved.push(agentType);
@@ -1304,9 +1459,9 @@ var parseMcpAgentList = (input5) => {
1304
1459
  };
1305
1460
 
1306
1461
  // src/resolve-target-agents.ts
1307
- var normalizeRequestedAgents = (input5) => {
1308
- if (!input5 || input5.length === 0) return void 0;
1309
- const rawList = [...input5];
1462
+ var normalizeRequestedAgents = (input7) => {
1463
+ if (!input7 || input7.length === 0) return void 0;
1464
+ const rawList = [...input7];
1310
1465
  if (rawList.every((item) => isMcpAgentType(item))) {
1311
1466
  return rawList;
1312
1467
  }
@@ -1366,29 +1521,29 @@ var REMOTE_URL_REGEX = /^https?:\/\//i;
1366
1521
  var HAS_WHITESPACE_REGEX = /\s/;
1367
1522
  var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
1368
1523
  var PATH_SEPARATOR_REGEX = /[/\\]/;
1369
- var stripVersionSuffix = (input5) => {
1370
- if (input5.startsWith("@")) {
1371
- const secondAtIndex = input5.indexOf("@", 1);
1372
- if (secondAtIndex > 0) return input5.slice(0, secondAtIndex);
1373
- return input5;
1374
- }
1375
- const atIndex = input5.lastIndexOf("@");
1376
- if (atIndex > 0) return input5.slice(0, atIndex);
1377
- return input5;
1378
- };
1379
- var stripScopePrefix = (input5) => {
1380
- if (!input5.startsWith("@") || !input5.includes("/")) return input5;
1381
- const parts = input5.split("/");
1382
- return parts[1] || input5;
1383
- };
1384
- var stripPathPrefix = (input5) => {
1385
- if (!PATH_SEPARATOR_REGEX.test(input5)) return input5;
1386
- const segments = input5.split(PATH_SEPARATOR_REGEX);
1524
+ var stripVersionSuffix = (input7) => {
1525
+ if (input7.startsWith("@")) {
1526
+ const secondAtIndex = input7.indexOf("@", 1);
1527
+ if (secondAtIndex > 0) return input7.slice(0, secondAtIndex);
1528
+ return input7;
1529
+ }
1530
+ const atIndex = input7.lastIndexOf("@");
1531
+ if (atIndex > 0) return input7.slice(0, atIndex);
1532
+ return input7;
1533
+ };
1534
+ var stripScopePrefix = (input7) => {
1535
+ if (!input7.startsWith("@") || !input7.includes("/")) return input7;
1536
+ const parts = input7.split("/");
1537
+ return parts[1] || input7;
1538
+ };
1539
+ var stripPathPrefix = (input7) => {
1540
+ if (!PATH_SEPARATOR_REGEX.test(input7)) return input7;
1541
+ const segments = input7.split(PATH_SEPARATOR_REGEX);
1387
1542
  const basename = segments[segments.length - 1];
1388
- return basename || input5;
1543
+ return basename || input7;
1389
1544
  };
1390
- var extractPackageName = (input5) => {
1391
- let name = stripVersionSuffix(input5);
1545
+ var extractPackageName = (input7) => {
1546
+ let name = stripVersionSuffix(input7);
1392
1547
  name = stripScopePrefix(name);
1393
1548
  name = stripPathPrefix(name);
1394
1549
  name = name.replace(SCRIPT_EXTENSION_REGEX, "");
@@ -1406,9 +1561,9 @@ var extractPackageName = (input5) => {
1406
1561
  }
1407
1562
  return name || MCP_DEFAULT_SERVER_NAME;
1408
1563
  };
1409
- var inferNameFromUrl = (input5) => {
1564
+ var inferNameFromUrl = (input7) => {
1410
1565
  try {
1411
- const url = new URL(input5);
1566
+ const url = new URL(input7);
1412
1567
  const host = url.hostname;
1413
1568
  const labels = host.split(".").filter((segment) => segment.length > 0);
1414
1569
  if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
@@ -1437,8 +1592,8 @@ var inferNameFromCommand = (command) => {
1437
1592
  const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
1438
1593
  return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
1439
1594
  };
1440
- var parseMcpSource = (input5) => {
1441
- const trimmed = input5.trim();
1595
+ var parseMcpSource = (input7) => {
1596
+ const trimmed = input7.trim();
1442
1597
  if (trimmed.length === 0) {
1443
1598
  throw new Error(
1444
1599
  "Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
@@ -1492,18 +1647,11 @@ var installMcpServer = (options) => {
1492
1647
  cwd,
1493
1648
  transport: requestedTransport
1494
1649
  });
1495
- const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1496
- const results = allAgents.map((agentType) => {
1497
- const incompatibleReason = incompatibleMap.get(agentType);
1498
- if (incompatibleReason) {
1499
- return {
1500
- agent: agentType,
1501
- success: false,
1502
- path: "",
1503
- error: incompatibleReason
1504
- };
1505
- }
1506
- return installMcpServerForAgent(serverName, serverConfig, agentType, { global: isGlobal, cwd });
1650
+ const results = installToCompatibleAgents(serverName, serverConfig, {
1651
+ allAgents,
1652
+ incompatible,
1653
+ global: isGlobal,
1654
+ cwd
1507
1655
  });
1508
1656
  return { serverName, config: serverConfig, results };
1509
1657
  };
@@ -1533,9 +1681,15 @@ var listInstalledMcpServers = (options = {}) => {
1533
1681
  var removeMcpServerFromAgent = (serverName, agentType, options = {}) => {
1534
1682
  const agent = getMcpAgentConfig(agentType);
1535
1683
  const { target } = agentConfigStore.resolveTarget(agent, options);
1684
+ const coHosted = getCoHostedAgents(agentType, options);
1536
1685
  try {
1537
1686
  const { removed } = agentConfigStore.removeServer(agent, serverName, options);
1538
- return { agent: agentType, path: target.configPath, removed };
1687
+ return {
1688
+ agent: agentType,
1689
+ path: target.configPath,
1690
+ removed,
1691
+ coAffectedAgents: removed && coHosted.length > 0 ? coHosted : void 0
1692
+ };
1539
1693
  } catch (error) {
1540
1694
  return {
1541
1695
  agent: agentType,
@@ -1552,24 +1706,153 @@ var removeMcpServer = (options) => {
1552
1706
  global: options.global,
1553
1707
  cwd: options.cwd
1554
1708
  });
1709
+ const clusters = resolveConfigClusters(allAgents, {
1710
+ global: options.global,
1711
+ cwd: options.cwd
1712
+ });
1555
1713
  const results = [];
1556
- for (const agentType of allAgents) {
1557
- const result = removeMcpServerFromAgent(options.name, agentType, {
1558
- global: options.global,
1559
- cwd: options.cwd
1560
- });
1561
- if (result.removed || result.error) results.push(result);
1714
+ for (const cluster of clusters) {
1715
+ const primaryAgentType = cluster.targetAgents[0];
1716
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1717
+ try {
1718
+ const { removed } = agentConfigStore.removeServer(primaryAgent, options.name, {
1719
+ global: options.global,
1720
+ cwd: options.cwd
1721
+ });
1722
+ if (removed) {
1723
+ for (const agentType of cluster.targetAgents) {
1724
+ results.push({
1725
+ agent: agentType,
1726
+ path: cluster.configPath,
1727
+ removed: true,
1728
+ coAffectedAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1729
+ });
1730
+ }
1731
+ }
1732
+ } catch (error) {
1733
+ const errorMsg = toErrorMessage(error);
1734
+ for (const agentType of cluster.targetAgents) {
1735
+ results.push({
1736
+ agent: agentType,
1737
+ path: cluster.configPath,
1738
+ removed: false,
1739
+ error: errorMsg
1740
+ });
1741
+ }
1742
+ }
1562
1743
  }
1563
1744
  return results;
1564
1745
  };
1565
1746
 
1747
+ // src/update-mcp-server.ts
1748
+ var toRemoteServerConfig = (config, defaultTransport = "http") => {
1749
+ const {
1750
+ command: _droppedCommand,
1751
+ args: _droppedArgs,
1752
+ env: _droppedEnv,
1753
+ ...remoteConfig
1754
+ } = config;
1755
+ return {
1756
+ ...remoteConfig,
1757
+ type: remoteConfig.type ?? defaultTransport
1758
+ };
1759
+ };
1760
+ var toStdioServerConfig = (config) => {
1761
+ const {
1762
+ url: _droppedUrl,
1763
+ type: _droppedType,
1764
+ headers: _droppedHeaders,
1765
+ ...stdioConfig
1766
+ } = config;
1767
+ return stdioConfig;
1768
+ };
1769
+ var detectUpdateTransition = (incoming, previous) => {
1770
+ if (!previous) {
1771
+ return incoming.url ? "switch-to-remote" : "switch-to-stdio";
1772
+ }
1773
+ if (incoming.url && !incoming.command) {
1774
+ return "switch-to-remote";
1775
+ }
1776
+ if (incoming.command && !incoming.url) {
1777
+ return "switch-to-stdio";
1778
+ }
1779
+ if (incoming.url || !incoming.command && previous.url) {
1780
+ return "merge-remote";
1781
+ }
1782
+ return "merge-stdio";
1783
+ };
1784
+ var sanitizeUpdatedServerConfig = (incoming, previous) => {
1785
+ const transition = detectUpdateTransition(incoming, previous);
1786
+ const targetTransport = incoming.type ?? previous?.type ?? "http";
1787
+ switch (transition) {
1788
+ case "switch-to-remote": {
1789
+ const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
1790
+ return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
1791
+ }
1792
+ case "switch-to-stdio": {
1793
+ const cleanBase = previous ? toStdioServerConfig(previous) : {};
1794
+ return toStdioServerConfig({ ...cleanBase, ...incoming });
1795
+ }
1796
+ case "merge-remote": {
1797
+ return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
1798
+ }
1799
+ case "merge-stdio": {
1800
+ return toStdioServerConfig({ ...previous, ...incoming });
1801
+ }
1802
+ }
1803
+ };
1804
+ var updateMcpServer = (options) => {
1805
+ const isGlobal = options.global ?? false;
1806
+ const cwd = options.cwd ?? process.cwd();
1807
+ let previousConfig = options.previousConfig;
1808
+ if (!previousConfig) {
1809
+ const existing = listInstalledMcpServers({
1810
+ global: isGlobal,
1811
+ cwd,
1812
+ agents: options.agents
1813
+ });
1814
+ const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
1815
+ if (found) {
1816
+ previousConfig = found.serverConfig;
1817
+ }
1818
+ }
1819
+ const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
1820
+ let targetAgents = options.agents;
1821
+ if (!targetAgents || targetAgents.length === 0) {
1822
+ const existing = listInstalledMcpServers({ global: isGlobal, cwd });
1823
+ targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
1824
+ }
1825
+ const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
1826
+ const { allAgents, incompatible } = resolveTargetAgents({
1827
+ requested: targetAgents,
1828
+ global: isGlobal,
1829
+ cwd,
1830
+ transport: requestedTransport
1831
+ });
1832
+ const results = installToCompatibleAgents(options.serverName, serverConfig, {
1833
+ allAgents,
1834
+ incompatible,
1835
+ global: isGlobal,
1836
+ cwd
1837
+ });
1838
+ return {
1839
+ serverName: options.serverName,
1840
+ config: serverConfig,
1841
+ results,
1842
+ incompatible
1843
+ };
1844
+ };
1845
+
1566
1846
  // src/interactive/main-menu.ts
1567
1847
  var import_prompts10 = require("@inquirer/prompts");
1568
- var import_picocolors10 = __toESM(require("picocolors"), 1);
1848
+ var import_picocolors15 = __toESM(require("picocolors"), 1);
1569
1849
 
1570
1850
  // src/interactive/wizard-add.ts
1571
1851
  var import_prompts7 = require("@inquirer/prompts");
1572
- var import_picocolors7 = __toESM(require("picocolors"), 1);
1852
+ var import_picocolors11 = __toESM(require("picocolors"), 1);
1853
+
1854
+ // src/utils/co-hosted-feedback.ts
1855
+ var import_picocolors2 = __toESM(require("picocolors"), 1);
1573
1856
 
1574
1857
  // src/utils/logger.ts
1575
1858
  var import_picocolors = __toESM(require("picocolors"), 1);
@@ -1588,13 +1871,269 @@ var logger = {
1588
1871
  }
1589
1872
  };
1590
1873
 
1874
+ // src/utils/co-hosted-feedback.ts
1875
+ var formatCoHostedBadge = (kind, agents) => {
1876
+ if (!agents || agents.length === 0) return "";
1877
+ const label = kind === "configured" ? "co-configured" : "co-affected";
1878
+ return ` ${import_picocolors2.default.yellow(`(${label}: ${agents.join(", ")})`)}`;
1879
+ };
1880
+ var logCoHostedNotice = (kind, agents) => {
1881
+ if (!agents || agents.length === 0) return;
1882
+ const actionText = kind === "configured" ? "Also configured for" : "Also affects";
1883
+ logger.info(
1884
+ ` ${import_picocolors2.default.dim("Note:")} ${actionText} co-hosted agent(s): ${import_picocolors2.default.yellow(agents.join(", "))}`
1885
+ );
1886
+ };
1887
+
1591
1888
  // src/interactive/prompts/agents.ts
1592
- var import_prompts2 = require("@inquirer/prompts");
1889
+ var import_picocolors6 = __toESM(require("picocolors"), 1);
1890
+
1891
+ // src/interactive/utils/build-linked-agent-choices.ts
1593
1892
  var import_picocolors3 = __toESM(require("picocolors"), 1);
1893
+ var buildLinkedAgentChoices = (options) => {
1894
+ const { agents, checkedAgents, detectedAgents = [], scopeOptions = {} } = options;
1895
+ const alignedCheckedSet = new Set(checkedAgents);
1896
+ for (const agent of checkedAgents) {
1897
+ const coHosted = getCoHostedAgents(agent, scopeOptions);
1898
+ for (const co of coHosted) {
1899
+ if (agents.includes(co)) {
1900
+ alignedCheckedSet.add(co);
1901
+ }
1902
+ }
1903
+ }
1904
+ return agents.map((agent) => {
1905
+ const config = getMcpAgentConfig(agent);
1906
+ const displayName = config?.displayName ?? agent;
1907
+ const isDetected = detectedAgents.includes(agent);
1908
+ const coHosted = getCoHostedAgents(agent, scopeOptions).filter(
1909
+ (co) => agents.includes(co)
1910
+ );
1911
+ const detectedBadge = isDetected ? import_picocolors3.default.green(" [detected]") : "";
1912
+ const sharedBadge = coHosted.length > 0 ? import_picocolors3.default.dim(` [shared: ${coHosted.join(", ")}]`) : "";
1913
+ const label = `${displayName} ${import_picocolors3.default.dim(`(${agent})`)}${detectedBadge}${sharedBadge}`;
1914
+ return {
1915
+ name: label,
1916
+ value: agent,
1917
+ checked: alignedCheckedSet.has(agent),
1918
+ linkedValues: coHosted,
1919
+ description: coHosted.length > 0 ? `Linked with ${coHosted.map((a) => getMcpAgentConfig(a).displayName).join(", ")} (shared configuration)` : void 0
1920
+ };
1921
+ });
1922
+ };
1923
+
1924
+ // src/interactive/prompts/linked-checkbox.ts
1925
+ var import_core = require("@inquirer/core");
1926
+ var import_picocolors4 = __toESM(require("picocolors"), 1);
1927
+ var defaultTheme = {
1928
+ icon: {
1929
+ checked: import_picocolors4.default.green("[x]"),
1930
+ unchecked: import_picocolors4.default.dim("[ ]"),
1931
+ cursor: import_picocolors4.default.cyan(">"),
1932
+ disabledChecked: import_picocolors4.default.dim("[x]"),
1933
+ disabledUnchecked: import_picocolors4.default.dim("[-]")
1934
+ },
1935
+ style: {
1936
+ disabled: (text) => import_picocolors4.default.dim(text),
1937
+ renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
1938
+ description: (text) => import_picocolors4.default.cyan(text),
1939
+ keysHelpTip: (keys) => keys.map(([key, action]) => `${import_picocolors4.default.bold(key)} ${import_picocolors4.default.dim(action)}`).join(import_picocolors4.default.dim(" | ")),
1940
+ highlight: (text) => import_picocolors4.default.cyan(text)
1941
+ },
1942
+ i18n: {
1943
+ disabledError: "This option is disabled and cannot be toggled."
1944
+ }
1945
+ };
1946
+ function isSelectable(item) {
1947
+ return !import_core.Separator.isSeparator(item) && !item.disabled;
1948
+ }
1949
+ function isNavigable(item) {
1950
+ return !import_core.Separator.isSeparator(item);
1951
+ }
1952
+ function isChecked(item) {
1953
+ return !import_core.Separator.isSeparator(item) && item.checked;
1954
+ }
1955
+ function normalizeChoices(choices) {
1956
+ return choices.map((choice) => {
1957
+ if (import_core.Separator.isSeparator(choice)) {
1958
+ return choice;
1959
+ }
1960
+ if (typeof choice !== "object" || choice === null || !("value" in choice)) {
1961
+ const name2 = String(choice);
1962
+ return {
1963
+ value: choice,
1964
+ name: name2,
1965
+ short: name2,
1966
+ checkedName: name2,
1967
+ disabled: false,
1968
+ checked: false,
1969
+ linkedValues: []
1970
+ };
1971
+ }
1972
+ const name = choice.name ?? String(choice.value);
1973
+ return {
1974
+ value: choice.value,
1975
+ name,
1976
+ short: choice.short ?? name,
1977
+ checkedName: choice.checkedName ?? name,
1978
+ description: choice.description,
1979
+ disabled: choice.disabled ?? false,
1980
+ checked: choice.checked ?? false,
1981
+ linkedValues: choice.linkedValues ?? []
1982
+ };
1983
+ });
1984
+ }
1985
+ var linkedCheckbox = (0, import_core.createPrompt)(
1986
+ (config, done) => {
1987
+ const { pageSize = 10, loop = true, required, validate = () => true } = config;
1988
+ const theme = (0, import_core.makeTheme)(defaultTheme, config.theme);
1989
+ const [status, setStatus] = (0, import_core.useState)("idle");
1990
+ const prefix = (0, import_core.usePrefix)({ status, theme });
1991
+ const [items, setItems] = (0, import_core.useState)(() => normalizeChoices(config.choices));
1992
+ const bounds = (0, import_core.useMemo)(() => {
1993
+ const first = items.findIndex(isNavigable);
1994
+ let last = -1;
1995
+ for (let i = items.length - 1; i >= 0; i--) {
1996
+ if (isNavigable(items[i])) {
1997
+ last = i;
1998
+ break;
1999
+ }
2000
+ }
2001
+ if (first === -1 || last === -1) {
2002
+ throw new import_core.ValidationError("[linkedCheckbox prompt] No selectable choices.");
2003
+ }
2004
+ return { first, last };
2005
+ }, [items]);
2006
+ const [active, setActive] = (0, import_core.useState)(bounds.first);
2007
+ const [errorMsg, setError] = (0, import_core.useState)();
2008
+ const toggleWithLinked = (targetIndex) => {
2009
+ const targetItem = items[targetIndex];
2010
+ if (!targetItem || import_core.Separator.isSeparator(targetItem) || targetItem.disabled) {
2011
+ return;
2012
+ }
2013
+ const nextChecked = !targetItem.checked;
2014
+ const targetValue = targetItem.value;
2015
+ const linked = new Set(targetItem.linkedValues);
2016
+ setItems(
2017
+ (prevItems) => prevItems.map((item) => {
2018
+ if (import_core.Separator.isSeparator(item) || item.disabled) {
2019
+ return item;
2020
+ }
2021
+ const isTargetOrLinked = item.value === targetValue || linked.has(item.value) || item.linkedValues.includes(targetValue);
2022
+ if (isTargetOrLinked) {
2023
+ return { ...item, checked: nextChecked };
2024
+ }
2025
+ return item;
2026
+ })
2027
+ );
2028
+ };
2029
+ (0, import_core.useKeypress)(async (key) => {
2030
+ if ((0, import_core.isEnterKey)(key)) {
2031
+ const selection = items.filter(isChecked);
2032
+ const isValid = await validate([...selection]);
2033
+ if (required && selection.length === 0) {
2034
+ setError("At least one choice must be selected");
2035
+ } else if (isValid === true) {
2036
+ setStatus("done");
2037
+ done(selection.map((choice) => choice.value));
2038
+ } else {
2039
+ setError(typeof isValid === "string" ? isValid : "You must select a valid value");
2040
+ }
2041
+ } else if ((0, import_core.isUpKey)(key) || (0, import_core.isDownKey)(key)) {
2042
+ if (errorMsg) setError(void 0);
2043
+ if (loop || (0, import_core.isUpKey)(key) && active !== bounds.first || (0, import_core.isDownKey)(key) && active !== bounds.last) {
2044
+ const offset = (0, import_core.isUpKey)(key) ? -1 : 1;
2045
+ let next = active;
2046
+ do {
2047
+ next = (next + offset + items.length) % items.length;
2048
+ } while (!isNavigable(items[next]));
2049
+ setActive(next);
2050
+ }
2051
+ } else if ((0, import_core.isSpaceKey)(key)) {
2052
+ const activeItem = items[active];
2053
+ if (activeItem && !import_core.Separator.isSeparator(activeItem)) {
2054
+ if (activeItem.disabled) {
2055
+ setError(theme.i18n.disabledError);
2056
+ } else {
2057
+ setError(void 0);
2058
+ toggleWithLinked(active);
2059
+ }
2060
+ }
2061
+ } else if (key.name === "a") {
2062
+ const hasUnchecked = items.some((choice) => isSelectable(choice) && !choice.checked);
2063
+ setItems(
2064
+ (prevItems) => prevItems.map((item) => isSelectable(item) ? { ...item, checked: hasUnchecked } : item)
2065
+ );
2066
+ } else if ((0, import_core.isNumberKey)(key)) {
2067
+ const selectedIndex = Number(key.name) - 1;
2068
+ let selectableIndex = -1;
2069
+ const position = items.findIndex((item) => {
2070
+ if (import_core.Separator.isSeparator(item)) return false;
2071
+ selectableIndex++;
2072
+ return selectableIndex === selectedIndex;
2073
+ });
2074
+ const selectedItem = items[position];
2075
+ if (selectedItem && isSelectable(selectedItem)) {
2076
+ setActive(position);
2077
+ setError(void 0);
2078
+ toggleWithLinked(position);
2079
+ }
2080
+ }
2081
+ });
2082
+ const message = theme.style.message(config.message, status);
2083
+ let description;
2084
+ const page = (0, import_core.usePagination)({
2085
+ items,
2086
+ active,
2087
+ renderItem({ item, isActive }) {
2088
+ if (import_core.Separator.isSeparator(item)) {
2089
+ return ` ${item.separator}`;
2090
+ }
2091
+ const cursor = isActive ? theme.icon.cursor : " ";
2092
+ if (item.disabled) {
2093
+ const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
2094
+ const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
2095
+ return theme.style.disabled(`${cursor} ${checkbox2} ${item.name} ${disabledLabel}`);
2096
+ }
2097
+ if (isActive) {
2098
+ description = item.description;
2099
+ }
2100
+ const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
2101
+ const name = item.checked ? item.checkedName : item.name;
2102
+ const color = isActive ? theme.style.highlight : (x) => x;
2103
+ return color(`${cursor} ${checkbox} ${name}`);
2104
+ },
2105
+ pageSize,
2106
+ loop
2107
+ });
2108
+ if (status === "done") {
2109
+ const selection = items.filter(isChecked);
2110
+ const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
2111
+ return [prefix, message, answer].filter(Boolean).join(" ");
2112
+ }
2113
+ const helpLine = theme.style.keysHelpTip([
2114
+ ["up/down", "navigate"],
2115
+ ["space", "toggle"],
2116
+ ["a", "all"],
2117
+ ["enter", "submit"]
2118
+ ]);
2119
+ const lines = [
2120
+ [prefix, message].filter(Boolean).join(" "),
2121
+ page,
2122
+ helpLine
2123
+ ];
2124
+ if (description) {
2125
+ lines.push(theme.style.description(description));
2126
+ }
2127
+ if (errorMsg) {
2128
+ lines.push(theme.style.error(errorMsg));
2129
+ }
2130
+ return lines.join("\n");
2131
+ }
2132
+ );
1594
2133
 
1595
2134
  // src/interactive/prompts/scope.ts
1596
2135
  var import_prompts = require("@inquirer/prompts");
1597
- var import_picocolors2 = __toESM(require("picocolors"), 1);
2136
+ var import_picocolors5 = __toESM(require("picocolors"), 1);
1598
2137
  var promptScope = async (options = {}) => {
1599
2138
  const initialGlobal = options.defaultGlobal ?? options.global;
1600
2139
  if (initialGlobal !== void 0) {
@@ -1605,11 +2144,11 @@ var promptScope = async (options = {}) => {
1605
2144
  message: options.message ?? "Select MCP scope:",
1606
2145
  choices: [
1607
2146
  {
1608
- name: `Current Project - ${import_picocolors2.default.dim(cwd)}`,
2147
+ name: `Current Project - ${import_picocolors5.default.dim(cwd)}`,
1609
2148
  value: false
1610
2149
  },
1611
2150
  {
1612
- name: `Global User Config - ${import_picocolors2.default.dim("applies across all projects")}`,
2151
+ name: `Global User Config - ${import_picocolors5.default.dim("applies across all projects")}`,
1613
2152
  value: true
1614
2153
  }
1615
2154
  ]
@@ -1629,26 +2168,23 @@ var promptScopeAndAgents = async (options = {}) => {
1629
2168
  cwd
1630
2169
  });
1631
2170
  const detected = resolution.detected;
1632
- const availableAgentTypes = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2171
+ const rawAvailable = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2172
+ const availableAgentTypes = sortAgentsWithClusters(rawAvailable, { global: isGlobal, cwd });
1633
2173
  if (detected.length > 0) {
1634
2174
  logger.info(
1635
- `Detected configured agents: ${import_picocolors3.default.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2175
+ `Detected configured agents: ${import_picocolors6.default.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
1636
2176
  );
1637
2177
  } else {
1638
2178
  logger.warn(`No active ${isGlobal ? "global" : "project"} agents detected`);
1639
2179
  }
1640
2180
  const defaultChecked = options.defaultAgents && options.defaultAgents.length > 0 ? options.defaultAgents : detected;
1641
- const choices = availableAgentTypes.map((agentType) => {
1642
- const config = getMcpAgentConfig(agentType);
1643
- const isDetected = detected.includes(agentType);
1644
- const label = `${config.displayName} ${import_picocolors3.default.dim(`(${agentType})`)}${isDetected ? import_picocolors3.default.green(" [detected]") : ""}`;
1645
- return {
1646
- name: label,
1647
- value: agentType,
1648
- checked: defaultChecked.includes(agentType)
1649
- };
2181
+ const choices = buildLinkedAgentChoices({
2182
+ agents: availableAgentTypes,
2183
+ checkedAgents: defaultChecked,
2184
+ detectedAgents: detected,
2185
+ scopeOptions: { global: isGlobal, cwd }
1650
2186
  });
1651
- const selectedAgents = await (0, import_prompts2.checkbox)({
2187
+ const selectedAgents = await linkedCheckbox({
1652
2188
  message: "Select target agents (Space to select, Enter to confirm):",
1653
2189
  choices,
1654
2190
  validate: (chosen) => {
@@ -1665,7 +2201,7 @@ var promptScopeAndAgents = async (options = {}) => {
1665
2201
  };
1666
2202
 
1667
2203
  // src/interactive/prompts/args.ts
1668
- var import_prompts3 = require("@inquirer/prompts");
2204
+ var import_prompts2 = require("@inquirer/prompts");
1669
2205
  var parseArgsString = (rawText) => {
1670
2206
  const matches = rawText.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g);
1671
2207
  if (!matches) return [];
@@ -1680,32 +2216,67 @@ var promptArgsConfig = async (initialArgs = []) => {
1680
2216
  if (initialArgs.length > 0) {
1681
2217
  return initialArgs;
1682
2218
  }
1683
- const needArgs = await (0, import_prompts3.confirm)({
2219
+ const needArgs = await (0, import_prompts2.confirm)({
1684
2220
  message: "Configure command arguments (e.g. file paths, connection strings)?",
1685
2221
  default: false
1686
2222
  });
1687
2223
  if (!needArgs) {
1688
2224
  return [];
1689
2225
  }
1690
- const raw = await (0, import_prompts3.input)({
2226
+ const raw = await (0, import_prompts2.input)({
1691
2227
  message: "Enter command arguments (space-separated, wrap paths with spaces in quotes):",
1692
2228
  validate: (val) => val.trim() ? true : "Arguments cannot be empty"
1693
2229
  });
1694
2230
  return parseArgsString(raw.trim());
1695
2231
  };
2232
+ var formatArgsString = (args) => {
2233
+ return args.map((arg) => arg.includes(" ") || arg.includes('"') ? `"${arg.replace(/"/g, '\\"')}"` : arg).join(" ");
2234
+ };
2235
+ var promptEditArgs = async (currentArgs = []) => {
2236
+ const defaultStr = formatArgsString(currentArgs);
2237
+ const raw = await (0, import_prompts2.input)({
2238
+ message: "Edit command arguments (space-separated, wrap paths with spaces in quotes, leave empty to clear):",
2239
+ default: defaultStr
2240
+ });
2241
+ const trimmed = raw.trim();
2242
+ if (!trimmed) {
2243
+ return [];
2244
+ }
2245
+ return parseArgsString(trimmed);
2246
+ };
1696
2247
 
1697
2248
  // src/interactive/prompts/env.ts
1698
2249
  var import_prompts5 = require("@inquirer/prompts");
1699
- var import_picocolors5 = __toESM(require("picocolors"), 1);
2250
+ var import_picocolors9 = __toESM(require("picocolors"), 1);
2251
+
2252
+ // src/utils/mask-secret.ts
2253
+ var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
2254
+ var maskSecretValue = (key, value) => {
2255
+ if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
2256
+ return value;
2257
+ }
2258
+ return `${value.slice(0, 2)}***${value.slice(-2)}`;
2259
+ };
2260
+ var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
2261
+ var maskSecretHeader = (key, value) => {
2262
+ if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
2263
+ return value;
2264
+ }
2265
+ return `${value.slice(0, 4)}***${value.slice(-3)}`;
2266
+ };
2267
+
2268
+ // src/interactive/prompts/kv.ts
2269
+ var import_prompts4 = require("@inquirer/prompts");
2270
+ var import_picocolors8 = __toESM(require("picocolors"), 1);
1700
2271
 
1701
2272
  // src/interactive/prompts/multiline.ts
1702
2273
  var import_node_readline = require("readline");
1703
- var import_prompts4 = require("@inquirer/prompts");
1704
- var import_picocolors4 = __toESM(require("picocolors"), 1);
2274
+ var import_prompts3 = require("@inquirer/prompts");
2275
+ var import_picocolors7 = __toESM(require("picocolors"), 1);
1705
2276
  var readMultilineTextFromTerminal = async (message, endHint = "When done pasting, enter END on a new line or press Enter twice to finish") => {
1706
- console.log(import_picocolors4.default.cyan(`
2277
+ console.log(import_picocolors7.default.cyan(`
1707
2278
  ${message}`));
1708
- console.log(import_picocolors4.default.dim(` (Hint: ${endHint})
2279
+ console.log(import_picocolors7.default.dim(` (Hint: ${endHint})
1709
2280
  `));
1710
2281
  return new Promise((resolve) => {
1711
2282
  const rl = (0, import_node_readline.createInterface)({
@@ -1749,7 +2320,7 @@ ${message}`));
1749
2320
  };
1750
2321
  var promptEditorText = async (options) => {
1751
2322
  try {
1752
- return await (0, import_prompts4.editor)({
2323
+ return await (0, import_prompts3.editor)({
1753
2324
  message: options.message,
1754
2325
  default: options.defaultText ?? "",
1755
2326
  postfix: options.postfix
@@ -1759,8 +2330,157 @@ var promptEditorText = async (options) => {
1759
2330
  }
1760
2331
  };
1761
2332
 
2333
+ // src/interactive/prompts/kv.ts
2334
+ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
2335
+ let items = { ...currentItems };
2336
+ while (true) {
2337
+ const keys = Object.keys(items);
2338
+ console.log();
2339
+ if (keys.length === 0) {
2340
+ console.log(import_picocolors8.default.dim(` No ${options.itemsNoun} configured.`));
2341
+ } else {
2342
+ console.log(import_picocolors8.default.cyan(import_picocolors8.default.bold(` Configured ${options.title} (${keys.length}):`)));
2343
+ for (const [k, v] of Object.entries(items)) {
2344
+ const sep = options.separator === "=" ? "=" : ": ";
2345
+ console.log(` ${import_picocolors8.default.bold(k)}${sep}${import_picocolors8.default.dim(options.maskValue(k, v))}`);
2346
+ }
2347
+ }
2348
+ console.log();
2349
+ const choice = await (0, import_prompts4.select)({
2350
+ message: `Manage ${options.itemsNoun}:`,
2351
+ choices: [
2352
+ {
2353
+ name: "Open in system default editor ($EDITOR)",
2354
+ value: "editor"
2355
+ },
2356
+ {
2357
+ name: `Add or modify a ${options.itemNoun}`,
2358
+ value: "upsert"
2359
+ },
2360
+ ...keys.length > 0 ? [
2361
+ {
2362
+ name: `Delete a ${options.itemNoun}`,
2363
+ value: "delete"
2364
+ }
2365
+ ] : [],
2366
+ {
2367
+ name: `Paste multiline ${options.itemsNoun} into terminal`,
2368
+ value: "paste"
2369
+ },
2370
+ ...keys.length > 0 ? [
2371
+ {
2372
+ name: `Clear all ${options.itemsNoun}`,
2373
+ value: "clear"
2374
+ }
2375
+ ] : [],
2376
+ {
2377
+ name: `Done (finish editing ${options.itemsNoun})`,
2378
+ value: "done"
2379
+ }
2380
+ ]
2381
+ });
2382
+ if (choice === "done") {
2383
+ return items;
2384
+ }
2385
+ if (choice === "editor") {
2386
+ const defaultText = options.formatText(items);
2387
+ const text = await promptEditorText({
2388
+ message: options.editorMessage,
2389
+ postfix: options.editorPostfix,
2390
+ defaultText
2391
+ });
2392
+ const parsed = options.parseText(text);
2393
+ items = parsed;
2394
+ logger.success(`${options.title} updated (${Object.keys(items).length} total)`);
2395
+ } else if (choice === "upsert") {
2396
+ const key = await (0, import_prompts4.input)({
2397
+ message: options.keyPromptMessage,
2398
+ validate: (val) => {
2399
+ const trimmed = val.trim();
2400
+ if (!trimmed) return `${options.itemNoun} name cannot be empty`;
2401
+ if (/\s/.test(trimmed)) return `${options.itemNoun} name cannot contain spaces`;
2402
+ return true;
2403
+ }
2404
+ });
2405
+ const trimmedKey = key.trim();
2406
+ const existingVal = items[trimmedKey];
2407
+ const isSecret = options.isSecretKey(trimmedKey);
2408
+ let newVal;
2409
+ if (isSecret) {
2410
+ newVal = await (0, import_prompts4.password)({
2411
+ message: existingVal !== void 0 ? `New value for (${trimmedKey}) [leave empty to keep current]:` : `${options.valuePromptMessage} for (${trimmedKey}) [sensitive content masked]:`,
2412
+ mask: "*"
2413
+ });
2414
+ if (existingVal !== void 0 && newVal === "") {
2415
+ newVal = existingVal;
2416
+ }
2417
+ } else {
2418
+ newVal = await (0, import_prompts4.input)({
2419
+ message: `${options.valuePromptMessage} for (${trimmedKey}):`,
2420
+ default: existingVal
2421
+ });
2422
+ }
2423
+ items[trimmedKey] = newVal;
2424
+ logger.success(`${existingVal !== void 0 ? "Updated" : "Added"}: ${import_picocolors8.default.cyan(trimmedKey)}`);
2425
+ } else if (choice === "delete") {
2426
+ const toDelete = await (0, import_prompts4.select)({
2427
+ message: `Select ${options.itemNoun} to delete:`,
2428
+ choices: [
2429
+ ...keys.map((k) => ({ name: k, value: k })),
2430
+ { name: "Cancel", value: "__cancel__" }
2431
+ ]
2432
+ });
2433
+ if (toDelete !== "__cancel__") {
2434
+ delete items[toDelete];
2435
+ logger.success(`Deleted: ${import_picocolors8.default.cyan(toDelete)}`);
2436
+ }
2437
+ } else if (choice === "paste") {
2438
+ const pasted = await readMultilineTextFromTerminal(options.pasteMessage);
2439
+ const parsed = options.parseText(pasted);
2440
+ const count = Object.keys(parsed).length;
2441
+ if (count === 0) {
2442
+ logger.warn(`No valid ${options.itemsNoun} recognized`);
2443
+ } else {
2444
+ if (keys.length > 0) {
2445
+ const pasteMode = await (0, import_prompts4.select)({
2446
+ message: `How to apply pasted ${options.itemsNoun}?`,
2447
+ choices: [
2448
+ { name: `Merge with existing ${options.itemsNoun}`, value: "merge" },
2449
+ { name: `Replace all existing ${options.itemsNoun}`, value: "replace" }
2450
+ ]
2451
+ });
2452
+ if (pasteMode === "replace") {
2453
+ items = parsed;
2454
+ } else {
2455
+ Object.assign(items, parsed);
2456
+ }
2457
+ } else {
2458
+ items = parsed;
2459
+ }
2460
+ logger.success(`Successfully applied ${import_picocolors8.default.cyan(String(count))} ${options.itemsNoun}`);
2461
+ }
2462
+ } else if (choice === "clear") {
2463
+ const confirmClear = await (0, import_prompts4.confirm)({
2464
+ message: `Are you sure you want to clear all ${options.itemsNoun}?`,
2465
+ default: false
2466
+ });
2467
+ if (confirmClear) {
2468
+ items = {};
2469
+ logger.success(`Cleared all ${options.itemsNoun}`);
2470
+ }
2471
+ }
2472
+ }
2473
+ };
2474
+
1762
2475
  // src/interactive/prompts/env.ts
1763
- var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
2476
+ var formatEnvText = (env) => {
2477
+ return Object.entries(env).map(([key, value]) => {
2478
+ if (/[\s"']/.test(value)) {
2479
+ return `${key}="${value.replace(/"/g, '\\"')}"`;
2480
+ }
2481
+ return `${key}=${value}`;
2482
+ }).join("\n");
2483
+ };
1764
2484
  var parseEnvText = (rawText) => {
1765
2485
  const result = {};
1766
2486
  const lines = rawText.split(/\r?\n/);
@@ -1786,7 +2506,7 @@ var promptEnvConfig = async (initialEnv = {}) => {
1786
2506
  const env = { ...initialEnv };
1787
2507
  const initialCount = Object.keys(env).length;
1788
2508
  if (initialCount > 0) {
1789
- logger.info(`Includes ${import_picocolors5.default.cyan(String(initialCount))} preset environment variables`);
2509
+ logger.info(`Includes ${import_picocolors9.default.cyan(String(initialCount))} preset environment variables`);
1790
2510
  }
1791
2511
  const mode = await (0, import_prompts5.select)({
1792
2512
  message: "Configure environment variables?",
@@ -1815,7 +2535,8 @@ var promptEnvConfig = async (initialEnv = {}) => {
1815
2535
  if (mode === "paste" || mode === "editor") {
1816
2536
  const pasted = mode === "editor" ? await promptEditorText({
1817
2537
  message: "Paste or edit environment variables in editor, then save and exit:",
1818
- postfix: ".env"
2538
+ postfix: ".env",
2539
+ defaultText: formatEnvText(env)
1819
2540
  }) : await readMultilineTextFromTerminal("Paste .env formatted content (multiline supported):");
1820
2541
  const parsed = parseEnvText(pasted);
1821
2542
  const count = Object.keys(parsed).length;
@@ -1823,10 +2544,9 @@ var promptEnvConfig = async (initialEnv = {}) => {
1823
2544
  logger.warn("No valid KEY=VALUE pairs recognized");
1824
2545
  } else {
1825
2546
  Object.assign(env, parsed);
1826
- logger.success(`Successfully parsed ${import_picocolors5.default.cyan(String(count))} environment variables:`);
2547
+ logger.success(`Successfully parsed ${import_picocolors9.default.cyan(String(count))} environment variables:`);
1827
2548
  for (const [k, v] of Object.entries(parsed)) {
1828
- const masked = SECRET_KEY_PATTERN.test(k) && v.length > 4 ? `${v.slice(0, 2)}***${v.slice(-2)}` : v;
1829
- console.log(` ${import_picocolors5.default.bold(k)}=${import_picocolors5.default.dim(masked)}`);
2549
+ console.log(` ${import_picocolors9.default.bold(k)}=${import_picocolors9.default.dim(maskSecretValue(k, v))}`);
1830
2550
  }
1831
2551
  }
1832
2552
  return env;
@@ -1857,15 +2577,32 @@ var promptEnvConfig = async (initialEnv = {}) => {
1857
2577
  });
1858
2578
  }
1859
2579
  env[trimmedKey] = val;
1860
- logger.success(`Added: ${import_picocolors5.default.cyan(trimmedKey)}`);
2580
+ logger.success(`Added: ${import_picocolors9.default.cyan(trimmedKey)}`);
1861
2581
  }
1862
2582
  return env;
1863
2583
  };
2584
+ var promptEditEnvConfig = async (currentEnv = {}) => promptEditKeyValueConfig(currentEnv, {
2585
+ title: "Environment Variables",
2586
+ itemNoun: "variable",
2587
+ itemsNoun: "environment variables",
2588
+ separator: "=",
2589
+ editorPostfix: ".env",
2590
+ editorMessage: "Edit environment variables in editor, then save and exit:",
2591
+ pasteMessage: "Paste .env formatted content (multiline supported):",
2592
+ keyPromptMessage: "Variable name (Key):",
2593
+ valuePromptMessage: "Value",
2594
+ isSecretKey: (k) => SECRET_KEY_PATTERN.test(k),
2595
+ maskValue: maskSecretValue,
2596
+ formatText: formatEnvText,
2597
+ parseText: parseEnvText
2598
+ });
1864
2599
 
1865
2600
  // src/interactive/prompts/headers.ts
1866
2601
  var import_prompts6 = require("@inquirer/prompts");
1867
- var import_picocolors6 = __toESM(require("picocolors"), 1);
1868
- var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
2602
+ var import_picocolors10 = __toESM(require("picocolors"), 1);
2603
+ var formatHeadersText = (headers) => {
2604
+ return Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n");
2605
+ };
1869
2606
  var parseHeadersText = (rawText) => {
1870
2607
  const result = {};
1871
2608
  const lines = rawText.split(/\r?\n/);
@@ -1921,7 +2658,8 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1921
2658
  }
1922
2659
  if (mode === "paste" || mode === "editor") {
1923
2660
  const pasted = mode === "editor" ? await promptEditorText({
1924
- message: "Paste or edit HTTP headers in editor, then save and exit:"
2661
+ message: "Paste or edit HTTP headers in editor, then save and exit:",
2662
+ defaultText: formatHeadersText(headers)
1925
2663
  }) : await readMultilineTextFromTerminal(
1926
2664
  "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):"
1927
2665
  );
@@ -1931,10 +2669,9 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1931
2669
  logger.warn("No valid Key: Value pairs recognized");
1932
2670
  } else {
1933
2671
  Object.assign(headers, parsed);
1934
- logger.success(`Successfully parsed ${import_picocolors6.default.cyan(String(count))} headers:`);
2672
+ logger.success(`Successfully parsed ${import_picocolors10.default.cyan(String(count))} headers:`);
1935
2673
  for (const [k, v] of Object.entries(parsed)) {
1936
- const masked = SECRET_HEADER_PATTERN.test(k) && v.length > 8 ? `${v.slice(0, 4)}***${v.slice(-3)}` : v;
1937
- console.log(` ${import_picocolors6.default.bold(k)}: ${import_picocolors6.default.dim(masked)}`);
2674
+ console.log(` ${import_picocolors10.default.bold(k)}: ${import_picocolors10.default.dim(maskSecretHeader(k, v))}`);
1938
2675
  }
1939
2676
  }
1940
2677
  return headers;
@@ -1965,15 +2702,29 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1965
2702
  });
1966
2703
  }
1967
2704
  headers[trimmedName] = val;
1968
- logger.success(`Added: ${import_picocolors6.default.cyan(trimmedName)}`);
2705
+ logger.success(`Added: ${import_picocolors10.default.cyan(trimmedName)}`);
1969
2706
  }
1970
2707
  return headers;
1971
2708
  };
2709
+ var promptEditHeadersConfig = async (currentHeaders = {}) => promptEditKeyValueConfig(currentHeaders, {
2710
+ title: "HTTP Headers",
2711
+ itemNoun: "header",
2712
+ itemsNoun: "HTTP headers",
2713
+ separator: ":",
2714
+ editorMessage: "Edit HTTP headers in editor, then save and exit:",
2715
+ pasteMessage: "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):",
2716
+ keyPromptMessage: "Header name (e.g. Authorization):",
2717
+ valuePromptMessage: "Header value",
2718
+ isSecretKey: (k) => SECRET_HEADER_PATTERN.test(k),
2719
+ maskValue: maskSecretHeader,
2720
+ formatText: formatHeadersText,
2721
+ parseText: parseHeadersText
2722
+ });
1972
2723
 
1973
2724
  // src/interactive/wizard-add.ts
1974
2725
  var wizardAdd = async (initial = {}) => {
1975
2726
  const cwd = initial.cwd ?? process.cwd();
1976
- logger.info(import_picocolors7.default.bold("Welcome to the MCP interactive add wizard"));
2727
+ logger.info(import_picocolors11.default.bold("Welcome to the MCP interactive add wizard"));
1977
2728
  let source = initial.source;
1978
2729
  if (!source) {
1979
2730
  const sourceType = await (0, import_prompts7.select)({
@@ -1995,7 +2746,7 @@ var wizardAdd = async (initial = {}) => {
1995
2746
  });
1996
2747
  if (sourceType === "npm") {
1997
2748
  source = await (0, import_prompts7.input)({
1998
- message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres or mcp-server-git):",
2749
+ message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres):",
1999
2750
  validate: (val) => val.trim() ? true : "Package name cannot be empty"
2000
2751
  });
2001
2752
  } else if (sourceType === "remote") {
@@ -2063,25 +2814,25 @@ var wizardAdd = async (initial = {}) => {
2063
2814
  if (parsed.type !== "remote") {
2064
2815
  env = await promptEnvConfig(env);
2065
2816
  }
2066
- console.log("\n" + import_picocolors7.default.cyan(import_picocolors7.default.bold("Configuration Preview:")));
2067
- console.log(` ${import_picocolors7.default.bold("Server Name:")} ${import_picocolors7.default.green(serverName)}`);
2068
- console.log(` ${import_picocolors7.default.bold("Server Type:")} ${import_picocolors7.default.magenta(parsed.type)}`);
2069
- console.log(` ${import_picocolors7.default.bold("Source/Command:")} ${import_picocolors7.default.dim(source)}`);
2070
- console.log(` ${import_picocolors7.default.bold("Scope:")} ${isGlobal ? import_picocolors7.default.yellow("Global") : import_picocolors7.default.blue("Project")}`);
2071
- console.log(` ${import_picocolors7.default.bold("Target Agents:")} ${import_picocolors7.default.cyan(selectedAgents.join(", "))}`);
2817
+ console.log("\n" + import_picocolors11.default.cyan(import_picocolors11.default.bold("Configuration Preview:")));
2818
+ console.log(` ${import_picocolors11.default.bold("Server Name:")} ${import_picocolors11.default.green(serverName)}`);
2819
+ console.log(` ${import_picocolors11.default.bold("Server Type:")} ${import_picocolors11.default.magenta(parsed.type)}`);
2820
+ console.log(` ${import_picocolors11.default.bold("Source/Command:")} ${import_picocolors11.default.dim(source)}`);
2821
+ console.log(` ${import_picocolors11.default.bold("Scope:")} ${isGlobal ? import_picocolors11.default.yellow("Global") : import_picocolors11.default.blue("Project")}`);
2822
+ console.log(` ${import_picocolors11.default.bold("Target Agents:")} ${import_picocolors11.default.cyan(selectedAgents.join(", "))}`);
2072
2823
  if (args.length > 0) {
2073
- console.log(` ${import_picocolors7.default.bold("Arguments:")} ${import_picocolors7.default.dim(args.join(" "))}`);
2824
+ console.log(` ${import_picocolors11.default.bold("Arguments:")} ${import_picocolors11.default.dim(args.join(" "))}`);
2074
2825
  }
2075
2826
  if (transport) {
2076
- console.log(` ${import_picocolors7.default.bold("Transport:")} ${import_picocolors7.default.magenta(transport)}`);
2827
+ console.log(` ${import_picocolors11.default.bold("Transport:")} ${import_picocolors11.default.magenta(transport)}`);
2077
2828
  }
2078
2829
  const envKeys = Object.keys(env);
2079
2830
  if (envKeys.length > 0) {
2080
- console.log(` ${import_picocolors7.default.bold("Environment Variables:")} ${import_picocolors7.default.dim(envKeys.join(", "))} (${envKeys.length})`);
2831
+ console.log(` ${import_picocolors11.default.bold("Environment Variables:")} ${import_picocolors11.default.dim(envKeys.join(", "))} (${envKeys.length})`);
2081
2832
  }
2082
2833
  const headerKeys = Object.keys(headers);
2083
2834
  if (headerKeys.length > 0) {
2084
- console.log(` ${import_picocolors7.default.bold("Headers:")} ${import_picocolors7.default.dim(headerKeys.join(", "))} (${headerKeys.length})`);
2835
+ console.log(` ${import_picocolors11.default.bold("Headers:")} ${import_picocolors11.default.dim(headerKeys.join(", "))} (${headerKeys.length})`);
2085
2836
  }
2086
2837
  console.log();
2087
2838
  const proceed = await (0, import_prompts7.confirm)({
@@ -2104,41 +2855,103 @@ var wizardAdd = async (initial = {}) => {
2104
2855
  env
2105
2856
  });
2106
2857
  logger.info(
2107
- `Writing ${import_picocolors7.default.bold(result.serverName)} to ${import_picocolors7.default.cyan(String(result.results.length))} agent config files...`
2858
+ `Writing ${import_picocolors11.default.bold(result.serverName)} to ${import_picocolors11.default.cyan(String(result.results.length))} agent config files...`
2108
2859
  );
2109
2860
  let allSuccess = true;
2110
2861
  for (const record of result.results) {
2111
2862
  if (record.success) {
2112
- logger.success(`${import_picocolors7.default.cyan(record.agent)}: Successfully written to ${import_picocolors7.default.dim(record.path)}`);
2863
+ logger.success(
2864
+ `${import_picocolors11.default.cyan(record.agent)}: Successfully written to ${import_picocolors11.default.dim(record.path)}${formatCoHostedBadge("configured", record.coConfiguredAgents)}`
2865
+ );
2113
2866
  } else {
2114
2867
  allSuccess = false;
2115
- logger.error(`${import_picocolors7.default.cyan(record.agent)}: Failed to write - ${record.error}`);
2868
+ logger.error(`${import_picocolors11.default.cyan(record.agent)}: Failed to write - ${record.error}`);
2116
2869
  }
2117
2870
  }
2118
2871
  if (allSuccess) {
2119
- logger.success(import_picocolors7.default.bold(`MCP server "${serverName}" configured successfully!`));
2872
+ logger.success(import_picocolors11.default.bold(`MCP server "${serverName}" configured successfully!`));
2120
2873
  }
2121
2874
  return allSuccess;
2122
2875
  };
2123
2876
 
2124
2877
  // src/interactive/wizard-manage.ts
2125
2878
  var import_prompts8 = require("@inquirer/prompts");
2126
- var import_picocolors8 = __toESM(require("picocolors"), 1);
2879
+ var import_picocolors13 = __toESM(require("picocolors"), 1);
2880
+
2881
+ // src/utils/display-server-details.ts
2882
+ var import_picocolors12 = __toESM(require("picocolors"), 1);
2883
+ var displayServerDetails = ({
2884
+ serverName,
2885
+ config,
2886
+ agents,
2887
+ hasDivergence,
2888
+ global: isGlobal,
2889
+ titlePrefix = "MCP Server Details"
2890
+ }) => {
2891
+ console.log("\n" + import_picocolors12.default.cyan(import_picocolors12.default.bold(`${titlePrefix}: [${serverName}]`)));
2892
+ if (isGlobal !== void 0) {
2893
+ console.log(` ${import_picocolors12.default.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
2894
+ }
2895
+ if (agents && agents.length > 0) {
2896
+ console.log(
2897
+ ` ${import_picocolors12.default.bold("Configured Agents:")} ${import_picocolors12.default.green(agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2898
+ );
2899
+ }
2900
+ if (hasDivergence) {
2901
+ console.log(
2902
+ ` ${import_picocolors12.default.yellow(import_picocolors12.default.bold("Notice:"))} ${import_picocolors12.default.yellow("Configurations differ across installed agents. Showing configuration from the first agent.")}`
2903
+ );
2904
+ }
2905
+ const isRemote = Boolean(config.url && config.url.length > 0);
2906
+ if (isRemote) {
2907
+ console.log(` ${import_picocolors12.default.bold("Transport:")} ${import_picocolors12.default.magenta(config.type ?? "http")}`);
2908
+ console.log(` ${import_picocolors12.default.bold("URL:")} ${import_picocolors12.default.dim(config.url ?? "")}`);
2909
+ const headerKeys = Object.keys(config.headers ?? {});
2910
+ if (headerKeys.length > 0) {
2911
+ console.log(` ${import_picocolors12.default.bold("Headers:")} ${import_picocolors12.default.cyan(String(headerKeys.length))}`);
2912
+ for (const [k, v] of Object.entries(config.headers ?? {})) {
2913
+ console.log(` ${import_picocolors12.default.bold(k)}: ${import_picocolors12.default.dim(maskSecretHeader(k, v))}`);
2914
+ }
2915
+ } else {
2916
+ console.log(` ${import_picocolors12.default.bold("Headers:")} ${import_picocolors12.default.dim("(none)")}`);
2917
+ }
2918
+ } else {
2919
+ console.log(` ${import_picocolors12.default.bold("Command:")} ${import_picocolors12.default.magenta(config.command ?? "")}`);
2920
+ const argsStr = config.args && config.args.length > 0 ? config.args.join(" ") : "(none)";
2921
+ console.log(` ${import_picocolors12.default.bold("Arguments:")} ${import_picocolors12.default.dim(argsStr)}`);
2922
+ const envKeys = Object.keys(config.env ?? {});
2923
+ if (envKeys.length > 0) {
2924
+ console.log(` ${import_picocolors12.default.bold("Environment Variables:")} ${import_picocolors12.default.cyan(String(envKeys.length))}`);
2925
+ for (const [k, v] of Object.entries(config.env ?? {})) {
2926
+ console.log(` ${import_picocolors12.default.bold(k)}=${import_picocolors12.default.dim(maskSecretValue(k, v))}`);
2927
+ }
2928
+ } else {
2929
+ console.log(` ${import_picocolors12.default.bold("Environment Variables:")} ${import_picocolors12.default.dim("(none)")}`);
2930
+ }
2931
+ }
2932
+ console.log();
2933
+ };
2127
2934
 
2128
2935
  // src/interactive/utils/group-installed-servers.ts
2129
2936
  var normalizeServerConfig = parseServerConfig;
2130
2937
  var groupInstalledServersByName = (installed) => {
2131
2938
  const grouped = /* @__PURE__ */ new Map();
2132
2939
  for (const item of installed) {
2940
+ const itemConfig = normalizeServerConfig(item.config);
2133
2941
  let entry = grouped.get(item.serverName);
2134
2942
  if (!entry) {
2135
2943
  entry = {
2136
2944
  serverName: item.serverName,
2137
2945
  agents: [],
2138
2946
  paths: [],
2139
- config: normalizeServerConfig(item.config)
2947
+ config: itemConfig,
2948
+ hasDivergence: false
2140
2949
  };
2141
2950
  grouped.set(item.serverName, entry);
2951
+ } else if (!entry.hasDivergence) {
2952
+ if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
2953
+ entry.hasDivergence = true;
2954
+ }
2142
2955
  }
2143
2956
  if (!entry.agents.includes(item.agent)) {
2144
2957
  entry.agents.push(item.agent);
@@ -2151,6 +2964,221 @@ var groupInstalledServersByName = (installed) => {
2151
2964
  };
2152
2965
 
2153
2966
  // src/interactive/wizard-manage.ts
2967
+ var promptSwitchServerType = async (currentConfig, serverName) => {
2968
+ const isRemote = Boolean(currentConfig.url && currentConfig.url.length > 0);
2969
+ if (isRemote) {
2970
+ const newCmd = await (0, import_prompts8.input)({
2971
+ message: "Executable command (e.g. node, npx):",
2972
+ validate: (val) => val.trim() ? true : "Command cannot be empty"
2973
+ });
2974
+ const newArgs = await promptEditArgs([]);
2975
+ const newEnv = await promptEditEnvConfig({});
2976
+ logger.success(`Switched [${serverName}] configuration to stdio mode`);
2977
+ return {
2978
+ command: newCmd.trim(),
2979
+ args: newArgs.length > 0 ? newArgs : void 0,
2980
+ env: Object.keys(newEnv).length > 0 ? newEnv : void 0
2981
+ };
2982
+ }
2983
+ const newUrl = await (0, import_prompts8.input)({
2984
+ message: "Remote server URL:",
2985
+ validate: (val) => {
2986
+ const trimmed = val.trim();
2987
+ if (!trimmed) return "URL cannot be empty";
2988
+ if (!/^https?:\/\//i.test(trimmed)) {
2989
+ return "Please enter a valid URL starting with http:// or https://";
2990
+ }
2991
+ return true;
2992
+ }
2993
+ });
2994
+ const transport = await (0, import_prompts8.select)({
2995
+ message: "Select remote transport protocol:",
2996
+ choices: [
2997
+ { name: "HTTP", value: "http" },
2998
+ { name: "SSE (Server-Sent Events)", value: "sse" }
2999
+ ],
3000
+ default: "http"
3001
+ });
3002
+ const newHeaders = await promptEditHeadersConfig({});
3003
+ logger.success(`Switched [${serverName}] configuration to remote mode`);
3004
+ return {
3005
+ url: newUrl.trim(),
3006
+ type: transport,
3007
+ headers: Object.keys(newHeaders).length > 0 ? newHeaders : void 0
3008
+ };
3009
+ };
3010
+ var handleEditServerConfig = async (options) => {
3011
+ const { targetGroup } = options;
3012
+ const isGlobal = options.global ?? false;
3013
+ const cwd = options.cwd ?? process.cwd();
3014
+ const serverName = targetGroup.serverName;
3015
+ let workingConfig = {
3016
+ ...targetGroup.config,
3017
+ args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
3018
+ env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
3019
+ headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
3020
+ };
3021
+ while (true) {
3022
+ const isRemote = Boolean(workingConfig.url && workingConfig.url.length > 0);
3023
+ displayServerDetails({
3024
+ serverName,
3025
+ config: workingConfig,
3026
+ titlePrefix: "Edit Server Configuration"
3027
+ });
3028
+ const editChoices = isRemote ? [
3029
+ { name: "Edit HTTP Headers (headers)", value: "headers" },
3030
+ { name: "Edit Remote URL (url)", value: "url" },
3031
+ { name: "Edit Transport Protocol (type)", value: "transport" },
3032
+ { name: "Switch to local command (stdio)", value: "switch_type" },
3033
+ { name: "Reset changes to original", value: "reset" },
3034
+ { name: "Save and apply changes", value: "save" },
3035
+ { name: "Cancel (discard changes)", value: "cancel" }
3036
+ ] : [
3037
+ { name: "Edit Environment Variables (env)", value: "env" },
3038
+ { name: "Edit Command Arguments (args)", value: "args" },
3039
+ { name: "Edit Executable Command (command)", value: "command" },
3040
+ { name: "Switch to remote server (HTTP/SSE)", value: "switch_type" },
3041
+ { name: "Reset changes to original", value: "reset" },
3042
+ { name: "Save and apply changes", value: "save" },
3043
+ { name: "Cancel (discard changes)", value: "cancel" }
3044
+ ];
3045
+ const editAction = await (0, import_prompts8.select)({
3046
+ message: `What would you like to modify in [${serverName}]?`,
3047
+ choices: editChoices
3048
+ });
3049
+ if (editAction === "cancel") {
3050
+ logger.info("Modification cancelled; changes discarded");
3051
+ return;
3052
+ }
3053
+ if (editAction === "reset") {
3054
+ workingConfig = {
3055
+ ...targetGroup.config,
3056
+ args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
3057
+ env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
3058
+ headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
3059
+ };
3060
+ logger.info("Configuration reset to original");
3061
+ continue;
3062
+ }
3063
+ if (editAction === "switch_type") {
3064
+ workingConfig = await promptSwitchServerType(workingConfig, serverName);
3065
+ continue;
3066
+ }
3067
+ if (editAction === "env") {
3068
+ workingConfig.env = await promptEditEnvConfig(workingConfig.env ?? {});
3069
+ } else if (editAction === "args") {
3070
+ workingConfig.args = await promptEditArgs(workingConfig.args ?? []);
3071
+ } else if (editAction === "command") {
3072
+ const newCmd = await (0, import_prompts8.input)({
3073
+ message: "Executable command:",
3074
+ default: workingConfig.command,
3075
+ validate: (val) => val.trim() ? true : "Command cannot be empty"
3076
+ });
3077
+ workingConfig.command = newCmd.trim();
3078
+ } else if (editAction === "headers") {
3079
+ workingConfig.headers = await promptEditHeadersConfig(workingConfig.headers ?? {});
3080
+ } else if (editAction === "url") {
3081
+ const newUrl = await (0, import_prompts8.input)({
3082
+ message: "Remote server URL:",
3083
+ default: workingConfig.url,
3084
+ validate: (val) => {
3085
+ const trimmed = val.trim();
3086
+ if (!trimmed) return "URL cannot be empty";
3087
+ if (!/^https?:\/\//i.test(trimmed)) {
3088
+ return "Please enter a valid URL starting with http:// or https://";
3089
+ }
3090
+ return true;
3091
+ }
3092
+ });
3093
+ workingConfig.url = newUrl.trim();
3094
+ } else if (editAction === "transport") {
3095
+ workingConfig.type = await (0, import_prompts8.select)({
3096
+ message: "Select remote transport protocol:",
3097
+ choices: [
3098
+ { name: "HTTP", value: "http" },
3099
+ { name: "SSE (Server-Sent Events)", value: "sse" }
3100
+ ],
3101
+ default: workingConfig.type === "sse" ? "sse" : "http"
3102
+ });
3103
+ } else if (editAction === "save") {
3104
+ let targetAgents = targetGroup.agents;
3105
+ if (targetGroup.agents.length > 1) {
3106
+ const sortedAgents = sortAgentsWithClusters(targetGroup.agents, { global: isGlobal, cwd });
3107
+ const choices = buildLinkedAgentChoices({
3108
+ agents: sortedAgents,
3109
+ checkedAgents: sortedAgents,
3110
+ scopeOptions: { global: isGlobal, cwd }
3111
+ });
3112
+ targetAgents = await linkedCheckbox({
3113
+ message: "Select agents to update configuration (Space to toggle):",
3114
+ choices,
3115
+ loop: false,
3116
+ validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
3117
+ });
3118
+ if (targetAgents.length < targetGroup.agents.length) {
3119
+ const unselected = targetGroup.agents.filter((a) => !targetAgents.includes(a));
3120
+ const unselectedNames = unselected.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3121
+ logger.info(
3122
+ `Note: Updating only a subset of agents. Server configurations will diverge from: ${unselectedNames}.`
3123
+ );
3124
+ }
3125
+ }
3126
+ const requestedTransport = workingConfig.url ? workingConfig.type ?? "http" : "stdio";
3127
+ const resolution = resolveTargetAgents({
3128
+ requested: targetAgents,
3129
+ global: isGlobal,
3130
+ cwd,
3131
+ transport: requestedTransport
3132
+ });
3133
+ if (resolution.incompatible.length > 0) {
3134
+ for (const item of resolution.incompatible) {
3135
+ logger.warn(`Skipping ${import_picocolors13.default.cyan(item.agent)}: ${item.reason}`);
3136
+ }
3137
+ }
3138
+ if (resolution.compatibleAgents.length === 0) {
3139
+ logger.error(
3140
+ `None of the selected agents support ${requestedTransport} transport. Cannot update.`
3141
+ );
3142
+ continue;
3143
+ }
3144
+ const agentNames = resolution.compatibleAgents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3145
+ const confirmed = await (0, import_prompts8.confirm)({
3146
+ message: `Confirm updating configuration for [${serverName}] across: ${agentNames}?`,
3147
+ default: true
3148
+ });
3149
+ if (!confirmed) {
3150
+ logger.warn("Update cancelled");
3151
+ continue;
3152
+ }
3153
+ const updateResult = updateMcpServer({
3154
+ serverName,
3155
+ config: workingConfig,
3156
+ previousConfig: targetGroup.config,
3157
+ agents: resolution.compatibleAgents,
3158
+ global: isGlobal,
3159
+ cwd
3160
+ });
3161
+ let updatedAny = false;
3162
+ const succeededAgents = [];
3163
+ for (const res of updateResult.results) {
3164
+ if (res.success) {
3165
+ updatedAny = true;
3166
+ succeededAgents.push(res.agent);
3167
+ logger.success(
3168
+ `${import_picocolors13.default.cyan(res.agent)}: Successfully updated configuration in ${import_picocolors13.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
3169
+ );
3170
+ } else {
3171
+ logger.error(`${import_picocolors13.default.cyan(res.agent)}: Update failed - ${res.error}`);
3172
+ }
3173
+ }
3174
+ if (updatedAny) {
3175
+ targetGroup.config = updateResult.config;
3176
+ logger.success(`Configuration for [${serverName}] updated successfully!`);
3177
+ return;
3178
+ }
3179
+ }
3180
+ }
3181
+ };
2154
3182
  var wizardManage = async (options = {}) => {
2155
3183
  const cwd = options.cwd ?? process.cwd();
2156
3184
  const isGlobal = await promptScope({
@@ -2164,51 +3192,57 @@ var wizardManage = async (options = {}) => {
2164
3192
  return;
2165
3193
  }
2166
3194
  const grouped = groupInstalledServersByName(installed);
3195
+ let pendingServerName = options.serverName;
3196
+ const refreshGroupedServers = () => {
3197
+ const freshInstalled = listInstalledMcpServers({ global: isGlobal, cwd });
3198
+ const freshGrouped = groupInstalledServersByName(freshInstalled);
3199
+ grouped.clear();
3200
+ for (const [name, grp] of freshGrouped) {
3201
+ grouped.set(name, grp);
3202
+ }
3203
+ };
2167
3204
  while (true) {
2168
- const choices = Array.from(grouped.values()).map((g) => {
2169
- const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
2170
- return {
2171
- name: `${import_picocolors8.default.bold(g.serverName)} ${import_picocolors8.default.dim(`(configured in: ${agentNames})`)}`,
2172
- value: g.serverName
2173
- };
2174
- });
2175
- choices.push({
2176
- name: `Back`,
2177
- value: "__back__"
2178
- });
2179
- const chosenServerName = await (0, import_prompts8.select)({
2180
- message: "Select MCP server to manage or sync:",
2181
- choices
2182
- });
2183
- if (chosenServerName === "__back__") {
2184
- return;
3205
+ let chosenServerName;
3206
+ if (pendingServerName && grouped.has(pendingServerName)) {
3207
+ chosenServerName = pendingServerName;
3208
+ pendingServerName = void 0;
3209
+ } else {
3210
+ pendingServerName = void 0;
3211
+ const choices = Array.from(grouped.values()).map((g) => {
3212
+ const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3213
+ return {
3214
+ name: `${import_picocolors13.default.bold(g.serverName)} ${import_picocolors13.default.dim(`(configured in: ${agentNames})`)}`,
3215
+ value: g.serverName
3216
+ };
3217
+ });
3218
+ choices.push({
3219
+ name: `Back`,
3220
+ value: "__back__"
3221
+ });
3222
+ chosenServerName = await (0, import_prompts8.select)({
3223
+ message: "Select MCP server to manage or sync:",
3224
+ choices
3225
+ });
3226
+ if (chosenServerName === "__back__") {
3227
+ return;
3228
+ }
2185
3229
  }
2186
3230
  const targetGroup = grouped.get(chosenServerName);
2187
3231
  if (!targetGroup) continue;
2188
- console.log("\n" + import_picocolors8.default.cyan(import_picocolors8.default.bold(`MCP Server Details: [${chosenServerName}]`)));
2189
- console.log(` ${import_picocolors8.default.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
2190
- console.log(
2191
- ` ${import_picocolors8.default.bold("Configured Agents:")} ${import_picocolors8.default.green(targetGroup.agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2192
- );
2193
- const cfg = targetGroup.config;
2194
- if (isRemoteServerConfig(cfg)) {
2195
- console.log(` ${import_picocolors8.default.bold("URL:")} ${import_picocolors8.default.dim(cfg.url)} (${cfg.type})`);
2196
- if (cfg.headers && Object.keys(cfg.headers).length > 0) {
2197
- console.log(` ${import_picocolors8.default.bold("Headers:")} ${Object.keys(cfg.headers).join(", ")}`);
2198
- }
2199
- } else if (isStdioServerConfig(cfg)) {
2200
- console.log(` ${import_picocolors8.default.bold("Command:")} ${import_picocolors8.default.magenta(cfg.command)}`);
2201
- if (cfg.args && cfg.args.length > 0) {
2202
- console.log(` ${import_picocolors8.default.bold("Arguments:")} ${import_picocolors8.default.dim(cfg.args.join(" "))}`);
2203
- }
2204
- if (cfg.env && Object.keys(cfg.env).length > 0) {
2205
- console.log(` ${import_picocolors8.default.bold("Environment Variables:")} ${import_picocolors8.default.dim(Object.keys(cfg.env).join(", "))}`);
2206
- }
2207
- }
2208
- console.log();
3232
+ displayServerDetails({
3233
+ serverName: chosenServerName,
3234
+ config: targetGroup.config,
3235
+ agents: targetGroup.agents,
3236
+ global: isGlobal,
3237
+ hasDivergence: targetGroup.hasDivergence
3238
+ });
2209
3239
  const action = await (0, import_prompts8.select)({
2210
3240
  message: `What would you like to do with [${chosenServerName}]?`,
2211
3241
  choices: [
3242
+ {
3243
+ name: "Edit server configuration",
3244
+ value: "edit"
3245
+ },
2212
3246
  {
2213
3247
  name: "Sync / clone to other agents",
2214
3248
  value: "sync"
@@ -2220,20 +3254,34 @@ var wizardManage = async (options = {}) => {
2220
3254
  ]
2221
3255
  });
2222
3256
  if (action === "back") continue;
3257
+ if (action === "edit") {
3258
+ await handleEditServerConfig({
3259
+ targetGroup,
3260
+ global: isGlobal,
3261
+ cwd
3262
+ });
3263
+ refreshGroupedServers();
3264
+ continue;
3265
+ }
2223
3266
  if (action === "sync") {
2224
3267
  const allAllowedAgents = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2225
- const candidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
2226
- if (candidateAgents.length === 0) {
2227
- logger.info("All supported agents in this scope already have this MCP server configured; no sync needed");
3268
+ const rawCandidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
3269
+ if (rawCandidateAgents.length === 0) {
3270
+ logger.info(
3271
+ "All supported agents in this scope already have this MCP server configured; no sync needed"
3272
+ );
2228
3273
  continue;
2229
3274
  }
2230
- const selectedToSync = await (0, import_prompts8.checkbox)({
3275
+ const candidateAgents = sortAgentsWithClusters(rawCandidateAgents, { global: isGlobal, cwd });
3276
+ const choices = buildLinkedAgentChoices({
3277
+ agents: candidateAgents,
3278
+ checkedAgents: [],
3279
+ scopeOptions: { global: isGlobal, cwd }
3280
+ });
3281
+ const selectedToSync = await linkedCheckbox({
2231
3282
  message: "Select target agents to sync to (Space to select):",
2232
- choices: candidateAgents.map((a) => ({
2233
- name: `${getMcpAgentConfig(a).displayName} (${a})`,
2234
- value: a,
2235
- checked: false
2236
- })),
3283
+ choices,
3284
+ loop: false,
2237
3285
  validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
2238
3286
  });
2239
3287
  const confirmed = await (0, import_prompts8.confirm)({
@@ -2244,25 +3292,37 @@ var wizardManage = async (options = {}) => {
2244
3292
  logger.warn("Sync cancelled");
2245
3293
  continue;
2246
3294
  }
2247
- for (const targetAgent of selectedToSync) {
2248
- const res = installMcpServerForAgent(chosenServerName, targetGroup.config, targetAgent, {
2249
- global: isGlobal,
2250
- cwd
2251
- });
3295
+ const syncResult = updateMcpServer({
3296
+ serverName: chosenServerName,
3297
+ config: targetGroup.config,
3298
+ agents: selectedToSync,
3299
+ global: isGlobal,
3300
+ cwd
3301
+ });
3302
+ for (const item of syncResult.incompatible) {
3303
+ logger.warn(`Skipping ${import_picocolors13.default.cyan(item.agent)}: ${item.reason}`);
3304
+ }
3305
+ for (const res of syncResult.results) {
3306
+ if (syncResult.incompatible.some((i) => i.agent === res.agent)) {
3307
+ continue;
3308
+ }
2252
3309
  if (res.success) {
2253
- logger.success(`${import_picocolors8.default.cyan(targetAgent)}: Successfully synced to ${import_picocolors8.default.dim(res.path)}`);
2254
- targetGroup.agents.push(targetAgent);
3310
+ logger.success(
3311
+ `${import_picocolors13.default.cyan(res.agent)}: Successfully synced to ${import_picocolors13.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
3312
+ );
3313
+ targetGroup.agents.push(res.agent);
2255
3314
  } else {
2256
- logger.error(`${import_picocolors8.default.cyan(targetAgent)}: Sync failed - ${res.error}`);
3315
+ logger.error(`${import_picocolors13.default.cyan(res.agent)}: Sync failed - ${res.error}`);
2257
3316
  }
2258
3317
  }
3318
+ refreshGroupedServers();
2259
3319
  }
2260
3320
  }
2261
3321
  };
2262
3322
 
2263
3323
  // src/interactive/wizard-remove.ts
2264
3324
  var import_prompts9 = require("@inquirer/prompts");
2265
- var import_picocolors9 = __toESM(require("picocolors"), 1);
3325
+ var import_picocolors14 = __toESM(require("picocolors"), 1);
2266
3326
  var wizardRemove = async (options = {}) => {
2267
3327
  const cwd = options.cwd ?? process.cwd();
2268
3328
  const isGlobal = await promptScope({
@@ -2279,7 +3339,7 @@ var wizardRemove = async (options = {}) => {
2279
3339
  let serverName = options.name;
2280
3340
  if (!serverName) {
2281
3341
  const choices = Array.from(serverMap.values()).map((g) => ({
2282
- name: `${import_picocolors9.default.bold(g.serverName)} ${import_picocolors9.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
3342
+ name: `${import_picocolors14.default.bold(g.serverName)} ${import_picocolors14.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
2283
3343
  value: g.serverName
2284
3344
  }));
2285
3345
  serverName = await (0, import_prompts9.select)({
@@ -2287,20 +3347,22 @@ var wizardRemove = async (options = {}) => {
2287
3347
  choices
2288
3348
  });
2289
3349
  }
2290
- const installedAgents = serverMap.get(serverName)?.agents || [];
2291
- if (installedAgents.length === 0) {
3350
+ const rawInstalledAgents = serverMap.get(serverName)?.agents || [];
3351
+ if (rawInstalledAgents.length === 0) {
2292
3352
  logger.warn(`No agents found with [${serverName}] installed`);
2293
3353
  return false;
2294
3354
  }
3355
+ const installedAgents = sortAgentsWithClusters(rawInstalledAgents, { global: isGlobal, cwd });
2295
3356
  let targetAgents = options.agents;
2296
3357
  if (!targetAgents || targetAgents.length === 0) {
2297
- targetAgents = await (0, import_prompts9.checkbox)({
3358
+ const choices = buildLinkedAgentChoices({
3359
+ agents: installedAgents,
3360
+ checkedAgents: installedAgents,
3361
+ scopeOptions: { global: isGlobal, cwd }
3362
+ });
3363
+ targetAgents = await linkedCheckbox({
2298
3364
  message: `Select agents to remove [${serverName}] from:`,
2299
- choices: installedAgents.map((agent) => ({
2300
- name: `${getMcpAgentConfig(agent)?.displayName ?? agent} (${agent})`,
2301
- value: agent,
2302
- checked: true
2303
- })),
3365
+ choices,
2304
3366
  validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
2305
3367
  });
2306
3368
  } else {
@@ -2328,10 +3390,12 @@ var wizardRemove = async (options = {}) => {
2328
3390
  let removedCount = 0;
2329
3391
  for (const res of results) {
2330
3392
  if (res.removed) {
2331
- logger.success(`${import_picocolors9.default.cyan(res.agent)}: Successfully removed from ${import_picocolors9.default.dim(res.path)}`);
3393
+ logger.success(
3394
+ `${import_picocolors14.default.cyan(res.agent)}: Successfully removed from ${import_picocolors14.default.dim(res.path)}${formatCoHostedBadge("affected", res.coAffectedAgents)}`
3395
+ );
2332
3396
  removedCount++;
2333
3397
  } else if (res.error) {
2334
- logger.error(`${import_picocolors9.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
3398
+ logger.error(`${import_picocolors14.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
2335
3399
  }
2336
3400
  }
2337
3401
  if (removedCount > 0) {
@@ -2345,8 +3409,8 @@ var wizardRemove = async (options = {}) => {
2345
3409
  // src/interactive/main-menu.ts
2346
3410
  var mainMenu = async () => {
2347
3411
  console.log();
2348
- console.log(import_picocolors10.default.bold(import_picocolors10.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
2349
- console.log(import_picocolors10.default.dim("Cross-platform MCP server configuration & synchronization tool"));
3412
+ console.log(import_picocolors15.default.bold(import_picocolors15.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
3413
+ console.log(import_picocolors15.default.dim("Cross-platform MCP server configuration & synchronization tool"));
2350
3414
  console.log();
2351
3415
  while (true) {
2352
3416
  try {
@@ -2372,7 +3436,7 @@ var mainMenu = async () => {
2372
3436
  ]
2373
3437
  });
2374
3438
  if (action === "exit") {
2375
- console.log(import_picocolors10.default.dim("Goodbye!"));
3439
+ console.log(import_picocolors15.default.dim("Goodbye!"));
2376
3440
  break;
2377
3441
  }
2378
3442
  if (action === "add") {
@@ -2385,26 +3449,241 @@ var mainMenu = async () => {
2385
3449
  console.log();
2386
3450
  } catch (error) {
2387
3451
  if (error?.name === "ExitPromptError") {
2388
- console.log("\n" + import_picocolors10.default.dim("Exited."));
3452
+ console.log("\n" + import_picocolors15.default.dim("Exited."));
2389
3453
  break;
2390
3454
  }
2391
3455
  throw error;
2392
3456
  }
2393
3457
  }
2394
3458
  };
3459
+
3460
+ // src/utils/resolve-transport.ts
3461
+ var resolveTransport = (input7) => {
3462
+ if (!input7) return void 0;
3463
+ if (input7 === "http" || input7 === "sse") return input7;
3464
+ throw new Error(`Unsupported transport "${input7}" (expected: http, sse)`);
3465
+ };
3466
+
3467
+ // src/cli/manage.ts
3468
+ var import_commander = require("commander");
3469
+ var import_picocolors16 = __toESM(require("picocolors"), 1);
3470
+
3471
+ // src/utils/parse-key-value-list.ts
3472
+ var parseKeyValueList = (entries, separator) => {
3473
+ if (!entries || entries.length === 0) return {};
3474
+ const result = {};
3475
+ for (const entry of entries) {
3476
+ const splitIndex = entry.indexOf(separator);
3477
+ if (splitIndex === -1) {
3478
+ throw new Error(`Invalid entry "${entry}": expected "${separator}" separator`);
3479
+ }
3480
+ const key = entry.slice(0, splitIndex).trim();
3481
+ const value = entry.slice(splitIndex + separator.length).trim();
3482
+ if (!key) throw new Error(`Invalid entry "${entry}": empty key`);
3483
+ result[key] = value;
3484
+ }
3485
+ return result;
3486
+ };
3487
+
3488
+ // src/cli/manage.ts
3489
+ var requireTargetServerGroup = (serverName, scope) => {
3490
+ const installed = listInstalledMcpServers(scope);
3491
+ const grouped = groupInstalledServersByName(installed);
3492
+ const targetGroup = grouped.get(serverName);
3493
+ if (!targetGroup) {
3494
+ logger.error(
3495
+ `MCP server "${serverName}" is not configured in ${scope.global ? "global" : "project"} scope.`
3496
+ );
3497
+ process.exitCode = 1;
3498
+ return void 0;
3499
+ }
3500
+ return targetGroup;
3501
+ };
3502
+ var mcpManageCommand = new import_commander.Command("manage").description("Inspect, modify, and sync installed MCP servers across coding agents").argument("[server-name]", "Optional server name to inspect or manage").option("-a, --agent <agents...>", "Target specific agents for update").option("-g, --global", "Manage global scope servers instead of project").option("-t, --transport <type>", "Transport type for remote servers (http or sse)").option("--header <header...>", "HTTP header (Header: Value), repeatable").option("--clear-headers", "Clear all HTTP headers for remote servers").option("--env <env...>", "Env var for stdio servers (KEY=VALUE), repeatable").option("--clear-env", "Clear all environment variables for stdio servers").option("--args <args...>", "CLI arguments for stdio/package servers").option("--clear-args", "Clear all arguments for stdio/package servers").option("--command <command>", "Executable command for stdio servers").option("--url <url>", "Remote endpoint URL").option("-y, --yes", "Skip interactive prompts").action(async (serverName, options) => {
3503
+ try {
3504
+ const cwd = process.cwd();
3505
+ const isGlobal = Boolean(options.global);
3506
+ const hasModifications = options.command !== void 0 || options.args !== void 0 || Boolean(options.clearArgs) || options.env !== void 0 || Boolean(options.clearEnv) || options.header !== void 0 || Boolean(options.clearHeaders) || options.url !== void 0 || options.transport !== void 0;
3507
+ const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
3508
+ if (hasModifications) {
3509
+ if (options.url !== void 0 && options.command !== void 0) {
3510
+ logger.error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
3511
+ process.exitCode = 1;
3512
+ return;
3513
+ }
3514
+ if (!serverName) {
3515
+ logger.error('Missing required argument: "server-name" when passing modification flags.');
3516
+ process.exitCode = 1;
3517
+ return;
3518
+ }
3519
+ const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
3520
+ if (!targetGroup) {
3521
+ return;
3522
+ }
3523
+ const isCurrentRemote = Boolean(targetGroup.config.url && targetGroup.config.url.length > 0);
3524
+ const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
3525
+ if (willBeRemote) {
3526
+ const ignoredStdioFlags = [];
3527
+ if (options.env !== void 0) ignoredStdioFlags.push("--env");
3528
+ if (options.clearEnv) ignoredStdioFlags.push("--clear-env");
3529
+ if (options.args !== void 0) ignoredStdioFlags.push("--args");
3530
+ if (options.clearArgs) ignoredStdioFlags.push("--clear-args");
3531
+ if (ignoredStdioFlags.length > 0) {
3532
+ const hint = options.url !== void 0 ? "When configuring a remote server, stdio flags are ignored." : "Use --command to switch to stdio mode.";
3533
+ logger.warn(
3534
+ `Server "${serverName}" is a remote server. The following stdio flags will be ignored: ${ignoredStdioFlags.join(", ")}. ${hint}`
3535
+ );
3536
+ }
3537
+ } else {
3538
+ const ignoredRemoteFlags = [];
3539
+ if (options.header !== void 0) ignoredRemoteFlags.push("--header");
3540
+ if (options.clearHeaders) ignoredRemoteFlags.push("--clear-headers");
3541
+ if (options.transport !== void 0) ignoredRemoteFlags.push("--transport");
3542
+ if (ignoredRemoteFlags.length > 0) {
3543
+ const hint = options.command !== void 0 ? "When configuring a stdio server, remote flags are ignored." : "Use --url to switch to remote mode.";
3544
+ logger.warn(
3545
+ `Server "${serverName}" is a stdio server. The following remote flags will be ignored: ${ignoredRemoteFlags.join(", ")}. ${hint}`
3546
+ );
3547
+ }
3548
+ }
3549
+ const incomingDelta = {};
3550
+ if (options.command !== void 0) {
3551
+ incomingDelta.command = options.command;
3552
+ }
3553
+ if (options.clearArgs) {
3554
+ incomingDelta.args = void 0;
3555
+ }
3556
+ if (options.args !== void 0) {
3557
+ incomingDelta.args = options.args;
3558
+ }
3559
+ if (options.url !== void 0) {
3560
+ incomingDelta.url = options.url;
3561
+ }
3562
+ if (options.transport !== void 0) {
3563
+ incomingDelta.type = resolveTransport(options.transport);
3564
+ }
3565
+ if (options.clearEnv) {
3566
+ incomingDelta.env = void 0;
3567
+ }
3568
+ if (options.env !== void 0) {
3569
+ const parsedEnv = parseKeyValueList(options.env, "=");
3570
+ const baseEnv = options.clearEnv ? {} : targetGroup.config.env ?? {};
3571
+ incomingDelta.env = { ...baseEnv, ...parsedEnv };
3572
+ }
3573
+ if (options.clearHeaders) {
3574
+ incomingDelta.headers = void 0;
3575
+ }
3576
+ if (options.header !== void 0) {
3577
+ const parsedHeaders = parseKeyValueList(options.header, ":");
3578
+ const baseHeaders = options.clearHeaders ? {} : targetGroup.config.headers ?? {};
3579
+ incomingDelta.headers = { ...baseHeaders, ...parsedHeaders };
3580
+ }
3581
+ let targetAgents = targetGroup.agents;
3582
+ if (options.agent !== void 0) {
3583
+ const parsed = parseMcpAgentList(options.agent);
3584
+ if (!parsed || parsed.length === 0) {
3585
+ logger.error(`No valid agents recognized from: "${options.agent.join(", ")}".`);
3586
+ process.exitCode = 1;
3587
+ return;
3588
+ }
3589
+ targetAgents = parsed;
3590
+ }
3591
+ const updateResult = updateMcpServer({
3592
+ serverName,
3593
+ config: incomingDelta,
3594
+ previousConfig: targetGroup.config,
3595
+ agents: targetAgents,
3596
+ global: isGlobal,
3597
+ cwd
3598
+ });
3599
+ for (const item of updateResult.incompatible) {
3600
+ logger.warn(`Skipping ${import_picocolors16.default.cyan(item.agent)}: ${item.reason}`);
3601
+ }
3602
+ const attemptedResults = updateResult.results.filter(
3603
+ (r) => !updateResult.incompatible.some((i) => i.agent === r.agent)
3604
+ );
3605
+ if (attemptedResults.length === 0) {
3606
+ const requestedTransport = updateResult.config.url ? updateResult.config.type ?? "http" : "stdio";
3607
+ logger.error(
3608
+ `None of the target agents support ${requestedTransport} transport. Update aborted.`
3609
+ );
3610
+ process.exitCode = 1;
3611
+ return;
3612
+ }
3613
+ logger.info(
3614
+ `Updating ${import_picocolors16.default.bold(serverName)} across ${import_picocolors16.default.cyan(String(attemptedResults.length))} agent(s)...`
3615
+ );
3616
+ let allSuccess = true;
3617
+ for (const res of attemptedResults) {
3618
+ if (res.success) {
3619
+ logger.success(`${import_picocolors16.default.cyan(res.agent)}: Successfully updated in ${import_picocolors16.default.dim(res.path)}`);
3620
+ logCoHostedNotice("configured", res.coConfiguredAgents);
3621
+ } else {
3622
+ allSuccess = false;
3623
+ logger.error(`${import_picocolors16.default.cyan(res.agent)}: Update failed - ${res.error}`);
3624
+ }
3625
+ }
3626
+ if (!allSuccess) {
3627
+ process.exitCode = 1;
3628
+ }
3629
+ return;
3630
+ }
3631
+ if (!isInteractive) {
3632
+ if (!serverName) {
3633
+ logger.error(
3634
+ 'Missing required argument: "server-name" for non-interactive manage command. Specify a server name or use interactive terminal.'
3635
+ );
3636
+ process.exitCode = 1;
3637
+ return;
3638
+ }
3639
+ const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
3640
+ if (!targetGroup) {
3641
+ return;
3642
+ }
3643
+ displayServerDetails({
3644
+ serverName,
3645
+ config: targetGroup.config,
3646
+ agents: targetGroup.agents,
3647
+ global: isGlobal,
3648
+ hasDivergence: targetGroup.hasDivergence
3649
+ });
3650
+ return;
3651
+ }
3652
+ await wizardManage({
3653
+ global: options.global,
3654
+ serverName
3655
+ });
3656
+ } catch (error) {
3657
+ if (error && typeof error === "object" && "name" in error && error.name === "ExitPromptError") {
3658
+ process.exit(0);
3659
+ }
3660
+ logger.error(toErrorMessage(error));
3661
+ process.exitCode = 1;
3662
+ }
3663
+ });
2395
3664
  // Annotate the CommonJS export names for ESM import in node:
2396
3665
  0 && (module.exports = {
2397
3666
  AgentConfigStore,
2398
3667
  DEFAULT_REMOTE_TRANSPORT,
2399
3668
  NPX_COMMAND,
2400
3669
  NPX_DASH_Y,
3670
+ SECRET_HEADER_PATTERN,
3671
+ SECRET_KEY_PATTERN,
2401
3672
  add,
2402
3673
  agentConfigStore,
3674
+ buildLinkedAgentChoices,
2403
3675
  buildMcpServerConfig,
2404
3676
  createAgentTransform,
2405
3677
  detectGloballyInstalledMcpAgents,
2406
3678
  detectProjectInstalledMcpAgents,
3679
+ detectUpdateTransition,
3680
+ displayServerDetails,
2407
3681
  extractPackageName,
3682
+ formatArgsString,
3683
+ formatEnvText,
3684
+ formatHeadersText,
3685
+ getCandidateAgentsForScope,
3686
+ getCoHostedAgents,
2408
3687
  getMcpAgentConfig,
2409
3688
  getMcpAgentTypes,
2410
3689
  getMcpAgentsSupportingProjectScope,
@@ -2413,17 +3692,22 @@ var mainMenu = async () => {
2413
3692
  installMcpServer,
2414
3693
  installMcpServerForAgent,
2415
3694
  installMcpServerForAgents,
3695
+ installToCompatibleAgents,
2416
3696
  isMcpAgentType,
2417
3697
  isMcpTransportSupported,
2418
3698
  isRemoteMcpSource,
2419
3699
  isRemoteServerConfig,
2420
3700
  isStdioServerConfig,
3701
+ linkedCheckbox,
2421
3702
  list,
2422
3703
  listInstalledMcpServers,
2423
3704
  listServersInConfigFile,
2424
3705
  mainMenu,
3706
+ maskSecretHeader,
3707
+ maskSecretValue,
2425
3708
  mcpAgentAliases,
2426
3709
  mcpAgents,
3710
+ mcpManageCommand,
2427
3711
  normalizeServerConfig,
2428
3712
  parseArgsString,
2429
3713
  parseEnvText,
@@ -2432,20 +3716,33 @@ var mainMenu = async () => {
2432
3716
  parseServerConfig,
2433
3717
  parseSource,
2434
3718
  promptArgsConfig,
3719
+ promptEditArgs,
3720
+ promptEditEnvConfig,
3721
+ promptEditHeadersConfig,
3722
+ promptEditKeyValueConfig,
2435
3723
  promptEnvConfig,
2436
3724
  promptHeadersConfig,
2437
3725
  promptScope,
2438
3726
  promptScopeAndAgents,
3727
+ promptSwitchServerType,
2439
3728
  readConfigFile,
2440
3729
  remove,
2441
3730
  removeMcpServer,
2442
3731
  removeMcpServerFromAgent,
2443
3732
  removeServerFromConfigFile,
3733
+ resolveConfigClusters,
2444
3734
  resolveMcpAgentAlias,
2445
3735
  resolveMcpConfigTarget,
2446
3736
  resolveTargetAgents,
3737
+ resolveTransport,
3738
+ sanitizeUpdatedServerConfig,
3739
+ sortAgentsWithClusters,
3740
+ toRemoteServerConfig,
3741
+ toStdioServerConfig,
2447
3742
  transformServerConfig,
2448
3743
  transformServerConfigForAgent,
3744
+ update,
3745
+ updateMcpServer,
2449
3746
  wizardAdd,
2450
3747
  wizardManage,
2451
3748
  wizardRemove,