@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.
@@ -452,9 +452,9 @@ var mcpAgentAliases = {
452
452
  var getMcpAgentConfig = (agentType) => mcpAgents[agentType];
453
453
  var getMcpAgentTypes = () => Object.values(mcpAgents).map((config) => config.name);
454
454
  var isMcpAgentType = (value) => value in mcpAgents;
455
- var resolveMcpAgentAlias = (input5) => {
456
- if (isMcpAgentType(input5)) return input5;
457
- return mcpAgentAliases[input5] ?? null;
455
+ var resolveMcpAgentAlias = (input7) => {
456
+ if (isMcpAgentType(input7)) return input7;
457
+ return mcpAgentAliases[input7] ?? null;
458
458
  };
459
459
  var isMcpTransportSupported = (agent, transport) => agent.supportedTransports.includes(transport);
460
460
  var detectProjectInstalledMcpAgents = (cwd) => getMcpAgentTypes().filter(
@@ -965,6 +965,74 @@ var agentConfigStore = new AgentConfigStore();
965
965
  // src/utils/to-error-message.ts
966
966
  var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
967
967
 
968
+ // src/resolve-config-clusters.ts
969
+ var getCandidateAgentsForScope = (options = {}) => {
970
+ return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
971
+ };
972
+ var getCoHostedAgents = (agentType, options = {}) => {
973
+ const currentAgent = getMcpAgentConfig(agentType);
974
+ const currentTarget = resolveMcpConfigTarget(currentAgent, options);
975
+ const candidates = getCandidateAgentsForScope(options);
976
+ const coHosted = [];
977
+ for (const candidateType of candidates) {
978
+ if (candidateType === agentType) continue;
979
+ const candidateConfig = getMcpAgentConfig(candidateType);
980
+ const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
981
+ if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
982
+ coHosted.push(candidateType);
983
+ }
984
+ }
985
+ return coHosted;
986
+ };
987
+ var resolveConfigClusters = (agentTypes, options = {}) => {
988
+ const clustersByPath = /* @__PURE__ */ new Map();
989
+ for (const agentType of agentTypes) {
990
+ const agentConfig = getMcpAgentConfig(agentType);
991
+ const target = resolveMcpConfigTarget(agentConfig, options);
992
+ let keyMap = clustersByPath.get(target.configPath);
993
+ if (!keyMap) {
994
+ keyMap = /* @__PURE__ */ new Map();
995
+ clustersByPath.set(target.configPath, keyMap);
996
+ }
997
+ let cluster = keyMap.get(target.configKey);
998
+ if (!cluster) {
999
+ const allCoHosted = getCoHostedAgents(agentType, options);
1000
+ cluster = {
1001
+ configPath: target.configPath,
1002
+ configKey: target.configKey,
1003
+ targetAgents: [],
1004
+ coHostedAgents: allCoHosted
1005
+ };
1006
+ keyMap.set(target.configKey, cluster);
1007
+ }
1008
+ if (!cluster.targetAgents.includes(agentType)) {
1009
+ cluster.targetAgents.push(agentType);
1010
+ }
1011
+ }
1012
+ const clusters = [];
1013
+ for (const keyMap of clustersByPath.values()) {
1014
+ for (const cluster of keyMap.values()) {
1015
+ cluster.coHostedAgents = cluster.coHostedAgents.filter(
1016
+ (co) => !cluster.targetAgents.includes(co)
1017
+ );
1018
+ clusters.push(cluster);
1019
+ }
1020
+ }
1021
+ return clusters;
1022
+ };
1023
+ var sortAgentsWithClusters = (agentTypes, options = {}) => {
1024
+ const clusters = resolveConfigClusters(agentTypes, options);
1025
+ const sorted = [];
1026
+ for (const cluster of clusters) {
1027
+ for (const agent of cluster.targetAgents) {
1028
+ if (!sorted.includes(agent)) {
1029
+ sorted.push(agent);
1030
+ }
1031
+ }
1032
+ }
1033
+ return sorted;
1034
+ };
1035
+
968
1036
  // src/transforms/index.ts
969
1037
  var DIALECT_PRESETS = {
970
1038
  vscode: {
@@ -1180,12 +1248,18 @@ var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {
1180
1248
  const agent = getMcpAgentConfig(agentType);
1181
1249
  const isGlobal = options.global ?? false;
1182
1250
  const { target } = agentConfigStore.resolveTarget(agent, options);
1251
+ const coHosted = getCoHostedAgents(agentType, options);
1183
1252
  try {
1184
1253
  const transformed = transformServerConfigForAgent(agent, serverName, serverConfig, {
1185
1254
  global: isGlobal
1186
1255
  });
1187
1256
  agentConfigStore.writeServer(agent, serverName, transformed, options);
1188
- return { agent: agentType, success: true, path: target.configPath };
1257
+ return {
1258
+ agent: agentType,
1259
+ success: true,
1260
+ path: target.configPath,
1261
+ coConfiguredAgents: coHosted.length > 0 ? coHosted : void 0
1262
+ };
1189
1263
  } catch (error) {
1190
1264
  return {
1191
1265
  agent: agentType,
@@ -1195,16 +1269,69 @@ var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {
1195
1269
  };
1196
1270
  }
1197
1271
  };
1198
- var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => agentTypes.map(
1199
- (agentType) => installMcpServerForAgent(serverName, serverConfig, agentType, options)
1200
- );
1272
+ var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => {
1273
+ const clusters = resolveConfigClusters(agentTypes, options);
1274
+ const resultsByAgent = /* @__PURE__ */ new Map();
1275
+ const isGlobal = options.global ?? false;
1276
+ for (const cluster of clusters) {
1277
+ const primaryAgentType = cluster.targetAgents[0];
1278
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1279
+ try {
1280
+ const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
1281
+ global: isGlobal
1282
+ });
1283
+ agentConfigStore.writeServer(primaryAgent, serverName, transformed, options);
1284
+ for (const agentType of cluster.targetAgents) {
1285
+ resultsByAgent.set(agentType, {
1286
+ agent: agentType,
1287
+ success: true,
1288
+ path: cluster.configPath,
1289
+ coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1290
+ });
1291
+ }
1292
+ } catch (error) {
1293
+ const errorMsg = toErrorMessage(error);
1294
+ for (const agentType of cluster.targetAgents) {
1295
+ resultsByAgent.set(agentType, {
1296
+ agent: agentType,
1297
+ success: false,
1298
+ path: cluster.configPath,
1299
+ error: errorMsg
1300
+ });
1301
+ }
1302
+ }
1303
+ }
1304
+ return agentTypes.map((agentType) => resultsByAgent.get(agentType));
1305
+ };
1306
+ var installToCompatibleAgents = (serverName, serverConfig, options) => {
1307
+ const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
1308
+ const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1309
+ const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
1310
+ const installedResults = installMcpServerForAgents(serverName, serverConfig, compatibleAgents, {
1311
+ global: isGlobal,
1312
+ cwd
1313
+ });
1314
+ const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
1315
+ return allAgents.map((agentType) => {
1316
+ const incompatibleReason = incompatibleMap.get(agentType);
1317
+ if (incompatibleReason) {
1318
+ return {
1319
+ agent: agentType,
1320
+ success: false,
1321
+ path: "",
1322
+ error: incompatibleReason
1323
+ };
1324
+ }
1325
+ return installedMap.get(agentType);
1326
+ });
1327
+ };
1201
1328
 
1202
1329
  // src/utils/parse-mcp-agent-list.ts
1203
- var parseMcpAgentList = (input5) => {
1204
- if (!input5 || input5.length === 0) return void 0;
1205
- if (input5.includes("*")) return getMcpAgentTypes();
1330
+ var parseMcpAgentList = (input7) => {
1331
+ if (!input7 || input7.length === 0) return void 0;
1332
+ if (input7.includes("*")) return getMcpAgentTypes();
1206
1333
  const resolved = [];
1207
- for (const value of input5) {
1334
+ for (const value of input7) {
1208
1335
  const agentType = resolveMcpAgentAlias(value);
1209
1336
  if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
1210
1337
  resolved.push(agentType);
@@ -1213,9 +1340,9 @@ var parseMcpAgentList = (input5) => {
1213
1340
  };
1214
1341
 
1215
1342
  // src/resolve-target-agents.ts
1216
- var normalizeRequestedAgents = (input5) => {
1217
- if (!input5 || input5.length === 0) return void 0;
1218
- const rawList = [...input5];
1343
+ var normalizeRequestedAgents = (input7) => {
1344
+ if (!input7 || input7.length === 0) return void 0;
1345
+ const rawList = [...input7];
1219
1346
  if (rawList.every((item) => isMcpAgentType(item))) {
1220
1347
  return rawList;
1221
1348
  }
@@ -1275,29 +1402,29 @@ var REMOTE_URL_REGEX = /^https?:\/\//i;
1275
1402
  var HAS_WHITESPACE_REGEX = /\s/;
1276
1403
  var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
1277
1404
  var PATH_SEPARATOR_REGEX = /[/\\]/;
1278
- var stripVersionSuffix = (input5) => {
1279
- if (input5.startsWith("@")) {
1280
- const secondAtIndex = input5.indexOf("@", 1);
1281
- if (secondAtIndex > 0) return input5.slice(0, secondAtIndex);
1282
- return input5;
1283
- }
1284
- const atIndex = input5.lastIndexOf("@");
1285
- if (atIndex > 0) return input5.slice(0, atIndex);
1286
- return input5;
1287
- };
1288
- var stripScopePrefix = (input5) => {
1289
- if (!input5.startsWith("@") || !input5.includes("/")) return input5;
1290
- const parts = input5.split("/");
1291
- return parts[1] || input5;
1292
- };
1293
- var stripPathPrefix = (input5) => {
1294
- if (!PATH_SEPARATOR_REGEX.test(input5)) return input5;
1295
- const segments = input5.split(PATH_SEPARATOR_REGEX);
1405
+ var stripVersionSuffix = (input7) => {
1406
+ if (input7.startsWith("@")) {
1407
+ const secondAtIndex = input7.indexOf("@", 1);
1408
+ if (secondAtIndex > 0) return input7.slice(0, secondAtIndex);
1409
+ return input7;
1410
+ }
1411
+ const atIndex = input7.lastIndexOf("@");
1412
+ if (atIndex > 0) return input7.slice(0, atIndex);
1413
+ return input7;
1414
+ };
1415
+ var stripScopePrefix = (input7) => {
1416
+ if (!input7.startsWith("@") || !input7.includes("/")) return input7;
1417
+ const parts = input7.split("/");
1418
+ return parts[1] || input7;
1419
+ };
1420
+ var stripPathPrefix = (input7) => {
1421
+ if (!PATH_SEPARATOR_REGEX.test(input7)) return input7;
1422
+ const segments = input7.split(PATH_SEPARATOR_REGEX);
1296
1423
  const basename = segments[segments.length - 1];
1297
- return basename || input5;
1424
+ return basename || input7;
1298
1425
  };
1299
- var extractPackageName = (input5) => {
1300
- let name = stripVersionSuffix(input5);
1426
+ var extractPackageName = (input7) => {
1427
+ let name = stripVersionSuffix(input7);
1301
1428
  name = stripScopePrefix(name);
1302
1429
  name = stripPathPrefix(name);
1303
1430
  name = name.replace(SCRIPT_EXTENSION_REGEX, "");
@@ -1315,9 +1442,9 @@ var extractPackageName = (input5) => {
1315
1442
  }
1316
1443
  return name || MCP_DEFAULT_SERVER_NAME;
1317
1444
  };
1318
- var inferNameFromUrl = (input5) => {
1445
+ var inferNameFromUrl = (input7) => {
1319
1446
  try {
1320
- const url = new URL(input5);
1447
+ const url = new URL(input7);
1321
1448
  const host = url.hostname;
1322
1449
  const labels = host.split(".").filter((segment) => segment.length > 0);
1323
1450
  if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
@@ -1346,8 +1473,8 @@ var inferNameFromCommand = (command) => {
1346
1473
  const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
1347
1474
  return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
1348
1475
  };
1349
- var parseMcpSource = (input5) => {
1350
- const trimmed = input5.trim();
1476
+ var parseMcpSource = (input7) => {
1477
+ const trimmed = input7.trim();
1351
1478
  if (trimmed.length === 0) {
1352
1479
  throw new Error(
1353
1480
  "Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
@@ -1401,18 +1528,11 @@ var installMcpServer = (options) => {
1401
1528
  cwd,
1402
1529
  transport: requestedTransport
1403
1530
  });
1404
- const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1405
- const results = allAgents.map((agentType) => {
1406
- const incompatibleReason = incompatibleMap.get(agentType);
1407
- if (incompatibleReason) {
1408
- return {
1409
- agent: agentType,
1410
- success: false,
1411
- path: "",
1412
- error: incompatibleReason
1413
- };
1414
- }
1415
- return installMcpServerForAgent(serverName, serverConfig, agentType, { global: isGlobal, cwd });
1531
+ const results = installToCompatibleAgents(serverName, serverConfig, {
1532
+ allAgents,
1533
+ incompatible,
1534
+ global: isGlobal,
1535
+ cwd
1416
1536
  });
1417
1537
  return { serverName, config: serverConfig, results };
1418
1538
  };
@@ -1442,9 +1562,15 @@ var listInstalledMcpServers = (options = {}) => {
1442
1562
  var removeMcpServerFromAgent = (serverName, agentType, options = {}) => {
1443
1563
  const agent = getMcpAgentConfig(agentType);
1444
1564
  const { target } = agentConfigStore.resolveTarget(agent, options);
1565
+ const coHosted = getCoHostedAgents(agentType, options);
1445
1566
  try {
1446
1567
  const { removed } = agentConfigStore.removeServer(agent, serverName, options);
1447
- return { agent: agentType, path: target.configPath, removed };
1568
+ return {
1569
+ agent: agentType,
1570
+ path: target.configPath,
1571
+ removed,
1572
+ coAffectedAgents: removed && coHosted.length > 0 ? coHosted : void 0
1573
+ };
1448
1574
  } catch (error) {
1449
1575
  return {
1450
1576
  agent: agentType,
@@ -1461,24 +1587,153 @@ var removeMcpServer = (options) => {
1461
1587
  global: options.global,
1462
1588
  cwd: options.cwd
1463
1589
  });
1590
+ const clusters = resolveConfigClusters(allAgents, {
1591
+ global: options.global,
1592
+ cwd: options.cwd
1593
+ });
1464
1594
  const results = [];
1465
- for (const agentType of allAgents) {
1466
- const result = removeMcpServerFromAgent(options.name, agentType, {
1467
- global: options.global,
1468
- cwd: options.cwd
1469
- });
1470
- if (result.removed || result.error) results.push(result);
1595
+ for (const cluster of clusters) {
1596
+ const primaryAgentType = cluster.targetAgents[0];
1597
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1598
+ try {
1599
+ const { removed } = agentConfigStore.removeServer(primaryAgent, options.name, {
1600
+ global: options.global,
1601
+ cwd: options.cwd
1602
+ });
1603
+ if (removed) {
1604
+ for (const agentType of cluster.targetAgents) {
1605
+ results.push({
1606
+ agent: agentType,
1607
+ path: cluster.configPath,
1608
+ removed: true,
1609
+ coAffectedAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1610
+ });
1611
+ }
1612
+ }
1613
+ } catch (error) {
1614
+ const errorMsg = toErrorMessage(error);
1615
+ for (const agentType of cluster.targetAgents) {
1616
+ results.push({
1617
+ agent: agentType,
1618
+ path: cluster.configPath,
1619
+ removed: false,
1620
+ error: errorMsg
1621
+ });
1622
+ }
1623
+ }
1471
1624
  }
1472
1625
  return results;
1473
1626
  };
1474
1627
 
1628
+ // src/update-mcp-server.ts
1629
+ var toRemoteServerConfig = (config, defaultTransport = "http") => {
1630
+ const {
1631
+ command: _droppedCommand,
1632
+ args: _droppedArgs,
1633
+ env: _droppedEnv,
1634
+ ...remoteConfig
1635
+ } = config;
1636
+ return {
1637
+ ...remoteConfig,
1638
+ type: remoteConfig.type ?? defaultTransport
1639
+ };
1640
+ };
1641
+ var toStdioServerConfig = (config) => {
1642
+ const {
1643
+ url: _droppedUrl,
1644
+ type: _droppedType,
1645
+ headers: _droppedHeaders,
1646
+ ...stdioConfig
1647
+ } = config;
1648
+ return stdioConfig;
1649
+ };
1650
+ var detectUpdateTransition = (incoming, previous) => {
1651
+ if (!previous) {
1652
+ return incoming.url ? "switch-to-remote" : "switch-to-stdio";
1653
+ }
1654
+ if (incoming.url && !incoming.command) {
1655
+ return "switch-to-remote";
1656
+ }
1657
+ if (incoming.command && !incoming.url) {
1658
+ return "switch-to-stdio";
1659
+ }
1660
+ if (incoming.url || !incoming.command && previous.url) {
1661
+ return "merge-remote";
1662
+ }
1663
+ return "merge-stdio";
1664
+ };
1665
+ var sanitizeUpdatedServerConfig = (incoming, previous) => {
1666
+ const transition = detectUpdateTransition(incoming, previous);
1667
+ const targetTransport = incoming.type ?? previous?.type ?? "http";
1668
+ switch (transition) {
1669
+ case "switch-to-remote": {
1670
+ const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
1671
+ return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
1672
+ }
1673
+ case "switch-to-stdio": {
1674
+ const cleanBase = previous ? toStdioServerConfig(previous) : {};
1675
+ return toStdioServerConfig({ ...cleanBase, ...incoming });
1676
+ }
1677
+ case "merge-remote": {
1678
+ return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
1679
+ }
1680
+ case "merge-stdio": {
1681
+ return toStdioServerConfig({ ...previous, ...incoming });
1682
+ }
1683
+ }
1684
+ };
1685
+ var updateMcpServer = (options) => {
1686
+ const isGlobal = options.global ?? false;
1687
+ const cwd = options.cwd ?? process.cwd();
1688
+ let previousConfig = options.previousConfig;
1689
+ if (!previousConfig) {
1690
+ const existing = listInstalledMcpServers({
1691
+ global: isGlobal,
1692
+ cwd,
1693
+ agents: options.agents
1694
+ });
1695
+ const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
1696
+ if (found) {
1697
+ previousConfig = found.serverConfig;
1698
+ }
1699
+ }
1700
+ const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
1701
+ let targetAgents = options.agents;
1702
+ if (!targetAgents || targetAgents.length === 0) {
1703
+ const existing = listInstalledMcpServers({ global: isGlobal, cwd });
1704
+ targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
1705
+ }
1706
+ const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
1707
+ const { allAgents, incompatible } = resolveTargetAgents({
1708
+ requested: targetAgents,
1709
+ global: isGlobal,
1710
+ cwd,
1711
+ transport: requestedTransport
1712
+ });
1713
+ const results = installToCompatibleAgents(options.serverName, serverConfig, {
1714
+ allAgents,
1715
+ incompatible,
1716
+ global: isGlobal,
1717
+ cwd
1718
+ });
1719
+ return {
1720
+ serverName: options.serverName,
1721
+ config: serverConfig,
1722
+ results,
1723
+ incompatible
1724
+ };
1725
+ };
1726
+
1475
1727
  // src/interactive/main-menu.ts
1476
1728
  import { select as select8 } from "@inquirer/prompts";
1477
- import pc10 from "picocolors";
1729
+ import pc15 from "picocolors";
1478
1730
 
1479
1731
  // src/interactive/wizard-add.ts
1480
- import { confirm as confirm2, input as input4, select as select5 } from "@inquirer/prompts";
1481
- import pc7 from "picocolors";
1732
+ import { confirm as confirm5, input as input5, select as select5 } from "@inquirer/prompts";
1733
+ import pc11 from "picocolors";
1734
+
1735
+ // src/utils/co-hosted-feedback.ts
1736
+ import pc2 from "picocolors";
1482
1737
 
1483
1738
  // src/utils/logger.ts
1484
1739
  import pc from "picocolors";
@@ -1497,13 +1752,284 @@ var logger = {
1497
1752
  }
1498
1753
  };
1499
1754
 
1755
+ // src/utils/co-hosted-feedback.ts
1756
+ var formatCoHostedBadge = (kind, agents) => {
1757
+ if (!agents || agents.length === 0) return "";
1758
+ const label = kind === "configured" ? "co-configured" : "co-affected";
1759
+ return ` ${pc2.yellow(`(${label}: ${agents.join(", ")})`)}`;
1760
+ };
1761
+ var logCoHostedNotice = (kind, agents) => {
1762
+ if (!agents || agents.length === 0) return;
1763
+ const actionText = kind === "configured" ? "Also configured for" : "Also affects";
1764
+ logger.info(
1765
+ ` ${pc2.dim("Note:")} ${actionText} co-hosted agent(s): ${pc2.yellow(agents.join(", "))}`
1766
+ );
1767
+ };
1768
+
1500
1769
  // src/interactive/prompts/agents.ts
1501
- import { checkbox } from "@inquirer/prompts";
1770
+ import pc6 from "picocolors";
1771
+
1772
+ // src/interactive/utils/build-linked-agent-choices.ts
1502
1773
  import pc3 from "picocolors";
1774
+ var buildLinkedAgentChoices = (options) => {
1775
+ const { agents, checkedAgents, detectedAgents = [], scopeOptions = {} } = options;
1776
+ const alignedCheckedSet = new Set(checkedAgents);
1777
+ for (const agent of checkedAgents) {
1778
+ const coHosted = getCoHostedAgents(agent, scopeOptions);
1779
+ for (const co of coHosted) {
1780
+ if (agents.includes(co)) {
1781
+ alignedCheckedSet.add(co);
1782
+ }
1783
+ }
1784
+ }
1785
+ return agents.map((agent) => {
1786
+ const config = getMcpAgentConfig(agent);
1787
+ const displayName = config?.displayName ?? agent;
1788
+ const isDetected = detectedAgents.includes(agent);
1789
+ const coHosted = getCoHostedAgents(agent, scopeOptions).filter(
1790
+ (co) => agents.includes(co)
1791
+ );
1792
+ const detectedBadge = isDetected ? pc3.green(" [detected]") : "";
1793
+ const sharedBadge = coHosted.length > 0 ? pc3.dim(` [shared: ${coHosted.join(", ")}]`) : "";
1794
+ const label = `${displayName} ${pc3.dim(`(${agent})`)}${detectedBadge}${sharedBadge}`;
1795
+ return {
1796
+ name: label,
1797
+ value: agent,
1798
+ checked: alignedCheckedSet.has(agent),
1799
+ linkedValues: coHosted,
1800
+ description: coHosted.length > 0 ? `Linked with ${coHosted.map((a) => getMcpAgentConfig(a).displayName).join(", ")} (shared configuration)` : void 0
1801
+ };
1802
+ });
1803
+ };
1804
+
1805
+ // src/interactive/prompts/linked-checkbox.ts
1806
+ import {
1807
+ Separator,
1808
+ ValidationError,
1809
+ createPrompt,
1810
+ isDownKey,
1811
+ isEnterKey,
1812
+ isNumberKey,
1813
+ isSpaceKey,
1814
+ isUpKey,
1815
+ makeTheme,
1816
+ useKeypress,
1817
+ useMemo,
1818
+ usePagination,
1819
+ usePrefix,
1820
+ useState
1821
+ } from "@inquirer/core";
1822
+ import pc4 from "picocolors";
1823
+ var defaultTheme = {
1824
+ icon: {
1825
+ checked: pc4.green("[x]"),
1826
+ unchecked: pc4.dim("[ ]"),
1827
+ cursor: pc4.cyan(">"),
1828
+ disabledChecked: pc4.dim("[x]"),
1829
+ disabledUnchecked: pc4.dim("[-]")
1830
+ },
1831
+ style: {
1832
+ disabled: (text) => pc4.dim(text),
1833
+ renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
1834
+ description: (text) => pc4.cyan(text),
1835
+ keysHelpTip: (keys) => keys.map(([key, action]) => `${pc4.bold(key)} ${pc4.dim(action)}`).join(pc4.dim(" | ")),
1836
+ highlight: (text) => pc4.cyan(text)
1837
+ },
1838
+ i18n: {
1839
+ disabledError: "This option is disabled and cannot be toggled."
1840
+ }
1841
+ };
1842
+ function isSelectable(item) {
1843
+ return !Separator.isSeparator(item) && !item.disabled;
1844
+ }
1845
+ function isNavigable(item) {
1846
+ return !Separator.isSeparator(item);
1847
+ }
1848
+ function isChecked(item) {
1849
+ return !Separator.isSeparator(item) && item.checked;
1850
+ }
1851
+ function normalizeChoices(choices) {
1852
+ return choices.map((choice) => {
1853
+ if (Separator.isSeparator(choice)) {
1854
+ return choice;
1855
+ }
1856
+ if (typeof choice !== "object" || choice === null || !("value" in choice)) {
1857
+ const name2 = String(choice);
1858
+ return {
1859
+ value: choice,
1860
+ name: name2,
1861
+ short: name2,
1862
+ checkedName: name2,
1863
+ disabled: false,
1864
+ checked: false,
1865
+ linkedValues: []
1866
+ };
1867
+ }
1868
+ const name = choice.name ?? String(choice.value);
1869
+ return {
1870
+ value: choice.value,
1871
+ name,
1872
+ short: choice.short ?? name,
1873
+ checkedName: choice.checkedName ?? name,
1874
+ description: choice.description,
1875
+ disabled: choice.disabled ?? false,
1876
+ checked: choice.checked ?? false,
1877
+ linkedValues: choice.linkedValues ?? []
1878
+ };
1879
+ });
1880
+ }
1881
+ var linkedCheckbox = createPrompt(
1882
+ (config, done) => {
1883
+ const { pageSize = 10, loop = true, required, validate = () => true } = config;
1884
+ const theme = makeTheme(defaultTheme, config.theme);
1885
+ const [status, setStatus] = useState("idle");
1886
+ const prefix = usePrefix({ status, theme });
1887
+ const [items, setItems] = useState(() => normalizeChoices(config.choices));
1888
+ const bounds = useMemo(() => {
1889
+ const first = items.findIndex(isNavigable);
1890
+ let last = -1;
1891
+ for (let i = items.length - 1; i >= 0; i--) {
1892
+ if (isNavigable(items[i])) {
1893
+ last = i;
1894
+ break;
1895
+ }
1896
+ }
1897
+ if (first === -1 || last === -1) {
1898
+ throw new ValidationError("[linkedCheckbox prompt] No selectable choices.");
1899
+ }
1900
+ return { first, last };
1901
+ }, [items]);
1902
+ const [active, setActive] = useState(bounds.first);
1903
+ const [errorMsg, setError] = useState();
1904
+ const toggleWithLinked = (targetIndex) => {
1905
+ const targetItem = items[targetIndex];
1906
+ if (!targetItem || Separator.isSeparator(targetItem) || targetItem.disabled) {
1907
+ return;
1908
+ }
1909
+ const nextChecked = !targetItem.checked;
1910
+ const targetValue = targetItem.value;
1911
+ const linked = new Set(targetItem.linkedValues);
1912
+ setItems(
1913
+ (prevItems) => prevItems.map((item) => {
1914
+ if (Separator.isSeparator(item) || item.disabled) {
1915
+ return item;
1916
+ }
1917
+ const isTargetOrLinked = item.value === targetValue || linked.has(item.value) || item.linkedValues.includes(targetValue);
1918
+ if (isTargetOrLinked) {
1919
+ return { ...item, checked: nextChecked };
1920
+ }
1921
+ return item;
1922
+ })
1923
+ );
1924
+ };
1925
+ useKeypress(async (key) => {
1926
+ if (isEnterKey(key)) {
1927
+ const selection = items.filter(isChecked);
1928
+ const isValid = await validate([...selection]);
1929
+ if (required && selection.length === 0) {
1930
+ setError("At least one choice must be selected");
1931
+ } else if (isValid === true) {
1932
+ setStatus("done");
1933
+ done(selection.map((choice) => choice.value));
1934
+ } else {
1935
+ setError(typeof isValid === "string" ? isValid : "You must select a valid value");
1936
+ }
1937
+ } else if (isUpKey(key) || isDownKey(key)) {
1938
+ if (errorMsg) setError(void 0);
1939
+ if (loop || isUpKey(key) && active !== bounds.first || isDownKey(key) && active !== bounds.last) {
1940
+ const offset = isUpKey(key) ? -1 : 1;
1941
+ let next = active;
1942
+ do {
1943
+ next = (next + offset + items.length) % items.length;
1944
+ } while (!isNavigable(items[next]));
1945
+ setActive(next);
1946
+ }
1947
+ } else if (isSpaceKey(key)) {
1948
+ const activeItem = items[active];
1949
+ if (activeItem && !Separator.isSeparator(activeItem)) {
1950
+ if (activeItem.disabled) {
1951
+ setError(theme.i18n.disabledError);
1952
+ } else {
1953
+ setError(void 0);
1954
+ toggleWithLinked(active);
1955
+ }
1956
+ }
1957
+ } else if (key.name === "a") {
1958
+ const hasUnchecked = items.some((choice) => isSelectable(choice) && !choice.checked);
1959
+ setItems(
1960
+ (prevItems) => prevItems.map((item) => isSelectable(item) ? { ...item, checked: hasUnchecked } : item)
1961
+ );
1962
+ } else if (isNumberKey(key)) {
1963
+ const selectedIndex = Number(key.name) - 1;
1964
+ let selectableIndex = -1;
1965
+ const position = items.findIndex((item) => {
1966
+ if (Separator.isSeparator(item)) return false;
1967
+ selectableIndex++;
1968
+ return selectableIndex === selectedIndex;
1969
+ });
1970
+ const selectedItem = items[position];
1971
+ if (selectedItem && isSelectable(selectedItem)) {
1972
+ setActive(position);
1973
+ setError(void 0);
1974
+ toggleWithLinked(position);
1975
+ }
1976
+ }
1977
+ });
1978
+ const message = theme.style.message(config.message, status);
1979
+ let description;
1980
+ const page = usePagination({
1981
+ items,
1982
+ active,
1983
+ renderItem({ item, isActive }) {
1984
+ if (Separator.isSeparator(item)) {
1985
+ return ` ${item.separator}`;
1986
+ }
1987
+ const cursor = isActive ? theme.icon.cursor : " ";
1988
+ if (item.disabled) {
1989
+ const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
1990
+ const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
1991
+ return theme.style.disabled(`${cursor} ${checkbox2} ${item.name} ${disabledLabel}`);
1992
+ }
1993
+ if (isActive) {
1994
+ description = item.description;
1995
+ }
1996
+ const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
1997
+ const name = item.checked ? item.checkedName : item.name;
1998
+ const color = isActive ? theme.style.highlight : (x) => x;
1999
+ return color(`${cursor} ${checkbox} ${name}`);
2000
+ },
2001
+ pageSize,
2002
+ loop
2003
+ });
2004
+ if (status === "done") {
2005
+ const selection = items.filter(isChecked);
2006
+ const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
2007
+ return [prefix, message, answer].filter(Boolean).join(" ");
2008
+ }
2009
+ const helpLine = theme.style.keysHelpTip([
2010
+ ["up/down", "navigate"],
2011
+ ["space", "toggle"],
2012
+ ["a", "all"],
2013
+ ["enter", "submit"]
2014
+ ]);
2015
+ const lines = [
2016
+ [prefix, message].filter(Boolean).join(" "),
2017
+ page,
2018
+ helpLine
2019
+ ];
2020
+ if (description) {
2021
+ lines.push(theme.style.description(description));
2022
+ }
2023
+ if (errorMsg) {
2024
+ lines.push(theme.style.error(errorMsg));
2025
+ }
2026
+ return lines.join("\n");
2027
+ }
2028
+ );
1503
2029
 
1504
2030
  // src/interactive/prompts/scope.ts
1505
2031
  import { select } from "@inquirer/prompts";
1506
- import pc2 from "picocolors";
2032
+ import pc5 from "picocolors";
1507
2033
  var promptScope = async (options = {}) => {
1508
2034
  const initialGlobal = options.defaultGlobal ?? options.global;
1509
2035
  if (initialGlobal !== void 0) {
@@ -1514,11 +2040,11 @@ var promptScope = async (options = {}) => {
1514
2040
  message: options.message ?? "Select MCP scope:",
1515
2041
  choices: [
1516
2042
  {
1517
- name: `Current Project - ${pc2.dim(cwd)}`,
2043
+ name: `Current Project - ${pc5.dim(cwd)}`,
1518
2044
  value: false
1519
2045
  },
1520
2046
  {
1521
- name: `Global User Config - ${pc2.dim("applies across all projects")}`,
2047
+ name: `Global User Config - ${pc5.dim("applies across all projects")}`,
1522
2048
  value: true
1523
2049
  }
1524
2050
  ]
@@ -1538,26 +2064,23 @@ var promptScopeAndAgents = async (options = {}) => {
1538
2064
  cwd
1539
2065
  });
1540
2066
  const detected = resolution.detected;
1541
- const availableAgentTypes = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2067
+ const rawAvailable = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2068
+ const availableAgentTypes = sortAgentsWithClusters(rawAvailable, { global: isGlobal, cwd });
1542
2069
  if (detected.length > 0) {
1543
2070
  logger.info(
1544
- `Detected configured agents: ${pc3.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2071
+ `Detected configured agents: ${pc6.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
1545
2072
  );
1546
2073
  } else {
1547
2074
  logger.warn(`No active ${isGlobal ? "global" : "project"} agents detected`);
1548
2075
  }
1549
2076
  const defaultChecked = options.defaultAgents && options.defaultAgents.length > 0 ? options.defaultAgents : detected;
1550
- const choices = availableAgentTypes.map((agentType) => {
1551
- const config = getMcpAgentConfig(agentType);
1552
- const isDetected = detected.includes(agentType);
1553
- const label = `${config.displayName} ${pc3.dim(`(${agentType})`)}${isDetected ? pc3.green(" [detected]") : ""}`;
1554
- return {
1555
- name: label,
1556
- value: agentType,
1557
- checked: defaultChecked.includes(agentType)
1558
- };
2077
+ const choices = buildLinkedAgentChoices({
2078
+ agents: availableAgentTypes,
2079
+ checkedAgents: defaultChecked,
2080
+ detectedAgents: detected,
2081
+ scopeOptions: { global: isGlobal, cwd }
1559
2082
  });
1560
- const selectedAgents = await checkbox({
2083
+ const selectedAgents = await linkedCheckbox({
1561
2084
  message: "Select target agents (Space to select, Enter to confirm):",
1562
2085
  choices,
1563
2086
  validate: (chosen) => {
@@ -1602,19 +2125,54 @@ var promptArgsConfig = async (initialArgs = []) => {
1602
2125
  });
1603
2126
  return parseArgsString(raw.trim());
1604
2127
  };
2128
+ var formatArgsString = (args) => {
2129
+ return args.map((arg) => arg.includes(" ") || arg.includes('"') ? `"${arg.replace(/"/g, '\\"')}"` : arg).join(" ");
2130
+ };
2131
+ var promptEditArgs = async (currentArgs = []) => {
2132
+ const defaultStr = formatArgsString(currentArgs);
2133
+ const raw = await input({
2134
+ message: "Edit command arguments (space-separated, wrap paths with spaces in quotes, leave empty to clear):",
2135
+ default: defaultStr
2136
+ });
2137
+ const trimmed = raw.trim();
2138
+ if (!trimmed) {
2139
+ return [];
2140
+ }
2141
+ return parseArgsString(trimmed);
2142
+ };
1605
2143
 
1606
2144
  // src/interactive/prompts/env.ts
1607
- import { input as input2, password, select as select3 } from "@inquirer/prompts";
1608
- import pc5 from "picocolors";
2145
+ import { input as input3, password as password2, select as select3 } from "@inquirer/prompts";
2146
+ import pc9 from "picocolors";
2147
+
2148
+ // src/utils/mask-secret.ts
2149
+ var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
2150
+ var maskSecretValue = (key, value) => {
2151
+ if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
2152
+ return value;
2153
+ }
2154
+ return `${value.slice(0, 2)}***${value.slice(-2)}`;
2155
+ };
2156
+ var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
2157
+ var maskSecretHeader = (key, value) => {
2158
+ if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
2159
+ return value;
2160
+ }
2161
+ return `${value.slice(0, 4)}***${value.slice(-3)}`;
2162
+ };
2163
+
2164
+ // src/interactive/prompts/kv.ts
2165
+ import { confirm as confirm2, input as input2, password, select as select2 } from "@inquirer/prompts";
2166
+ import pc8 from "picocolors";
1609
2167
 
1610
2168
  // src/interactive/prompts/multiline.ts
1611
2169
  import { createInterface } from "readline";
1612
2170
  import { editor } from "@inquirer/prompts";
1613
- import pc4 from "picocolors";
2171
+ import pc7 from "picocolors";
1614
2172
  var readMultilineTextFromTerminal = async (message, endHint = "When done pasting, enter END on a new line or press Enter twice to finish") => {
1615
- console.log(pc4.cyan(`
2173
+ console.log(pc7.cyan(`
1616
2174
  ${message}`));
1617
- console.log(pc4.dim(` (Hint: ${endHint})
2175
+ console.log(pc7.dim(` (Hint: ${endHint})
1618
2176
  `));
1619
2177
  return new Promise((resolve) => {
1620
2178
  const rl = createInterface({
@@ -1668,8 +2226,157 @@ var promptEditorText = async (options) => {
1668
2226
  }
1669
2227
  };
1670
2228
 
2229
+ // src/interactive/prompts/kv.ts
2230
+ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
2231
+ let items = { ...currentItems };
2232
+ while (true) {
2233
+ const keys = Object.keys(items);
2234
+ console.log();
2235
+ if (keys.length === 0) {
2236
+ console.log(pc8.dim(` No ${options.itemsNoun} configured.`));
2237
+ } else {
2238
+ console.log(pc8.cyan(pc8.bold(` Configured ${options.title} (${keys.length}):`)));
2239
+ for (const [k, v] of Object.entries(items)) {
2240
+ const sep = options.separator === "=" ? "=" : ": ";
2241
+ console.log(` ${pc8.bold(k)}${sep}${pc8.dim(options.maskValue(k, v))}`);
2242
+ }
2243
+ }
2244
+ console.log();
2245
+ const choice = await select2({
2246
+ message: `Manage ${options.itemsNoun}:`,
2247
+ choices: [
2248
+ {
2249
+ name: "Open in system default editor ($EDITOR)",
2250
+ value: "editor"
2251
+ },
2252
+ {
2253
+ name: `Add or modify a ${options.itemNoun}`,
2254
+ value: "upsert"
2255
+ },
2256
+ ...keys.length > 0 ? [
2257
+ {
2258
+ name: `Delete a ${options.itemNoun}`,
2259
+ value: "delete"
2260
+ }
2261
+ ] : [],
2262
+ {
2263
+ name: `Paste multiline ${options.itemsNoun} into terminal`,
2264
+ value: "paste"
2265
+ },
2266
+ ...keys.length > 0 ? [
2267
+ {
2268
+ name: `Clear all ${options.itemsNoun}`,
2269
+ value: "clear"
2270
+ }
2271
+ ] : [],
2272
+ {
2273
+ name: `Done (finish editing ${options.itemsNoun})`,
2274
+ value: "done"
2275
+ }
2276
+ ]
2277
+ });
2278
+ if (choice === "done") {
2279
+ return items;
2280
+ }
2281
+ if (choice === "editor") {
2282
+ const defaultText = options.formatText(items);
2283
+ const text = await promptEditorText({
2284
+ message: options.editorMessage,
2285
+ postfix: options.editorPostfix,
2286
+ defaultText
2287
+ });
2288
+ const parsed = options.parseText(text);
2289
+ items = parsed;
2290
+ logger.success(`${options.title} updated (${Object.keys(items).length} total)`);
2291
+ } else if (choice === "upsert") {
2292
+ const key = await input2({
2293
+ message: options.keyPromptMessage,
2294
+ validate: (val) => {
2295
+ const trimmed = val.trim();
2296
+ if (!trimmed) return `${options.itemNoun} name cannot be empty`;
2297
+ if (/\s/.test(trimmed)) return `${options.itemNoun} name cannot contain spaces`;
2298
+ return true;
2299
+ }
2300
+ });
2301
+ const trimmedKey = key.trim();
2302
+ const existingVal = items[trimmedKey];
2303
+ const isSecret = options.isSecretKey(trimmedKey);
2304
+ let newVal;
2305
+ if (isSecret) {
2306
+ newVal = await password({
2307
+ message: existingVal !== void 0 ? `New value for (${trimmedKey}) [leave empty to keep current]:` : `${options.valuePromptMessage} for (${trimmedKey}) [sensitive content masked]:`,
2308
+ mask: "*"
2309
+ });
2310
+ if (existingVal !== void 0 && newVal === "") {
2311
+ newVal = existingVal;
2312
+ }
2313
+ } else {
2314
+ newVal = await input2({
2315
+ message: `${options.valuePromptMessage} for (${trimmedKey}):`,
2316
+ default: existingVal
2317
+ });
2318
+ }
2319
+ items[trimmedKey] = newVal;
2320
+ logger.success(`${existingVal !== void 0 ? "Updated" : "Added"}: ${pc8.cyan(trimmedKey)}`);
2321
+ } else if (choice === "delete") {
2322
+ const toDelete = await select2({
2323
+ message: `Select ${options.itemNoun} to delete:`,
2324
+ choices: [
2325
+ ...keys.map((k) => ({ name: k, value: k })),
2326
+ { name: "Cancel", value: "__cancel__" }
2327
+ ]
2328
+ });
2329
+ if (toDelete !== "__cancel__") {
2330
+ delete items[toDelete];
2331
+ logger.success(`Deleted: ${pc8.cyan(toDelete)}`);
2332
+ }
2333
+ } else if (choice === "paste") {
2334
+ const pasted = await readMultilineTextFromTerminal(options.pasteMessage);
2335
+ const parsed = options.parseText(pasted);
2336
+ const count = Object.keys(parsed).length;
2337
+ if (count === 0) {
2338
+ logger.warn(`No valid ${options.itemsNoun} recognized`);
2339
+ } else {
2340
+ if (keys.length > 0) {
2341
+ const pasteMode = await select2({
2342
+ message: `How to apply pasted ${options.itemsNoun}?`,
2343
+ choices: [
2344
+ { name: `Merge with existing ${options.itemsNoun}`, value: "merge" },
2345
+ { name: `Replace all existing ${options.itemsNoun}`, value: "replace" }
2346
+ ]
2347
+ });
2348
+ if (pasteMode === "replace") {
2349
+ items = parsed;
2350
+ } else {
2351
+ Object.assign(items, parsed);
2352
+ }
2353
+ } else {
2354
+ items = parsed;
2355
+ }
2356
+ logger.success(`Successfully applied ${pc8.cyan(String(count))} ${options.itemsNoun}`);
2357
+ }
2358
+ } else if (choice === "clear") {
2359
+ const confirmClear = await confirm2({
2360
+ message: `Are you sure you want to clear all ${options.itemsNoun}?`,
2361
+ default: false
2362
+ });
2363
+ if (confirmClear) {
2364
+ items = {};
2365
+ logger.success(`Cleared all ${options.itemsNoun}`);
2366
+ }
2367
+ }
2368
+ }
2369
+ };
2370
+
1671
2371
  // src/interactive/prompts/env.ts
1672
- var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
2372
+ var formatEnvText = (env) => {
2373
+ return Object.entries(env).map(([key, value]) => {
2374
+ if (/[\s"']/.test(value)) {
2375
+ return `${key}="${value.replace(/"/g, '\\"')}"`;
2376
+ }
2377
+ return `${key}=${value}`;
2378
+ }).join("\n");
2379
+ };
1673
2380
  var parseEnvText = (rawText) => {
1674
2381
  const result = {};
1675
2382
  const lines = rawText.split(/\r?\n/);
@@ -1695,7 +2402,7 @@ var promptEnvConfig = async (initialEnv = {}) => {
1695
2402
  const env = { ...initialEnv };
1696
2403
  const initialCount = Object.keys(env).length;
1697
2404
  if (initialCount > 0) {
1698
- logger.info(`Includes ${pc5.cyan(String(initialCount))} preset environment variables`);
2405
+ logger.info(`Includes ${pc9.cyan(String(initialCount))} preset environment variables`);
1699
2406
  }
1700
2407
  const mode = await select3({
1701
2408
  message: "Configure environment variables?",
@@ -1724,7 +2431,8 @@ var promptEnvConfig = async (initialEnv = {}) => {
1724
2431
  if (mode === "paste" || mode === "editor") {
1725
2432
  const pasted = mode === "editor" ? await promptEditorText({
1726
2433
  message: "Paste or edit environment variables in editor, then save and exit:",
1727
- postfix: ".env"
2434
+ postfix: ".env",
2435
+ defaultText: formatEnvText(env)
1728
2436
  }) : await readMultilineTextFromTerminal("Paste .env formatted content (multiline supported):");
1729
2437
  const parsed = parseEnvText(pasted);
1730
2438
  const count = Object.keys(parsed).length;
@@ -1732,17 +2440,16 @@ var promptEnvConfig = async (initialEnv = {}) => {
1732
2440
  logger.warn("No valid KEY=VALUE pairs recognized");
1733
2441
  } else {
1734
2442
  Object.assign(env, parsed);
1735
- logger.success(`Successfully parsed ${pc5.cyan(String(count))} environment variables:`);
2443
+ logger.success(`Successfully parsed ${pc9.cyan(String(count))} environment variables:`);
1736
2444
  for (const [k, v] of Object.entries(parsed)) {
1737
- const masked = SECRET_KEY_PATTERN.test(k) && v.length > 4 ? `${v.slice(0, 2)}***${v.slice(-2)}` : v;
1738
- console.log(` ${pc5.bold(k)}=${pc5.dim(masked)}`);
2445
+ console.log(` ${pc9.bold(k)}=${pc9.dim(maskSecretValue(k, v))}`);
1739
2446
  }
1740
2447
  }
1741
2448
  return env;
1742
2449
  }
1743
2450
  logger.info("Entering environment variables (leave key empty and press enter to finish):");
1744
2451
  while (true) {
1745
- const key = await input2({
2452
+ const key = await input3({
1746
2453
  message: "Variable name (Key, leave empty to finish):",
1747
2454
  validate: (val2) => {
1748
2455
  const trimmed = val2.trim();
@@ -1756,25 +2463,42 @@ var promptEnvConfig = async (initialEnv = {}) => {
1756
2463
  const isSecret = SECRET_KEY_PATTERN.test(trimmedKey);
1757
2464
  let val;
1758
2465
  if (isSecret) {
1759
- val = await password({
2466
+ val = await password2({
1760
2467
  message: `Value for (${trimmedKey}) [secret masked]:`,
1761
2468
  mask: "*"
1762
2469
  });
1763
2470
  } else {
1764
- val = await input2({
2471
+ val = await input3({
1765
2472
  message: `Value for (${trimmedKey}):`
1766
2473
  });
1767
2474
  }
1768
2475
  env[trimmedKey] = val;
1769
- logger.success(`Added: ${pc5.cyan(trimmedKey)}`);
2476
+ logger.success(`Added: ${pc9.cyan(trimmedKey)}`);
1770
2477
  }
1771
2478
  return env;
1772
2479
  };
2480
+ var promptEditEnvConfig = async (currentEnv = {}) => promptEditKeyValueConfig(currentEnv, {
2481
+ title: "Environment Variables",
2482
+ itemNoun: "variable",
2483
+ itemsNoun: "environment variables",
2484
+ separator: "=",
2485
+ editorPostfix: ".env",
2486
+ editorMessage: "Edit environment variables in editor, then save and exit:",
2487
+ pasteMessage: "Paste .env formatted content (multiline supported):",
2488
+ keyPromptMessage: "Variable name (Key):",
2489
+ valuePromptMessage: "Value",
2490
+ isSecretKey: (k) => SECRET_KEY_PATTERN.test(k),
2491
+ maskValue: maskSecretValue,
2492
+ formatText: formatEnvText,
2493
+ parseText: parseEnvText
2494
+ });
1773
2495
 
1774
2496
  // src/interactive/prompts/headers.ts
1775
- import { input as input3, password as password2, select as select4 } from "@inquirer/prompts";
1776
- import pc6 from "picocolors";
1777
- var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
2497
+ import { input as input4, password as password3, select as select4 } from "@inquirer/prompts";
2498
+ import pc10 from "picocolors";
2499
+ var formatHeadersText = (headers) => {
2500
+ return Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n");
2501
+ };
1778
2502
  var parseHeadersText = (rawText) => {
1779
2503
  const result = {};
1780
2504
  const lines = rawText.split(/\r?\n/);
@@ -1830,7 +2554,8 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1830
2554
  }
1831
2555
  if (mode === "paste" || mode === "editor") {
1832
2556
  const pasted = mode === "editor" ? await promptEditorText({
1833
- message: "Paste or edit HTTP headers in editor, then save and exit:"
2557
+ message: "Paste or edit HTTP headers in editor, then save and exit:",
2558
+ defaultText: formatHeadersText(headers)
1834
2559
  }) : await readMultilineTextFromTerminal(
1835
2560
  "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):"
1836
2561
  );
@@ -1840,17 +2565,16 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1840
2565
  logger.warn("No valid Key: Value pairs recognized");
1841
2566
  } else {
1842
2567
  Object.assign(headers, parsed);
1843
- logger.success(`Successfully parsed ${pc6.cyan(String(count))} headers:`);
2568
+ logger.success(`Successfully parsed ${pc10.cyan(String(count))} headers:`);
1844
2569
  for (const [k, v] of Object.entries(parsed)) {
1845
- const masked = SECRET_HEADER_PATTERN.test(k) && v.length > 8 ? `${v.slice(0, 4)}***${v.slice(-3)}` : v;
1846
- console.log(` ${pc6.bold(k)}: ${pc6.dim(masked)}`);
2570
+ console.log(` ${pc10.bold(k)}: ${pc10.dim(maskSecretHeader(k, v))}`);
1847
2571
  }
1848
2572
  }
1849
2573
  return headers;
1850
2574
  }
1851
2575
  logger.info("Entering HTTP headers (leave header name empty and press enter to finish):");
1852
2576
  while (true) {
1853
- const name = await input3({
2577
+ const name = await input4({
1854
2578
  message: "Header name (e.g. Authorization, leave empty to finish):",
1855
2579
  validate: (val2) => {
1856
2580
  const trimmed = val2.trim();
@@ -1864,25 +2588,39 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1864
2588
  const isSecret = SECRET_HEADER_PATTERN.test(trimmedName);
1865
2589
  let val;
1866
2590
  if (isSecret) {
1867
- val = await password2({
2591
+ val = await password3({
1868
2592
  message: `Header value for (${trimmedName}) [sensitive content masked]:`,
1869
2593
  mask: "*"
1870
2594
  });
1871
2595
  } else {
1872
- val = await input3({
2596
+ val = await input4({
1873
2597
  message: `Header value for (${trimmedName}):`
1874
2598
  });
1875
2599
  }
1876
2600
  headers[trimmedName] = val;
1877
- logger.success(`Added: ${pc6.cyan(trimmedName)}`);
2601
+ logger.success(`Added: ${pc10.cyan(trimmedName)}`);
1878
2602
  }
1879
2603
  return headers;
1880
2604
  };
2605
+ var promptEditHeadersConfig = async (currentHeaders = {}) => promptEditKeyValueConfig(currentHeaders, {
2606
+ title: "HTTP Headers",
2607
+ itemNoun: "header",
2608
+ itemsNoun: "HTTP headers",
2609
+ separator: ":",
2610
+ editorMessage: "Edit HTTP headers in editor, then save and exit:",
2611
+ pasteMessage: "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):",
2612
+ keyPromptMessage: "Header name (e.g. Authorization):",
2613
+ valuePromptMessage: "Header value",
2614
+ isSecretKey: (k) => SECRET_HEADER_PATTERN.test(k),
2615
+ maskValue: maskSecretHeader,
2616
+ formatText: formatHeadersText,
2617
+ parseText: parseHeadersText
2618
+ });
1881
2619
 
1882
2620
  // src/interactive/wizard-add.ts
1883
2621
  var wizardAdd = async (initial = {}) => {
1884
2622
  const cwd = initial.cwd ?? process.cwd();
1885
- logger.info(pc7.bold("Welcome to the MCP interactive add wizard"));
2623
+ logger.info(pc11.bold("Welcome to the MCP interactive add wizard"));
1886
2624
  let source = initial.source;
1887
2625
  if (!source) {
1888
2626
  const sourceType = await select5({
@@ -1903,12 +2641,12 @@ var wizardAdd = async (initial = {}) => {
1903
2641
  ]
1904
2642
  });
1905
2643
  if (sourceType === "npm") {
1906
- source = await input4({
1907
- message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres or mcp-server-git):",
2644
+ source = await input5({
2645
+ message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres):",
1908
2646
  validate: (val) => val.trim() ? true : "Package name cannot be empty"
1909
2647
  });
1910
2648
  } else if (sourceType === "remote") {
1911
- source = await input4({
2649
+ source = await input5({
1912
2650
  message: "Enter remote server URL (e.g. https://mcp.example.com/sse):",
1913
2651
  validate: (val) => {
1914
2652
  const trimmed = val.trim();
@@ -1918,7 +2656,7 @@ var wizardAdd = async (initial = {}) => {
1918
2656
  }
1919
2657
  });
1920
2658
  } else {
1921
- source = await input4({
2659
+ source = await input5({
1922
2660
  message: "Enter command and arguments (e.g. python -m my_mcp_server or docker run ...):",
1923
2661
  validate: (val) => val.trim() ? true : "Command cannot be empty"
1924
2662
  });
@@ -1928,7 +2666,7 @@ var wizardAdd = async (initial = {}) => {
1928
2666
  const parsed = parseMcpSource(source);
1929
2667
  let serverName = initial.name;
1930
2668
  if (!serverName) {
1931
- serverName = await input4({
2669
+ serverName = await input5({
1932
2670
  message: "MCP server name:",
1933
2671
  default: parsed.inferredName,
1934
2672
  validate: (val) => val.trim() ? true : "Server name cannot be empty"
@@ -1950,7 +2688,7 @@ var wizardAdd = async (initial = {}) => {
1950
2688
  });
1951
2689
  }
1952
2690
  if (Object.keys(headers).length === 0) {
1953
- const needHeader = await confirm2({
2691
+ const needHeader = await confirm5({
1954
2692
  message: "Configure HTTP headers (e.g. Authorization Bearer token)?",
1955
2693
  default: false
1956
2694
  });
@@ -1972,28 +2710,28 @@ var wizardAdd = async (initial = {}) => {
1972
2710
  if (parsed.type !== "remote") {
1973
2711
  env = await promptEnvConfig(env);
1974
2712
  }
1975
- console.log("\n" + pc7.cyan(pc7.bold("Configuration Preview:")));
1976
- console.log(` ${pc7.bold("Server Name:")} ${pc7.green(serverName)}`);
1977
- console.log(` ${pc7.bold("Server Type:")} ${pc7.magenta(parsed.type)}`);
1978
- console.log(` ${pc7.bold("Source/Command:")} ${pc7.dim(source)}`);
1979
- console.log(` ${pc7.bold("Scope:")} ${isGlobal ? pc7.yellow("Global") : pc7.blue("Project")}`);
1980
- console.log(` ${pc7.bold("Target Agents:")} ${pc7.cyan(selectedAgents.join(", "))}`);
2713
+ console.log("\n" + pc11.cyan(pc11.bold("Configuration Preview:")));
2714
+ console.log(` ${pc11.bold("Server Name:")} ${pc11.green(serverName)}`);
2715
+ console.log(` ${pc11.bold("Server Type:")} ${pc11.magenta(parsed.type)}`);
2716
+ console.log(` ${pc11.bold("Source/Command:")} ${pc11.dim(source)}`);
2717
+ console.log(` ${pc11.bold("Scope:")} ${isGlobal ? pc11.yellow("Global") : pc11.blue("Project")}`);
2718
+ console.log(` ${pc11.bold("Target Agents:")} ${pc11.cyan(selectedAgents.join(", "))}`);
1981
2719
  if (args.length > 0) {
1982
- console.log(` ${pc7.bold("Arguments:")} ${pc7.dim(args.join(" "))}`);
2720
+ console.log(` ${pc11.bold("Arguments:")} ${pc11.dim(args.join(" "))}`);
1983
2721
  }
1984
2722
  if (transport) {
1985
- console.log(` ${pc7.bold("Transport:")} ${pc7.magenta(transport)}`);
2723
+ console.log(` ${pc11.bold("Transport:")} ${pc11.magenta(transport)}`);
1986
2724
  }
1987
2725
  const envKeys = Object.keys(env);
1988
2726
  if (envKeys.length > 0) {
1989
- console.log(` ${pc7.bold("Environment Variables:")} ${pc7.dim(envKeys.join(", "))} (${envKeys.length})`);
2727
+ console.log(` ${pc11.bold("Environment Variables:")} ${pc11.dim(envKeys.join(", "))} (${envKeys.length})`);
1990
2728
  }
1991
2729
  const headerKeys = Object.keys(headers);
1992
2730
  if (headerKeys.length > 0) {
1993
- console.log(` ${pc7.bold("Headers:")} ${pc7.dim(headerKeys.join(", "))} (${headerKeys.length})`);
2731
+ console.log(` ${pc11.bold("Headers:")} ${pc11.dim(headerKeys.join(", "))} (${headerKeys.length})`);
1994
2732
  }
1995
2733
  console.log();
1996
- const proceed = await confirm2({
2734
+ const proceed = await confirm5({
1997
2735
  message: "Confirm installation with this configuration?",
1998
2736
  default: true
1999
2737
  });
@@ -2013,41 +2751,103 @@ var wizardAdd = async (initial = {}) => {
2013
2751
  env
2014
2752
  });
2015
2753
  logger.info(
2016
- `Writing ${pc7.bold(result.serverName)} to ${pc7.cyan(String(result.results.length))} agent config files...`
2754
+ `Writing ${pc11.bold(result.serverName)} to ${pc11.cyan(String(result.results.length))} agent config files...`
2017
2755
  );
2018
2756
  let allSuccess = true;
2019
2757
  for (const record of result.results) {
2020
2758
  if (record.success) {
2021
- logger.success(`${pc7.cyan(record.agent)}: Successfully written to ${pc7.dim(record.path)}`);
2759
+ logger.success(
2760
+ `${pc11.cyan(record.agent)}: Successfully written to ${pc11.dim(record.path)}${formatCoHostedBadge("configured", record.coConfiguredAgents)}`
2761
+ );
2022
2762
  } else {
2023
2763
  allSuccess = false;
2024
- logger.error(`${pc7.cyan(record.agent)}: Failed to write - ${record.error}`);
2764
+ logger.error(`${pc11.cyan(record.agent)}: Failed to write - ${record.error}`);
2025
2765
  }
2026
2766
  }
2027
2767
  if (allSuccess) {
2028
- logger.success(pc7.bold(`MCP server "${serverName}" configured successfully!`));
2768
+ logger.success(pc11.bold(`MCP server "${serverName}" configured successfully!`));
2029
2769
  }
2030
2770
  return allSuccess;
2031
2771
  };
2032
2772
 
2033
2773
  // src/interactive/wizard-manage.ts
2034
- import { checkbox as checkbox2, confirm as confirm3, select as select6 } from "@inquirer/prompts";
2035
- import pc8 from "picocolors";
2774
+ import { confirm as confirm6, input as input6, select as select6 } from "@inquirer/prompts";
2775
+ import pc13 from "picocolors";
2776
+
2777
+ // src/utils/display-server-details.ts
2778
+ import pc12 from "picocolors";
2779
+ var displayServerDetails = ({
2780
+ serverName,
2781
+ config,
2782
+ agents,
2783
+ hasDivergence,
2784
+ global: isGlobal,
2785
+ titlePrefix = "MCP Server Details"
2786
+ }) => {
2787
+ console.log("\n" + pc12.cyan(pc12.bold(`${titlePrefix}: [${serverName}]`)));
2788
+ if (isGlobal !== void 0) {
2789
+ console.log(` ${pc12.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
2790
+ }
2791
+ if (agents && agents.length > 0) {
2792
+ console.log(
2793
+ ` ${pc12.bold("Configured Agents:")} ${pc12.green(agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2794
+ );
2795
+ }
2796
+ if (hasDivergence) {
2797
+ console.log(
2798
+ ` ${pc12.yellow(pc12.bold("Notice:"))} ${pc12.yellow("Configurations differ across installed agents. Showing configuration from the first agent.")}`
2799
+ );
2800
+ }
2801
+ const isRemote = Boolean(config.url && config.url.length > 0);
2802
+ if (isRemote) {
2803
+ console.log(` ${pc12.bold("Transport:")} ${pc12.magenta(config.type ?? "http")}`);
2804
+ console.log(` ${pc12.bold("URL:")} ${pc12.dim(config.url ?? "")}`);
2805
+ const headerKeys = Object.keys(config.headers ?? {});
2806
+ if (headerKeys.length > 0) {
2807
+ console.log(` ${pc12.bold("Headers:")} ${pc12.cyan(String(headerKeys.length))}`);
2808
+ for (const [k, v] of Object.entries(config.headers ?? {})) {
2809
+ console.log(` ${pc12.bold(k)}: ${pc12.dim(maskSecretHeader(k, v))}`);
2810
+ }
2811
+ } else {
2812
+ console.log(` ${pc12.bold("Headers:")} ${pc12.dim("(none)")}`);
2813
+ }
2814
+ } else {
2815
+ console.log(` ${pc12.bold("Command:")} ${pc12.magenta(config.command ?? "")}`);
2816
+ const argsStr = config.args && config.args.length > 0 ? config.args.join(" ") : "(none)";
2817
+ console.log(` ${pc12.bold("Arguments:")} ${pc12.dim(argsStr)}`);
2818
+ const envKeys = Object.keys(config.env ?? {});
2819
+ if (envKeys.length > 0) {
2820
+ console.log(` ${pc12.bold("Environment Variables:")} ${pc12.cyan(String(envKeys.length))}`);
2821
+ for (const [k, v] of Object.entries(config.env ?? {})) {
2822
+ console.log(` ${pc12.bold(k)}=${pc12.dim(maskSecretValue(k, v))}`);
2823
+ }
2824
+ } else {
2825
+ console.log(` ${pc12.bold("Environment Variables:")} ${pc12.dim("(none)")}`);
2826
+ }
2827
+ }
2828
+ console.log();
2829
+ };
2036
2830
 
2037
2831
  // src/interactive/utils/group-installed-servers.ts
2038
2832
  var normalizeServerConfig = parseServerConfig;
2039
2833
  var groupInstalledServersByName = (installed) => {
2040
2834
  const grouped = /* @__PURE__ */ new Map();
2041
2835
  for (const item of installed) {
2836
+ const itemConfig = normalizeServerConfig(item.config);
2042
2837
  let entry = grouped.get(item.serverName);
2043
2838
  if (!entry) {
2044
2839
  entry = {
2045
2840
  serverName: item.serverName,
2046
2841
  agents: [],
2047
2842
  paths: [],
2048
- config: normalizeServerConfig(item.config)
2843
+ config: itemConfig,
2844
+ hasDivergence: false
2049
2845
  };
2050
2846
  grouped.set(item.serverName, entry);
2847
+ } else if (!entry.hasDivergence) {
2848
+ if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
2849
+ entry.hasDivergence = true;
2850
+ }
2051
2851
  }
2052
2852
  if (!entry.agents.includes(item.agent)) {
2053
2853
  entry.agents.push(item.agent);
@@ -2060,6 +2860,221 @@ var groupInstalledServersByName = (installed) => {
2060
2860
  };
2061
2861
 
2062
2862
  // src/interactive/wizard-manage.ts
2863
+ var promptSwitchServerType = async (currentConfig, serverName) => {
2864
+ const isRemote = Boolean(currentConfig.url && currentConfig.url.length > 0);
2865
+ if (isRemote) {
2866
+ const newCmd = await input6({
2867
+ message: "Executable command (e.g. node, npx):",
2868
+ validate: (val) => val.trim() ? true : "Command cannot be empty"
2869
+ });
2870
+ const newArgs = await promptEditArgs([]);
2871
+ const newEnv = await promptEditEnvConfig({});
2872
+ logger.success(`Switched [${serverName}] configuration to stdio mode`);
2873
+ return {
2874
+ command: newCmd.trim(),
2875
+ args: newArgs.length > 0 ? newArgs : void 0,
2876
+ env: Object.keys(newEnv).length > 0 ? newEnv : void 0
2877
+ };
2878
+ }
2879
+ const newUrl = await input6({
2880
+ message: "Remote server URL:",
2881
+ validate: (val) => {
2882
+ const trimmed = val.trim();
2883
+ if (!trimmed) return "URL cannot be empty";
2884
+ if (!/^https?:\/\//i.test(trimmed)) {
2885
+ return "Please enter a valid URL starting with http:// or https://";
2886
+ }
2887
+ return true;
2888
+ }
2889
+ });
2890
+ const transport = await select6({
2891
+ message: "Select remote transport protocol:",
2892
+ choices: [
2893
+ { name: "HTTP", value: "http" },
2894
+ { name: "SSE (Server-Sent Events)", value: "sse" }
2895
+ ],
2896
+ default: "http"
2897
+ });
2898
+ const newHeaders = await promptEditHeadersConfig({});
2899
+ logger.success(`Switched [${serverName}] configuration to remote mode`);
2900
+ return {
2901
+ url: newUrl.trim(),
2902
+ type: transport,
2903
+ headers: Object.keys(newHeaders).length > 0 ? newHeaders : void 0
2904
+ };
2905
+ };
2906
+ var handleEditServerConfig = async (options) => {
2907
+ const { targetGroup } = options;
2908
+ const isGlobal = options.global ?? false;
2909
+ const cwd = options.cwd ?? process.cwd();
2910
+ const serverName = targetGroup.serverName;
2911
+ let workingConfig = {
2912
+ ...targetGroup.config,
2913
+ args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
2914
+ env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
2915
+ headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
2916
+ };
2917
+ while (true) {
2918
+ const isRemote = Boolean(workingConfig.url && workingConfig.url.length > 0);
2919
+ displayServerDetails({
2920
+ serverName,
2921
+ config: workingConfig,
2922
+ titlePrefix: "Edit Server Configuration"
2923
+ });
2924
+ const editChoices = isRemote ? [
2925
+ { name: "Edit HTTP Headers (headers)", value: "headers" },
2926
+ { name: "Edit Remote URL (url)", value: "url" },
2927
+ { name: "Edit Transport Protocol (type)", value: "transport" },
2928
+ { name: "Switch to local command (stdio)", value: "switch_type" },
2929
+ { name: "Reset changes to original", value: "reset" },
2930
+ { name: "Save and apply changes", value: "save" },
2931
+ { name: "Cancel (discard changes)", value: "cancel" }
2932
+ ] : [
2933
+ { name: "Edit Environment Variables (env)", value: "env" },
2934
+ { name: "Edit Command Arguments (args)", value: "args" },
2935
+ { name: "Edit Executable Command (command)", value: "command" },
2936
+ { name: "Switch to remote server (HTTP/SSE)", value: "switch_type" },
2937
+ { name: "Reset changes to original", value: "reset" },
2938
+ { name: "Save and apply changes", value: "save" },
2939
+ { name: "Cancel (discard changes)", value: "cancel" }
2940
+ ];
2941
+ const editAction = await select6({
2942
+ message: `What would you like to modify in [${serverName}]?`,
2943
+ choices: editChoices
2944
+ });
2945
+ if (editAction === "cancel") {
2946
+ logger.info("Modification cancelled; changes discarded");
2947
+ return;
2948
+ }
2949
+ if (editAction === "reset") {
2950
+ workingConfig = {
2951
+ ...targetGroup.config,
2952
+ args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
2953
+ env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
2954
+ headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
2955
+ };
2956
+ logger.info("Configuration reset to original");
2957
+ continue;
2958
+ }
2959
+ if (editAction === "switch_type") {
2960
+ workingConfig = await promptSwitchServerType(workingConfig, serverName);
2961
+ continue;
2962
+ }
2963
+ if (editAction === "env") {
2964
+ workingConfig.env = await promptEditEnvConfig(workingConfig.env ?? {});
2965
+ } else if (editAction === "args") {
2966
+ workingConfig.args = await promptEditArgs(workingConfig.args ?? []);
2967
+ } else if (editAction === "command") {
2968
+ const newCmd = await input6({
2969
+ message: "Executable command:",
2970
+ default: workingConfig.command,
2971
+ validate: (val) => val.trim() ? true : "Command cannot be empty"
2972
+ });
2973
+ workingConfig.command = newCmd.trim();
2974
+ } else if (editAction === "headers") {
2975
+ workingConfig.headers = await promptEditHeadersConfig(workingConfig.headers ?? {});
2976
+ } else if (editAction === "url") {
2977
+ const newUrl = await input6({
2978
+ message: "Remote server URL:",
2979
+ default: workingConfig.url,
2980
+ validate: (val) => {
2981
+ const trimmed = val.trim();
2982
+ if (!trimmed) return "URL cannot be empty";
2983
+ if (!/^https?:\/\//i.test(trimmed)) {
2984
+ return "Please enter a valid URL starting with http:// or https://";
2985
+ }
2986
+ return true;
2987
+ }
2988
+ });
2989
+ workingConfig.url = newUrl.trim();
2990
+ } else if (editAction === "transport") {
2991
+ workingConfig.type = await select6({
2992
+ message: "Select remote transport protocol:",
2993
+ choices: [
2994
+ { name: "HTTP", value: "http" },
2995
+ { name: "SSE (Server-Sent Events)", value: "sse" }
2996
+ ],
2997
+ default: workingConfig.type === "sse" ? "sse" : "http"
2998
+ });
2999
+ } else if (editAction === "save") {
3000
+ let targetAgents = targetGroup.agents;
3001
+ if (targetGroup.agents.length > 1) {
3002
+ const sortedAgents = sortAgentsWithClusters(targetGroup.agents, { global: isGlobal, cwd });
3003
+ const choices = buildLinkedAgentChoices({
3004
+ agents: sortedAgents,
3005
+ checkedAgents: sortedAgents,
3006
+ scopeOptions: { global: isGlobal, cwd }
3007
+ });
3008
+ targetAgents = await linkedCheckbox({
3009
+ message: "Select agents to update configuration (Space to toggle):",
3010
+ choices,
3011
+ loop: false,
3012
+ validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
3013
+ });
3014
+ if (targetAgents.length < targetGroup.agents.length) {
3015
+ const unselected = targetGroup.agents.filter((a) => !targetAgents.includes(a));
3016
+ const unselectedNames = unselected.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3017
+ logger.info(
3018
+ `Note: Updating only a subset of agents. Server configurations will diverge from: ${unselectedNames}.`
3019
+ );
3020
+ }
3021
+ }
3022
+ const requestedTransport = workingConfig.url ? workingConfig.type ?? "http" : "stdio";
3023
+ const resolution = resolveTargetAgents({
3024
+ requested: targetAgents,
3025
+ global: isGlobal,
3026
+ cwd,
3027
+ transport: requestedTransport
3028
+ });
3029
+ if (resolution.incompatible.length > 0) {
3030
+ for (const item of resolution.incompatible) {
3031
+ logger.warn(`Skipping ${pc13.cyan(item.agent)}: ${item.reason}`);
3032
+ }
3033
+ }
3034
+ if (resolution.compatibleAgents.length === 0) {
3035
+ logger.error(
3036
+ `None of the selected agents support ${requestedTransport} transport. Cannot update.`
3037
+ );
3038
+ continue;
3039
+ }
3040
+ const agentNames = resolution.compatibleAgents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3041
+ const confirmed = await confirm6({
3042
+ message: `Confirm updating configuration for [${serverName}] across: ${agentNames}?`,
3043
+ default: true
3044
+ });
3045
+ if (!confirmed) {
3046
+ logger.warn("Update cancelled");
3047
+ continue;
3048
+ }
3049
+ const updateResult = updateMcpServer({
3050
+ serverName,
3051
+ config: workingConfig,
3052
+ previousConfig: targetGroup.config,
3053
+ agents: resolution.compatibleAgents,
3054
+ global: isGlobal,
3055
+ cwd
3056
+ });
3057
+ let updatedAny = false;
3058
+ const succeededAgents = [];
3059
+ for (const res of updateResult.results) {
3060
+ if (res.success) {
3061
+ updatedAny = true;
3062
+ succeededAgents.push(res.agent);
3063
+ logger.success(
3064
+ `${pc13.cyan(res.agent)}: Successfully updated configuration in ${pc13.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
3065
+ );
3066
+ } else {
3067
+ logger.error(`${pc13.cyan(res.agent)}: Update failed - ${res.error}`);
3068
+ }
3069
+ }
3070
+ if (updatedAny) {
3071
+ targetGroup.config = updateResult.config;
3072
+ logger.success(`Configuration for [${serverName}] updated successfully!`);
3073
+ return;
3074
+ }
3075
+ }
3076
+ }
3077
+ };
2063
3078
  var wizardManage = async (options = {}) => {
2064
3079
  const cwd = options.cwd ?? process.cwd();
2065
3080
  const isGlobal = await promptScope({
@@ -2073,51 +3088,57 @@ var wizardManage = async (options = {}) => {
2073
3088
  return;
2074
3089
  }
2075
3090
  const grouped = groupInstalledServersByName(installed);
3091
+ let pendingServerName = options.serverName;
3092
+ const refreshGroupedServers = () => {
3093
+ const freshInstalled = listInstalledMcpServers({ global: isGlobal, cwd });
3094
+ const freshGrouped = groupInstalledServersByName(freshInstalled);
3095
+ grouped.clear();
3096
+ for (const [name, grp] of freshGrouped) {
3097
+ grouped.set(name, grp);
3098
+ }
3099
+ };
2076
3100
  while (true) {
2077
- const choices = Array.from(grouped.values()).map((g) => {
2078
- const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
2079
- return {
2080
- name: `${pc8.bold(g.serverName)} ${pc8.dim(`(configured in: ${agentNames})`)}`,
2081
- value: g.serverName
2082
- };
2083
- });
2084
- choices.push({
2085
- name: `Back`,
2086
- value: "__back__"
2087
- });
2088
- const chosenServerName = await select6({
2089
- message: "Select MCP server to manage or sync:",
2090
- choices
2091
- });
2092
- if (chosenServerName === "__back__") {
2093
- return;
3101
+ let chosenServerName;
3102
+ if (pendingServerName && grouped.has(pendingServerName)) {
3103
+ chosenServerName = pendingServerName;
3104
+ pendingServerName = void 0;
3105
+ } else {
3106
+ pendingServerName = void 0;
3107
+ const choices = Array.from(grouped.values()).map((g) => {
3108
+ const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3109
+ return {
3110
+ name: `${pc13.bold(g.serverName)} ${pc13.dim(`(configured in: ${agentNames})`)}`,
3111
+ value: g.serverName
3112
+ };
3113
+ });
3114
+ choices.push({
3115
+ name: `Back`,
3116
+ value: "__back__"
3117
+ });
3118
+ chosenServerName = await select6({
3119
+ message: "Select MCP server to manage or sync:",
3120
+ choices
3121
+ });
3122
+ if (chosenServerName === "__back__") {
3123
+ return;
3124
+ }
2094
3125
  }
2095
3126
  const targetGroup = grouped.get(chosenServerName);
2096
3127
  if (!targetGroup) continue;
2097
- console.log("\n" + pc8.cyan(pc8.bold(`MCP Server Details: [${chosenServerName}]`)));
2098
- console.log(` ${pc8.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
2099
- console.log(
2100
- ` ${pc8.bold("Configured Agents:")} ${pc8.green(targetGroup.agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2101
- );
2102
- const cfg = targetGroup.config;
2103
- if (isRemoteServerConfig(cfg)) {
2104
- console.log(` ${pc8.bold("URL:")} ${pc8.dim(cfg.url)} (${cfg.type})`);
2105
- if (cfg.headers && Object.keys(cfg.headers).length > 0) {
2106
- console.log(` ${pc8.bold("Headers:")} ${Object.keys(cfg.headers).join(", ")}`);
2107
- }
2108
- } else if (isStdioServerConfig(cfg)) {
2109
- console.log(` ${pc8.bold("Command:")} ${pc8.magenta(cfg.command)}`);
2110
- if (cfg.args && cfg.args.length > 0) {
2111
- console.log(` ${pc8.bold("Arguments:")} ${pc8.dim(cfg.args.join(" "))}`);
2112
- }
2113
- if (cfg.env && Object.keys(cfg.env).length > 0) {
2114
- console.log(` ${pc8.bold("Environment Variables:")} ${pc8.dim(Object.keys(cfg.env).join(", "))}`);
2115
- }
2116
- }
2117
- console.log();
3128
+ displayServerDetails({
3129
+ serverName: chosenServerName,
3130
+ config: targetGroup.config,
3131
+ agents: targetGroup.agents,
3132
+ global: isGlobal,
3133
+ hasDivergence: targetGroup.hasDivergence
3134
+ });
2118
3135
  const action = await select6({
2119
3136
  message: `What would you like to do with [${chosenServerName}]?`,
2120
3137
  choices: [
3138
+ {
3139
+ name: "Edit server configuration",
3140
+ value: "edit"
3141
+ },
2121
3142
  {
2122
3143
  name: "Sync / clone to other agents",
2123
3144
  value: "sync"
@@ -2129,23 +3150,37 @@ var wizardManage = async (options = {}) => {
2129
3150
  ]
2130
3151
  });
2131
3152
  if (action === "back") continue;
3153
+ if (action === "edit") {
3154
+ await handleEditServerConfig({
3155
+ targetGroup,
3156
+ global: isGlobal,
3157
+ cwd
3158
+ });
3159
+ refreshGroupedServers();
3160
+ continue;
3161
+ }
2132
3162
  if (action === "sync") {
2133
3163
  const allAllowedAgents = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2134
- const candidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
2135
- if (candidateAgents.length === 0) {
2136
- logger.info("All supported agents in this scope already have this MCP server configured; no sync needed");
3164
+ const rawCandidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
3165
+ if (rawCandidateAgents.length === 0) {
3166
+ logger.info(
3167
+ "All supported agents in this scope already have this MCP server configured; no sync needed"
3168
+ );
2137
3169
  continue;
2138
3170
  }
2139
- const selectedToSync = await checkbox2({
3171
+ const candidateAgents = sortAgentsWithClusters(rawCandidateAgents, { global: isGlobal, cwd });
3172
+ const choices = buildLinkedAgentChoices({
3173
+ agents: candidateAgents,
3174
+ checkedAgents: [],
3175
+ scopeOptions: { global: isGlobal, cwd }
3176
+ });
3177
+ const selectedToSync = await linkedCheckbox({
2140
3178
  message: "Select target agents to sync to (Space to select):",
2141
- choices: candidateAgents.map((a) => ({
2142
- name: `${getMcpAgentConfig(a).displayName} (${a})`,
2143
- value: a,
2144
- checked: false
2145
- })),
3179
+ choices,
3180
+ loop: false,
2146
3181
  validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
2147
3182
  });
2148
- const confirmed = await confirm3({
3183
+ const confirmed = await confirm6({
2149
3184
  message: `Confirm syncing configuration of [${chosenServerName}] to: ${selectedToSync.join(", ")}?`,
2150
3185
  default: true
2151
3186
  });
@@ -2153,25 +3188,37 @@ var wizardManage = async (options = {}) => {
2153
3188
  logger.warn("Sync cancelled");
2154
3189
  continue;
2155
3190
  }
2156
- for (const targetAgent of selectedToSync) {
2157
- const res = installMcpServerForAgent(chosenServerName, targetGroup.config, targetAgent, {
2158
- global: isGlobal,
2159
- cwd
2160
- });
3191
+ const syncResult = updateMcpServer({
3192
+ serverName: chosenServerName,
3193
+ config: targetGroup.config,
3194
+ agents: selectedToSync,
3195
+ global: isGlobal,
3196
+ cwd
3197
+ });
3198
+ for (const item of syncResult.incompatible) {
3199
+ logger.warn(`Skipping ${pc13.cyan(item.agent)}: ${item.reason}`);
3200
+ }
3201
+ for (const res of syncResult.results) {
3202
+ if (syncResult.incompatible.some((i) => i.agent === res.agent)) {
3203
+ continue;
3204
+ }
2161
3205
  if (res.success) {
2162
- logger.success(`${pc8.cyan(targetAgent)}: Successfully synced to ${pc8.dim(res.path)}`);
2163
- targetGroup.agents.push(targetAgent);
3206
+ logger.success(
3207
+ `${pc13.cyan(res.agent)}: Successfully synced to ${pc13.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
3208
+ );
3209
+ targetGroup.agents.push(res.agent);
2164
3210
  } else {
2165
- logger.error(`${pc8.cyan(targetAgent)}: Sync failed - ${res.error}`);
3211
+ logger.error(`${pc13.cyan(res.agent)}: Sync failed - ${res.error}`);
2166
3212
  }
2167
3213
  }
3214
+ refreshGroupedServers();
2168
3215
  }
2169
3216
  }
2170
3217
  };
2171
3218
 
2172
3219
  // src/interactive/wizard-remove.ts
2173
- import { checkbox as checkbox3, confirm as confirm4, select as select7 } from "@inquirer/prompts";
2174
- import pc9 from "picocolors";
3220
+ import { confirm as confirm7, select as select7 } from "@inquirer/prompts";
3221
+ import pc14 from "picocolors";
2175
3222
  var wizardRemove = async (options = {}) => {
2176
3223
  const cwd = options.cwd ?? process.cwd();
2177
3224
  const isGlobal = await promptScope({
@@ -2188,7 +3235,7 @@ var wizardRemove = async (options = {}) => {
2188
3235
  let serverName = options.name;
2189
3236
  if (!serverName) {
2190
3237
  const choices = Array.from(serverMap.values()).map((g) => ({
2191
- name: `${pc9.bold(g.serverName)} ${pc9.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
3238
+ name: `${pc14.bold(g.serverName)} ${pc14.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
2192
3239
  value: g.serverName
2193
3240
  }));
2194
3241
  serverName = await select7({
@@ -2196,20 +3243,22 @@ var wizardRemove = async (options = {}) => {
2196
3243
  choices
2197
3244
  });
2198
3245
  }
2199
- const installedAgents = serverMap.get(serverName)?.agents || [];
2200
- if (installedAgents.length === 0) {
3246
+ const rawInstalledAgents = serverMap.get(serverName)?.agents || [];
3247
+ if (rawInstalledAgents.length === 0) {
2201
3248
  logger.warn(`No agents found with [${serverName}] installed`);
2202
3249
  return false;
2203
3250
  }
3251
+ const installedAgents = sortAgentsWithClusters(rawInstalledAgents, { global: isGlobal, cwd });
2204
3252
  let targetAgents = options.agents;
2205
3253
  if (!targetAgents || targetAgents.length === 0) {
2206
- targetAgents = await checkbox3({
3254
+ const choices = buildLinkedAgentChoices({
3255
+ agents: installedAgents,
3256
+ checkedAgents: installedAgents,
3257
+ scopeOptions: { global: isGlobal, cwd }
3258
+ });
3259
+ targetAgents = await linkedCheckbox({
2207
3260
  message: `Select agents to remove [${serverName}] from:`,
2208
- choices: installedAgents.map((agent) => ({
2209
- name: `${getMcpAgentConfig(agent)?.displayName ?? agent} (${agent})`,
2210
- value: agent,
2211
- checked: true
2212
- })),
3261
+ choices,
2213
3262
  validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
2214
3263
  });
2215
3264
  } else {
@@ -2220,7 +3269,7 @@ var wizardRemove = async (options = {}) => {
2220
3269
  }
2221
3270
  targetAgents = validAgents;
2222
3271
  }
2223
- const confirmed = await confirm4({
3272
+ const confirmed = await confirm7({
2224
3273
  message: `Confirm removing MCP server [${serverName}] from ${targetAgents.join(", ")}?`,
2225
3274
  default: true
2226
3275
  });
@@ -2237,10 +3286,12 @@ var wizardRemove = async (options = {}) => {
2237
3286
  let removedCount = 0;
2238
3287
  for (const res of results) {
2239
3288
  if (res.removed) {
2240
- logger.success(`${pc9.cyan(res.agent)}: Successfully removed from ${pc9.dim(res.path)}`);
3289
+ logger.success(
3290
+ `${pc14.cyan(res.agent)}: Successfully removed from ${pc14.dim(res.path)}${formatCoHostedBadge("affected", res.coAffectedAgents)}`
3291
+ );
2241
3292
  removedCount++;
2242
3293
  } else if (res.error) {
2243
- logger.error(`${pc9.cyan(res.agent)}: Failed to remove - ${res.error}`);
3294
+ logger.error(`${pc14.cyan(res.agent)}: Failed to remove - ${res.error}`);
2244
3295
  }
2245
3296
  }
2246
3297
  if (removedCount > 0) {
@@ -2254,8 +3305,8 @@ var wizardRemove = async (options = {}) => {
2254
3305
  // src/interactive/main-menu.ts
2255
3306
  var mainMenu = async () => {
2256
3307
  console.log();
2257
- console.log(pc10.bold(pc10.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
2258
- console.log(pc10.dim("Cross-platform MCP server configuration & synchronization tool"));
3308
+ console.log(pc15.bold(pc15.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
3309
+ console.log(pc15.dim("Cross-platform MCP server configuration & synchronization tool"));
2259
3310
  console.log();
2260
3311
  while (true) {
2261
3312
  try {
@@ -2281,7 +3332,7 @@ var mainMenu = async () => {
2281
3332
  ]
2282
3333
  });
2283
3334
  if (action === "exit") {
2284
- console.log(pc10.dim("Goodbye!"));
3335
+ console.log(pc15.dim("Goodbye!"));
2285
3336
  break;
2286
3337
  }
2287
3338
  if (action === "add") {
@@ -2294,7 +3345,7 @@ var mainMenu = async () => {
2294
3345
  console.log();
2295
3346
  } catch (error) {
2296
3347
  if (error?.name === "ExitPromptError") {
2297
- console.log("\n" + pc10.dim("Exited."));
3348
+ console.log("\n" + pc15.dim("Exited."));
2298
3349
  break;
2299
3350
  }
2300
3351
  throw error;
@@ -2302,6 +3353,211 @@ var mainMenu = async () => {
2302
3353
  }
2303
3354
  };
2304
3355
 
3356
+ // src/utils/resolve-transport.ts
3357
+ var resolveTransport = (input7) => {
3358
+ if (!input7) return void 0;
3359
+ if (input7 === "http" || input7 === "sse") return input7;
3360
+ throw new Error(`Unsupported transport "${input7}" (expected: http, sse)`);
3361
+ };
3362
+
3363
+ // src/cli/manage.ts
3364
+ import { Command } from "commander";
3365
+ import pc16 from "picocolors";
3366
+
3367
+ // src/utils/parse-key-value-list.ts
3368
+ var parseKeyValueList = (entries, separator) => {
3369
+ if (!entries || entries.length === 0) return {};
3370
+ const result = {};
3371
+ for (const entry of entries) {
3372
+ const splitIndex = entry.indexOf(separator);
3373
+ if (splitIndex === -1) {
3374
+ throw new Error(`Invalid entry "${entry}": expected "${separator}" separator`);
3375
+ }
3376
+ const key = entry.slice(0, splitIndex).trim();
3377
+ const value = entry.slice(splitIndex + separator.length).trim();
3378
+ if (!key) throw new Error(`Invalid entry "${entry}": empty key`);
3379
+ result[key] = value;
3380
+ }
3381
+ return result;
3382
+ };
3383
+
3384
+ // src/cli/manage.ts
3385
+ var requireTargetServerGroup = (serverName, scope) => {
3386
+ const installed = listInstalledMcpServers(scope);
3387
+ const grouped = groupInstalledServersByName(installed);
3388
+ const targetGroup = grouped.get(serverName);
3389
+ if (!targetGroup) {
3390
+ logger.error(
3391
+ `MCP server "${serverName}" is not configured in ${scope.global ? "global" : "project"} scope.`
3392
+ );
3393
+ process.exitCode = 1;
3394
+ return void 0;
3395
+ }
3396
+ return targetGroup;
3397
+ };
3398
+ var mcpManageCommand = new 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) => {
3399
+ try {
3400
+ const cwd = process.cwd();
3401
+ const isGlobal = Boolean(options.global);
3402
+ 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;
3403
+ const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
3404
+ if (hasModifications) {
3405
+ if (options.url !== void 0 && options.command !== void 0) {
3406
+ logger.error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
3407
+ process.exitCode = 1;
3408
+ return;
3409
+ }
3410
+ if (!serverName) {
3411
+ logger.error('Missing required argument: "server-name" when passing modification flags.');
3412
+ process.exitCode = 1;
3413
+ return;
3414
+ }
3415
+ const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
3416
+ if (!targetGroup) {
3417
+ return;
3418
+ }
3419
+ const isCurrentRemote = Boolean(targetGroup.config.url && targetGroup.config.url.length > 0);
3420
+ const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
3421
+ if (willBeRemote) {
3422
+ const ignoredStdioFlags = [];
3423
+ if (options.env !== void 0) ignoredStdioFlags.push("--env");
3424
+ if (options.clearEnv) ignoredStdioFlags.push("--clear-env");
3425
+ if (options.args !== void 0) ignoredStdioFlags.push("--args");
3426
+ if (options.clearArgs) ignoredStdioFlags.push("--clear-args");
3427
+ if (ignoredStdioFlags.length > 0) {
3428
+ const hint = options.url !== void 0 ? "When configuring a remote server, stdio flags are ignored." : "Use --command to switch to stdio mode.";
3429
+ logger.warn(
3430
+ `Server "${serverName}" is a remote server. The following stdio flags will be ignored: ${ignoredStdioFlags.join(", ")}. ${hint}`
3431
+ );
3432
+ }
3433
+ } else {
3434
+ const ignoredRemoteFlags = [];
3435
+ if (options.header !== void 0) ignoredRemoteFlags.push("--header");
3436
+ if (options.clearHeaders) ignoredRemoteFlags.push("--clear-headers");
3437
+ if (options.transport !== void 0) ignoredRemoteFlags.push("--transport");
3438
+ if (ignoredRemoteFlags.length > 0) {
3439
+ const hint = options.command !== void 0 ? "When configuring a stdio server, remote flags are ignored." : "Use --url to switch to remote mode.";
3440
+ logger.warn(
3441
+ `Server "${serverName}" is a stdio server. The following remote flags will be ignored: ${ignoredRemoteFlags.join(", ")}. ${hint}`
3442
+ );
3443
+ }
3444
+ }
3445
+ const incomingDelta = {};
3446
+ if (options.command !== void 0) {
3447
+ incomingDelta.command = options.command;
3448
+ }
3449
+ if (options.clearArgs) {
3450
+ incomingDelta.args = void 0;
3451
+ }
3452
+ if (options.args !== void 0) {
3453
+ incomingDelta.args = options.args;
3454
+ }
3455
+ if (options.url !== void 0) {
3456
+ incomingDelta.url = options.url;
3457
+ }
3458
+ if (options.transport !== void 0) {
3459
+ incomingDelta.type = resolveTransport(options.transport);
3460
+ }
3461
+ if (options.clearEnv) {
3462
+ incomingDelta.env = void 0;
3463
+ }
3464
+ if (options.env !== void 0) {
3465
+ const parsedEnv = parseKeyValueList(options.env, "=");
3466
+ const baseEnv = options.clearEnv ? {} : targetGroup.config.env ?? {};
3467
+ incomingDelta.env = { ...baseEnv, ...parsedEnv };
3468
+ }
3469
+ if (options.clearHeaders) {
3470
+ incomingDelta.headers = void 0;
3471
+ }
3472
+ if (options.header !== void 0) {
3473
+ const parsedHeaders = parseKeyValueList(options.header, ":");
3474
+ const baseHeaders = options.clearHeaders ? {} : targetGroup.config.headers ?? {};
3475
+ incomingDelta.headers = { ...baseHeaders, ...parsedHeaders };
3476
+ }
3477
+ let targetAgents = targetGroup.agents;
3478
+ if (options.agent !== void 0) {
3479
+ const parsed = parseMcpAgentList(options.agent);
3480
+ if (!parsed || parsed.length === 0) {
3481
+ logger.error(`No valid agents recognized from: "${options.agent.join(", ")}".`);
3482
+ process.exitCode = 1;
3483
+ return;
3484
+ }
3485
+ targetAgents = parsed;
3486
+ }
3487
+ const updateResult = updateMcpServer({
3488
+ serverName,
3489
+ config: incomingDelta,
3490
+ previousConfig: targetGroup.config,
3491
+ agents: targetAgents,
3492
+ global: isGlobal,
3493
+ cwd
3494
+ });
3495
+ for (const item of updateResult.incompatible) {
3496
+ logger.warn(`Skipping ${pc16.cyan(item.agent)}: ${item.reason}`);
3497
+ }
3498
+ const attemptedResults = updateResult.results.filter(
3499
+ (r) => !updateResult.incompatible.some((i) => i.agent === r.agent)
3500
+ );
3501
+ if (attemptedResults.length === 0) {
3502
+ const requestedTransport = updateResult.config.url ? updateResult.config.type ?? "http" : "stdio";
3503
+ logger.error(
3504
+ `None of the target agents support ${requestedTransport} transport. Update aborted.`
3505
+ );
3506
+ process.exitCode = 1;
3507
+ return;
3508
+ }
3509
+ logger.info(
3510
+ `Updating ${pc16.bold(serverName)} across ${pc16.cyan(String(attemptedResults.length))} agent(s)...`
3511
+ );
3512
+ let allSuccess = true;
3513
+ for (const res of attemptedResults) {
3514
+ if (res.success) {
3515
+ logger.success(`${pc16.cyan(res.agent)}: Successfully updated in ${pc16.dim(res.path)}`);
3516
+ logCoHostedNotice("configured", res.coConfiguredAgents);
3517
+ } else {
3518
+ allSuccess = false;
3519
+ logger.error(`${pc16.cyan(res.agent)}: Update failed - ${res.error}`);
3520
+ }
3521
+ }
3522
+ if (!allSuccess) {
3523
+ process.exitCode = 1;
3524
+ }
3525
+ return;
3526
+ }
3527
+ if (!isInteractive) {
3528
+ if (!serverName) {
3529
+ logger.error(
3530
+ 'Missing required argument: "server-name" for non-interactive manage command. Specify a server name or use interactive terminal.'
3531
+ );
3532
+ process.exitCode = 1;
3533
+ return;
3534
+ }
3535
+ const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
3536
+ if (!targetGroup) {
3537
+ return;
3538
+ }
3539
+ displayServerDetails({
3540
+ serverName,
3541
+ config: targetGroup.config,
3542
+ agents: targetGroup.agents,
3543
+ global: isGlobal,
3544
+ hasDivergence: targetGroup.hasDivergence
3545
+ });
3546
+ return;
3547
+ }
3548
+ await wizardManage({
3549
+ global: options.global,
3550
+ serverName
3551
+ });
3552
+ } catch (error) {
3553
+ if (error && typeof error === "object" && "name" in error && error.name === "ExitPromptError") {
3554
+ process.exit(0);
3555
+ }
3556
+ logger.error(toErrorMessage(error));
3557
+ process.exitCode = 1;
3558
+ }
3559
+ });
3560
+
2305
3561
  export {
2306
3562
  mcpAgents,
2307
3563
  mcpAgentAliases,
@@ -2328,11 +3584,16 @@ export {
2328
3584
  AgentConfigStore,
2329
3585
  agentConfigStore,
2330
3586
  toErrorMessage,
3587
+ getCandidateAgentsForScope,
3588
+ getCoHostedAgents,
3589
+ resolveConfigClusters,
3590
+ sortAgentsWithClusters,
2331
3591
  transformServerConfig,
2332
3592
  createAgentTransform,
2333
3593
  transformServerConfigForAgent,
2334
3594
  installMcpServerForAgent,
2335
3595
  installMcpServerForAgents,
3596
+ installToCompatibleAgents,
2336
3597
  parseMcpAgentList,
2337
3598
  resolveTargetAgents,
2338
3599
  extractPackageName,
@@ -2342,19 +3603,43 @@ export {
2342
3603
  listInstalledMcpServers,
2343
3604
  removeMcpServerFromAgent,
2344
3605
  removeMcpServer,
3606
+ toRemoteServerConfig,
3607
+ toStdioServerConfig,
3608
+ detectUpdateTransition,
3609
+ sanitizeUpdatedServerConfig,
3610
+ updateMcpServer,
2345
3611
  logger,
3612
+ logCoHostedNotice,
3613
+ buildLinkedAgentChoices,
3614
+ linkedCheckbox,
2346
3615
  promptScope,
2347
3616
  promptScopeAndAgents,
2348
3617
  parseArgsString,
2349
3618
  promptArgsConfig,
3619
+ formatArgsString,
3620
+ promptEditArgs,
3621
+ SECRET_KEY_PATTERN,
3622
+ maskSecretValue,
3623
+ SECRET_HEADER_PATTERN,
3624
+ maskSecretHeader,
3625
+ promptEditKeyValueConfig,
3626
+ formatEnvText,
2350
3627
  parseEnvText,
2351
3628
  promptEnvConfig,
3629
+ promptEditEnvConfig,
3630
+ formatHeadersText,
2352
3631
  parseHeadersText,
2353
3632
  promptHeadersConfig,
3633
+ promptEditHeadersConfig,
2354
3634
  wizardAdd,
3635
+ displayServerDetails,
2355
3636
  normalizeServerConfig,
2356
3637
  groupInstalledServersByName,
3638
+ promptSwitchServerType,
2357
3639
  wizardManage,
2358
3640
  wizardRemove,
2359
- mainMenu
3641
+ mainMenu,
3642
+ resolveTransport,
3643
+ parseKeyValueList,
3644
+ mcpManageCommand
2360
3645
  };