@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/cli.cjs CHANGED
@@ -23,11 +23,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
 
25
25
  // src/cli.ts
26
- var import_commander4 = require("commander");
26
+ var import_commander5 = require("commander");
27
27
 
28
28
  // src/cli/add.ts
29
- var import_commander = require("commander");
30
- var import_picocolors11 = __toESM(require("picocolors"), 1);
29
+ var import_commander2 = require("commander");
30
+ var import_picocolors17 = __toESM(require("picocolors"), 1);
31
31
 
32
32
  // src/agents.ts
33
33
  var import_node_fs = require("fs");
@@ -483,9 +483,9 @@ var mcpAgentAliases = {
483
483
  var getMcpAgentConfig = (agentType) => mcpAgents[agentType];
484
484
  var getMcpAgentTypes = () => Object.values(mcpAgents).map((config) => config.name);
485
485
  var isMcpAgentType = (value) => value in mcpAgents;
486
- var resolveMcpAgentAlias = (input5) => {
487
- if (isMcpAgentType(input5)) return input5;
488
- return mcpAgentAliases[input5] ?? null;
486
+ var resolveMcpAgentAlias = (input7) => {
487
+ if (isMcpAgentType(input7)) return input7;
488
+ return mcpAgentAliases[input7] ?? null;
489
489
  };
490
490
  var isMcpTransportSupported = (agent, transport) => agent.supportedTransports.includes(transport);
491
491
  var detectProjectInstalledMcpAgents = (cwd) => getMcpAgentTypes().filter(
@@ -577,8 +577,6 @@ var buildMcpServerConfig = (parsed, options = {}) => {
577
577
  };
578
578
 
579
579
  // src/parse-server-config.ts
580
- var isRemoteServerConfig = (config) => typeof config.url === "string" && config.url.length > 0;
581
- var isStdioServerConfig = (config) => typeof config.command === "string" && config.command.length > 0;
582
580
  var parseServerConfig = (raw) => {
583
581
  if (!raw || typeof raw !== "object") return {};
584
582
  const data = raw;
@@ -996,6 +994,74 @@ var agentConfigStore = new AgentConfigStore();
996
994
  // src/utils/to-error-message.ts
997
995
  var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
998
996
 
997
+ // src/resolve-config-clusters.ts
998
+ var getCandidateAgentsForScope = (options = {}) => {
999
+ return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
1000
+ };
1001
+ var getCoHostedAgents = (agentType, options = {}) => {
1002
+ const currentAgent = getMcpAgentConfig(agentType);
1003
+ const currentTarget = resolveMcpConfigTarget(currentAgent, options);
1004
+ const candidates = getCandidateAgentsForScope(options);
1005
+ const coHosted = [];
1006
+ for (const candidateType of candidates) {
1007
+ if (candidateType === agentType) continue;
1008
+ const candidateConfig = getMcpAgentConfig(candidateType);
1009
+ const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
1010
+ if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
1011
+ coHosted.push(candidateType);
1012
+ }
1013
+ }
1014
+ return coHosted;
1015
+ };
1016
+ var resolveConfigClusters = (agentTypes, options = {}) => {
1017
+ const clustersByPath = /* @__PURE__ */ new Map();
1018
+ for (const agentType of agentTypes) {
1019
+ const agentConfig = getMcpAgentConfig(agentType);
1020
+ const target = resolveMcpConfigTarget(agentConfig, options);
1021
+ let keyMap = clustersByPath.get(target.configPath);
1022
+ if (!keyMap) {
1023
+ keyMap = /* @__PURE__ */ new Map();
1024
+ clustersByPath.set(target.configPath, keyMap);
1025
+ }
1026
+ let cluster = keyMap.get(target.configKey);
1027
+ if (!cluster) {
1028
+ const allCoHosted = getCoHostedAgents(agentType, options);
1029
+ cluster = {
1030
+ configPath: target.configPath,
1031
+ configKey: target.configKey,
1032
+ targetAgents: [],
1033
+ coHostedAgents: allCoHosted
1034
+ };
1035
+ keyMap.set(target.configKey, cluster);
1036
+ }
1037
+ if (!cluster.targetAgents.includes(agentType)) {
1038
+ cluster.targetAgents.push(agentType);
1039
+ }
1040
+ }
1041
+ const clusters = [];
1042
+ for (const keyMap of clustersByPath.values()) {
1043
+ for (const cluster of keyMap.values()) {
1044
+ cluster.coHostedAgents = cluster.coHostedAgents.filter(
1045
+ (co) => !cluster.targetAgents.includes(co)
1046
+ );
1047
+ clusters.push(cluster);
1048
+ }
1049
+ }
1050
+ return clusters;
1051
+ };
1052
+ var sortAgentsWithClusters = (agentTypes, options = {}) => {
1053
+ const clusters = resolveConfigClusters(agentTypes, options);
1054
+ const sorted = [];
1055
+ for (const cluster of clusters) {
1056
+ for (const agent of cluster.targetAgents) {
1057
+ if (!sorted.includes(agent)) {
1058
+ sorted.push(agent);
1059
+ }
1060
+ }
1061
+ }
1062
+ return sorted;
1063
+ };
1064
+
999
1065
  // src/transforms/index.ts
1000
1066
  var DIALECT_PRESETS = {
1001
1067
  vscode: {
@@ -1204,32 +1270,69 @@ var transformServerConfigForAgent = (agent, serverName, config, context = { glob
1204
1270
  };
1205
1271
 
1206
1272
  // src/installer.ts
1207
- var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {}) => {
1208
- const agent = getMcpAgentConfig(agentType);
1273
+ var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => {
1274
+ const clusters = resolveConfigClusters(agentTypes, options);
1275
+ const resultsByAgent = /* @__PURE__ */ new Map();
1209
1276
  const isGlobal = options.global ?? false;
1210
- const { target } = agentConfigStore.resolveTarget(agent, options);
1211
- try {
1212
- const transformed = transformServerConfigForAgent(agent, serverName, serverConfig, {
1213
- global: isGlobal
1214
- });
1215
- agentConfigStore.writeServer(agent, serverName, transformed, options);
1216
- return { agent: agentType, success: true, path: target.configPath };
1217
- } catch (error) {
1218
- return {
1219
- agent: agentType,
1220
- success: false,
1221
- path: target.configPath,
1222
- error: toErrorMessage(error)
1223
- };
1277
+ for (const cluster of clusters) {
1278
+ const primaryAgentType = cluster.targetAgents[0];
1279
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1280
+ try {
1281
+ const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
1282
+ global: isGlobal
1283
+ });
1284
+ agentConfigStore.writeServer(primaryAgent, serverName, transformed, options);
1285
+ for (const agentType of cluster.targetAgents) {
1286
+ resultsByAgent.set(agentType, {
1287
+ agent: agentType,
1288
+ success: true,
1289
+ path: cluster.configPath,
1290
+ coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1291
+ });
1292
+ }
1293
+ } catch (error) {
1294
+ const errorMsg = toErrorMessage(error);
1295
+ for (const agentType of cluster.targetAgents) {
1296
+ resultsByAgent.set(agentType, {
1297
+ agent: agentType,
1298
+ success: false,
1299
+ path: cluster.configPath,
1300
+ error: errorMsg
1301
+ });
1302
+ }
1303
+ }
1224
1304
  }
1305
+ return agentTypes.map((agentType) => resultsByAgent.get(agentType));
1306
+ };
1307
+ var installToCompatibleAgents = (serverName, serverConfig, options) => {
1308
+ const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
1309
+ const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1310
+ const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
1311
+ const installedResults = installMcpServerForAgents(serverName, serverConfig, compatibleAgents, {
1312
+ global: isGlobal,
1313
+ cwd
1314
+ });
1315
+ const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
1316
+ return allAgents.map((agentType) => {
1317
+ const incompatibleReason = incompatibleMap.get(agentType);
1318
+ if (incompatibleReason) {
1319
+ return {
1320
+ agent: agentType,
1321
+ success: false,
1322
+ path: "",
1323
+ error: incompatibleReason
1324
+ };
1325
+ }
1326
+ return installedMap.get(agentType);
1327
+ });
1225
1328
  };
1226
1329
 
1227
1330
  // src/utils/parse-mcp-agent-list.ts
1228
- var parseMcpAgentList = (input5) => {
1229
- if (!input5 || input5.length === 0) return void 0;
1230
- if (input5.includes("*")) return getMcpAgentTypes();
1331
+ var parseMcpAgentList = (input7) => {
1332
+ if (!input7 || input7.length === 0) return void 0;
1333
+ if (input7.includes("*")) return getMcpAgentTypes();
1231
1334
  const resolved = [];
1232
- for (const value of input5) {
1335
+ for (const value of input7) {
1233
1336
  const agentType = resolveMcpAgentAlias(value);
1234
1337
  if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
1235
1338
  resolved.push(agentType);
@@ -1238,9 +1341,9 @@ var parseMcpAgentList = (input5) => {
1238
1341
  };
1239
1342
 
1240
1343
  // src/resolve-target-agents.ts
1241
- var normalizeRequestedAgents = (input5) => {
1242
- if (!input5 || input5.length === 0) return void 0;
1243
- const rawList = [...input5];
1344
+ var normalizeRequestedAgents = (input7) => {
1345
+ if (!input7 || input7.length === 0) return void 0;
1346
+ const rawList = [...input7];
1244
1347
  if (rawList.every((item) => isMcpAgentType(item))) {
1245
1348
  return rawList;
1246
1349
  }
@@ -1300,29 +1403,29 @@ var REMOTE_URL_REGEX = /^https?:\/\//i;
1300
1403
  var HAS_WHITESPACE_REGEX = /\s/;
1301
1404
  var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
1302
1405
  var PATH_SEPARATOR_REGEX = /[/\\]/;
1303
- var stripVersionSuffix = (input5) => {
1304
- if (input5.startsWith("@")) {
1305
- const secondAtIndex = input5.indexOf("@", 1);
1306
- if (secondAtIndex > 0) return input5.slice(0, secondAtIndex);
1307
- return input5;
1308
- }
1309
- const atIndex = input5.lastIndexOf("@");
1310
- if (atIndex > 0) return input5.slice(0, atIndex);
1311
- return input5;
1312
- };
1313
- var stripScopePrefix = (input5) => {
1314
- if (!input5.startsWith("@") || !input5.includes("/")) return input5;
1315
- const parts = input5.split("/");
1316
- return parts[1] || input5;
1317
- };
1318
- var stripPathPrefix = (input5) => {
1319
- if (!PATH_SEPARATOR_REGEX.test(input5)) return input5;
1320
- const segments = input5.split(PATH_SEPARATOR_REGEX);
1406
+ var stripVersionSuffix = (input7) => {
1407
+ if (input7.startsWith("@")) {
1408
+ const secondAtIndex = input7.indexOf("@", 1);
1409
+ if (secondAtIndex > 0) return input7.slice(0, secondAtIndex);
1410
+ return input7;
1411
+ }
1412
+ const atIndex = input7.lastIndexOf("@");
1413
+ if (atIndex > 0) return input7.slice(0, atIndex);
1414
+ return input7;
1415
+ };
1416
+ var stripScopePrefix = (input7) => {
1417
+ if (!input7.startsWith("@") || !input7.includes("/")) return input7;
1418
+ const parts = input7.split("/");
1419
+ return parts[1] || input7;
1420
+ };
1421
+ var stripPathPrefix = (input7) => {
1422
+ if (!PATH_SEPARATOR_REGEX.test(input7)) return input7;
1423
+ const segments = input7.split(PATH_SEPARATOR_REGEX);
1321
1424
  const basename = segments[segments.length - 1];
1322
- return basename || input5;
1425
+ return basename || input7;
1323
1426
  };
1324
- var extractPackageName = (input5) => {
1325
- let name = stripVersionSuffix(input5);
1427
+ var extractPackageName = (input7) => {
1428
+ let name = stripVersionSuffix(input7);
1326
1429
  name = stripScopePrefix(name);
1327
1430
  name = stripPathPrefix(name);
1328
1431
  name = name.replace(SCRIPT_EXTENSION_REGEX, "");
@@ -1340,9 +1443,9 @@ var extractPackageName = (input5) => {
1340
1443
  }
1341
1444
  return name || MCP_DEFAULT_SERVER_NAME;
1342
1445
  };
1343
- var inferNameFromUrl = (input5) => {
1446
+ var inferNameFromUrl = (input7) => {
1344
1447
  try {
1345
- const url = new URL(input5);
1448
+ const url = new URL(input7);
1346
1449
  const host = url.hostname;
1347
1450
  const labels = host.split(".").filter((segment) => segment.length > 0);
1348
1451
  if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
@@ -1371,8 +1474,8 @@ var inferNameFromCommand = (command) => {
1371
1474
  const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
1372
1475
  return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
1373
1476
  };
1374
- var parseMcpSource = (input5) => {
1375
- const trimmed = input5.trim();
1477
+ var parseMcpSource = (input7) => {
1478
+ const trimmed = input7.trim();
1376
1479
  if (trimmed.length === 0) {
1377
1480
  throw new Error(
1378
1481
  "Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
@@ -1425,18 +1528,11 @@ var installMcpServer = (options) => {
1425
1528
  cwd,
1426
1529
  transport: requestedTransport
1427
1530
  });
1428
- const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1429
- const results = allAgents.map((agentType) => {
1430
- const incompatibleReason = incompatibleMap.get(agentType);
1431
- if (incompatibleReason) {
1432
- return {
1433
- agent: agentType,
1434
- success: false,
1435
- path: "",
1436
- error: incompatibleReason
1437
- };
1438
- }
1439
- return installMcpServerForAgent(serverName, serverConfig, agentType, { global: isGlobal, cwd });
1531
+ const results = installToCompatibleAgents(serverName, serverConfig, {
1532
+ allAgents,
1533
+ incompatible,
1534
+ global: isGlobal,
1535
+ cwd
1440
1536
  });
1441
1537
  return { serverName, config: serverConfig, results };
1442
1538
  };
@@ -1463,21 +1559,6 @@ var listInstalledMcpServers = (options = {}) => {
1463
1559
  };
1464
1560
 
1465
1561
  // src/remove.ts
1466
- var removeMcpServerFromAgent = (serverName, agentType, options = {}) => {
1467
- const agent = getMcpAgentConfig(agentType);
1468
- const { target } = agentConfigStore.resolveTarget(agent, options);
1469
- try {
1470
- const { removed } = agentConfigStore.removeServer(agent, serverName, options);
1471
- return { agent: agentType, path: target.configPath, removed };
1472
- } catch (error) {
1473
- return {
1474
- agent: agentType,
1475
- path: target.configPath,
1476
- removed: false,
1477
- error: toErrorMessage(error)
1478
- };
1479
- }
1480
- };
1481
1562
  var removeMcpServer = (options) => {
1482
1563
  const { allAgents } = resolveTargetAgents({
1483
1564
  requested: options.agents,
@@ -1485,24 +1566,153 @@ var removeMcpServer = (options) => {
1485
1566
  global: options.global,
1486
1567
  cwd: options.cwd
1487
1568
  });
1569
+ const clusters = resolveConfigClusters(allAgents, {
1570
+ global: options.global,
1571
+ cwd: options.cwd
1572
+ });
1488
1573
  const results = [];
1489
- for (const agentType of allAgents) {
1490
- const result = removeMcpServerFromAgent(options.name, agentType, {
1491
- global: options.global,
1492
- cwd: options.cwd
1493
- });
1494
- if (result.removed || result.error) results.push(result);
1574
+ for (const cluster of clusters) {
1575
+ const primaryAgentType = cluster.targetAgents[0];
1576
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1577
+ try {
1578
+ const { removed } = agentConfigStore.removeServer(primaryAgent, options.name, {
1579
+ global: options.global,
1580
+ cwd: options.cwd
1581
+ });
1582
+ if (removed) {
1583
+ for (const agentType of cluster.targetAgents) {
1584
+ results.push({
1585
+ agent: agentType,
1586
+ path: cluster.configPath,
1587
+ removed: true,
1588
+ coAffectedAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1589
+ });
1590
+ }
1591
+ }
1592
+ } catch (error) {
1593
+ const errorMsg = toErrorMessage(error);
1594
+ for (const agentType of cluster.targetAgents) {
1595
+ results.push({
1596
+ agent: agentType,
1597
+ path: cluster.configPath,
1598
+ removed: false,
1599
+ error: errorMsg
1600
+ });
1601
+ }
1602
+ }
1495
1603
  }
1496
1604
  return results;
1497
1605
  };
1498
1606
 
1607
+ // src/update-mcp-server.ts
1608
+ var toRemoteServerConfig = (config, defaultTransport = "http") => {
1609
+ const {
1610
+ command: _droppedCommand,
1611
+ args: _droppedArgs,
1612
+ env: _droppedEnv,
1613
+ ...remoteConfig
1614
+ } = config;
1615
+ return {
1616
+ ...remoteConfig,
1617
+ type: remoteConfig.type ?? defaultTransport
1618
+ };
1619
+ };
1620
+ var toStdioServerConfig = (config) => {
1621
+ const {
1622
+ url: _droppedUrl,
1623
+ type: _droppedType,
1624
+ headers: _droppedHeaders,
1625
+ ...stdioConfig
1626
+ } = config;
1627
+ return stdioConfig;
1628
+ };
1629
+ var detectUpdateTransition = (incoming, previous) => {
1630
+ if (!previous) {
1631
+ return incoming.url ? "switch-to-remote" : "switch-to-stdio";
1632
+ }
1633
+ if (incoming.url && !incoming.command) {
1634
+ return "switch-to-remote";
1635
+ }
1636
+ if (incoming.command && !incoming.url) {
1637
+ return "switch-to-stdio";
1638
+ }
1639
+ if (incoming.url || !incoming.command && previous.url) {
1640
+ return "merge-remote";
1641
+ }
1642
+ return "merge-stdio";
1643
+ };
1644
+ var sanitizeUpdatedServerConfig = (incoming, previous) => {
1645
+ const transition = detectUpdateTransition(incoming, previous);
1646
+ const targetTransport = incoming.type ?? previous?.type ?? "http";
1647
+ switch (transition) {
1648
+ case "switch-to-remote": {
1649
+ const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
1650
+ return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
1651
+ }
1652
+ case "switch-to-stdio": {
1653
+ const cleanBase = previous ? toStdioServerConfig(previous) : {};
1654
+ return toStdioServerConfig({ ...cleanBase, ...incoming });
1655
+ }
1656
+ case "merge-remote": {
1657
+ return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
1658
+ }
1659
+ case "merge-stdio": {
1660
+ return toStdioServerConfig({ ...previous, ...incoming });
1661
+ }
1662
+ }
1663
+ };
1664
+ var updateMcpServer = (options) => {
1665
+ const isGlobal = options.global ?? false;
1666
+ const cwd = options.cwd ?? process.cwd();
1667
+ let previousConfig = options.previousConfig;
1668
+ if (!previousConfig) {
1669
+ const existing = listInstalledMcpServers({
1670
+ global: isGlobal,
1671
+ cwd,
1672
+ agents: options.agents
1673
+ });
1674
+ const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
1675
+ if (found) {
1676
+ previousConfig = found.serverConfig;
1677
+ }
1678
+ }
1679
+ const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
1680
+ let targetAgents = options.agents;
1681
+ if (!targetAgents || targetAgents.length === 0) {
1682
+ const existing = listInstalledMcpServers({ global: isGlobal, cwd });
1683
+ targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
1684
+ }
1685
+ const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
1686
+ const { allAgents, incompatible } = resolveTargetAgents({
1687
+ requested: targetAgents,
1688
+ global: isGlobal,
1689
+ cwd,
1690
+ transport: requestedTransport
1691
+ });
1692
+ const results = installToCompatibleAgents(options.serverName, serverConfig, {
1693
+ allAgents,
1694
+ incompatible,
1695
+ global: isGlobal,
1696
+ cwd
1697
+ });
1698
+ return {
1699
+ serverName: options.serverName,
1700
+ config: serverConfig,
1701
+ results,
1702
+ incompatible
1703
+ };
1704
+ };
1705
+
1499
1706
  // src/interactive/main-menu.ts
1500
1707
  var import_prompts10 = require("@inquirer/prompts");
1501
- var import_picocolors10 = __toESM(require("picocolors"), 1);
1708
+ var import_picocolors15 = __toESM(require("picocolors"), 1);
1502
1709
 
1503
1710
  // src/interactive/wizard-add.ts
1504
1711
  var import_prompts7 = require("@inquirer/prompts");
1505
- var import_picocolors7 = __toESM(require("picocolors"), 1);
1712
+ var import_picocolors11 = __toESM(require("picocolors"), 1);
1713
+
1714
+ // src/utils/co-hosted-feedback.ts
1715
+ var import_picocolors2 = __toESM(require("picocolors"), 1);
1506
1716
 
1507
1717
  // src/utils/logger.ts
1508
1718
  var import_picocolors = __toESM(require("picocolors"), 1);
@@ -1521,13 +1731,269 @@ var logger = {
1521
1731
  }
1522
1732
  };
1523
1733
 
1734
+ // src/utils/co-hosted-feedback.ts
1735
+ var formatCoHostedBadge = (kind, agents) => {
1736
+ if (!agents || agents.length === 0) return "";
1737
+ const label = kind === "configured" ? "co-configured" : "co-affected";
1738
+ return ` ${import_picocolors2.default.yellow(`(${label}: ${agents.join(", ")})`)}`;
1739
+ };
1740
+ var logCoHostedNotice = (kind, agents) => {
1741
+ if (!agents || agents.length === 0) return;
1742
+ const actionText = kind === "configured" ? "Also configured for" : "Also affects";
1743
+ logger.info(
1744
+ ` ${import_picocolors2.default.dim("Note:")} ${actionText} co-hosted agent(s): ${import_picocolors2.default.yellow(agents.join(", "))}`
1745
+ );
1746
+ };
1747
+
1524
1748
  // src/interactive/prompts/agents.ts
1525
- var import_prompts2 = require("@inquirer/prompts");
1749
+ var import_picocolors6 = __toESM(require("picocolors"), 1);
1750
+
1751
+ // src/interactive/utils/build-linked-agent-choices.ts
1526
1752
  var import_picocolors3 = __toESM(require("picocolors"), 1);
1753
+ var buildLinkedAgentChoices = (options) => {
1754
+ const { agents, checkedAgents, detectedAgents = [], scopeOptions = {} } = options;
1755
+ const alignedCheckedSet = new Set(checkedAgents);
1756
+ for (const agent of checkedAgents) {
1757
+ const coHosted = getCoHostedAgents(agent, scopeOptions);
1758
+ for (const co of coHosted) {
1759
+ if (agents.includes(co)) {
1760
+ alignedCheckedSet.add(co);
1761
+ }
1762
+ }
1763
+ }
1764
+ return agents.map((agent) => {
1765
+ const config = getMcpAgentConfig(agent);
1766
+ const displayName = config?.displayName ?? agent;
1767
+ const isDetected = detectedAgents.includes(agent);
1768
+ const coHosted = getCoHostedAgents(agent, scopeOptions).filter(
1769
+ (co) => agents.includes(co)
1770
+ );
1771
+ const detectedBadge = isDetected ? import_picocolors3.default.green(" [detected]") : "";
1772
+ const sharedBadge = coHosted.length > 0 ? import_picocolors3.default.dim(` [shared: ${coHosted.join(", ")}]`) : "";
1773
+ const label = `${displayName} ${import_picocolors3.default.dim(`(${agent})`)}${detectedBadge}${sharedBadge}`;
1774
+ return {
1775
+ name: label,
1776
+ value: agent,
1777
+ checked: alignedCheckedSet.has(agent),
1778
+ linkedValues: coHosted,
1779
+ description: coHosted.length > 0 ? `Linked with ${coHosted.map((a) => getMcpAgentConfig(a).displayName).join(", ")} (shared configuration)` : void 0
1780
+ };
1781
+ });
1782
+ };
1783
+
1784
+ // src/interactive/prompts/linked-checkbox.ts
1785
+ var import_core = require("@inquirer/core");
1786
+ var import_picocolors4 = __toESM(require("picocolors"), 1);
1787
+ var defaultTheme = {
1788
+ icon: {
1789
+ checked: import_picocolors4.default.green("[x]"),
1790
+ unchecked: import_picocolors4.default.dim("[ ]"),
1791
+ cursor: import_picocolors4.default.cyan(">"),
1792
+ disabledChecked: import_picocolors4.default.dim("[x]"),
1793
+ disabledUnchecked: import_picocolors4.default.dim("[-]")
1794
+ },
1795
+ style: {
1796
+ disabled: (text) => import_picocolors4.default.dim(text),
1797
+ renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
1798
+ description: (text) => import_picocolors4.default.cyan(text),
1799
+ keysHelpTip: (keys) => keys.map(([key, action]) => `${import_picocolors4.default.bold(key)} ${import_picocolors4.default.dim(action)}`).join(import_picocolors4.default.dim(" | ")),
1800
+ highlight: (text) => import_picocolors4.default.cyan(text)
1801
+ },
1802
+ i18n: {
1803
+ disabledError: "This option is disabled and cannot be toggled."
1804
+ }
1805
+ };
1806
+ function isSelectable(item) {
1807
+ return !import_core.Separator.isSeparator(item) && !item.disabled;
1808
+ }
1809
+ function isNavigable(item) {
1810
+ return !import_core.Separator.isSeparator(item);
1811
+ }
1812
+ function isChecked(item) {
1813
+ return !import_core.Separator.isSeparator(item) && item.checked;
1814
+ }
1815
+ function normalizeChoices(choices) {
1816
+ return choices.map((choice) => {
1817
+ if (import_core.Separator.isSeparator(choice)) {
1818
+ return choice;
1819
+ }
1820
+ if (typeof choice !== "object" || choice === null || !("value" in choice)) {
1821
+ const name2 = String(choice);
1822
+ return {
1823
+ value: choice,
1824
+ name: name2,
1825
+ short: name2,
1826
+ checkedName: name2,
1827
+ disabled: false,
1828
+ checked: false,
1829
+ linkedValues: []
1830
+ };
1831
+ }
1832
+ const name = choice.name ?? String(choice.value);
1833
+ return {
1834
+ value: choice.value,
1835
+ name,
1836
+ short: choice.short ?? name,
1837
+ checkedName: choice.checkedName ?? name,
1838
+ description: choice.description,
1839
+ disabled: choice.disabled ?? false,
1840
+ checked: choice.checked ?? false,
1841
+ linkedValues: choice.linkedValues ?? []
1842
+ };
1843
+ });
1844
+ }
1845
+ var linkedCheckbox = (0, import_core.createPrompt)(
1846
+ (config, done) => {
1847
+ const { pageSize = 10, loop = true, required, validate = () => true } = config;
1848
+ const theme = (0, import_core.makeTheme)(defaultTheme, config.theme);
1849
+ const [status, setStatus] = (0, import_core.useState)("idle");
1850
+ const prefix = (0, import_core.usePrefix)({ status, theme });
1851
+ const [items, setItems] = (0, import_core.useState)(() => normalizeChoices(config.choices));
1852
+ const bounds = (0, import_core.useMemo)(() => {
1853
+ const first = items.findIndex(isNavigable);
1854
+ let last = -1;
1855
+ for (let i = items.length - 1; i >= 0; i--) {
1856
+ if (isNavigable(items[i])) {
1857
+ last = i;
1858
+ break;
1859
+ }
1860
+ }
1861
+ if (first === -1 || last === -1) {
1862
+ throw new import_core.ValidationError("[linkedCheckbox prompt] No selectable choices.");
1863
+ }
1864
+ return { first, last };
1865
+ }, [items]);
1866
+ const [active, setActive] = (0, import_core.useState)(bounds.first);
1867
+ const [errorMsg, setError] = (0, import_core.useState)();
1868
+ const toggleWithLinked = (targetIndex) => {
1869
+ const targetItem = items[targetIndex];
1870
+ if (!targetItem || import_core.Separator.isSeparator(targetItem) || targetItem.disabled) {
1871
+ return;
1872
+ }
1873
+ const nextChecked = !targetItem.checked;
1874
+ const targetValue = targetItem.value;
1875
+ const linked = new Set(targetItem.linkedValues);
1876
+ setItems(
1877
+ (prevItems) => prevItems.map((item) => {
1878
+ if (import_core.Separator.isSeparator(item) || item.disabled) {
1879
+ return item;
1880
+ }
1881
+ const isTargetOrLinked = item.value === targetValue || linked.has(item.value) || item.linkedValues.includes(targetValue);
1882
+ if (isTargetOrLinked) {
1883
+ return { ...item, checked: nextChecked };
1884
+ }
1885
+ return item;
1886
+ })
1887
+ );
1888
+ };
1889
+ (0, import_core.useKeypress)(async (key) => {
1890
+ if ((0, import_core.isEnterKey)(key)) {
1891
+ const selection = items.filter(isChecked);
1892
+ const isValid = await validate([...selection]);
1893
+ if (required && selection.length === 0) {
1894
+ setError("At least one choice must be selected");
1895
+ } else if (isValid === true) {
1896
+ setStatus("done");
1897
+ done(selection.map((choice) => choice.value));
1898
+ } else {
1899
+ setError(typeof isValid === "string" ? isValid : "You must select a valid value");
1900
+ }
1901
+ } else if ((0, import_core.isUpKey)(key) || (0, import_core.isDownKey)(key)) {
1902
+ if (errorMsg) setError(void 0);
1903
+ if (loop || (0, import_core.isUpKey)(key) && active !== bounds.first || (0, import_core.isDownKey)(key) && active !== bounds.last) {
1904
+ const offset = (0, import_core.isUpKey)(key) ? -1 : 1;
1905
+ let next = active;
1906
+ do {
1907
+ next = (next + offset + items.length) % items.length;
1908
+ } while (!isNavigable(items[next]));
1909
+ setActive(next);
1910
+ }
1911
+ } else if ((0, import_core.isSpaceKey)(key)) {
1912
+ const activeItem = items[active];
1913
+ if (activeItem && !import_core.Separator.isSeparator(activeItem)) {
1914
+ if (activeItem.disabled) {
1915
+ setError(theme.i18n.disabledError);
1916
+ } else {
1917
+ setError(void 0);
1918
+ toggleWithLinked(active);
1919
+ }
1920
+ }
1921
+ } else if (key.name === "a") {
1922
+ const hasUnchecked = items.some((choice) => isSelectable(choice) && !choice.checked);
1923
+ setItems(
1924
+ (prevItems) => prevItems.map((item) => isSelectable(item) ? { ...item, checked: hasUnchecked } : item)
1925
+ );
1926
+ } else if ((0, import_core.isNumberKey)(key)) {
1927
+ const selectedIndex = Number(key.name) - 1;
1928
+ let selectableIndex = -1;
1929
+ const position = items.findIndex((item) => {
1930
+ if (import_core.Separator.isSeparator(item)) return false;
1931
+ selectableIndex++;
1932
+ return selectableIndex === selectedIndex;
1933
+ });
1934
+ const selectedItem = items[position];
1935
+ if (selectedItem && isSelectable(selectedItem)) {
1936
+ setActive(position);
1937
+ setError(void 0);
1938
+ toggleWithLinked(position);
1939
+ }
1940
+ }
1941
+ });
1942
+ const message = theme.style.message(config.message, status);
1943
+ let description;
1944
+ const page = (0, import_core.usePagination)({
1945
+ items,
1946
+ active,
1947
+ renderItem({ item, isActive }) {
1948
+ if (import_core.Separator.isSeparator(item)) {
1949
+ return ` ${item.separator}`;
1950
+ }
1951
+ const cursor = isActive ? theme.icon.cursor : " ";
1952
+ if (item.disabled) {
1953
+ const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
1954
+ const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
1955
+ return theme.style.disabled(`${cursor} ${checkbox2} ${item.name} ${disabledLabel}`);
1956
+ }
1957
+ if (isActive) {
1958
+ description = item.description;
1959
+ }
1960
+ const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
1961
+ const name = item.checked ? item.checkedName : item.name;
1962
+ const color = isActive ? theme.style.highlight : (x) => x;
1963
+ return color(`${cursor} ${checkbox} ${name}`);
1964
+ },
1965
+ pageSize,
1966
+ loop
1967
+ });
1968
+ if (status === "done") {
1969
+ const selection = items.filter(isChecked);
1970
+ const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
1971
+ return [prefix, message, answer].filter(Boolean).join(" ");
1972
+ }
1973
+ const helpLine = theme.style.keysHelpTip([
1974
+ ["up/down", "navigate"],
1975
+ ["space", "toggle"],
1976
+ ["a", "all"],
1977
+ ["enter", "submit"]
1978
+ ]);
1979
+ const lines = [
1980
+ [prefix, message].filter(Boolean).join(" "),
1981
+ page,
1982
+ helpLine
1983
+ ];
1984
+ if (description) {
1985
+ lines.push(theme.style.description(description));
1986
+ }
1987
+ if (errorMsg) {
1988
+ lines.push(theme.style.error(errorMsg));
1989
+ }
1990
+ return lines.join("\n");
1991
+ }
1992
+ );
1527
1993
 
1528
1994
  // src/interactive/prompts/scope.ts
1529
1995
  var import_prompts = require("@inquirer/prompts");
1530
- var import_picocolors2 = __toESM(require("picocolors"), 1);
1996
+ var import_picocolors5 = __toESM(require("picocolors"), 1);
1531
1997
  var promptScope = async (options = {}) => {
1532
1998
  const initialGlobal = options.defaultGlobal ?? options.global;
1533
1999
  if (initialGlobal !== void 0) {
@@ -1538,11 +2004,11 @@ var promptScope = async (options = {}) => {
1538
2004
  message: options.message ?? "Select MCP scope:",
1539
2005
  choices: [
1540
2006
  {
1541
- name: `Current Project - ${import_picocolors2.default.dim(cwd)}`,
2007
+ name: `Current Project - ${import_picocolors5.default.dim(cwd)}`,
1542
2008
  value: false
1543
2009
  },
1544
2010
  {
1545
- name: `Global User Config - ${import_picocolors2.default.dim("applies across all projects")}`,
2011
+ name: `Global User Config - ${import_picocolors5.default.dim("applies across all projects")}`,
1546
2012
  value: true
1547
2013
  }
1548
2014
  ]
@@ -1562,26 +2028,23 @@ var promptScopeAndAgents = async (options = {}) => {
1562
2028
  cwd
1563
2029
  });
1564
2030
  const detected = resolution.detected;
1565
- const availableAgentTypes = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2031
+ const rawAvailable = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2032
+ const availableAgentTypes = sortAgentsWithClusters(rawAvailable, { global: isGlobal, cwd });
1566
2033
  if (detected.length > 0) {
1567
2034
  logger.info(
1568
- `Detected configured agents: ${import_picocolors3.default.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2035
+ `Detected configured agents: ${import_picocolors6.default.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
1569
2036
  );
1570
2037
  } else {
1571
2038
  logger.warn(`No active ${isGlobal ? "global" : "project"} agents detected`);
1572
2039
  }
1573
2040
  const defaultChecked = options.defaultAgents && options.defaultAgents.length > 0 ? options.defaultAgents : detected;
1574
- const choices = availableAgentTypes.map((agentType) => {
1575
- const config = getMcpAgentConfig(agentType);
1576
- const isDetected = detected.includes(agentType);
1577
- const label = `${config.displayName} ${import_picocolors3.default.dim(`(${agentType})`)}${isDetected ? import_picocolors3.default.green(" [detected]") : ""}`;
1578
- return {
1579
- name: label,
1580
- value: agentType,
1581
- checked: defaultChecked.includes(agentType)
1582
- };
2041
+ const choices = buildLinkedAgentChoices({
2042
+ agents: availableAgentTypes,
2043
+ checkedAgents: defaultChecked,
2044
+ detectedAgents: detected,
2045
+ scopeOptions: { global: isGlobal, cwd }
1583
2046
  });
1584
- const selectedAgents = await (0, import_prompts2.checkbox)({
2047
+ const selectedAgents = await linkedCheckbox({
1585
2048
  message: "Select target agents (Space to select, Enter to confirm):",
1586
2049
  choices,
1587
2050
  validate: (chosen) => {
@@ -1598,7 +2061,7 @@ var promptScopeAndAgents = async (options = {}) => {
1598
2061
  };
1599
2062
 
1600
2063
  // src/interactive/prompts/args.ts
1601
- var import_prompts3 = require("@inquirer/prompts");
2064
+ var import_prompts2 = require("@inquirer/prompts");
1602
2065
  var parseArgsString = (rawText) => {
1603
2066
  const matches = rawText.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g);
1604
2067
  if (!matches) return [];
@@ -1613,32 +2076,67 @@ var promptArgsConfig = async (initialArgs = []) => {
1613
2076
  if (initialArgs.length > 0) {
1614
2077
  return initialArgs;
1615
2078
  }
1616
- const needArgs = await (0, import_prompts3.confirm)({
2079
+ const needArgs = await (0, import_prompts2.confirm)({
1617
2080
  message: "Configure command arguments (e.g. file paths, connection strings)?",
1618
2081
  default: false
1619
2082
  });
1620
2083
  if (!needArgs) {
1621
2084
  return [];
1622
2085
  }
1623
- const raw = await (0, import_prompts3.input)({
2086
+ const raw = await (0, import_prompts2.input)({
1624
2087
  message: "Enter command arguments (space-separated, wrap paths with spaces in quotes):",
1625
2088
  validate: (val) => val.trim() ? true : "Arguments cannot be empty"
1626
2089
  });
1627
2090
  return parseArgsString(raw.trim());
1628
2091
  };
2092
+ var formatArgsString = (args) => {
2093
+ return args.map((arg) => arg.includes(" ") || arg.includes('"') ? `"${arg.replace(/"/g, '\\"')}"` : arg).join(" ");
2094
+ };
2095
+ var promptEditArgs = async (currentArgs = []) => {
2096
+ const defaultStr = formatArgsString(currentArgs);
2097
+ const raw = await (0, import_prompts2.input)({
2098
+ message: "Edit command arguments (space-separated, wrap paths with spaces in quotes, leave empty to clear):",
2099
+ default: defaultStr
2100
+ });
2101
+ const trimmed = raw.trim();
2102
+ if (!trimmed) {
2103
+ return [];
2104
+ }
2105
+ return parseArgsString(trimmed);
2106
+ };
1629
2107
 
1630
2108
  // src/interactive/prompts/env.ts
1631
2109
  var import_prompts5 = require("@inquirer/prompts");
1632
- var import_picocolors5 = __toESM(require("picocolors"), 1);
2110
+ var import_picocolors9 = __toESM(require("picocolors"), 1);
2111
+
2112
+ // src/utils/mask-secret.ts
2113
+ var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
2114
+ var maskSecretValue = (key, value) => {
2115
+ if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
2116
+ return value;
2117
+ }
2118
+ return `${value.slice(0, 2)}***${value.slice(-2)}`;
2119
+ };
2120
+ var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
2121
+ var maskSecretHeader = (key, value) => {
2122
+ if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
2123
+ return value;
2124
+ }
2125
+ return `${value.slice(0, 4)}***${value.slice(-3)}`;
2126
+ };
2127
+
2128
+ // src/interactive/prompts/kv.ts
2129
+ var import_prompts4 = require("@inquirer/prompts");
2130
+ var import_picocolors8 = __toESM(require("picocolors"), 1);
1633
2131
 
1634
2132
  // src/interactive/prompts/multiline.ts
1635
2133
  var import_node_readline = require("readline");
1636
- var import_prompts4 = require("@inquirer/prompts");
1637
- var import_picocolors4 = __toESM(require("picocolors"), 1);
2134
+ var import_prompts3 = require("@inquirer/prompts");
2135
+ var import_picocolors7 = __toESM(require("picocolors"), 1);
1638
2136
  var readMultilineTextFromTerminal = async (message, endHint = "When done pasting, enter END on a new line or press Enter twice to finish") => {
1639
- console.log(import_picocolors4.default.cyan(`
2137
+ console.log(import_picocolors7.default.cyan(`
1640
2138
  ${message}`));
1641
- console.log(import_picocolors4.default.dim(` (Hint: ${endHint})
2139
+ console.log(import_picocolors7.default.dim(` (Hint: ${endHint})
1642
2140
  `));
1643
2141
  return new Promise((resolve) => {
1644
2142
  const rl = (0, import_node_readline.createInterface)({
@@ -1682,7 +2180,7 @@ ${message}`));
1682
2180
  };
1683
2181
  var promptEditorText = async (options) => {
1684
2182
  try {
1685
- return await (0, import_prompts4.editor)({
2183
+ return await (0, import_prompts3.editor)({
1686
2184
  message: options.message,
1687
2185
  default: options.defaultText ?? "",
1688
2186
  postfix: options.postfix
@@ -1692,8 +2190,157 @@ var promptEditorText = async (options) => {
1692
2190
  }
1693
2191
  };
1694
2192
 
2193
+ // src/interactive/prompts/kv.ts
2194
+ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
2195
+ let items = { ...currentItems };
2196
+ while (true) {
2197
+ const keys = Object.keys(items);
2198
+ console.log();
2199
+ if (keys.length === 0) {
2200
+ console.log(import_picocolors8.default.dim(` No ${options.itemsNoun} configured.`));
2201
+ } else {
2202
+ console.log(import_picocolors8.default.cyan(import_picocolors8.default.bold(` Configured ${options.title} (${keys.length}):`)));
2203
+ for (const [k, v] of Object.entries(items)) {
2204
+ const sep = options.separator === "=" ? "=" : ": ";
2205
+ console.log(` ${import_picocolors8.default.bold(k)}${sep}${import_picocolors8.default.dim(options.maskValue(k, v))}`);
2206
+ }
2207
+ }
2208
+ console.log();
2209
+ const choice = await (0, import_prompts4.select)({
2210
+ message: `Manage ${options.itemsNoun}:`,
2211
+ choices: [
2212
+ {
2213
+ name: "Open in system default editor ($EDITOR)",
2214
+ value: "editor"
2215
+ },
2216
+ {
2217
+ name: `Add or modify a ${options.itemNoun}`,
2218
+ value: "upsert"
2219
+ },
2220
+ ...keys.length > 0 ? [
2221
+ {
2222
+ name: `Delete a ${options.itemNoun}`,
2223
+ value: "delete"
2224
+ }
2225
+ ] : [],
2226
+ {
2227
+ name: `Paste multiline ${options.itemsNoun} into terminal`,
2228
+ value: "paste"
2229
+ },
2230
+ ...keys.length > 0 ? [
2231
+ {
2232
+ name: `Clear all ${options.itemsNoun}`,
2233
+ value: "clear"
2234
+ }
2235
+ ] : [],
2236
+ {
2237
+ name: `Done (finish editing ${options.itemsNoun})`,
2238
+ value: "done"
2239
+ }
2240
+ ]
2241
+ });
2242
+ if (choice === "done") {
2243
+ return items;
2244
+ }
2245
+ if (choice === "editor") {
2246
+ const defaultText = options.formatText(items);
2247
+ const text = await promptEditorText({
2248
+ message: options.editorMessage,
2249
+ postfix: options.editorPostfix,
2250
+ defaultText
2251
+ });
2252
+ const parsed = options.parseText(text);
2253
+ items = parsed;
2254
+ logger.success(`${options.title} updated (${Object.keys(items).length} total)`);
2255
+ } else if (choice === "upsert") {
2256
+ const key = await (0, import_prompts4.input)({
2257
+ message: options.keyPromptMessage,
2258
+ validate: (val) => {
2259
+ const trimmed = val.trim();
2260
+ if (!trimmed) return `${options.itemNoun} name cannot be empty`;
2261
+ if (/\s/.test(trimmed)) return `${options.itemNoun} name cannot contain spaces`;
2262
+ return true;
2263
+ }
2264
+ });
2265
+ const trimmedKey = key.trim();
2266
+ const existingVal = items[trimmedKey];
2267
+ const isSecret = options.isSecretKey(trimmedKey);
2268
+ let newVal;
2269
+ if (isSecret) {
2270
+ newVal = await (0, import_prompts4.password)({
2271
+ message: existingVal !== void 0 ? `New value for (${trimmedKey}) [leave empty to keep current]:` : `${options.valuePromptMessage} for (${trimmedKey}) [sensitive content masked]:`,
2272
+ mask: "*"
2273
+ });
2274
+ if (existingVal !== void 0 && newVal === "") {
2275
+ newVal = existingVal;
2276
+ }
2277
+ } else {
2278
+ newVal = await (0, import_prompts4.input)({
2279
+ message: `${options.valuePromptMessage} for (${trimmedKey}):`,
2280
+ default: existingVal
2281
+ });
2282
+ }
2283
+ items[trimmedKey] = newVal;
2284
+ logger.success(`${existingVal !== void 0 ? "Updated" : "Added"}: ${import_picocolors8.default.cyan(trimmedKey)}`);
2285
+ } else if (choice === "delete") {
2286
+ const toDelete = await (0, import_prompts4.select)({
2287
+ message: `Select ${options.itemNoun} to delete:`,
2288
+ choices: [
2289
+ ...keys.map((k) => ({ name: k, value: k })),
2290
+ { name: "Cancel", value: "__cancel__" }
2291
+ ]
2292
+ });
2293
+ if (toDelete !== "__cancel__") {
2294
+ delete items[toDelete];
2295
+ logger.success(`Deleted: ${import_picocolors8.default.cyan(toDelete)}`);
2296
+ }
2297
+ } else if (choice === "paste") {
2298
+ const pasted = await readMultilineTextFromTerminal(options.pasteMessage);
2299
+ const parsed = options.parseText(pasted);
2300
+ const count = Object.keys(parsed).length;
2301
+ if (count === 0) {
2302
+ logger.warn(`No valid ${options.itemsNoun} recognized`);
2303
+ } else {
2304
+ if (keys.length > 0) {
2305
+ const pasteMode = await (0, import_prompts4.select)({
2306
+ message: `How to apply pasted ${options.itemsNoun}?`,
2307
+ choices: [
2308
+ { name: `Merge with existing ${options.itemsNoun}`, value: "merge" },
2309
+ { name: `Replace all existing ${options.itemsNoun}`, value: "replace" }
2310
+ ]
2311
+ });
2312
+ if (pasteMode === "replace") {
2313
+ items = parsed;
2314
+ } else {
2315
+ Object.assign(items, parsed);
2316
+ }
2317
+ } else {
2318
+ items = parsed;
2319
+ }
2320
+ logger.success(`Successfully applied ${import_picocolors8.default.cyan(String(count))} ${options.itemsNoun}`);
2321
+ }
2322
+ } else if (choice === "clear") {
2323
+ const confirmClear = await (0, import_prompts4.confirm)({
2324
+ message: `Are you sure you want to clear all ${options.itemsNoun}?`,
2325
+ default: false
2326
+ });
2327
+ if (confirmClear) {
2328
+ items = {};
2329
+ logger.success(`Cleared all ${options.itemsNoun}`);
2330
+ }
2331
+ }
2332
+ }
2333
+ };
2334
+
1695
2335
  // src/interactive/prompts/env.ts
1696
- var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
2336
+ var formatEnvText = (env) => {
2337
+ return Object.entries(env).map(([key, value]) => {
2338
+ if (/[\s"']/.test(value)) {
2339
+ return `${key}="${value.replace(/"/g, '\\"')}"`;
2340
+ }
2341
+ return `${key}=${value}`;
2342
+ }).join("\n");
2343
+ };
1697
2344
  var parseEnvText = (rawText) => {
1698
2345
  const result = {};
1699
2346
  const lines = rawText.split(/\r?\n/);
@@ -1719,7 +2366,7 @@ var promptEnvConfig = async (initialEnv = {}) => {
1719
2366
  const env = { ...initialEnv };
1720
2367
  const initialCount = Object.keys(env).length;
1721
2368
  if (initialCount > 0) {
1722
- logger.info(`Includes ${import_picocolors5.default.cyan(String(initialCount))} preset environment variables`);
2369
+ logger.info(`Includes ${import_picocolors9.default.cyan(String(initialCount))} preset environment variables`);
1723
2370
  }
1724
2371
  const mode = await (0, import_prompts5.select)({
1725
2372
  message: "Configure environment variables?",
@@ -1748,7 +2395,8 @@ var promptEnvConfig = async (initialEnv = {}) => {
1748
2395
  if (mode === "paste" || mode === "editor") {
1749
2396
  const pasted = mode === "editor" ? await promptEditorText({
1750
2397
  message: "Paste or edit environment variables in editor, then save and exit:",
1751
- postfix: ".env"
2398
+ postfix: ".env",
2399
+ defaultText: formatEnvText(env)
1752
2400
  }) : await readMultilineTextFromTerminal("Paste .env formatted content (multiline supported):");
1753
2401
  const parsed = parseEnvText(pasted);
1754
2402
  const count = Object.keys(parsed).length;
@@ -1756,10 +2404,9 @@ var promptEnvConfig = async (initialEnv = {}) => {
1756
2404
  logger.warn("No valid KEY=VALUE pairs recognized");
1757
2405
  } else {
1758
2406
  Object.assign(env, parsed);
1759
- logger.success(`Successfully parsed ${import_picocolors5.default.cyan(String(count))} environment variables:`);
2407
+ logger.success(`Successfully parsed ${import_picocolors9.default.cyan(String(count))} environment variables:`);
1760
2408
  for (const [k, v] of Object.entries(parsed)) {
1761
- const masked = SECRET_KEY_PATTERN.test(k) && v.length > 4 ? `${v.slice(0, 2)}***${v.slice(-2)}` : v;
1762
- console.log(` ${import_picocolors5.default.bold(k)}=${import_picocolors5.default.dim(masked)}`);
2409
+ console.log(` ${import_picocolors9.default.bold(k)}=${import_picocolors9.default.dim(maskSecretValue(k, v))}`);
1763
2410
  }
1764
2411
  }
1765
2412
  return env;
@@ -1790,15 +2437,32 @@ var promptEnvConfig = async (initialEnv = {}) => {
1790
2437
  });
1791
2438
  }
1792
2439
  env[trimmedKey] = val;
1793
- logger.success(`Added: ${import_picocolors5.default.cyan(trimmedKey)}`);
2440
+ logger.success(`Added: ${import_picocolors9.default.cyan(trimmedKey)}`);
1794
2441
  }
1795
2442
  return env;
1796
2443
  };
2444
+ var promptEditEnvConfig = async (currentEnv = {}) => promptEditKeyValueConfig(currentEnv, {
2445
+ title: "Environment Variables",
2446
+ itemNoun: "variable",
2447
+ itemsNoun: "environment variables",
2448
+ separator: "=",
2449
+ editorPostfix: ".env",
2450
+ editorMessage: "Edit environment variables in editor, then save and exit:",
2451
+ pasteMessage: "Paste .env formatted content (multiline supported):",
2452
+ keyPromptMessage: "Variable name (Key):",
2453
+ valuePromptMessage: "Value",
2454
+ isSecretKey: (k) => SECRET_KEY_PATTERN.test(k),
2455
+ maskValue: maskSecretValue,
2456
+ formatText: formatEnvText,
2457
+ parseText: parseEnvText
2458
+ });
1797
2459
 
1798
2460
  // src/interactive/prompts/headers.ts
1799
2461
  var import_prompts6 = require("@inquirer/prompts");
1800
- var import_picocolors6 = __toESM(require("picocolors"), 1);
1801
- var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
2462
+ var import_picocolors10 = __toESM(require("picocolors"), 1);
2463
+ var formatHeadersText = (headers) => {
2464
+ return Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n");
2465
+ };
1802
2466
  var parseHeadersText = (rawText) => {
1803
2467
  const result = {};
1804
2468
  const lines = rawText.split(/\r?\n/);
@@ -1854,7 +2518,8 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1854
2518
  }
1855
2519
  if (mode === "paste" || mode === "editor") {
1856
2520
  const pasted = mode === "editor" ? await promptEditorText({
1857
- message: "Paste or edit HTTP headers in editor, then save and exit:"
2521
+ message: "Paste or edit HTTP headers in editor, then save and exit:",
2522
+ defaultText: formatHeadersText(headers)
1858
2523
  }) : await readMultilineTextFromTerminal(
1859
2524
  "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):"
1860
2525
  );
@@ -1864,10 +2529,9 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1864
2529
  logger.warn("No valid Key: Value pairs recognized");
1865
2530
  } else {
1866
2531
  Object.assign(headers, parsed);
1867
- logger.success(`Successfully parsed ${import_picocolors6.default.cyan(String(count))} headers:`);
2532
+ logger.success(`Successfully parsed ${import_picocolors10.default.cyan(String(count))} headers:`);
1868
2533
  for (const [k, v] of Object.entries(parsed)) {
1869
- const masked = SECRET_HEADER_PATTERN.test(k) && v.length > 8 ? `${v.slice(0, 4)}***${v.slice(-3)}` : v;
1870
- console.log(` ${import_picocolors6.default.bold(k)}: ${import_picocolors6.default.dim(masked)}`);
2534
+ console.log(` ${import_picocolors10.default.bold(k)}: ${import_picocolors10.default.dim(maskSecretHeader(k, v))}`);
1871
2535
  }
1872
2536
  }
1873
2537
  return headers;
@@ -1898,15 +2562,29 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
1898
2562
  });
1899
2563
  }
1900
2564
  headers[trimmedName] = val;
1901
- logger.success(`Added: ${import_picocolors6.default.cyan(trimmedName)}`);
2565
+ logger.success(`Added: ${import_picocolors10.default.cyan(trimmedName)}`);
1902
2566
  }
1903
2567
  return headers;
1904
2568
  };
2569
+ var promptEditHeadersConfig = async (currentHeaders = {}) => promptEditKeyValueConfig(currentHeaders, {
2570
+ title: "HTTP Headers",
2571
+ itemNoun: "header",
2572
+ itemsNoun: "HTTP headers",
2573
+ separator: ":",
2574
+ editorMessage: "Edit HTTP headers in editor, then save and exit:",
2575
+ pasteMessage: "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):",
2576
+ keyPromptMessage: "Header name (e.g. Authorization):",
2577
+ valuePromptMessage: "Header value",
2578
+ isSecretKey: (k) => SECRET_HEADER_PATTERN.test(k),
2579
+ maskValue: maskSecretHeader,
2580
+ formatText: formatHeadersText,
2581
+ parseText: parseHeadersText
2582
+ });
1905
2583
 
1906
2584
  // src/interactive/wizard-add.ts
1907
2585
  var wizardAdd = async (initial = {}) => {
1908
2586
  const cwd = initial.cwd ?? process.cwd();
1909
- logger.info(import_picocolors7.default.bold("Welcome to the MCP interactive add wizard"));
2587
+ logger.info(import_picocolors11.default.bold("Welcome to the MCP interactive add wizard"));
1910
2588
  let source = initial.source;
1911
2589
  if (!source) {
1912
2590
  const sourceType = await (0, import_prompts7.select)({
@@ -1928,7 +2606,7 @@ var wizardAdd = async (initial = {}) => {
1928
2606
  });
1929
2607
  if (sourceType === "npm") {
1930
2608
  source = await (0, import_prompts7.input)({
1931
- message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres or mcp-server-git):",
2609
+ message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres):",
1932
2610
  validate: (val) => val.trim() ? true : "Package name cannot be empty"
1933
2611
  });
1934
2612
  } else if (sourceType === "remote") {
@@ -1996,25 +2674,25 @@ var wizardAdd = async (initial = {}) => {
1996
2674
  if (parsed.type !== "remote") {
1997
2675
  env = await promptEnvConfig(env);
1998
2676
  }
1999
- console.log("\n" + import_picocolors7.default.cyan(import_picocolors7.default.bold("Configuration Preview:")));
2000
- console.log(` ${import_picocolors7.default.bold("Server Name:")} ${import_picocolors7.default.green(serverName)}`);
2001
- console.log(` ${import_picocolors7.default.bold("Server Type:")} ${import_picocolors7.default.magenta(parsed.type)}`);
2002
- console.log(` ${import_picocolors7.default.bold("Source/Command:")} ${import_picocolors7.default.dim(source)}`);
2003
- console.log(` ${import_picocolors7.default.bold("Scope:")} ${isGlobal ? import_picocolors7.default.yellow("Global") : import_picocolors7.default.blue("Project")}`);
2004
- console.log(` ${import_picocolors7.default.bold("Target Agents:")} ${import_picocolors7.default.cyan(selectedAgents.join(", "))}`);
2677
+ console.log("\n" + import_picocolors11.default.cyan(import_picocolors11.default.bold("Configuration Preview:")));
2678
+ console.log(` ${import_picocolors11.default.bold("Server Name:")} ${import_picocolors11.default.green(serverName)}`);
2679
+ console.log(` ${import_picocolors11.default.bold("Server Type:")} ${import_picocolors11.default.magenta(parsed.type)}`);
2680
+ console.log(` ${import_picocolors11.default.bold("Source/Command:")} ${import_picocolors11.default.dim(source)}`);
2681
+ console.log(` ${import_picocolors11.default.bold("Scope:")} ${isGlobal ? import_picocolors11.default.yellow("Global") : import_picocolors11.default.blue("Project")}`);
2682
+ console.log(` ${import_picocolors11.default.bold("Target Agents:")} ${import_picocolors11.default.cyan(selectedAgents.join(", "))}`);
2005
2683
  if (args.length > 0) {
2006
- console.log(` ${import_picocolors7.default.bold("Arguments:")} ${import_picocolors7.default.dim(args.join(" "))}`);
2684
+ console.log(` ${import_picocolors11.default.bold("Arguments:")} ${import_picocolors11.default.dim(args.join(" "))}`);
2007
2685
  }
2008
2686
  if (transport) {
2009
- console.log(` ${import_picocolors7.default.bold("Transport:")} ${import_picocolors7.default.magenta(transport)}`);
2687
+ console.log(` ${import_picocolors11.default.bold("Transport:")} ${import_picocolors11.default.magenta(transport)}`);
2010
2688
  }
2011
2689
  const envKeys = Object.keys(env);
2012
2690
  if (envKeys.length > 0) {
2013
- console.log(` ${import_picocolors7.default.bold("Environment Variables:")} ${import_picocolors7.default.dim(envKeys.join(", "))} (${envKeys.length})`);
2691
+ console.log(` ${import_picocolors11.default.bold("Environment Variables:")} ${import_picocolors11.default.dim(envKeys.join(", "))} (${envKeys.length})`);
2014
2692
  }
2015
2693
  const headerKeys = Object.keys(headers);
2016
2694
  if (headerKeys.length > 0) {
2017
- console.log(` ${import_picocolors7.default.bold("Headers:")} ${import_picocolors7.default.dim(headerKeys.join(", "))} (${headerKeys.length})`);
2695
+ console.log(` ${import_picocolors11.default.bold("Headers:")} ${import_picocolors11.default.dim(headerKeys.join(", "))} (${headerKeys.length})`);
2018
2696
  }
2019
2697
  console.log();
2020
2698
  const proceed = await (0, import_prompts7.confirm)({
@@ -2037,41 +2715,103 @@ var wizardAdd = async (initial = {}) => {
2037
2715
  env
2038
2716
  });
2039
2717
  logger.info(
2040
- `Writing ${import_picocolors7.default.bold(result.serverName)} to ${import_picocolors7.default.cyan(String(result.results.length))} agent config files...`
2718
+ `Writing ${import_picocolors11.default.bold(result.serverName)} to ${import_picocolors11.default.cyan(String(result.results.length))} agent config files...`
2041
2719
  );
2042
2720
  let allSuccess = true;
2043
2721
  for (const record of result.results) {
2044
2722
  if (record.success) {
2045
- logger.success(`${import_picocolors7.default.cyan(record.agent)}: Successfully written to ${import_picocolors7.default.dim(record.path)}`);
2723
+ logger.success(
2724
+ `${import_picocolors11.default.cyan(record.agent)}: Successfully written to ${import_picocolors11.default.dim(record.path)}${formatCoHostedBadge("configured", record.coConfiguredAgents)}`
2725
+ );
2046
2726
  } else {
2047
2727
  allSuccess = false;
2048
- logger.error(`${import_picocolors7.default.cyan(record.agent)}: Failed to write - ${record.error}`);
2728
+ logger.error(`${import_picocolors11.default.cyan(record.agent)}: Failed to write - ${record.error}`);
2049
2729
  }
2050
2730
  }
2051
2731
  if (allSuccess) {
2052
- logger.success(import_picocolors7.default.bold(`MCP server "${serverName}" configured successfully!`));
2732
+ logger.success(import_picocolors11.default.bold(`MCP server "${serverName}" configured successfully!`));
2053
2733
  }
2054
2734
  return allSuccess;
2055
2735
  };
2056
2736
 
2057
2737
  // src/interactive/wizard-manage.ts
2058
2738
  var import_prompts8 = require("@inquirer/prompts");
2059
- var import_picocolors8 = __toESM(require("picocolors"), 1);
2739
+ var import_picocolors13 = __toESM(require("picocolors"), 1);
2740
+
2741
+ // src/utils/display-server-details.ts
2742
+ var import_picocolors12 = __toESM(require("picocolors"), 1);
2743
+ var displayServerDetails = ({
2744
+ serverName,
2745
+ config,
2746
+ agents,
2747
+ hasDivergence,
2748
+ global: isGlobal,
2749
+ titlePrefix = "MCP Server Details"
2750
+ }) => {
2751
+ console.log("\n" + import_picocolors12.default.cyan(import_picocolors12.default.bold(`${titlePrefix}: [${serverName}]`)));
2752
+ if (isGlobal !== void 0) {
2753
+ console.log(` ${import_picocolors12.default.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
2754
+ }
2755
+ if (agents && agents.length > 0) {
2756
+ console.log(
2757
+ ` ${import_picocolors12.default.bold("Configured Agents:")} ${import_picocolors12.default.green(agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2758
+ );
2759
+ }
2760
+ if (hasDivergence) {
2761
+ console.log(
2762
+ ` ${import_picocolors12.default.yellow(import_picocolors12.default.bold("Notice:"))} ${import_picocolors12.default.yellow("Configurations differ across installed agents. Showing configuration from the first agent.")}`
2763
+ );
2764
+ }
2765
+ const isRemote = Boolean(config.url && config.url.length > 0);
2766
+ if (isRemote) {
2767
+ console.log(` ${import_picocolors12.default.bold("Transport:")} ${import_picocolors12.default.magenta(config.type ?? "http")}`);
2768
+ console.log(` ${import_picocolors12.default.bold("URL:")} ${import_picocolors12.default.dim(config.url ?? "")}`);
2769
+ const headerKeys = Object.keys(config.headers ?? {});
2770
+ if (headerKeys.length > 0) {
2771
+ console.log(` ${import_picocolors12.default.bold("Headers:")} ${import_picocolors12.default.cyan(String(headerKeys.length))}`);
2772
+ for (const [k, v] of Object.entries(config.headers ?? {})) {
2773
+ console.log(` ${import_picocolors12.default.bold(k)}: ${import_picocolors12.default.dim(maskSecretHeader(k, v))}`);
2774
+ }
2775
+ } else {
2776
+ console.log(` ${import_picocolors12.default.bold("Headers:")} ${import_picocolors12.default.dim("(none)")}`);
2777
+ }
2778
+ } else {
2779
+ console.log(` ${import_picocolors12.default.bold("Command:")} ${import_picocolors12.default.magenta(config.command ?? "")}`);
2780
+ const argsStr = config.args && config.args.length > 0 ? config.args.join(" ") : "(none)";
2781
+ console.log(` ${import_picocolors12.default.bold("Arguments:")} ${import_picocolors12.default.dim(argsStr)}`);
2782
+ const envKeys = Object.keys(config.env ?? {});
2783
+ if (envKeys.length > 0) {
2784
+ console.log(` ${import_picocolors12.default.bold("Environment Variables:")} ${import_picocolors12.default.cyan(String(envKeys.length))}`);
2785
+ for (const [k, v] of Object.entries(config.env ?? {})) {
2786
+ console.log(` ${import_picocolors12.default.bold(k)}=${import_picocolors12.default.dim(maskSecretValue(k, v))}`);
2787
+ }
2788
+ } else {
2789
+ console.log(` ${import_picocolors12.default.bold("Environment Variables:")} ${import_picocolors12.default.dim("(none)")}`);
2790
+ }
2791
+ }
2792
+ console.log();
2793
+ };
2060
2794
 
2061
2795
  // src/interactive/utils/group-installed-servers.ts
2062
2796
  var normalizeServerConfig = parseServerConfig;
2063
2797
  var groupInstalledServersByName = (installed) => {
2064
2798
  const grouped = /* @__PURE__ */ new Map();
2065
2799
  for (const item of installed) {
2800
+ const itemConfig = normalizeServerConfig(item.config);
2066
2801
  let entry = grouped.get(item.serverName);
2067
2802
  if (!entry) {
2068
2803
  entry = {
2069
2804
  serverName: item.serverName,
2070
2805
  agents: [],
2071
2806
  paths: [],
2072
- config: normalizeServerConfig(item.config)
2807
+ config: itemConfig,
2808
+ hasDivergence: false
2073
2809
  };
2074
2810
  grouped.set(item.serverName, entry);
2811
+ } else if (!entry.hasDivergence) {
2812
+ if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
2813
+ entry.hasDivergence = true;
2814
+ }
2075
2815
  }
2076
2816
  if (!entry.agents.includes(item.agent)) {
2077
2817
  entry.agents.push(item.agent);
@@ -2084,6 +2824,221 @@ var groupInstalledServersByName = (installed) => {
2084
2824
  };
2085
2825
 
2086
2826
  // src/interactive/wizard-manage.ts
2827
+ var promptSwitchServerType = async (currentConfig, serverName) => {
2828
+ const isRemote = Boolean(currentConfig.url && currentConfig.url.length > 0);
2829
+ if (isRemote) {
2830
+ const newCmd = await (0, import_prompts8.input)({
2831
+ message: "Executable command (e.g. node, npx):",
2832
+ validate: (val) => val.trim() ? true : "Command cannot be empty"
2833
+ });
2834
+ const newArgs = await promptEditArgs([]);
2835
+ const newEnv = await promptEditEnvConfig({});
2836
+ logger.success(`Switched [${serverName}] configuration to stdio mode`);
2837
+ return {
2838
+ command: newCmd.trim(),
2839
+ args: newArgs.length > 0 ? newArgs : void 0,
2840
+ env: Object.keys(newEnv).length > 0 ? newEnv : void 0
2841
+ };
2842
+ }
2843
+ const newUrl = await (0, import_prompts8.input)({
2844
+ message: "Remote server URL:",
2845
+ validate: (val) => {
2846
+ const trimmed = val.trim();
2847
+ if (!trimmed) return "URL cannot be empty";
2848
+ if (!/^https?:\/\//i.test(trimmed)) {
2849
+ return "Please enter a valid URL starting with http:// or https://";
2850
+ }
2851
+ return true;
2852
+ }
2853
+ });
2854
+ const transport = await (0, import_prompts8.select)({
2855
+ message: "Select remote transport protocol:",
2856
+ choices: [
2857
+ { name: "HTTP", value: "http" },
2858
+ { name: "SSE (Server-Sent Events)", value: "sse" }
2859
+ ],
2860
+ default: "http"
2861
+ });
2862
+ const newHeaders = await promptEditHeadersConfig({});
2863
+ logger.success(`Switched [${serverName}] configuration to remote mode`);
2864
+ return {
2865
+ url: newUrl.trim(),
2866
+ type: transport,
2867
+ headers: Object.keys(newHeaders).length > 0 ? newHeaders : void 0
2868
+ };
2869
+ };
2870
+ var handleEditServerConfig = async (options) => {
2871
+ const { targetGroup } = options;
2872
+ const isGlobal = options.global ?? false;
2873
+ const cwd = options.cwd ?? process.cwd();
2874
+ const serverName = targetGroup.serverName;
2875
+ let workingConfig = {
2876
+ ...targetGroup.config,
2877
+ args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
2878
+ env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
2879
+ headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
2880
+ };
2881
+ while (true) {
2882
+ const isRemote = Boolean(workingConfig.url && workingConfig.url.length > 0);
2883
+ displayServerDetails({
2884
+ serverName,
2885
+ config: workingConfig,
2886
+ titlePrefix: "Edit Server Configuration"
2887
+ });
2888
+ const editChoices = isRemote ? [
2889
+ { name: "Edit HTTP Headers (headers)", value: "headers" },
2890
+ { name: "Edit Remote URL (url)", value: "url" },
2891
+ { name: "Edit Transport Protocol (type)", value: "transport" },
2892
+ { name: "Switch to local command (stdio)", value: "switch_type" },
2893
+ { name: "Reset changes to original", value: "reset" },
2894
+ { name: "Save and apply changes", value: "save" },
2895
+ { name: "Cancel (discard changes)", value: "cancel" }
2896
+ ] : [
2897
+ { name: "Edit Environment Variables (env)", value: "env" },
2898
+ { name: "Edit Command Arguments (args)", value: "args" },
2899
+ { name: "Edit Executable Command (command)", value: "command" },
2900
+ { name: "Switch to remote server (HTTP/SSE)", value: "switch_type" },
2901
+ { name: "Reset changes to original", value: "reset" },
2902
+ { name: "Save and apply changes", value: "save" },
2903
+ { name: "Cancel (discard changes)", value: "cancel" }
2904
+ ];
2905
+ const editAction = await (0, import_prompts8.select)({
2906
+ message: `What would you like to modify in [${serverName}]?`,
2907
+ choices: editChoices
2908
+ });
2909
+ if (editAction === "cancel") {
2910
+ logger.info("Modification cancelled; changes discarded");
2911
+ return;
2912
+ }
2913
+ if (editAction === "reset") {
2914
+ workingConfig = {
2915
+ ...targetGroup.config,
2916
+ args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
2917
+ env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
2918
+ headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
2919
+ };
2920
+ logger.info("Configuration reset to original");
2921
+ continue;
2922
+ }
2923
+ if (editAction === "switch_type") {
2924
+ workingConfig = await promptSwitchServerType(workingConfig, serverName);
2925
+ continue;
2926
+ }
2927
+ if (editAction === "env") {
2928
+ workingConfig.env = await promptEditEnvConfig(workingConfig.env ?? {});
2929
+ } else if (editAction === "args") {
2930
+ workingConfig.args = await promptEditArgs(workingConfig.args ?? []);
2931
+ } else if (editAction === "command") {
2932
+ const newCmd = await (0, import_prompts8.input)({
2933
+ message: "Executable command:",
2934
+ default: workingConfig.command,
2935
+ validate: (val) => val.trim() ? true : "Command cannot be empty"
2936
+ });
2937
+ workingConfig.command = newCmd.trim();
2938
+ } else if (editAction === "headers") {
2939
+ workingConfig.headers = await promptEditHeadersConfig(workingConfig.headers ?? {});
2940
+ } else if (editAction === "url") {
2941
+ const newUrl = await (0, import_prompts8.input)({
2942
+ message: "Remote server URL:",
2943
+ default: workingConfig.url,
2944
+ validate: (val) => {
2945
+ const trimmed = val.trim();
2946
+ if (!trimmed) return "URL cannot be empty";
2947
+ if (!/^https?:\/\//i.test(trimmed)) {
2948
+ return "Please enter a valid URL starting with http:// or https://";
2949
+ }
2950
+ return true;
2951
+ }
2952
+ });
2953
+ workingConfig.url = newUrl.trim();
2954
+ } else if (editAction === "transport") {
2955
+ workingConfig.type = await (0, import_prompts8.select)({
2956
+ message: "Select remote transport protocol:",
2957
+ choices: [
2958
+ { name: "HTTP", value: "http" },
2959
+ { name: "SSE (Server-Sent Events)", value: "sse" }
2960
+ ],
2961
+ default: workingConfig.type === "sse" ? "sse" : "http"
2962
+ });
2963
+ } else if (editAction === "save") {
2964
+ let targetAgents = targetGroup.agents;
2965
+ if (targetGroup.agents.length > 1) {
2966
+ const sortedAgents = sortAgentsWithClusters(targetGroup.agents, { global: isGlobal, cwd });
2967
+ const choices = buildLinkedAgentChoices({
2968
+ agents: sortedAgents,
2969
+ checkedAgents: sortedAgents,
2970
+ scopeOptions: { global: isGlobal, cwd }
2971
+ });
2972
+ targetAgents = await linkedCheckbox({
2973
+ message: "Select agents to update configuration (Space to toggle):",
2974
+ choices,
2975
+ loop: false,
2976
+ validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
2977
+ });
2978
+ if (targetAgents.length < targetGroup.agents.length) {
2979
+ const unselected = targetGroup.agents.filter((a) => !targetAgents.includes(a));
2980
+ const unselectedNames = unselected.map((a) => getMcpAgentConfig(a).displayName).join(", ");
2981
+ logger.info(
2982
+ `Note: Updating only a subset of agents. Server configurations will diverge from: ${unselectedNames}.`
2983
+ );
2984
+ }
2985
+ }
2986
+ const requestedTransport = workingConfig.url ? workingConfig.type ?? "http" : "stdio";
2987
+ const resolution = resolveTargetAgents({
2988
+ requested: targetAgents,
2989
+ global: isGlobal,
2990
+ cwd,
2991
+ transport: requestedTransport
2992
+ });
2993
+ if (resolution.incompatible.length > 0) {
2994
+ for (const item of resolution.incompatible) {
2995
+ logger.warn(`Skipping ${import_picocolors13.default.cyan(item.agent)}: ${item.reason}`);
2996
+ }
2997
+ }
2998
+ if (resolution.compatibleAgents.length === 0) {
2999
+ logger.error(
3000
+ `None of the selected agents support ${requestedTransport} transport. Cannot update.`
3001
+ );
3002
+ continue;
3003
+ }
3004
+ const agentNames = resolution.compatibleAgents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3005
+ const confirmed = await (0, import_prompts8.confirm)({
3006
+ message: `Confirm updating configuration for [${serverName}] across: ${agentNames}?`,
3007
+ default: true
3008
+ });
3009
+ if (!confirmed) {
3010
+ logger.warn("Update cancelled");
3011
+ continue;
3012
+ }
3013
+ const updateResult = updateMcpServer({
3014
+ serverName,
3015
+ config: workingConfig,
3016
+ previousConfig: targetGroup.config,
3017
+ agents: resolution.compatibleAgents,
3018
+ global: isGlobal,
3019
+ cwd
3020
+ });
3021
+ let updatedAny = false;
3022
+ const succeededAgents = [];
3023
+ for (const res of updateResult.results) {
3024
+ if (res.success) {
3025
+ updatedAny = true;
3026
+ succeededAgents.push(res.agent);
3027
+ logger.success(
3028
+ `${import_picocolors13.default.cyan(res.agent)}: Successfully updated configuration in ${import_picocolors13.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
3029
+ );
3030
+ } else {
3031
+ logger.error(`${import_picocolors13.default.cyan(res.agent)}: Update failed - ${res.error}`);
3032
+ }
3033
+ }
3034
+ if (updatedAny) {
3035
+ targetGroup.config = updateResult.config;
3036
+ logger.success(`Configuration for [${serverName}] updated successfully!`);
3037
+ return;
3038
+ }
3039
+ }
3040
+ }
3041
+ };
2087
3042
  var wizardManage = async (options = {}) => {
2088
3043
  const cwd = options.cwd ?? process.cwd();
2089
3044
  const isGlobal = await promptScope({
@@ -2097,51 +3052,57 @@ var wizardManage = async (options = {}) => {
2097
3052
  return;
2098
3053
  }
2099
3054
  const grouped = groupInstalledServersByName(installed);
3055
+ let pendingServerName = options.serverName;
3056
+ const refreshGroupedServers = () => {
3057
+ const freshInstalled = listInstalledMcpServers({ global: isGlobal, cwd });
3058
+ const freshGrouped = groupInstalledServersByName(freshInstalled);
3059
+ grouped.clear();
3060
+ for (const [name, grp] of freshGrouped) {
3061
+ grouped.set(name, grp);
3062
+ }
3063
+ };
2100
3064
  while (true) {
2101
- const choices = Array.from(grouped.values()).map((g) => {
2102
- const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
2103
- return {
2104
- name: `${import_picocolors8.default.bold(g.serverName)} ${import_picocolors8.default.dim(`(configured in: ${agentNames})`)}`,
2105
- value: g.serverName
2106
- };
2107
- });
2108
- choices.push({
2109
- name: `Back`,
2110
- value: "__back__"
2111
- });
2112
- const chosenServerName = await (0, import_prompts8.select)({
2113
- message: "Select MCP server to manage or sync:",
2114
- choices
2115
- });
2116
- if (chosenServerName === "__back__") {
2117
- return;
3065
+ let chosenServerName;
3066
+ if (pendingServerName && grouped.has(pendingServerName)) {
3067
+ chosenServerName = pendingServerName;
3068
+ pendingServerName = void 0;
3069
+ } else {
3070
+ pendingServerName = void 0;
3071
+ const choices = Array.from(grouped.values()).map((g) => {
3072
+ const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3073
+ return {
3074
+ name: `${import_picocolors13.default.bold(g.serverName)} ${import_picocolors13.default.dim(`(configured in: ${agentNames})`)}`,
3075
+ value: g.serverName
3076
+ };
3077
+ });
3078
+ choices.push({
3079
+ name: `Back`,
3080
+ value: "__back__"
3081
+ });
3082
+ chosenServerName = await (0, import_prompts8.select)({
3083
+ message: "Select MCP server to manage or sync:",
3084
+ choices
3085
+ });
3086
+ if (chosenServerName === "__back__") {
3087
+ return;
3088
+ }
2118
3089
  }
2119
3090
  const targetGroup = grouped.get(chosenServerName);
2120
3091
  if (!targetGroup) continue;
2121
- console.log("\n" + import_picocolors8.default.cyan(import_picocolors8.default.bold(`MCP Server Details: [${chosenServerName}]`)));
2122
- console.log(` ${import_picocolors8.default.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
2123
- console.log(
2124
- ` ${import_picocolors8.default.bold("Configured Agents:")} ${import_picocolors8.default.green(targetGroup.agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2125
- );
2126
- const cfg = targetGroup.config;
2127
- if (isRemoteServerConfig(cfg)) {
2128
- console.log(` ${import_picocolors8.default.bold("URL:")} ${import_picocolors8.default.dim(cfg.url)} (${cfg.type})`);
2129
- if (cfg.headers && Object.keys(cfg.headers).length > 0) {
2130
- console.log(` ${import_picocolors8.default.bold("Headers:")} ${Object.keys(cfg.headers).join(", ")}`);
2131
- }
2132
- } else if (isStdioServerConfig(cfg)) {
2133
- console.log(` ${import_picocolors8.default.bold("Command:")} ${import_picocolors8.default.magenta(cfg.command)}`);
2134
- if (cfg.args && cfg.args.length > 0) {
2135
- console.log(` ${import_picocolors8.default.bold("Arguments:")} ${import_picocolors8.default.dim(cfg.args.join(" "))}`);
2136
- }
2137
- if (cfg.env && Object.keys(cfg.env).length > 0) {
2138
- console.log(` ${import_picocolors8.default.bold("Environment Variables:")} ${import_picocolors8.default.dim(Object.keys(cfg.env).join(", "))}`);
2139
- }
2140
- }
2141
- console.log();
3092
+ displayServerDetails({
3093
+ serverName: chosenServerName,
3094
+ config: targetGroup.config,
3095
+ agents: targetGroup.agents,
3096
+ global: isGlobal,
3097
+ hasDivergence: targetGroup.hasDivergence
3098
+ });
2142
3099
  const action = await (0, import_prompts8.select)({
2143
3100
  message: `What would you like to do with [${chosenServerName}]?`,
2144
3101
  choices: [
3102
+ {
3103
+ name: "Edit server configuration",
3104
+ value: "edit"
3105
+ },
2145
3106
  {
2146
3107
  name: "Sync / clone to other agents",
2147
3108
  value: "sync"
@@ -2153,20 +3114,34 @@ var wizardManage = async (options = {}) => {
2153
3114
  ]
2154
3115
  });
2155
3116
  if (action === "back") continue;
3117
+ if (action === "edit") {
3118
+ await handleEditServerConfig({
3119
+ targetGroup,
3120
+ global: isGlobal,
3121
+ cwd
3122
+ });
3123
+ refreshGroupedServers();
3124
+ continue;
3125
+ }
2156
3126
  if (action === "sync") {
2157
3127
  const allAllowedAgents = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2158
- const candidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
2159
- if (candidateAgents.length === 0) {
2160
- logger.info("All supported agents in this scope already have this MCP server configured; no sync needed");
3128
+ const rawCandidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
3129
+ if (rawCandidateAgents.length === 0) {
3130
+ logger.info(
3131
+ "All supported agents in this scope already have this MCP server configured; no sync needed"
3132
+ );
2161
3133
  continue;
2162
3134
  }
2163
- const selectedToSync = await (0, import_prompts8.checkbox)({
3135
+ const candidateAgents = sortAgentsWithClusters(rawCandidateAgents, { global: isGlobal, cwd });
3136
+ const choices = buildLinkedAgentChoices({
3137
+ agents: candidateAgents,
3138
+ checkedAgents: [],
3139
+ scopeOptions: { global: isGlobal, cwd }
3140
+ });
3141
+ const selectedToSync = await linkedCheckbox({
2164
3142
  message: "Select target agents to sync to (Space to select):",
2165
- choices: candidateAgents.map((a) => ({
2166
- name: `${getMcpAgentConfig(a).displayName} (${a})`,
2167
- value: a,
2168
- checked: false
2169
- })),
3143
+ choices,
3144
+ loop: false,
2170
3145
  validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
2171
3146
  });
2172
3147
  const confirmed = await (0, import_prompts8.confirm)({
@@ -2177,25 +3152,37 @@ var wizardManage = async (options = {}) => {
2177
3152
  logger.warn("Sync cancelled");
2178
3153
  continue;
2179
3154
  }
2180
- for (const targetAgent of selectedToSync) {
2181
- const res = installMcpServerForAgent(chosenServerName, targetGroup.config, targetAgent, {
2182
- global: isGlobal,
2183
- cwd
2184
- });
3155
+ const syncResult = updateMcpServer({
3156
+ serverName: chosenServerName,
3157
+ config: targetGroup.config,
3158
+ agents: selectedToSync,
3159
+ global: isGlobal,
3160
+ cwd
3161
+ });
3162
+ for (const item of syncResult.incompatible) {
3163
+ logger.warn(`Skipping ${import_picocolors13.default.cyan(item.agent)}: ${item.reason}`);
3164
+ }
3165
+ for (const res of syncResult.results) {
3166
+ if (syncResult.incompatible.some((i) => i.agent === res.agent)) {
3167
+ continue;
3168
+ }
2185
3169
  if (res.success) {
2186
- logger.success(`${import_picocolors8.default.cyan(targetAgent)}: Successfully synced to ${import_picocolors8.default.dim(res.path)}`);
2187
- targetGroup.agents.push(targetAgent);
3170
+ logger.success(
3171
+ `${import_picocolors13.default.cyan(res.agent)}: Successfully synced to ${import_picocolors13.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
3172
+ );
3173
+ targetGroup.agents.push(res.agent);
2188
3174
  } else {
2189
- logger.error(`${import_picocolors8.default.cyan(targetAgent)}: Sync failed - ${res.error}`);
3175
+ logger.error(`${import_picocolors13.default.cyan(res.agent)}: Sync failed - ${res.error}`);
2190
3176
  }
2191
3177
  }
3178
+ refreshGroupedServers();
2192
3179
  }
2193
3180
  }
2194
3181
  };
2195
3182
 
2196
3183
  // src/interactive/wizard-remove.ts
2197
3184
  var import_prompts9 = require("@inquirer/prompts");
2198
- var import_picocolors9 = __toESM(require("picocolors"), 1);
3185
+ var import_picocolors14 = __toESM(require("picocolors"), 1);
2199
3186
  var wizardRemove = async (options = {}) => {
2200
3187
  const cwd = options.cwd ?? process.cwd();
2201
3188
  const isGlobal = await promptScope({
@@ -2212,7 +3199,7 @@ var wizardRemove = async (options = {}) => {
2212
3199
  let serverName = options.name;
2213
3200
  if (!serverName) {
2214
3201
  const choices = Array.from(serverMap.values()).map((g) => ({
2215
- name: `${import_picocolors9.default.bold(g.serverName)} ${import_picocolors9.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
3202
+ name: `${import_picocolors14.default.bold(g.serverName)} ${import_picocolors14.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
2216
3203
  value: g.serverName
2217
3204
  }));
2218
3205
  serverName = await (0, import_prompts9.select)({
@@ -2220,20 +3207,22 @@ var wizardRemove = async (options = {}) => {
2220
3207
  choices
2221
3208
  });
2222
3209
  }
2223
- const installedAgents = serverMap.get(serverName)?.agents || [];
2224
- if (installedAgents.length === 0) {
3210
+ const rawInstalledAgents = serverMap.get(serverName)?.agents || [];
3211
+ if (rawInstalledAgents.length === 0) {
2225
3212
  logger.warn(`No agents found with [${serverName}] installed`);
2226
3213
  return false;
2227
3214
  }
3215
+ const installedAgents = sortAgentsWithClusters(rawInstalledAgents, { global: isGlobal, cwd });
2228
3216
  let targetAgents = options.agents;
2229
3217
  if (!targetAgents || targetAgents.length === 0) {
2230
- targetAgents = await (0, import_prompts9.checkbox)({
3218
+ const choices = buildLinkedAgentChoices({
3219
+ agents: installedAgents,
3220
+ checkedAgents: installedAgents,
3221
+ scopeOptions: { global: isGlobal, cwd }
3222
+ });
3223
+ targetAgents = await linkedCheckbox({
2231
3224
  message: `Select agents to remove [${serverName}] from:`,
2232
- choices: installedAgents.map((agent) => ({
2233
- name: `${getMcpAgentConfig(agent)?.displayName ?? agent} (${agent})`,
2234
- value: agent,
2235
- checked: true
2236
- })),
3225
+ choices,
2237
3226
  validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
2238
3227
  });
2239
3228
  } else {
@@ -2261,10 +3250,12 @@ var wizardRemove = async (options = {}) => {
2261
3250
  let removedCount = 0;
2262
3251
  for (const res of results) {
2263
3252
  if (res.removed) {
2264
- logger.success(`${import_picocolors9.default.cyan(res.agent)}: Successfully removed from ${import_picocolors9.default.dim(res.path)}`);
3253
+ logger.success(
3254
+ `${import_picocolors14.default.cyan(res.agent)}: Successfully removed from ${import_picocolors14.default.dim(res.path)}${formatCoHostedBadge("affected", res.coAffectedAgents)}`
3255
+ );
2265
3256
  removedCount++;
2266
3257
  } else if (res.error) {
2267
- logger.error(`${import_picocolors9.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
3258
+ logger.error(`${import_picocolors14.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
2268
3259
  }
2269
3260
  }
2270
3261
  if (removedCount > 0) {
@@ -2278,8 +3269,8 @@ var wizardRemove = async (options = {}) => {
2278
3269
  // src/interactive/main-menu.ts
2279
3270
  var mainMenu = async () => {
2280
3271
  console.log();
2281
- console.log(import_picocolors10.default.bold(import_picocolors10.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
2282
- console.log(import_picocolors10.default.dim("Cross-platform MCP server configuration & synchronization tool"));
3272
+ console.log(import_picocolors15.default.bold(import_picocolors15.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
3273
+ console.log(import_picocolors15.default.dim("Cross-platform MCP server configuration & synchronization tool"));
2283
3274
  console.log();
2284
3275
  while (true) {
2285
3276
  try {
@@ -2305,7 +3296,7 @@ var mainMenu = async () => {
2305
3296
  ]
2306
3297
  });
2307
3298
  if (action === "exit") {
2308
- console.log(import_picocolors10.default.dim("Goodbye!"));
3299
+ console.log(import_picocolors15.default.dim("Goodbye!"));
2309
3300
  break;
2310
3301
  }
2311
3302
  if (action === "add") {
@@ -2318,7 +3309,7 @@ var mainMenu = async () => {
2318
3309
  console.log();
2319
3310
  } catch (error) {
2320
3311
  if (error?.name === "ExitPromptError") {
2321
- console.log("\n" + import_picocolors10.default.dim("Exited."));
3312
+ console.log("\n" + import_picocolors15.default.dim("Exited."));
2322
3313
  break;
2323
3314
  }
2324
3315
  throw error;
@@ -2326,10 +3317,18 @@ var mainMenu = async () => {
2326
3317
  }
2327
3318
  };
2328
3319
 
2329
- // src/utils/format-agent-list.ts
2330
- var formatAgentList = (agentList, emptyLabel = "(none)") => agentList.length === 0 ? emptyLabel : agentList.join(", ");
3320
+ // src/utils/resolve-transport.ts
3321
+ var resolveTransport = (input7) => {
3322
+ if (!input7) return void 0;
3323
+ if (input7 === "http" || input7 === "sse") return input7;
3324
+ throw new Error(`Unsupported transport "${input7}" (expected: http, sse)`);
3325
+ };
2331
3326
 
2332
- // src/cli/add.ts
3327
+ // src/cli/manage.ts
3328
+ var import_commander = require("commander");
3329
+ var import_picocolors16 = __toESM(require("picocolors"), 1);
3330
+
3331
+ // src/utils/parse-key-value-list.ts
2333
3332
  var parseKeyValueList = (entries, separator) => {
2334
3333
  if (!entries || entries.length === 0) return {};
2335
3334
  const result = {};
@@ -2345,12 +3344,189 @@ var parseKeyValueList = (entries, separator) => {
2345
3344
  }
2346
3345
  return result;
2347
3346
  };
2348
- var resolveTransport = (input5) => {
2349
- if (!input5) return void 0;
2350
- if (input5 === "http" || input5 === "sse") return input5;
2351
- throw new Error(`Unsupported transport "${input5}" (expected: http, sse)`);
3347
+
3348
+ // src/cli/manage.ts
3349
+ var requireTargetServerGroup = (serverName, scope) => {
3350
+ const installed = listInstalledMcpServers(scope);
3351
+ const grouped = groupInstalledServersByName(installed);
3352
+ const targetGroup = grouped.get(serverName);
3353
+ if (!targetGroup) {
3354
+ logger.error(
3355
+ `MCP server "${serverName}" is not configured in ${scope.global ? "global" : "project"} scope.`
3356
+ );
3357
+ process.exitCode = 1;
3358
+ return void 0;
3359
+ }
3360
+ return targetGroup;
2352
3361
  };
2353
- var mcpAddCommand = new import_commander.Command("add").description("Add an MCP server to coding agents").argument("[source]", "Remote URL, npm package, or command line").option("-a, --agent <agents...>", "Target specific agents (use '*' for all)").option("-g, --global", "Install to user-level config instead of project").option("-t, --transport <type>", "Transport type for remote servers (http or sse)").option("--header <header...>", "HTTP header (Key: Value), repeatable").option("--env <env...>", "Env var for stdio servers (KEY=VALUE), repeatable").option("--args <args...>", "CLI arguments for stdio/package servers").option("-n, --name <name>", "Server name override").option("-y, --yes", "Skip all prompts").option("--all", "Install to all supported agents").action(async (source, options) => {
3362
+ 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) => {
3363
+ try {
3364
+ const cwd = process.cwd();
3365
+ const isGlobal = Boolean(options.global);
3366
+ 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;
3367
+ const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
3368
+ if (hasModifications) {
3369
+ if (options.url !== void 0 && options.command !== void 0) {
3370
+ logger.error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
3371
+ process.exitCode = 1;
3372
+ return;
3373
+ }
3374
+ if (!serverName) {
3375
+ logger.error('Missing required argument: "server-name" when passing modification flags.');
3376
+ process.exitCode = 1;
3377
+ return;
3378
+ }
3379
+ const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
3380
+ if (!targetGroup) {
3381
+ return;
3382
+ }
3383
+ const isCurrentRemote = Boolean(targetGroup.config.url && targetGroup.config.url.length > 0);
3384
+ const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
3385
+ if (willBeRemote) {
3386
+ const ignoredStdioFlags = [];
3387
+ if (options.env !== void 0) ignoredStdioFlags.push("--env");
3388
+ if (options.clearEnv) ignoredStdioFlags.push("--clear-env");
3389
+ if (options.args !== void 0) ignoredStdioFlags.push("--args");
3390
+ if (options.clearArgs) ignoredStdioFlags.push("--clear-args");
3391
+ if (ignoredStdioFlags.length > 0) {
3392
+ const hint = options.url !== void 0 ? "When configuring a remote server, stdio flags are ignored." : "Use --command to switch to stdio mode.";
3393
+ logger.warn(
3394
+ `Server "${serverName}" is a remote server. The following stdio flags will be ignored: ${ignoredStdioFlags.join(", ")}. ${hint}`
3395
+ );
3396
+ }
3397
+ } else {
3398
+ const ignoredRemoteFlags = [];
3399
+ if (options.header !== void 0) ignoredRemoteFlags.push("--header");
3400
+ if (options.clearHeaders) ignoredRemoteFlags.push("--clear-headers");
3401
+ if (options.transport !== void 0) ignoredRemoteFlags.push("--transport");
3402
+ if (ignoredRemoteFlags.length > 0) {
3403
+ const hint = options.command !== void 0 ? "When configuring a stdio server, remote flags are ignored." : "Use --url to switch to remote mode.";
3404
+ logger.warn(
3405
+ `Server "${serverName}" is a stdio server. The following remote flags will be ignored: ${ignoredRemoteFlags.join(", ")}. ${hint}`
3406
+ );
3407
+ }
3408
+ }
3409
+ const incomingDelta = {};
3410
+ if (options.command !== void 0) {
3411
+ incomingDelta.command = options.command;
3412
+ }
3413
+ if (options.clearArgs) {
3414
+ incomingDelta.args = void 0;
3415
+ }
3416
+ if (options.args !== void 0) {
3417
+ incomingDelta.args = options.args;
3418
+ }
3419
+ if (options.url !== void 0) {
3420
+ incomingDelta.url = options.url;
3421
+ }
3422
+ if (options.transport !== void 0) {
3423
+ incomingDelta.type = resolveTransport(options.transport);
3424
+ }
3425
+ if (options.clearEnv) {
3426
+ incomingDelta.env = void 0;
3427
+ }
3428
+ if (options.env !== void 0) {
3429
+ const parsedEnv = parseKeyValueList(options.env, "=");
3430
+ const baseEnv = options.clearEnv ? {} : targetGroup.config.env ?? {};
3431
+ incomingDelta.env = { ...baseEnv, ...parsedEnv };
3432
+ }
3433
+ if (options.clearHeaders) {
3434
+ incomingDelta.headers = void 0;
3435
+ }
3436
+ if (options.header !== void 0) {
3437
+ const parsedHeaders = parseKeyValueList(options.header, ":");
3438
+ const baseHeaders = options.clearHeaders ? {} : targetGroup.config.headers ?? {};
3439
+ incomingDelta.headers = { ...baseHeaders, ...parsedHeaders };
3440
+ }
3441
+ let targetAgents = targetGroup.agents;
3442
+ if (options.agent !== void 0) {
3443
+ const parsed = parseMcpAgentList(options.agent);
3444
+ if (!parsed || parsed.length === 0) {
3445
+ logger.error(`No valid agents recognized from: "${options.agent.join(", ")}".`);
3446
+ process.exitCode = 1;
3447
+ return;
3448
+ }
3449
+ targetAgents = parsed;
3450
+ }
3451
+ const updateResult = updateMcpServer({
3452
+ serverName,
3453
+ config: incomingDelta,
3454
+ previousConfig: targetGroup.config,
3455
+ agents: targetAgents,
3456
+ global: isGlobal,
3457
+ cwd
3458
+ });
3459
+ for (const item of updateResult.incompatible) {
3460
+ logger.warn(`Skipping ${import_picocolors16.default.cyan(item.agent)}: ${item.reason}`);
3461
+ }
3462
+ const attemptedResults = updateResult.results.filter(
3463
+ (r) => !updateResult.incompatible.some((i) => i.agent === r.agent)
3464
+ );
3465
+ if (attemptedResults.length === 0) {
3466
+ const requestedTransport = updateResult.config.url ? updateResult.config.type ?? "http" : "stdio";
3467
+ logger.error(
3468
+ `None of the target agents support ${requestedTransport} transport. Update aborted.`
3469
+ );
3470
+ process.exitCode = 1;
3471
+ return;
3472
+ }
3473
+ logger.info(
3474
+ `Updating ${import_picocolors16.default.bold(serverName)} across ${import_picocolors16.default.cyan(String(attemptedResults.length))} agent(s)...`
3475
+ );
3476
+ let allSuccess = true;
3477
+ for (const res of attemptedResults) {
3478
+ if (res.success) {
3479
+ logger.success(`${import_picocolors16.default.cyan(res.agent)}: Successfully updated in ${import_picocolors16.default.dim(res.path)}`);
3480
+ logCoHostedNotice("configured", res.coConfiguredAgents);
3481
+ } else {
3482
+ allSuccess = false;
3483
+ logger.error(`${import_picocolors16.default.cyan(res.agent)}: Update failed - ${res.error}`);
3484
+ }
3485
+ }
3486
+ if (!allSuccess) {
3487
+ process.exitCode = 1;
3488
+ }
3489
+ return;
3490
+ }
3491
+ if (!isInteractive) {
3492
+ if (!serverName) {
3493
+ logger.error(
3494
+ 'Missing required argument: "server-name" for non-interactive manage command. Specify a server name or use interactive terminal.'
3495
+ );
3496
+ process.exitCode = 1;
3497
+ return;
3498
+ }
3499
+ const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
3500
+ if (!targetGroup) {
3501
+ return;
3502
+ }
3503
+ displayServerDetails({
3504
+ serverName,
3505
+ config: targetGroup.config,
3506
+ agents: targetGroup.agents,
3507
+ global: isGlobal,
3508
+ hasDivergence: targetGroup.hasDivergence
3509
+ });
3510
+ return;
3511
+ }
3512
+ await wizardManage({
3513
+ global: options.global,
3514
+ serverName
3515
+ });
3516
+ } catch (error) {
3517
+ if (error && typeof error === "object" && "name" in error && error.name === "ExitPromptError") {
3518
+ process.exit(0);
3519
+ }
3520
+ logger.error(toErrorMessage(error));
3521
+ process.exitCode = 1;
3522
+ }
3523
+ });
3524
+
3525
+ // src/utils/format-agent-list.ts
3526
+ var formatAgentList = (agentList, emptyLabel = "(none)") => agentList.length === 0 ? emptyLabel : agentList.join(", ");
3527
+
3528
+ // src/cli/add.ts
3529
+ var mcpAddCommand = new import_commander2.Command("add").description("Add an MCP server to coding agents").argument("[source]", "Remote URL, npm package, or command line").option("-a, --agent <agents...>", "Target specific agents (use '*' for all)").option("-g, --global", "Install to user-level config instead of project").option("-t, --transport <type>", "Transport type for remote servers (http or sse)").option("--header <header...>", "HTTP header (Key: Value), repeatable").option("--env <env...>", "Env var for stdio servers (KEY=VALUE), repeatable").option("--args <args...>", "CLI arguments for stdio/package servers").option("-n, --name <name>", "Server name override").option("-y, --yes", "Skip all prompts").option("--all", "Install to all supported agents").action(async (source, options) => {
2354
3530
  try {
2355
3531
  const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
2356
3532
  if (!source) {
@@ -2384,19 +3560,19 @@ var mcpAddCommand = new import_commander.Command("add").description("Add an MCP
2384
3560
  transport
2385
3561
  });
2386
3562
  if (resolvedTargets.agents.length === 0) {
2387
- const message = resolvedTargets.diagnostic ?? `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass ${import_picocolors11.default.cyan("-a <agent>")} (e.g. ${import_picocolors11.default.cyan("-a cursor")}) or ${import_picocolors11.default.cyan("--all")} to install.`;
3563
+ const message = resolvedTargets.diagnostic ?? `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass ${import_picocolors17.default.cyan("-a <agent>")} (e.g. ${import_picocolors17.default.cyan("-a cursor")}) or ${import_picocolors17.default.cyan("--all")} to install.`;
2388
3564
  logger.warn(message);
2389
3565
  process.exitCode = 1;
2390
3566
  return;
2391
3567
  }
2392
3568
  if (resolvedTargets.isDetected) {
2393
3569
  logger.info(
2394
- `Detected ${isGlobal ? "global" : "project"} agents: ${import_picocolors11.default.cyan(formatAgentList(resolvedTargets.detected, "(none detected)"))}`
3570
+ `Detected ${isGlobal ? "global" : "project"} agents: ${import_picocolors17.default.cyan(formatAgentList(resolvedTargets.detected, "(none detected)"))}`
2395
3571
  );
2396
3572
  if (resolvedTargets.incompatible.length > 0) {
2397
3573
  const skippedList = resolvedTargets.incompatible.map((item) => `${item.agent} (${item.reason})`).join(", ");
2398
3574
  logger.info(
2399
- `Skipping detected agents incompatible with ${transport}: ${import_picocolors11.default.yellow(skippedList)}`
3575
+ `Skipping detected agents incompatible with ${transport}: ${import_picocolors17.default.yellow(skippedList)}`
2400
3576
  );
2401
3577
  }
2402
3578
  }
@@ -2413,13 +3589,14 @@ var mcpAddCommand = new import_commander.Command("add").description("Add an MCP
2413
3589
  env: parseKeyValueList(options.env, "=")
2414
3590
  });
2415
3591
  logger.info(
2416
- `Installing ${import_picocolors11.default.bold(result.serverName)} (${import_picocolors11.default.cyan(parsed.type)}) to ${import_picocolors11.default.cyan(String(result.results.length))} agent(s)`
3592
+ `Installing ${import_picocolors17.default.bold(result.serverName)} (${import_picocolors17.default.cyan(parsed.type)}) to ${import_picocolors17.default.cyan(String(result.results.length))} agent(s)`
2417
3593
  );
2418
3594
  for (const record of result.results) {
2419
3595
  if (record.success) {
2420
- logger.success(`${import_picocolors11.default.cyan(record.agent)} ${import_picocolors11.default.dim(record.path)}`);
3596
+ logger.success(`${import_picocolors17.default.cyan(record.agent)} ${import_picocolors17.default.dim(record.path)}`);
3597
+ logCoHostedNotice("configured", record.coConfiguredAgents);
2421
3598
  } else {
2422
- logger.error(`${import_picocolors11.default.cyan(record.agent)}: ${record.error}`);
3599
+ logger.error(`${import_picocolors17.default.cyan(record.agent)}: ${record.error}`);
2423
3600
  }
2424
3601
  }
2425
3602
  if (result.results.some((record) => !record.success)) process.exitCode = 1;
@@ -2430,9 +3607,9 @@ var mcpAddCommand = new import_commander.Command("add").description("Add an MCP
2430
3607
  });
2431
3608
 
2432
3609
  // src/cli/list.ts
2433
- var import_commander2 = require("commander");
2434
- var import_picocolors12 = __toESM(require("picocolors"), 1);
2435
- var mcpListCommand = new import_commander2.Command("list").alias("ls").description("List installed MCP servers across agents").option("-g, --global", "List global configs instead of project").option("-a, --agent <agents...>", "Filter by specific agents").option("--json", "Output as JSON").action((options) => {
3610
+ var import_commander3 = require("commander");
3611
+ var import_picocolors18 = __toESM(require("picocolors"), 1);
3612
+ var mcpListCommand = new import_commander3.Command("list").alias("ls").description("List installed MCP servers across agents").option("-g, --global", "List global configs instead of project").option("-a, --agent <agents...>", "Filter by specific agents").option("--json", "Output as JSON").action((options) => {
2436
3613
  try {
2437
3614
  const entries = listInstalledMcpServers({
2438
3615
  global: Boolean(options.global),
@@ -2455,9 +3632,9 @@ var mcpListCommand = new import_commander2.Command("list").alias("ls").descripti
2455
3632
  }
2456
3633
  for (const [serverName, group] of grouped) {
2457
3634
  const agentLabels = group.map((record) => record.agent).join(", ");
2458
- console.log(` ${import_picocolors12.default.bold(serverName)} ${import_picocolors12.default.dim(`[${agentLabels}]`)}`);
3635
+ console.log(` ${import_picocolors18.default.bold(serverName)} ${import_picocolors18.default.dim(`[${agentLabels}]`)}`);
2459
3636
  const firstPath = group[0]?.path;
2460
- if (firstPath) console.log(` ${import_picocolors12.default.dim(firstPath)}`);
3637
+ if (firstPath) console.log(` ${import_picocolors18.default.dim(firstPath)}`);
2461
3638
  }
2462
3639
  } catch (error) {
2463
3640
  logger.error(toErrorMessage(error));
@@ -2466,9 +3643,9 @@ var mcpListCommand = new import_commander2.Command("list").alias("ls").descripti
2466
3643
  });
2467
3644
 
2468
3645
  // src/cli/remove.ts
2469
- var import_commander3 = require("commander");
2470
- var import_picocolors13 = __toESM(require("picocolors"), 1);
2471
- var mcpRemoveCommand = new import_commander3.Command("remove").alias("rm").description("Remove an MCP server from agent configs").argument("[name]", "Server name").option("-g, --global", "Remove from global scope").option("-a, --agent <agents...>", "Filter by specific agents (use '*' for all)").option("-y, --yes", "Skip confirmation prompts").action(async (name, options) => {
3646
+ var import_commander4 = require("commander");
3647
+ var import_picocolors19 = __toESM(require("picocolors"), 1);
3648
+ var mcpRemoveCommand = new import_commander4.Command("remove").alias("rm").description("Remove an MCP server from agent configs").argument("[name]", "Server name").option("-g, --global", "Remove from global scope").option("-a, --agent <agents...>", "Filter by specific agents (use '*' for all)").option("-y, --yes", "Skip confirmation prompts").action(async (name, options) => {
2472
3649
  try {
2473
3650
  const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
2474
3651
  if (!name) {
@@ -2491,16 +3668,17 @@ var mcpRemoveCommand = new import_commander3.Command("remove").alias("rm").descr
2491
3668
  cwd: process.cwd()
2492
3669
  });
2493
3670
  if (results.length === 0) {
2494
- logger.warn(`No agent config contained ${import_picocolors13.default.bold(name)}`);
3671
+ logger.warn(`No agent config contained ${import_picocolors19.default.bold(name)}`);
2495
3672
  return;
2496
3673
  }
2497
3674
  for (const record of results) {
2498
3675
  if (record.removed) {
2499
3676
  logger.success(
2500
- `${import_picocolors13.default.cyan(record.agent)} removed ${import_picocolors13.default.bold(name)} ${import_picocolors13.default.dim(record.path)}`
3677
+ `${import_picocolors19.default.cyan(record.agent)} removed ${import_picocolors19.default.bold(name)} ${import_picocolors19.default.dim(record.path)}`
2501
3678
  );
3679
+ logCoHostedNotice("affected", record.coAffectedAgents);
2502
3680
  } else {
2503
- logger.error(`${import_picocolors13.default.cyan(record.agent)}: ${record.error ?? "not found"}`);
3681
+ logger.error(`${import_picocolors19.default.cyan(record.agent)}: ${record.error ?? "not found"}`);
2504
3682
  }
2505
3683
  }
2506
3684
  } catch (error) {
@@ -2510,12 +3688,13 @@ var mcpRemoveCommand = new import_commander3.Command("remove").alias("rm").descr
2510
3688
  });
2511
3689
 
2512
3690
  // src/cli.ts
2513
- var VERSION = "0.1.0-beta.1";
3691
+ var VERSION = "0.1.0-beta.3";
2514
3692
  process.on("SIGINT", () => process.exit(0));
2515
3693
  process.on("SIGTERM", () => process.exit(0));
2516
- var program = new import_commander4.Command().name("mcps").description("Install, list, and remove MCP servers across AI coding agents").version(VERSION, "-v, --version", "display the version number");
3694
+ var program = new import_commander5.Command().name("mcps").description("Install, list, and remove MCP servers across AI coding agents").version(VERSION, "-v, --version", "display the version number");
2517
3695
  program.addCommand(mcpAddCommand);
2518
3696
  program.addCommand(mcpListCommand);
3697
+ program.addCommand(mcpManageCommand);
2519
3698
  program.addCommand(mcpRemoveCommand);
2520
3699
  var main = async () => {
2521
3700
  if (process.argv.length <= 2 && process.stdin.isTTY) {