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