@wuyax/mcps 0.1.0-beta.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -32,30 +32,21 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  AgentConfigStore: () => AgentConfigStore,
34
34
  DEFAULT_REMOTE_TRANSPORT: () => DEFAULT_REMOTE_TRANSPORT,
35
- NPX_COMMAND: () => NPX_COMMAND,
36
- NPX_DASH_Y: () => NPX_DASH_Y,
37
35
  SECRET_HEADER_PATTERN: () => SECRET_HEADER_PATTERN,
38
36
  SECRET_KEY_PATTERN: () => SECRET_KEY_PATTERN,
39
- add: () => installMcpServer,
40
37
  agentConfigStore: () => agentConfigStore,
41
- buildLinkedAgentChoices: () => buildLinkedAgentChoices,
38
+ applyServerConfigDelta: () => applyServerConfigDelta,
42
39
  buildMcpServerConfig: () => buildMcpServerConfig,
43
40
  createAgentTransform: () => createAgentTransform,
44
41
  detectGloballyInstalledMcpAgents: () => detectGloballyInstalledMcpAgents,
45
42
  detectProjectInstalledMcpAgents: () => detectProjectInstalledMcpAgents,
46
43
  detectUpdateTransition: () => detectUpdateTransition,
47
- displayServerDetails: () => displayServerDetails,
48
44
  extractPackageName: () => extractPackageName,
49
- formatArgsString: () => formatArgsString,
50
- formatEnvText: () => formatEnvText,
51
- formatHeadersText: () => formatHeadersText,
52
45
  getCandidateAgentsForScope: () => getCandidateAgentsForScope,
53
46
  getCoHostedAgents: () => getCoHostedAgents,
54
47
  getMcpAgentConfig: () => getMcpAgentConfig,
55
48
  getMcpAgentTypes: () => getMcpAgentTypes,
56
49
  getMcpAgentsSupportingProjectScope: () => getMcpAgentsSupportingProjectScope,
57
- groupInstalledServersByName: () => groupInstalledServersByName,
58
- install: () => installMcpServer,
59
50
  installMcpServer: () => installMcpServer,
60
51
  installMcpServerForAgent: () => installMcpServerForAgent,
61
52
  installMcpServerForAgents: () => installMcpServerForAgents,
@@ -65,55 +56,23 @@ __export(src_exports, {
65
56
  isRemoteMcpSource: () => isRemoteMcpSource,
66
57
  isRemoteServerConfig: () => isRemoteServerConfig,
67
58
  isStdioServerConfig: () => isStdioServerConfig,
68
- linkedCheckbox: () => linkedCheckbox,
69
- list: () => listInstalledMcpServers,
70
59
  listInstalledMcpServers: () => listInstalledMcpServers,
71
- listServersInConfigFile: () => listServersInConfigFile,
72
- mainMenu: () => mainMenu,
73
60
  maskSecretHeader: () => maskSecretHeader,
74
61
  maskSecretValue: () => maskSecretValue,
75
- mcpAgentAliases: () => mcpAgentAliases,
76
- mcpAgents: () => mcpAgents,
77
- mcpManageCommand: () => mcpManageCommand,
78
- normalizeServerConfig: () => normalizeServerConfig,
79
- parseArgsString: () => parseArgsString,
80
- parseEnvText: () => parseEnvText,
81
- parseHeadersText: () => parseHeadersText,
82
62
  parseMcpSource: () => parseMcpSource,
83
63
  parseServerConfig: () => parseServerConfig,
84
- parseSource: () => parseMcpSource,
85
- promptArgsConfig: () => promptArgsConfig,
86
- promptEditArgs: () => promptEditArgs,
87
- promptEditEnvConfig: () => promptEditEnvConfig,
88
- promptEditHeadersConfig: () => promptEditHeadersConfig,
89
- promptEditKeyValueConfig: () => promptEditKeyValueConfig,
90
- promptEnvConfig: () => promptEnvConfig,
91
- promptHeadersConfig: () => promptHeadersConfig,
92
- promptScope: () => promptScope,
93
- promptScopeAndAgents: () => promptScopeAndAgents,
94
- promptSwitchServerType: () => promptSwitchServerType,
95
- readConfigFile: () => readConfigFile,
96
- remove: () => removeMcpServer,
64
+ queryGroupedInstalledServers: () => queryGroupedInstalledServers,
97
65
  removeMcpServer: () => removeMcpServer,
98
- removeMcpServerFromAgent: () => removeMcpServerFromAgent,
99
- removeServerFromConfigFile: () => removeServerFromConfigFile,
100
66
  resolveConfigClusters: () => resolveConfigClusters,
101
67
  resolveMcpAgentAlias: () => resolveMcpAgentAlias,
102
68
  resolveMcpConfigTarget: () => resolveMcpConfigTarget,
103
69
  resolveTargetAgents: () => resolveTargetAgents,
104
- resolveTransport: () => resolveTransport,
105
70
  sanitizeUpdatedServerConfig: () => sanitizeUpdatedServerConfig,
106
- sortAgentsWithClusters: () => sortAgentsWithClusters,
71
+ sortAgentsByClusters: () => sortAgentsByClusters,
107
72
  toRemoteServerConfig: () => toRemoteServerConfig,
108
73
  toStdioServerConfig: () => toStdioServerConfig,
109
- transformServerConfig: () => transformServerConfig,
110
74
  transformServerConfigForAgent: () => transformServerConfigForAgent,
111
- update: () => updateMcpServer,
112
- updateMcpServer: () => updateMcpServer,
113
- wizardAdd: () => wizardAdd,
114
- wizardManage: () => wizardManage,
115
- wizardRemove: () => wizardRemove,
116
- writeServerToConfigFile: () => writeServerToConfigFile
75
+ updateMcpServer: () => updateMcpServer
117
76
  });
118
77
  module.exports = __toCommonJS(src_exports);
119
78
 
@@ -571,9 +530,9 @@ var mcpAgentAliases = {
571
530
  var getMcpAgentConfig = (agentType) => mcpAgents[agentType];
572
531
  var getMcpAgentTypes = () => Object.values(mcpAgents).map((config) => config.name);
573
532
  var isMcpAgentType = (value) => value in mcpAgents;
574
- var resolveMcpAgentAlias = (input7) => {
575
- if (isMcpAgentType(input7)) return input7;
576
- return mcpAgentAliases[input7] ?? null;
533
+ var resolveMcpAgentAlias = (input) => {
534
+ if (isMcpAgentType(input)) return input;
535
+ return mcpAgentAliases[input] ?? null;
577
536
  };
578
537
  var isMcpTransportSupported = (agent, transport) => agent.supportedTransports.includes(transport);
579
538
  var detectProjectInstalledMcpAgents = (cwd) => getMcpAgentTypes().filter(
@@ -625,7 +584,9 @@ var KNOWN_COMMAND_RUNNERS = /* @__PURE__ */ new Set([
625
584
  ]);
626
585
  var SCRIPT_EXTENSION_REGEX = /\.(?:js|ts|mjs|cjs|py|sh|rb|go)$/i;
627
586
 
628
- // src/build-server-config.ts
587
+ // src/server-config.ts
588
+ var isRemoteServerConfig = (config) => typeof config.url === "string" && config.url.length > 0;
589
+ var isStdioServerConfig = (config) => typeof config.command === "string" && config.command.length > 0;
629
590
  var buildMcpServerConfig = (parsed, options = {}) => {
630
591
  if (parsed.type === "remote") {
631
592
  const config2 = {
@@ -663,10 +624,6 @@ var buildMcpServerConfig = (parsed, options = {}) => {
663
624
  }
664
625
  return config;
665
626
  };
666
-
667
- // src/parse-server-config.ts
668
- var isRemoteServerConfig = (config) => typeof config.url === "string" && config.url.length > 0;
669
- var isStdioServerConfig = (config) => typeof config.command === "string" && config.command.length > 0;
670
627
  var parseServerConfig = (raw) => {
671
628
  if (!raw || typeof raw !== "object") return {};
672
629
  const data = raw;
@@ -693,6 +650,237 @@ var parseServerConfig = (raw) => {
693
650
  }
694
651
  return {};
695
652
  };
653
+ var toRemoteServerConfig = (config, defaultTransport = "http") => {
654
+ const {
655
+ command: _droppedCommand,
656
+ args: _droppedArgs,
657
+ env: _droppedEnv,
658
+ ...remoteConfig
659
+ } = config;
660
+ return {
661
+ ...remoteConfig,
662
+ type: remoteConfig.type ?? defaultTransport
663
+ };
664
+ };
665
+ var toStdioServerConfig = (config) => {
666
+ const {
667
+ url: _droppedUrl,
668
+ type: _droppedType,
669
+ headers: _droppedHeaders,
670
+ ...stdioConfig
671
+ } = config;
672
+ return stdioConfig;
673
+ };
674
+ var detectUpdateTransition = (incoming, previous) => {
675
+ if (!previous) {
676
+ return incoming.url ? "switch-to-remote" : "switch-to-stdio";
677
+ }
678
+ const previousIsRemote = Boolean(previous.url && previous.url.length > 0);
679
+ if (previousIsRemote) {
680
+ if (incoming.command && !incoming.url) {
681
+ return "switch-to-stdio";
682
+ }
683
+ return "merge-remote";
684
+ }
685
+ if (incoming.url && !incoming.command) {
686
+ return "switch-to-remote";
687
+ }
688
+ return "merge-stdio";
689
+ };
690
+ var sanitizeUpdatedServerConfig = (incoming, previous) => {
691
+ const transition = detectUpdateTransition(incoming, previous);
692
+ const targetTransport = incoming.type ?? previous?.type ?? "http";
693
+ switch (transition) {
694
+ case "switch-to-remote": {
695
+ const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
696
+ return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
697
+ }
698
+ case "switch-to-stdio": {
699
+ const cleanBase = previous ? toStdioServerConfig(previous) : {};
700
+ return toStdioServerConfig({ ...cleanBase, ...incoming });
701
+ }
702
+ case "merge-remote": {
703
+ return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
704
+ }
705
+ case "merge-stdio":
706
+ default: {
707
+ return toStdioServerConfig({ ...previous, ...incoming });
708
+ }
709
+ }
710
+ };
711
+ var applyServerConfigDelta = (previousConfig, options) => {
712
+ if (options.url !== void 0 && options.command !== void 0) {
713
+ throw new Error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
714
+ }
715
+ const isCurrentRemote = Boolean(previousConfig?.url && previousConfig.url.length > 0);
716
+ const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
717
+ const protocol = willBeRemote ? "remote" : "stdio";
718
+ const ignoredFlags = [];
719
+ if (willBeRemote) {
720
+ if (options.env !== void 0) ignoredFlags.push("--env");
721
+ if (options.clearEnv) ignoredFlags.push("--clear-env");
722
+ if (options.args !== void 0) ignoredFlags.push("--args");
723
+ if (options.clearArgs) ignoredFlags.push("--clear-args");
724
+ } else {
725
+ if (options.headers !== void 0) ignoredFlags.push("--header");
726
+ if (options.clearHeaders) ignoredFlags.push("--clear-headers");
727
+ if (options.transport !== void 0) ignoredFlags.push("--transport");
728
+ }
729
+ const incomingDelta = {};
730
+ if (options.command !== void 0) {
731
+ incomingDelta.command = options.command;
732
+ }
733
+ if (options.clearArgs) {
734
+ incomingDelta.args = void 0;
735
+ }
736
+ if (options.args !== void 0) {
737
+ incomingDelta.args = options.args;
738
+ }
739
+ if (options.url !== void 0) {
740
+ incomingDelta.url = options.url;
741
+ }
742
+ if (options.transport !== void 0) {
743
+ incomingDelta.type = options.transport;
744
+ }
745
+ if (options.clearEnv) {
746
+ incomingDelta.env = void 0;
747
+ }
748
+ if (options.env !== void 0) {
749
+ const baseEnv = options.clearEnv ? {} : previousConfig?.env ?? {};
750
+ incomingDelta.env = { ...baseEnv, ...options.env };
751
+ }
752
+ if (options.clearHeaders) {
753
+ incomingDelta.headers = void 0;
754
+ }
755
+ if (options.headers !== void 0) {
756
+ const baseHeaders = options.clearHeaders ? {} : previousConfig?.headers ?? {};
757
+ incomingDelta.headers = { ...baseHeaders, ...options.headers };
758
+ }
759
+ const transition = detectUpdateTransition(incomingDelta, previousConfig);
760
+ const cleanConfig = sanitizeUpdatedServerConfig(incomingDelta, previousConfig);
761
+ return {
762
+ config: cleanConfig,
763
+ transition,
764
+ protocol,
765
+ ignoredFlags
766
+ };
767
+ };
768
+
769
+ // src/source-parser.ts
770
+ var REMOTE_URL_REGEX = /^https?:\/\//i;
771
+ var HAS_WHITESPACE_REGEX = /\s/;
772
+ var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
773
+ var PATH_SEPARATOR_REGEX = /[/\\]/;
774
+ var stripVersionSuffix = (input) => {
775
+ if (input.startsWith("@")) {
776
+ const secondAtIndex = input.indexOf("@", 1);
777
+ if (secondAtIndex > 0) return input.slice(0, secondAtIndex);
778
+ return input;
779
+ }
780
+ const atIndex = input.lastIndexOf("@");
781
+ if (atIndex > 0) return input.slice(0, atIndex);
782
+ return input;
783
+ };
784
+ var stripScopePrefix = (input) => {
785
+ if (!input.startsWith("@") || !input.includes("/")) return input;
786
+ const parts = input.split("/");
787
+ return parts[1] || input;
788
+ };
789
+ var stripPathPrefix = (input) => {
790
+ if (!PATH_SEPARATOR_REGEX.test(input)) return input;
791
+ const segments = input.split(PATH_SEPARATOR_REGEX);
792
+ const basename = segments[segments.length - 1];
793
+ return basename || input;
794
+ };
795
+ var extractPackageName = (input) => {
796
+ let name = stripVersionSuffix(input);
797
+ name = stripScopePrefix(name);
798
+ name = stripPathPrefix(name);
799
+ name = name.replace(SCRIPT_EXTENSION_REGEX, "");
800
+ for (const prefix of PACKAGE_NAME_PREFIX_STRIP) {
801
+ if (name.startsWith(prefix)) {
802
+ name = name.slice(prefix.length);
803
+ break;
804
+ }
805
+ }
806
+ for (const suffix of PACKAGE_NAME_SUFFIX_STRIP) {
807
+ if (name.endsWith(suffix)) {
808
+ name = name.slice(0, -suffix.length);
809
+ break;
810
+ }
811
+ }
812
+ return name || MCP_DEFAULT_SERVER_NAME;
813
+ };
814
+ var inferNameFromUrl = (input) => {
815
+ try {
816
+ const url = new URL(input);
817
+ const host = url.hostname;
818
+ const labels = host.split(".").filter((segment) => segment.length > 0);
819
+ if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
820
+ const meaningfulLabels = labels.filter((label) => {
821
+ const lower = label.toLowerCase();
822
+ if (COMMON_TLD_LABELS.has(lower)) return false;
823
+ if (GENERIC_HOST_PREFIXES.has(lower)) return false;
824
+ return true;
825
+ });
826
+ if (meaningfulLabels.length > 0) return meaningfulLabels[0];
827
+ if (labels.length >= 2) return labels[labels.length - 2];
828
+ return labels[labels.length - 1] || MCP_DEFAULT_SERVER_NAME;
829
+ } catch {
830
+ return MCP_DEFAULT_SERVER_NAME;
831
+ }
832
+ };
833
+ var inferNameFromCommand = (command) => {
834
+ const tokens = command.trim().split(/\s+/);
835
+ const runnerBase = tokens[0]?.split(PATH_SEPARATOR_REGEX).pop() ?? "";
836
+ const startIndex = KNOWN_COMMAND_RUNNERS.has(runnerBase) ? 1 : 0;
837
+ for (let tokenIndex = startIndex; tokenIndex < tokens.length; tokenIndex += 1) {
838
+ const token = tokens[tokenIndex];
839
+ if (!token || token.startsWith("-")) continue;
840
+ return extractPackageName(token);
841
+ }
842
+ const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
843
+ return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
844
+ };
845
+ var parseMcpSource = (input) => {
846
+ const trimmed = input.trim();
847
+ if (trimmed.length === 0) {
848
+ throw new Error(
849
+ "Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
850
+ );
851
+ }
852
+ if (REMOTE_URL_REGEX.test(trimmed)) {
853
+ return {
854
+ type: "remote",
855
+ value: trimmed,
856
+ inferredName: inferNameFromUrl(trimmed)
857
+ };
858
+ }
859
+ if (HAS_WHITESPACE_REGEX.test(trimmed)) {
860
+ return {
861
+ type: "command",
862
+ value: trimmed,
863
+ inferredName: inferNameFromCommand(trimmed)
864
+ };
865
+ }
866
+ if (PACKAGE_NAME_REGEX.test(trimmed)) {
867
+ return {
868
+ type: "package",
869
+ value: trimmed,
870
+ inferredName: extractPackageName(trimmed)
871
+ };
872
+ }
873
+ return {
874
+ type: "command",
875
+ value: trimmed,
876
+ inferredName: inferNameFromCommand(trimmed)
877
+ };
878
+ };
879
+ var isRemoteMcpSource = (parsed) => parsed.type === "remote";
880
+
881
+ // src/config-store.ts
882
+ var import_node_fs6 = require("fs");
883
+ var import_node_path3 = require("path");
696
884
 
697
885
  // src/utils/is-plain-object.ts
698
886
  var isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
@@ -977,220 +1165,45 @@ var listServersInConfigFile = (filePath, format, dottedKey) => {
977
1165
  return isPlainObject(entries) ? entries : {};
978
1166
  };
979
1167
 
980
- // src/config-store.ts
981
- var import_node_fs6 = require("fs");
982
-
983
- // src/resolve-config-target.ts
984
- var import_node_path3 = require("path");
985
- var resolveMcpConfigTarget = (agent, options = {}) => {
986
- const isGlobal = options.global ?? false;
987
- const cwd = options.cwd ?? process.cwd();
988
- const configPath = agent.resolveConfigPath ? agent.resolveConfigPath({ global: isGlobal, cwd }) : !isGlobal && agent.projectConfigPath ? (0, import_node_path3.join)(cwd, agent.projectConfigPath) : agent.globalConfigPath;
989
- const configKey = !isGlobal && agent.projectConfigKey ? agent.projectConfigKey : agent.configKey;
990
- return { configPath, configKey };
991
- };
992
-
993
- // src/config-store.ts
994
- var FsConfigStoreAdapter = class {
995
- exists(filePath) {
996
- return (0, import_node_fs6.existsSync)(filePath);
997
- }
998
- read(target) {
999
- return readConfigFile(target.filePath, target.format);
1000
- }
1001
- writeServer(target, serverName, serverConfig) {
1002
- if (!target.dottedKey) {
1003
- throw new Error(`Cannot write server: missing dottedKey for ${target.filePath}`);
1004
- }
1005
- writeServerToConfigFile(
1006
- target.filePath,
1007
- target.format,
1008
- target.dottedKey,
1009
- serverName,
1010
- serverConfig
1011
- );
1012
- }
1013
- removeServer(target, serverName) {
1014
- if (!target.dottedKey) return false;
1015
- return removeServerFromConfigFile(
1016
- target.filePath,
1017
- target.format,
1018
- target.dottedKey,
1019
- serverName
1020
- );
1021
- }
1022
- listServers(target) {
1023
- if (!target.dottedKey) return {};
1024
- return listServersInConfigFile(target.filePath, target.format, target.dottedKey);
1025
- }
1026
- };
1027
- var AgentConfigStore = class {
1028
- constructor(adapter = new FsConfigStoreAdapter()) {
1029
- this.adapter = adapter;
1030
- }
1031
- adapter;
1032
- getAdapter() {
1033
- return this.adapter;
1034
- }
1035
- resolveTarget(agent, options = {}) {
1036
- const agentConfig = typeof agent === "string" ? getMcpAgentConfig(agent) : agent;
1037
- const target = resolveMcpConfigTarget(agentConfig, options);
1038
- return { agent: agentConfig, target };
1039
- }
1040
- resolveDescriptor(agent, options = {}) {
1041
- const { agent: agentConfig, target } = this.resolveTarget(agent, options);
1042
- return {
1043
- filePath: target.configPath,
1044
- format: agentConfig.format,
1045
- dottedKey: target.configKey
1046
- };
1047
- }
1048
- writeServer(agent, serverName, serverConfig, options = {}) {
1049
- const descriptor = this.resolveDescriptor(agent, options);
1050
- this.adapter.writeServer(descriptor, serverName, serverConfig);
1051
- return { path: descriptor.filePath };
1052
- }
1053
- removeServer(agent, serverName, options = {}) {
1054
- const descriptor = this.resolveDescriptor(agent, options);
1055
- if (!this.adapter.exists(descriptor.filePath)) {
1056
- return { path: descriptor.filePath, removed: false };
1057
- }
1058
- const removed = this.adapter.removeServer(descriptor, serverName);
1059
- return { path: descriptor.filePath, removed };
1060
- }
1061
- listServers(agent, options = {}) {
1062
- const descriptor = this.resolveDescriptor(agent, options);
1063
- if (!this.adapter.exists(descriptor.filePath)) {
1064
- return { path: descriptor.filePath, exists: false, servers: {} };
1065
- }
1066
- const servers = this.adapter.listServers(descriptor);
1067
- return { path: descriptor.filePath, exists: true, servers };
1068
- }
1069
- read(agent, options = {}) {
1070
- const descriptor = this.resolveDescriptor(agent, options);
1071
- if (!this.adapter.exists(descriptor.filePath)) {
1072
- return {};
1073
- }
1074
- return this.adapter.read(descriptor);
1075
- }
1076
- readServer(agent, serverName, options = {}) {
1077
- const { exists, servers } = this.listServers(agent, options);
1078
- if (!exists) return void 0;
1079
- return servers[serverName];
1080
- }
1081
- };
1082
- var agentConfigStore = new AgentConfigStore();
1083
-
1084
- // src/utils/to-error-message.ts
1085
- var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
1086
-
1087
- // src/resolve-config-clusters.ts
1088
- var getCandidateAgentsForScope = (options = {}) => {
1089
- return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
1090
- };
1091
- var getCoHostedAgents = (agentType, options = {}) => {
1092
- const currentAgent = getMcpAgentConfig(agentType);
1093
- const currentTarget = resolveMcpConfigTarget(currentAgent, options);
1094
- const candidates = getCandidateAgentsForScope(options);
1095
- const coHosted = [];
1096
- for (const candidateType of candidates) {
1097
- if (candidateType === agentType) continue;
1098
- const candidateConfig = getMcpAgentConfig(candidateType);
1099
- const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
1100
- if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
1101
- coHosted.push(candidateType);
1102
- }
1103
- }
1104
- return coHosted;
1105
- };
1106
- var resolveConfigClusters = (agentTypes, options = {}) => {
1107
- const clustersByPath = /* @__PURE__ */ new Map();
1108
- for (const agentType of agentTypes) {
1109
- const agentConfig = getMcpAgentConfig(agentType);
1110
- const target = resolveMcpConfigTarget(agentConfig, options);
1111
- let keyMap = clustersByPath.get(target.configPath);
1112
- if (!keyMap) {
1113
- keyMap = /* @__PURE__ */ new Map();
1114
- clustersByPath.set(target.configPath, keyMap);
1115
- }
1116
- let cluster = keyMap.get(target.configKey);
1117
- if (!cluster) {
1118
- const allCoHosted = getCoHostedAgents(agentType, options);
1119
- cluster = {
1120
- configPath: target.configPath,
1121
- configKey: target.configKey,
1122
- targetAgents: [],
1123
- coHostedAgents: allCoHosted
1124
- };
1125
- keyMap.set(target.configKey, cluster);
1126
- }
1127
- if (!cluster.targetAgents.includes(agentType)) {
1128
- cluster.targetAgents.push(agentType);
1129
- }
1130
- }
1131
- const clusters = [];
1132
- for (const keyMap of clustersByPath.values()) {
1133
- for (const cluster of keyMap.values()) {
1134
- cluster.coHostedAgents = cluster.coHostedAgents.filter(
1135
- (co) => !cluster.targetAgents.includes(co)
1136
- );
1137
- clusters.push(cluster);
1138
- }
1139
- }
1140
- return clusters;
1141
- };
1142
- var sortAgentsWithClusters = (agentTypes, options = {}) => {
1143
- const clusters = resolveConfigClusters(agentTypes, options);
1144
- const sorted = [];
1145
- for (const cluster of clusters) {
1146
- for (const agent of cluster.targetAgents) {
1147
- if (!sorted.includes(agent)) {
1148
- sorted.push(agent);
1149
- }
1150
- }
1151
- }
1152
- return sorted;
1153
- };
1154
-
1155
- // src/transforms/index.ts
1156
- var DIALECT_PRESETS = {
1157
- vscode: {
1158
- stdioTransport: "type-stdio",
1159
- remoteTransport: "type-http-sse"
1160
- },
1161
- augment: {
1162
- stdioTransport: "none",
1163
- remoteTransport: "type-http-sse"
1164
- },
1165
- amp: {
1166
- stdioTransport: "none",
1167
- remoteTransport: "none"
1168
- },
1169
- trae: {
1170
- stdioTransport: "none",
1171
- remoteTransport: "sse-only-type"
1172
- },
1173
- grok: {
1174
- stdioTransport: "none",
1175
- remoteTransport: "sse-only-type"
1176
- },
1177
- cline: {
1178
- stdioTransport: "none",
1179
- remoteTransport: "streamableHttp"
1180
- },
1181
- goose: {
1182
- stdioTransport: "type-stdio",
1183
- remoteTransport: "streamable_http",
1184
- commandField: "cmd",
1185
- envField: "envs",
1186
- urlField: "uri",
1187
- defaultEnvEmpty: true,
1188
- defaultHeadersEmpty: true,
1189
- includeServerName: true,
1190
- timeoutSeconds: GOOSE_TIMEOUT_SECONDS,
1191
- extraFields: {
1192
- description: "",
1193
- enabled: true
1168
+ // src/transforms/index.ts
1169
+ var DIALECT_PRESETS = {
1170
+ vscode: {
1171
+ stdioTransport: "type-stdio",
1172
+ remoteTransport: "type-http-sse"
1173
+ },
1174
+ augment: {
1175
+ stdioTransport: "none",
1176
+ remoteTransport: "type-http-sse"
1177
+ },
1178
+ amp: {
1179
+ stdioTransport: "none",
1180
+ remoteTransport: "none"
1181
+ },
1182
+ trae: {
1183
+ stdioTransport: "none",
1184
+ remoteTransport: "sse-only-type"
1185
+ },
1186
+ grok: {
1187
+ stdioTransport: "none",
1188
+ remoteTransport: "sse-only-type"
1189
+ },
1190
+ cline: {
1191
+ stdioTransport: "none",
1192
+ remoteTransport: "streamableHttp"
1193
+ },
1194
+ goose: {
1195
+ stdioTransport: "type-stdio",
1196
+ remoteTransport: "streamable_http",
1197
+ commandField: "cmd",
1198
+ envField: "envs",
1199
+ urlField: "uri",
1200
+ defaultEnvEmpty: true,
1201
+ defaultHeadersEmpty: true,
1202
+ includeServerName: true,
1203
+ timeoutSeconds: GOOSE_TIMEOUT_SECONDS,
1204
+ extraFields: {
1205
+ description: "",
1206
+ enabled: true
1194
1207
  }
1195
1208
  },
1196
1209
  "kimi-code": {
@@ -1362,115 +1375,323 @@ var transformServerConfigForAgent = (agent, serverName, config, context = { glob
1362
1375
  return config;
1363
1376
  };
1364
1377
 
1365
- // src/installer.ts
1366
- var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {}) => {
1367
- const agent = getMcpAgentConfig(agentType);
1378
+ // src/utils/to-error-message.ts
1379
+ var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
1380
+
1381
+ // src/config-store.ts
1382
+ var resolveMcpConfigTarget = (agent, options = {}) => {
1368
1383
  const isGlobal = options.global ?? false;
1369
- const { target } = agentConfigStore.resolveTarget(agent, options);
1370
- const coHosted = getCoHostedAgents(agentType, options);
1371
- try {
1372
- const transformed = transformServerConfigForAgent(agent, serverName, serverConfig, {
1373
- global: isGlobal
1374
- });
1375
- agentConfigStore.writeServer(agent, serverName, transformed, options);
1376
- return {
1377
- agent: agentType,
1378
- success: true,
1379
- path: target.configPath,
1380
- coConfiguredAgents: coHosted.length > 0 ? coHosted : void 0
1381
- };
1382
- } catch (error) {
1384
+ const cwd = options.cwd ?? process.cwd();
1385
+ const configPath = agent.resolveConfigPath ? agent.resolveConfigPath({ global: isGlobal, cwd }) : !isGlobal && agent.projectConfigPath ? (0, import_node_path3.join)(cwd, agent.projectConfigPath) : agent.globalConfigPath;
1386
+ const configKey = !isGlobal && agent.projectConfigKey ? agent.projectConfigKey : agent.configKey;
1387
+ return { configPath, configKey };
1388
+ };
1389
+ var getCandidateAgentsForScope = (options = {}) => {
1390
+ return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
1391
+ };
1392
+ var FsConfigStoreAdapter = class {
1393
+ exists(filePath) {
1394
+ return (0, import_node_fs6.existsSync)(filePath);
1395
+ }
1396
+ read(target) {
1397
+ return readConfigFile(target.filePath, target.format);
1398
+ }
1399
+ writeServer(target, serverName, serverConfig) {
1400
+ if (!target.dottedKey) {
1401
+ throw new Error(`Cannot write server: missing dottedKey for ${target.filePath}`);
1402
+ }
1403
+ writeServerToConfigFile(
1404
+ target.filePath,
1405
+ target.format,
1406
+ target.dottedKey,
1407
+ serverName,
1408
+ serverConfig
1409
+ );
1410
+ }
1411
+ removeServer(target, serverName) {
1412
+ if (!target.dottedKey) return false;
1413
+ return removeServerFromConfigFile(
1414
+ target.filePath,
1415
+ target.format,
1416
+ target.dottedKey,
1417
+ serverName
1418
+ );
1419
+ }
1420
+ listServers(target) {
1421
+ if (!target.dottedKey) return {};
1422
+ return listServersInConfigFile(target.filePath, target.format, target.dottedKey);
1423
+ }
1424
+ };
1425
+ var AgentConfigStore = class {
1426
+ constructor(adapter = new FsConfigStoreAdapter()) {
1427
+ this.adapter = adapter;
1428
+ }
1429
+ adapter;
1430
+ getAdapter() {
1431
+ return this.adapter;
1432
+ }
1433
+ // ---- Single-agent primitives ----
1434
+ resolveTarget(agent, options = {}) {
1435
+ const agentConfig = typeof agent === "string" ? getMcpAgentConfig(agent) : agent;
1436
+ const target = resolveMcpConfigTarget(agentConfig, options);
1437
+ return { agent: agentConfig, target };
1438
+ }
1439
+ resolveDescriptor(agent, options = {}) {
1440
+ const { agent: agentConfig, target } = this.resolveTarget(agent, options);
1383
1441
  return {
1384
- agent: agentType,
1385
- success: false,
1386
- path: target.configPath,
1387
- error: toErrorMessage(error)
1442
+ filePath: target.configPath,
1443
+ format: agentConfig.format,
1444
+ dottedKey: target.configKey
1388
1445
  };
1389
1446
  }
1390
- };
1391
- var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => {
1392
- const clusters = resolveConfigClusters(agentTypes, options);
1393
- const resultsByAgent = /* @__PURE__ */ new Map();
1394
- const isGlobal = options.global ?? false;
1395
- for (const cluster of clusters) {
1396
- const primaryAgentType = cluster.targetAgents[0];
1397
- const primaryAgent = getMcpAgentConfig(primaryAgentType);
1398
- try {
1399
- const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
1400
- global: isGlobal
1401
- });
1402
- agentConfigStore.writeServer(primaryAgent, serverName, transformed, options);
1403
- for (const agentType of cluster.targetAgents) {
1404
- resultsByAgent.set(agentType, {
1405
- agent: agentType,
1406
- success: true,
1407
- path: cluster.configPath,
1408
- coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1409
- });
1410
- }
1411
- } catch (error) {
1412
- const errorMsg = toErrorMessage(error);
1413
- for (const agentType of cluster.targetAgents) {
1414
- resultsByAgent.set(agentType, {
1415
- agent: agentType,
1416
- success: false,
1417
- path: cluster.configPath,
1418
- error: errorMsg
1419
- });
1420
- }
1447
+ writeServer(agent, serverName, serverConfig, options = {}) {
1448
+ const descriptor = this.resolveDescriptor(agent, options);
1449
+ this.adapter.writeServer(descriptor, serverName, serverConfig);
1450
+ return { path: descriptor.filePath };
1451
+ }
1452
+ removeServer(agent, serverName, options = {}) {
1453
+ const descriptor = this.resolveDescriptor(agent, options);
1454
+ if (!this.adapter.exists(descriptor.filePath)) {
1455
+ return { path: descriptor.filePath, removed: false };
1421
1456
  }
1457
+ const removed = this.adapter.removeServer(descriptor, serverName);
1458
+ return { path: descriptor.filePath, removed };
1422
1459
  }
1423
- return agentTypes.map((agentType) => resultsByAgent.get(agentType));
1424
- };
1425
- var installToCompatibleAgents = (serverName, serverConfig, options) => {
1426
- const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
1427
- const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1428
- const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
1429
- const installedResults = installMcpServerForAgents(serverName, serverConfig, compatibleAgents, {
1430
- global: isGlobal,
1431
- cwd
1432
- });
1433
- const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
1434
- return allAgents.map((agentType) => {
1435
- const incompatibleReason = incompatibleMap.get(agentType);
1436
- if (incompatibleReason) {
1460
+ listServers(agent, options = {}) {
1461
+ const descriptor = this.resolveDescriptor(agent, options);
1462
+ if (!this.adapter.exists(descriptor.filePath)) {
1463
+ return { path: descriptor.filePath, exists: false, servers: {} };
1464
+ }
1465
+ const servers = this.adapter.listServers(descriptor);
1466
+ return { path: descriptor.filePath, exists: true, servers };
1467
+ }
1468
+ /**
1469
+ * Batch lists servers for multiple agents, caching adapter reads for co-hosted
1470
+ * agents that share the exact same physical configuration file and dotted key.
1471
+ */
1472
+ listServersForAgents(agents, options = {}) {
1473
+ const readCache = /* @__PURE__ */ new Map();
1474
+ return agents.map((agentType) => {
1475
+ const descriptor = this.resolveDescriptor(agentType, options);
1476
+ const cacheKey = `${descriptor.filePath}::${descriptor.dottedKey ?? ""}`;
1477
+ let cached = readCache.get(cacheKey);
1478
+ if (!cached) {
1479
+ cached = this.listServers(agentType, options);
1480
+ readCache.set(cacheKey, cached);
1481
+ }
1437
1482
  return {
1438
1483
  agent: agentType,
1439
- success: false,
1440
- path: "",
1441
- error: incompatibleReason
1484
+ path: cached.path,
1485
+ exists: cached.exists,
1486
+ servers: cached.servers
1442
1487
  };
1488
+ });
1489
+ }
1490
+ read(agent, options = {}) {
1491
+ const descriptor = this.resolveDescriptor(agent, options);
1492
+ if (!this.adapter.exists(descriptor.filePath)) {
1493
+ return {};
1443
1494
  }
1444
- return installedMap.get(agentType);
1445
- });
1446
- };
1447
-
1448
- // src/utils/parse-mcp-agent-list.ts
1449
- var parseMcpAgentList = (input7) => {
1450
- if (!input7 || input7.length === 0) return void 0;
1451
- if (input7.includes("*")) return getMcpAgentTypes();
1452
- const resolved = [];
1453
- for (const value of input7) {
1454
- const agentType = resolveMcpAgentAlias(value);
1455
- if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
1456
- resolved.push(agentType);
1495
+ return this.adapter.read(descriptor);
1457
1496
  }
1458
- return resolved;
1459
- };
1460
-
1461
- // src/resolve-target-agents.ts
1462
- var normalizeRequestedAgents = (input7) => {
1463
- if (!input7 || input7.length === 0) return void 0;
1464
- const rawList = [...input7];
1465
- if (rawList.every((item) => isMcpAgentType(item))) {
1466
- return rawList;
1497
+ readServer(agent, serverName, options = {}) {
1498
+ const { exists, servers } = this.listServers(agent, options);
1499
+ if (!exists) return void 0;
1500
+ return servers[serverName];
1467
1501
  }
1468
- return parseMcpAgentList(rawList);
1469
- };
1470
- var resolveTargetAgents = (query = {}) => {
1471
- const cwd = query.cwd ?? process.cwd();
1472
- const isGlobal = query.global ?? false;
1473
- let explicitAgents = normalizeRequestedAgents(query.requested);
1502
+ // ---- Cluster & co-hosted awareness ----
1503
+ /**
1504
+ * Returns all other agents sharing the exact same physical configuration target
1505
+ * (same configPath and configKey) for the given scope.
1506
+ */
1507
+ getCoHostedAgents(agentType, options = {}) {
1508
+ const currentAgent = getMcpAgentConfig(agentType);
1509
+ const currentTarget = resolveMcpConfigTarget(currentAgent, options);
1510
+ const candidates = getCandidateAgentsForScope(options);
1511
+ const coHosted = [];
1512
+ for (const candidateType of candidates) {
1513
+ if (candidateType === agentType) continue;
1514
+ const candidateConfig = getMcpAgentConfig(candidateType);
1515
+ const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
1516
+ if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
1517
+ coHosted.push(candidateType);
1518
+ }
1519
+ }
1520
+ return coHosted;
1521
+ }
1522
+ /**
1523
+ * Resolves configuration clusters for a given list of requested agents.
1524
+ * Groups requested agents sharing the same physical config path, and tracks
1525
+ * any remaining co-hosted agents sharing the same target that were not in the request.
1526
+ */
1527
+ resolveConfigClusters(agentTypes, options = {}) {
1528
+ const clustersByPath = /* @__PURE__ */ new Map();
1529
+ for (const agentType of agentTypes) {
1530
+ const agentConfig = getMcpAgentConfig(agentType);
1531
+ const target = resolveMcpConfigTarget(agentConfig, options);
1532
+ let keyMap = clustersByPath.get(target.configPath);
1533
+ if (!keyMap) {
1534
+ keyMap = /* @__PURE__ */ new Map();
1535
+ clustersByPath.set(target.configPath, keyMap);
1536
+ }
1537
+ let cluster = keyMap.get(target.configKey);
1538
+ if (!cluster) {
1539
+ const allCoHosted = this.getCoHostedAgents(agentType, options);
1540
+ cluster = {
1541
+ configPath: target.configPath,
1542
+ configKey: target.configKey,
1543
+ targetAgents: [],
1544
+ coHostedAgents: allCoHosted
1545
+ };
1546
+ keyMap.set(target.configKey, cluster);
1547
+ }
1548
+ if (!cluster.targetAgents.includes(agentType)) {
1549
+ cluster.targetAgents.push(agentType);
1550
+ }
1551
+ }
1552
+ const clusters = [];
1553
+ for (const keyMap of clustersByPath.values()) {
1554
+ for (const cluster of keyMap.values()) {
1555
+ cluster.coHostedAgents = cluster.coHostedAgents.filter(
1556
+ (co) => !cluster.targetAgents.includes(co)
1557
+ );
1558
+ clusters.push(cluster);
1559
+ }
1560
+ }
1561
+ return clusters;
1562
+ }
1563
+ /**
1564
+ * Sorts agent types so that agents sharing the same physical config target
1565
+ * appear adjacent to each other in the returned array.
1566
+ */
1567
+ sortAgentsByClusters(agentTypes, options = {}) {
1568
+ const clusters = this.resolveConfigClusters(agentTypes, options);
1569
+ const sorted = [];
1570
+ for (const cluster of clusters) {
1571
+ for (const agent of cluster.targetAgents) {
1572
+ if (!sorted.includes(agent)) {
1573
+ sorted.push(agent);
1574
+ }
1575
+ }
1576
+ }
1577
+ return sorted;
1578
+ }
1579
+ /**
1580
+ * Alias for sortAgentsByClusters for backward compatibility.
1581
+ */
1582
+ sortAgentsWithClusters(agentTypes, options = {}) {
1583
+ return this.sortAgentsByClusters(agentTypes, options);
1584
+ }
1585
+ // ---- Batch operations with automatic clustering & dialect transforms ----
1586
+ /**
1587
+ * Batch write: transforms and writes a server config to multiple agents,
1588
+ * with automatic cluster deduplication, dialect transform, and co-hosted awareness.
1589
+ * Callers pass standard McpServerConfig; the Store applies per-Agent dialect transforms
1590
+ * internally before persisting.
1591
+ */
1592
+ writeServers(agents, serverName, serverConfig, options = {}) {
1593
+ const clusters = this.resolveConfigClusters(agents, options);
1594
+ const resultsByAgent = /* @__PURE__ */ new Map();
1595
+ const isGlobal = options.global ?? false;
1596
+ for (const cluster of clusters) {
1597
+ const primaryAgentType = cluster.targetAgents[0];
1598
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1599
+ try {
1600
+ const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
1601
+ global: isGlobal
1602
+ });
1603
+ const descriptor = this.resolveDescriptor(primaryAgent, options);
1604
+ this.adapter.writeServer(descriptor, serverName, transformed);
1605
+ for (const agentType of cluster.targetAgents) {
1606
+ resultsByAgent.set(agentType, {
1607
+ agent: agentType,
1608
+ success: true,
1609
+ path: cluster.configPath,
1610
+ coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1611
+ });
1612
+ }
1613
+ } catch (error) {
1614
+ const errorMsg = toErrorMessage(error);
1615
+ for (const agentType of cluster.targetAgents) {
1616
+ resultsByAgent.set(agentType, {
1617
+ agent: agentType,
1618
+ success: false,
1619
+ path: cluster.configPath,
1620
+ error: errorMsg
1621
+ });
1622
+ }
1623
+ }
1624
+ }
1625
+ return agents.map((agentType) => resultsByAgent.get(agentType));
1626
+ }
1627
+ /**
1628
+ * Batch remove: removes a server from multiple agents' configs,
1629
+ * with automatic cluster deduplication and co-hosted awareness.
1630
+ */
1631
+ removeServers(agents, serverName, options = {}) {
1632
+ const clusters = this.resolveConfigClusters(agents, options);
1633
+ const resultsByAgent = /* @__PURE__ */ new Map();
1634
+ for (const cluster of clusters) {
1635
+ const primaryAgentType = cluster.targetAgents[0];
1636
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1637
+ try {
1638
+ const { removed } = this.removeServer(primaryAgent, serverName, options);
1639
+ for (const agentType of cluster.targetAgents) {
1640
+ resultsByAgent.set(agentType, {
1641
+ agent: agentType,
1642
+ path: cluster.configPath,
1643
+ removed,
1644
+ coAffectedAgents: removed && cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1645
+ });
1646
+ }
1647
+ } catch (error) {
1648
+ const errorMsg = toErrorMessage(error);
1649
+ for (const agentType of cluster.targetAgents) {
1650
+ resultsByAgent.set(agentType, {
1651
+ agent: agentType,
1652
+ path: cluster.configPath,
1653
+ removed: false,
1654
+ error: errorMsg
1655
+ });
1656
+ }
1657
+ }
1658
+ }
1659
+ return agents.map((agentType) => resultsByAgent.get(agentType));
1660
+ }
1661
+ };
1662
+ var agentConfigStore = new AgentConfigStore();
1663
+
1664
+ // src/resolve-config-clusters.ts
1665
+ var getCoHostedAgents = (agentType, options = {}) => agentConfigStore.getCoHostedAgents(agentType, options);
1666
+ var resolveConfigClusters = (agentTypes, options = {}) => agentConfigStore.resolveConfigClusters(agentTypes, options);
1667
+ var sortAgentsByClusters = (agentTypes, options = {}) => agentConfigStore.sortAgentsByClusters(agentTypes, options);
1668
+
1669
+ // src/utils/parse-mcp-agent-list.ts
1670
+ var parseMcpAgentList = (input) => {
1671
+ if (!input || input.length === 0) return void 0;
1672
+ if (input.includes("*")) return getMcpAgentTypes();
1673
+ const resolved = [];
1674
+ for (const value of input) {
1675
+ const agentType = resolveMcpAgentAlias(value);
1676
+ if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
1677
+ resolved.push(agentType);
1678
+ }
1679
+ return resolved;
1680
+ };
1681
+
1682
+ // src/resolve-target-agents.ts
1683
+ var normalizeRequestedAgents = (input) => {
1684
+ if (!input || input.length === 0) return void 0;
1685
+ const rawList = [...input];
1686
+ if (rawList.every((item) => isMcpAgentType(item))) {
1687
+ return rawList;
1688
+ }
1689
+ return parseMcpAgentList(rawList);
1690
+ };
1691
+ var resolveTargetAgents = (query = {}) => {
1692
+ const cwd = query.cwd ?? process.cwd();
1693
+ const isGlobal = query.global ?? false;
1694
+ let explicitAgents = normalizeRequestedAgents(query.requested);
1474
1695
  if (query.all) {
1475
1696
  explicitAgents = getMcpAgentTypes();
1476
1697
  }
@@ -1516,117 +1737,41 @@ var resolveTargetAgents = (query = {}) => {
1516
1737
  };
1517
1738
  };
1518
1739
 
1519
- // src/source-parser.ts
1520
- var REMOTE_URL_REGEX = /^https?:\/\//i;
1521
- var HAS_WHITESPACE_REGEX = /\s/;
1522
- var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
1523
- var PATH_SEPARATOR_REGEX = /[/\\]/;
1524
- var stripVersionSuffix = (input7) => {
1525
- if (input7.startsWith("@")) {
1526
- const secondAtIndex = input7.indexOf("@", 1);
1527
- if (secondAtIndex > 0) return input7.slice(0, secondAtIndex);
1528
- return input7;
1529
- }
1530
- const atIndex = input7.lastIndexOf("@");
1531
- if (atIndex > 0) return input7.slice(0, atIndex);
1532
- return input7;
1533
- };
1534
- var stripScopePrefix = (input7) => {
1535
- if (!input7.startsWith("@") || !input7.includes("/")) return input7;
1536
- const parts = input7.split("/");
1537
- return parts[1] || input7;
1538
- };
1539
- var stripPathPrefix = (input7) => {
1540
- if (!PATH_SEPARATOR_REGEX.test(input7)) return input7;
1541
- const segments = input7.split(PATH_SEPARATOR_REGEX);
1542
- const basename = segments[segments.length - 1];
1543
- return basename || input7;
1740
+ // src/install-compat.ts
1741
+ var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {}) => {
1742
+ const results = agentConfigStore.writeServers([agentType], serverName, serverConfig, options);
1743
+ return results[0];
1544
1744
  };
1545
- var extractPackageName = (input7) => {
1546
- let name = stripVersionSuffix(input7);
1547
- name = stripScopePrefix(name);
1548
- name = stripPathPrefix(name);
1549
- name = name.replace(SCRIPT_EXTENSION_REGEX, "");
1550
- for (const prefix of PACKAGE_NAME_PREFIX_STRIP) {
1551
- if (name.startsWith(prefix)) {
1552
- name = name.slice(prefix.length);
1553
- break;
1745
+ var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => {
1746
+ return agentConfigStore.writeServers(agentTypes, serverName, serverConfig, options);
1747
+ };
1748
+ var installToCompatibleAgents = (serverName, serverConfig, options) => {
1749
+ const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
1750
+ const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1751
+ const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
1752
+ const installedResults = agentConfigStore.writeServers(
1753
+ compatibleAgents,
1754
+ serverName,
1755
+ serverConfig,
1756
+ {
1757
+ global: isGlobal,
1758
+ cwd
1554
1759
  }
1555
- }
1556
- for (const suffix of PACKAGE_NAME_SUFFIX_STRIP) {
1557
- if (name.endsWith(suffix)) {
1558
- name = name.slice(0, -suffix.length);
1559
- break;
1760
+ );
1761
+ const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
1762
+ return allAgents.map((agentType) => {
1763
+ const incompatibleReason = incompatibleMap.get(agentType);
1764
+ if (incompatibleReason) {
1765
+ return {
1766
+ agent: agentType,
1767
+ success: false,
1768
+ path: "",
1769
+ error: incompatibleReason
1770
+ };
1560
1771
  }
1561
- }
1562
- return name || MCP_DEFAULT_SERVER_NAME;
1563
- };
1564
- var inferNameFromUrl = (input7) => {
1565
- try {
1566
- const url = new URL(input7);
1567
- const host = url.hostname;
1568
- const labels = host.split(".").filter((segment) => segment.length > 0);
1569
- if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
1570
- const meaningfulLabels = labels.filter((label) => {
1571
- const lower = label.toLowerCase();
1572
- if (COMMON_TLD_LABELS.has(lower)) return false;
1573
- if (GENERIC_HOST_PREFIXES.has(lower)) return false;
1574
- return true;
1575
- });
1576
- if (meaningfulLabels.length > 0) return meaningfulLabels[0];
1577
- if (labels.length >= 2) return labels[labels.length - 2];
1578
- return labels[labels.length - 1] || MCP_DEFAULT_SERVER_NAME;
1579
- } catch {
1580
- return MCP_DEFAULT_SERVER_NAME;
1581
- }
1582
- };
1583
- var inferNameFromCommand = (command) => {
1584
- const tokens = command.trim().split(/\s+/);
1585
- const runnerBase = tokens[0]?.split(PATH_SEPARATOR_REGEX).pop() ?? "";
1586
- const startIndex = KNOWN_COMMAND_RUNNERS.has(runnerBase) ? 1 : 0;
1587
- for (let tokenIndex = startIndex; tokenIndex < tokens.length; tokenIndex += 1) {
1588
- const token = tokens[tokenIndex];
1589
- if (!token || token.startsWith("-")) continue;
1590
- return extractPackageName(token);
1591
- }
1592
- const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
1593
- return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
1594
- };
1595
- var parseMcpSource = (input7) => {
1596
- const trimmed = input7.trim();
1597
- if (trimmed.length === 0) {
1598
- throw new Error(
1599
- "Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
1600
- );
1601
- }
1602
- if (REMOTE_URL_REGEX.test(trimmed)) {
1603
- return {
1604
- type: "remote",
1605
- value: trimmed,
1606
- inferredName: inferNameFromUrl(trimmed)
1607
- };
1608
- }
1609
- if (HAS_WHITESPACE_REGEX.test(trimmed)) {
1610
- return {
1611
- type: "command",
1612
- value: trimmed,
1613
- inferredName: inferNameFromCommand(trimmed)
1614
- };
1615
- }
1616
- if (PACKAGE_NAME_REGEX.test(trimmed)) {
1617
- return {
1618
- type: "package",
1619
- value: trimmed,
1620
- inferredName: extractPackageName(trimmed)
1621
- };
1622
- }
1623
- return {
1624
- type: "command",
1625
- value: trimmed,
1626
- inferredName: inferNameFromCommand(trimmed)
1627
- };
1772
+ return installedMap.get(agentType);
1773
+ });
1628
1774
  };
1629
- var isRemoteMcpSource = (parsed) => parsed.type === "remote";
1630
1775
 
1631
1776
  // src/install-mcp-server.ts
1632
1777
  var installMcpServer = (options) => {
@@ -1660,15 +1805,14 @@ var installMcpServer = (options) => {
1660
1805
  var listInstalledMcpServers = (options = {}) => {
1661
1806
  const agentTypes = options.agents ?? getMcpAgentTypes();
1662
1807
  const collected = [];
1663
- for (const agentType of agentTypes) {
1664
- const agent = getMcpAgentConfig(agentType);
1665
- const { path, exists, servers } = agentConfigStore.listServers(agent, options);
1666
- if (!exists) continue;
1667
- for (const [serverName, rawConfig] of Object.entries(servers)) {
1808
+ const results = agentConfigStore.listServersForAgents(agentTypes, options);
1809
+ for (const item of results) {
1810
+ if (!item.exists) continue;
1811
+ for (const [serverName, rawConfig] of Object.entries(item.servers)) {
1668
1812
  collected.push({
1669
1813
  serverName,
1670
- agent: agentType,
1671
- path,
1814
+ agent: item.agent,
1815
+ path: item.path,
1672
1816
  config: rawConfig,
1673
1817
  serverConfig: parseServerConfig(rawConfig)
1674
1818
  });
@@ -1676,29 +1820,83 @@ var listInstalledMcpServers = (options = {}) => {
1676
1820
  }
1677
1821
  return collected;
1678
1822
  };
1823
+ var groupInstalledServersByName = (installed) => {
1824
+ const grouped = /* @__PURE__ */ new Map();
1825
+ for (const item of installed) {
1826
+ const itemConfig = item.serverConfig ?? parseServerConfig(item.config);
1827
+ let entry = grouped.get(item.serverName);
1828
+ if (!entry) {
1829
+ entry = {
1830
+ serverName: item.serverName,
1831
+ agents: [],
1832
+ paths: [],
1833
+ config: itemConfig,
1834
+ hasDivergence: false
1835
+ };
1836
+ grouped.set(item.serverName, entry);
1837
+ } else if (!entry.hasDivergence) {
1838
+ if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
1839
+ entry.hasDivergence = true;
1840
+ }
1841
+ }
1842
+ if (!entry.agents.includes(item.agent)) {
1843
+ entry.agents.push(item.agent);
1844
+ }
1845
+ if (!entry.paths.includes(item.path)) {
1846
+ entry.paths.push(item.path);
1847
+ }
1848
+ }
1849
+ return grouped;
1850
+ };
1851
+ var queryGroupedInstalledServers = (options = {}) => {
1852
+ const installed = listInstalledMcpServers(options);
1853
+ return groupInstalledServersByName(installed);
1854
+ };
1679
1855
 
1680
- // src/remove.ts
1681
- var removeMcpServerFromAgent = (serverName, agentType, options = {}) => {
1682
- const agent = getMcpAgentConfig(agentType);
1683
- const { target } = agentConfigStore.resolveTarget(agent, options);
1684
- const coHosted = getCoHostedAgents(agentType, options);
1685
- try {
1686
- const { removed } = agentConfigStore.removeServer(agent, serverName, options);
1687
- return {
1688
- agent: agentType,
1689
- path: target.configPath,
1690
- removed,
1691
- coAffectedAgents: removed && coHosted.length > 0 ? coHosted : void 0
1692
- };
1693
- } catch (error) {
1694
- return {
1695
- agent: agentType,
1696
- path: target.configPath,
1697
- removed: false,
1698
- error: toErrorMessage(error)
1699
- };
1856
+ // src/update-mcp-server.ts
1857
+ var updateMcpServer = (options) => {
1858
+ const isGlobal = options.global ?? false;
1859
+ const cwd = options.cwd ?? process.cwd();
1860
+ let previousConfig = options.previousConfig;
1861
+ if (!previousConfig) {
1862
+ const existing = listInstalledMcpServers({
1863
+ global: isGlobal,
1864
+ cwd,
1865
+ agents: options.agents
1866
+ });
1867
+ const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
1868
+ if (found) {
1869
+ previousConfig = found.serverConfig;
1870
+ }
1871
+ }
1872
+ const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
1873
+ let targetAgents = options.agents;
1874
+ if (!targetAgents || targetAgents.length === 0) {
1875
+ const existing = listInstalledMcpServers({ global: isGlobal, cwd });
1876
+ targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
1700
1877
  }
1878
+ const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
1879
+ const { allAgents, incompatible } = resolveTargetAgents({
1880
+ requested: targetAgents,
1881
+ global: isGlobal,
1882
+ cwd,
1883
+ transport: requestedTransport
1884
+ });
1885
+ const results = installToCompatibleAgents(options.serverName, serverConfig, {
1886
+ allAgents,
1887
+ incompatible,
1888
+ global: isGlobal,
1889
+ cwd
1890
+ });
1891
+ return {
1892
+ serverName: options.serverName,
1893
+ config: serverConfig,
1894
+ results,
1895
+ incompatible
1896
+ };
1701
1897
  };
1898
+
1899
+ // src/remove.ts
1702
1900
  var removeMcpServer = (options) => {
1703
1901
  const { allAgents } = resolveTargetAgents({
1704
1902
  requested: options.agents,
@@ -1706,549 +1904,13 @@ var removeMcpServer = (options) => {
1706
1904
  global: options.global,
1707
1905
  cwd: options.cwd
1708
1906
  });
1709
- const clusters = resolveConfigClusters(allAgents, {
1907
+ const storeResults = agentConfigStore.removeServers(allAgents, options.name, {
1710
1908
  global: options.global,
1711
1909
  cwd: options.cwd
1712
1910
  });
1713
- const results = [];
1714
- for (const cluster of clusters) {
1715
- const primaryAgentType = cluster.targetAgents[0];
1716
- const primaryAgent = getMcpAgentConfig(primaryAgentType);
1717
- try {
1718
- const { removed } = agentConfigStore.removeServer(primaryAgent, options.name, {
1719
- global: options.global,
1720
- cwd: options.cwd
1721
- });
1722
- if (removed) {
1723
- for (const agentType of cluster.targetAgents) {
1724
- results.push({
1725
- agent: agentType,
1726
- path: cluster.configPath,
1727
- removed: true,
1728
- coAffectedAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1729
- });
1730
- }
1731
- }
1732
- } catch (error) {
1733
- const errorMsg = toErrorMessage(error);
1734
- for (const agentType of cluster.targetAgents) {
1735
- results.push({
1736
- agent: agentType,
1737
- path: cluster.configPath,
1738
- removed: false,
1739
- error: errorMsg
1740
- });
1741
- }
1742
- }
1743
- }
1744
- return results;
1911
+ return storeResults.filter((r) => r.removed || Boolean(r.error));
1745
1912
  };
1746
1913
 
1747
- // src/update-mcp-server.ts
1748
- var toRemoteServerConfig = (config, defaultTransport = "http") => {
1749
- const {
1750
- command: _droppedCommand,
1751
- args: _droppedArgs,
1752
- env: _droppedEnv,
1753
- ...remoteConfig
1754
- } = config;
1755
- return {
1756
- ...remoteConfig,
1757
- type: remoteConfig.type ?? defaultTransport
1758
- };
1759
- };
1760
- var toStdioServerConfig = (config) => {
1761
- const {
1762
- url: _droppedUrl,
1763
- type: _droppedType,
1764
- headers: _droppedHeaders,
1765
- ...stdioConfig
1766
- } = config;
1767
- return stdioConfig;
1768
- };
1769
- var detectUpdateTransition = (incoming, previous) => {
1770
- if (!previous) {
1771
- return incoming.url ? "switch-to-remote" : "switch-to-stdio";
1772
- }
1773
- if (incoming.url && !incoming.command) {
1774
- return "switch-to-remote";
1775
- }
1776
- if (incoming.command && !incoming.url) {
1777
- return "switch-to-stdio";
1778
- }
1779
- if (incoming.url || !incoming.command && previous.url) {
1780
- return "merge-remote";
1781
- }
1782
- return "merge-stdio";
1783
- };
1784
- var sanitizeUpdatedServerConfig = (incoming, previous) => {
1785
- const transition = detectUpdateTransition(incoming, previous);
1786
- const targetTransport = incoming.type ?? previous?.type ?? "http";
1787
- switch (transition) {
1788
- case "switch-to-remote": {
1789
- const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
1790
- return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
1791
- }
1792
- case "switch-to-stdio": {
1793
- const cleanBase = previous ? toStdioServerConfig(previous) : {};
1794
- return toStdioServerConfig({ ...cleanBase, ...incoming });
1795
- }
1796
- case "merge-remote": {
1797
- return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
1798
- }
1799
- case "merge-stdio": {
1800
- return toStdioServerConfig({ ...previous, ...incoming });
1801
- }
1802
- }
1803
- };
1804
- var updateMcpServer = (options) => {
1805
- const isGlobal = options.global ?? false;
1806
- const cwd = options.cwd ?? process.cwd();
1807
- let previousConfig = options.previousConfig;
1808
- if (!previousConfig) {
1809
- const existing = listInstalledMcpServers({
1810
- global: isGlobal,
1811
- cwd,
1812
- agents: options.agents
1813
- });
1814
- const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
1815
- if (found) {
1816
- previousConfig = found.serverConfig;
1817
- }
1818
- }
1819
- const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
1820
- let targetAgents = options.agents;
1821
- if (!targetAgents || targetAgents.length === 0) {
1822
- const existing = listInstalledMcpServers({ global: isGlobal, cwd });
1823
- targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
1824
- }
1825
- const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
1826
- const { allAgents, incompatible } = resolveTargetAgents({
1827
- requested: targetAgents,
1828
- global: isGlobal,
1829
- cwd,
1830
- transport: requestedTransport
1831
- });
1832
- const results = installToCompatibleAgents(options.serverName, serverConfig, {
1833
- allAgents,
1834
- incompatible,
1835
- global: isGlobal,
1836
- cwd
1837
- });
1838
- return {
1839
- serverName: options.serverName,
1840
- config: serverConfig,
1841
- results,
1842
- incompatible
1843
- };
1844
- };
1845
-
1846
- // src/interactive/main-menu.ts
1847
- var import_prompts10 = require("@inquirer/prompts");
1848
- var import_picocolors15 = __toESM(require("picocolors"), 1);
1849
-
1850
- // src/interactive/wizard-add.ts
1851
- var import_prompts7 = require("@inquirer/prompts");
1852
- var import_picocolors11 = __toESM(require("picocolors"), 1);
1853
-
1854
- // src/utils/co-hosted-feedback.ts
1855
- var import_picocolors2 = __toESM(require("picocolors"), 1);
1856
-
1857
- // src/utils/logger.ts
1858
- var import_picocolors = __toESM(require("picocolors"), 1);
1859
- var logger = {
1860
- info: (message) => {
1861
- console.log(import_picocolors.default.cyan("i"), message);
1862
- },
1863
- success: (message) => {
1864
- console.log(import_picocolors.default.green("\u221A"), message);
1865
- },
1866
- warn: (message) => {
1867
- console.log(import_picocolors.default.yellow("!"), message);
1868
- },
1869
- error: (message) => {
1870
- console.error(import_picocolors.default.red("x"), message);
1871
- }
1872
- };
1873
-
1874
- // src/utils/co-hosted-feedback.ts
1875
- var formatCoHostedBadge = (kind, agents) => {
1876
- if (!agents || agents.length === 0) return "";
1877
- const label = kind === "configured" ? "co-configured" : "co-affected";
1878
- return ` ${import_picocolors2.default.yellow(`(${label}: ${agents.join(", ")})`)}`;
1879
- };
1880
- var logCoHostedNotice = (kind, agents) => {
1881
- if (!agents || agents.length === 0) return;
1882
- const actionText = kind === "configured" ? "Also configured for" : "Also affects";
1883
- logger.info(
1884
- ` ${import_picocolors2.default.dim("Note:")} ${actionText} co-hosted agent(s): ${import_picocolors2.default.yellow(agents.join(", "))}`
1885
- );
1886
- };
1887
-
1888
- // src/interactive/prompts/agents.ts
1889
- var import_picocolors6 = __toESM(require("picocolors"), 1);
1890
-
1891
- // src/interactive/utils/build-linked-agent-choices.ts
1892
- var import_picocolors3 = __toESM(require("picocolors"), 1);
1893
- var buildLinkedAgentChoices = (options) => {
1894
- const { agents, checkedAgents, detectedAgents = [], scopeOptions = {} } = options;
1895
- const alignedCheckedSet = new Set(checkedAgents);
1896
- for (const agent of checkedAgents) {
1897
- const coHosted = getCoHostedAgents(agent, scopeOptions);
1898
- for (const co of coHosted) {
1899
- if (agents.includes(co)) {
1900
- alignedCheckedSet.add(co);
1901
- }
1902
- }
1903
- }
1904
- return agents.map((agent) => {
1905
- const config = getMcpAgentConfig(agent);
1906
- const displayName = config?.displayName ?? agent;
1907
- const isDetected = detectedAgents.includes(agent);
1908
- const coHosted = getCoHostedAgents(agent, scopeOptions).filter(
1909
- (co) => agents.includes(co)
1910
- );
1911
- const detectedBadge = isDetected ? import_picocolors3.default.green(" [detected]") : "";
1912
- const sharedBadge = coHosted.length > 0 ? import_picocolors3.default.dim(` [shared: ${coHosted.join(", ")}]`) : "";
1913
- const label = `${displayName} ${import_picocolors3.default.dim(`(${agent})`)}${detectedBadge}${sharedBadge}`;
1914
- return {
1915
- name: label,
1916
- value: agent,
1917
- checked: alignedCheckedSet.has(agent),
1918
- linkedValues: coHosted,
1919
- description: coHosted.length > 0 ? `Linked with ${coHosted.map((a) => getMcpAgentConfig(a).displayName).join(", ")} (shared configuration)` : void 0
1920
- };
1921
- });
1922
- };
1923
-
1924
- // src/interactive/prompts/linked-checkbox.ts
1925
- var import_core = require("@inquirer/core");
1926
- var import_picocolors4 = __toESM(require("picocolors"), 1);
1927
- var defaultTheme = {
1928
- icon: {
1929
- checked: import_picocolors4.default.green("[x]"),
1930
- unchecked: import_picocolors4.default.dim("[ ]"),
1931
- cursor: import_picocolors4.default.cyan(">"),
1932
- disabledChecked: import_picocolors4.default.dim("[x]"),
1933
- disabledUnchecked: import_picocolors4.default.dim("[-]")
1934
- },
1935
- style: {
1936
- disabled: (text) => import_picocolors4.default.dim(text),
1937
- renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
1938
- description: (text) => import_picocolors4.default.cyan(text),
1939
- keysHelpTip: (keys) => keys.map(([key, action]) => `${import_picocolors4.default.bold(key)} ${import_picocolors4.default.dim(action)}`).join(import_picocolors4.default.dim(" | ")),
1940
- highlight: (text) => import_picocolors4.default.cyan(text)
1941
- },
1942
- i18n: {
1943
- disabledError: "This option is disabled and cannot be toggled."
1944
- }
1945
- };
1946
- function isSelectable(item) {
1947
- return !import_core.Separator.isSeparator(item) && !item.disabled;
1948
- }
1949
- function isNavigable(item) {
1950
- return !import_core.Separator.isSeparator(item);
1951
- }
1952
- function isChecked(item) {
1953
- return !import_core.Separator.isSeparator(item) && item.checked;
1954
- }
1955
- function normalizeChoices(choices) {
1956
- return choices.map((choice) => {
1957
- if (import_core.Separator.isSeparator(choice)) {
1958
- return choice;
1959
- }
1960
- if (typeof choice !== "object" || choice === null || !("value" in choice)) {
1961
- const name2 = String(choice);
1962
- return {
1963
- value: choice,
1964
- name: name2,
1965
- short: name2,
1966
- checkedName: name2,
1967
- disabled: false,
1968
- checked: false,
1969
- linkedValues: []
1970
- };
1971
- }
1972
- const name = choice.name ?? String(choice.value);
1973
- return {
1974
- value: choice.value,
1975
- name,
1976
- short: choice.short ?? name,
1977
- checkedName: choice.checkedName ?? name,
1978
- description: choice.description,
1979
- disabled: choice.disabled ?? false,
1980
- checked: choice.checked ?? false,
1981
- linkedValues: choice.linkedValues ?? []
1982
- };
1983
- });
1984
- }
1985
- var linkedCheckbox = (0, import_core.createPrompt)(
1986
- (config, done) => {
1987
- const { pageSize = 10, loop = true, required, validate = () => true } = config;
1988
- const theme = (0, import_core.makeTheme)(defaultTheme, config.theme);
1989
- const [status, setStatus] = (0, import_core.useState)("idle");
1990
- const prefix = (0, import_core.usePrefix)({ status, theme });
1991
- const [items, setItems] = (0, import_core.useState)(() => normalizeChoices(config.choices));
1992
- const bounds = (0, import_core.useMemo)(() => {
1993
- const first = items.findIndex(isNavigable);
1994
- let last = -1;
1995
- for (let i = items.length - 1; i >= 0; i--) {
1996
- if (isNavigable(items[i])) {
1997
- last = i;
1998
- break;
1999
- }
2000
- }
2001
- if (first === -1 || last === -1) {
2002
- throw new import_core.ValidationError("[linkedCheckbox prompt] No selectable choices.");
2003
- }
2004
- return { first, last };
2005
- }, [items]);
2006
- const [active, setActive] = (0, import_core.useState)(bounds.first);
2007
- const [errorMsg, setError] = (0, import_core.useState)();
2008
- const toggleWithLinked = (targetIndex) => {
2009
- const targetItem = items[targetIndex];
2010
- if (!targetItem || import_core.Separator.isSeparator(targetItem) || targetItem.disabled) {
2011
- return;
2012
- }
2013
- const nextChecked = !targetItem.checked;
2014
- const targetValue = targetItem.value;
2015
- const linked = new Set(targetItem.linkedValues);
2016
- setItems(
2017
- (prevItems) => prevItems.map((item) => {
2018
- if (import_core.Separator.isSeparator(item) || item.disabled) {
2019
- return item;
2020
- }
2021
- const isTargetOrLinked = item.value === targetValue || linked.has(item.value) || item.linkedValues.includes(targetValue);
2022
- if (isTargetOrLinked) {
2023
- return { ...item, checked: nextChecked };
2024
- }
2025
- return item;
2026
- })
2027
- );
2028
- };
2029
- (0, import_core.useKeypress)(async (key) => {
2030
- if ((0, import_core.isEnterKey)(key)) {
2031
- const selection = items.filter(isChecked);
2032
- const isValid = await validate([...selection]);
2033
- if (required && selection.length === 0) {
2034
- setError("At least one choice must be selected");
2035
- } else if (isValid === true) {
2036
- setStatus("done");
2037
- done(selection.map((choice) => choice.value));
2038
- } else {
2039
- setError(typeof isValid === "string" ? isValid : "You must select a valid value");
2040
- }
2041
- } else if ((0, import_core.isUpKey)(key) || (0, import_core.isDownKey)(key)) {
2042
- if (errorMsg) setError(void 0);
2043
- if (loop || (0, import_core.isUpKey)(key) && active !== bounds.first || (0, import_core.isDownKey)(key) && active !== bounds.last) {
2044
- const offset = (0, import_core.isUpKey)(key) ? -1 : 1;
2045
- let next = active;
2046
- do {
2047
- next = (next + offset + items.length) % items.length;
2048
- } while (!isNavigable(items[next]));
2049
- setActive(next);
2050
- }
2051
- } else if ((0, import_core.isSpaceKey)(key)) {
2052
- const activeItem = items[active];
2053
- if (activeItem && !import_core.Separator.isSeparator(activeItem)) {
2054
- if (activeItem.disabled) {
2055
- setError(theme.i18n.disabledError);
2056
- } else {
2057
- setError(void 0);
2058
- toggleWithLinked(active);
2059
- }
2060
- }
2061
- } else if (key.name === "a") {
2062
- const hasUnchecked = items.some((choice) => isSelectable(choice) && !choice.checked);
2063
- setItems(
2064
- (prevItems) => prevItems.map((item) => isSelectable(item) ? { ...item, checked: hasUnchecked } : item)
2065
- );
2066
- } else if ((0, import_core.isNumberKey)(key)) {
2067
- const selectedIndex = Number(key.name) - 1;
2068
- let selectableIndex = -1;
2069
- const position = items.findIndex((item) => {
2070
- if (import_core.Separator.isSeparator(item)) return false;
2071
- selectableIndex++;
2072
- return selectableIndex === selectedIndex;
2073
- });
2074
- const selectedItem = items[position];
2075
- if (selectedItem && isSelectable(selectedItem)) {
2076
- setActive(position);
2077
- setError(void 0);
2078
- toggleWithLinked(position);
2079
- }
2080
- }
2081
- });
2082
- const message = theme.style.message(config.message, status);
2083
- let description;
2084
- const page = (0, import_core.usePagination)({
2085
- items,
2086
- active,
2087
- renderItem({ item, isActive }) {
2088
- if (import_core.Separator.isSeparator(item)) {
2089
- return ` ${item.separator}`;
2090
- }
2091
- const cursor = isActive ? theme.icon.cursor : " ";
2092
- if (item.disabled) {
2093
- const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
2094
- const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
2095
- return theme.style.disabled(`${cursor} ${checkbox2} ${item.name} ${disabledLabel}`);
2096
- }
2097
- if (isActive) {
2098
- description = item.description;
2099
- }
2100
- const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
2101
- const name = item.checked ? item.checkedName : item.name;
2102
- const color = isActive ? theme.style.highlight : (x) => x;
2103
- return color(`${cursor} ${checkbox} ${name}`);
2104
- },
2105
- pageSize,
2106
- loop
2107
- });
2108
- if (status === "done") {
2109
- const selection = items.filter(isChecked);
2110
- const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
2111
- return [prefix, message, answer].filter(Boolean).join(" ");
2112
- }
2113
- const helpLine = theme.style.keysHelpTip([
2114
- ["up/down", "navigate"],
2115
- ["space", "toggle"],
2116
- ["a", "all"],
2117
- ["enter", "submit"]
2118
- ]);
2119
- const lines = [
2120
- [prefix, message].filter(Boolean).join(" "),
2121
- page,
2122
- helpLine
2123
- ];
2124
- if (description) {
2125
- lines.push(theme.style.description(description));
2126
- }
2127
- if (errorMsg) {
2128
- lines.push(theme.style.error(errorMsg));
2129
- }
2130
- return lines.join("\n");
2131
- }
2132
- );
2133
-
2134
- // src/interactive/prompts/scope.ts
2135
- var import_prompts = require("@inquirer/prompts");
2136
- var import_picocolors5 = __toESM(require("picocolors"), 1);
2137
- var promptScope = async (options = {}) => {
2138
- const initialGlobal = options.defaultGlobal ?? options.global;
2139
- if (initialGlobal !== void 0) {
2140
- return initialGlobal;
2141
- }
2142
- const cwd = options.cwd ?? process.cwd();
2143
- return (0, import_prompts.select)({
2144
- message: options.message ?? "Select MCP scope:",
2145
- choices: [
2146
- {
2147
- name: `Current Project - ${import_picocolors5.default.dim(cwd)}`,
2148
- value: false
2149
- },
2150
- {
2151
- name: `Global User Config - ${import_picocolors5.default.dim("applies across all projects")}`,
2152
- value: true
2153
- }
2154
- ]
2155
- });
2156
- };
2157
-
2158
- // src/interactive/prompts/agents.ts
2159
- var promptScopeAndAgents = async (options = {}) => {
2160
- const cwd = options.cwd ?? process.cwd();
2161
- const isGlobal = await promptScope({
2162
- cwd,
2163
- defaultGlobal: options.defaultGlobal,
2164
- message: "Select MCP installation scope:"
2165
- });
2166
- const resolution = resolveTargetAgents({
2167
- global: isGlobal,
2168
- cwd
2169
- });
2170
- const detected = resolution.detected;
2171
- const rawAvailable = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
2172
- const availableAgentTypes = sortAgentsWithClusters(rawAvailable, { global: isGlobal, cwd });
2173
- if (detected.length > 0) {
2174
- logger.info(
2175
- `Detected configured agents: ${import_picocolors6.default.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2176
- );
2177
- } else {
2178
- logger.warn(`No active ${isGlobal ? "global" : "project"} agents detected`);
2179
- }
2180
- const defaultChecked = options.defaultAgents && options.defaultAgents.length > 0 ? options.defaultAgents : detected;
2181
- const choices = buildLinkedAgentChoices({
2182
- agents: availableAgentTypes,
2183
- checkedAgents: defaultChecked,
2184
- detectedAgents: detected,
2185
- scopeOptions: { global: isGlobal, cwd }
2186
- });
2187
- const selectedAgents = await linkedCheckbox({
2188
- message: "Select target agents (Space to select, Enter to confirm):",
2189
- choices,
2190
- validate: (chosen) => {
2191
- if (chosen.length === 0) {
2192
- return "Please select at least one agent";
2193
- }
2194
- return true;
2195
- }
2196
- });
2197
- return {
2198
- global: isGlobal,
2199
- agents: selectedAgents
2200
- };
2201
- };
2202
-
2203
- // src/interactive/prompts/args.ts
2204
- var import_prompts2 = require("@inquirer/prompts");
2205
- var parseArgsString = (rawText) => {
2206
- const matches = rawText.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g);
2207
- if (!matches) return [];
2208
- return matches.map((arg) => {
2209
- if (arg.startsWith('"') && arg.endsWith('"') || arg.startsWith("'") && arg.endsWith("'")) {
2210
- return arg.slice(1, -1);
2211
- }
2212
- return arg;
2213
- });
2214
- };
2215
- var promptArgsConfig = async (initialArgs = []) => {
2216
- if (initialArgs.length > 0) {
2217
- return initialArgs;
2218
- }
2219
- const needArgs = await (0, import_prompts2.confirm)({
2220
- message: "Configure command arguments (e.g. file paths, connection strings)?",
2221
- default: false
2222
- });
2223
- if (!needArgs) {
2224
- return [];
2225
- }
2226
- const raw = await (0, import_prompts2.input)({
2227
- message: "Enter command arguments (space-separated, wrap paths with spaces in quotes):",
2228
- validate: (val) => val.trim() ? true : "Arguments cannot be empty"
2229
- });
2230
- return parseArgsString(raw.trim());
2231
- };
2232
- var formatArgsString = (args) => {
2233
- return args.map((arg) => arg.includes(" ") || arg.includes('"') ? `"${arg.replace(/"/g, '\\"')}"` : arg).join(" ");
2234
- };
2235
- var promptEditArgs = async (currentArgs = []) => {
2236
- const defaultStr = formatArgsString(currentArgs);
2237
- const raw = await (0, import_prompts2.input)({
2238
- message: "Edit command arguments (space-separated, wrap paths with spaces in quotes, leave empty to clear):",
2239
- default: defaultStr
2240
- });
2241
- const trimmed = raw.trim();
2242
- if (!trimmed) {
2243
- return [];
2244
- }
2245
- return parseArgsString(trimmed);
2246
- };
2247
-
2248
- // src/interactive/prompts/env.ts
2249
- var import_prompts5 = require("@inquirer/prompts");
2250
- var import_picocolors9 = __toESM(require("picocolors"), 1);
2251
-
2252
1914
  // src/utils/mask-secret.ts
2253
1915
  var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
2254
1916
  var maskSecretValue = (key, value) => {
@@ -2264,1431 +1926,25 @@ var maskSecretHeader = (key, value) => {
2264
1926
  }
2265
1927
  return `${value.slice(0, 4)}***${value.slice(-3)}`;
2266
1928
  };
2267
-
2268
- // src/interactive/prompts/kv.ts
2269
- var import_prompts4 = require("@inquirer/prompts");
2270
- var import_picocolors8 = __toESM(require("picocolors"), 1);
2271
-
2272
- // src/interactive/prompts/multiline.ts
2273
- var import_node_readline = require("readline");
2274
- var import_prompts3 = require("@inquirer/prompts");
2275
- var import_picocolors7 = __toESM(require("picocolors"), 1);
2276
- var readMultilineTextFromTerminal = async (message, endHint = "When done pasting, enter END on a new line or press Enter twice to finish") => {
2277
- console.log(import_picocolors7.default.cyan(`
2278
- ${message}`));
2279
- console.log(import_picocolors7.default.dim(` (Hint: ${endHint})
2280
- `));
2281
- return new Promise((resolve) => {
2282
- const rl = (0, import_node_readline.createInterface)({
2283
- input: process.stdin,
2284
- output: process.stdout
2285
- });
2286
- const lines = [];
2287
- let consecutiveEmpty = 0;
2288
- const cleanup = () => {
2289
- rl.removeAllListeners();
2290
- rl.close();
2291
- };
2292
- rl.on("line", (line) => {
2293
- const trimmed = line.trim();
2294
- if (trimmed === "END") {
2295
- cleanup();
2296
- resolve(lines.join("\n"));
2297
- return;
2298
- }
2299
- if (line === "") {
2300
- consecutiveEmpty++;
2301
- if (lines.length > 0) {
2302
- cleanup();
2303
- resolve(lines.join("\n"));
2304
- return;
2305
- }
2306
- if (consecutiveEmpty >= 2) {
2307
- cleanup();
2308
- resolve("");
2309
- return;
2310
- }
2311
- } else {
2312
- consecutiveEmpty = 0;
2313
- lines.push(line);
2314
- }
2315
- });
2316
- rl.on("close", () => {
2317
- resolve(lines.join("\n"));
2318
- });
2319
- });
2320
- };
2321
- var promptEditorText = async (options) => {
2322
- try {
2323
- return await (0, import_prompts3.editor)({
2324
- message: options.message,
2325
- default: options.defaultText ?? "",
2326
- postfix: options.postfix
2327
- });
2328
- } catch {
2329
- return readMultilineTextFromTerminal(options.message);
2330
- }
2331
- };
2332
-
2333
- // src/interactive/prompts/kv.ts
2334
- var promptEditKeyValueConfig = async (currentItems = {}, options) => {
2335
- let items = { ...currentItems };
2336
- while (true) {
2337
- const keys = Object.keys(items);
2338
- console.log();
2339
- if (keys.length === 0) {
2340
- console.log(import_picocolors8.default.dim(` No ${options.itemsNoun} configured.`));
2341
- } else {
2342
- console.log(import_picocolors8.default.cyan(import_picocolors8.default.bold(` Configured ${options.title} (${keys.length}):`)));
2343
- for (const [k, v] of Object.entries(items)) {
2344
- const sep = options.separator === "=" ? "=" : ": ";
2345
- console.log(` ${import_picocolors8.default.bold(k)}${sep}${import_picocolors8.default.dim(options.maskValue(k, v))}`);
2346
- }
2347
- }
2348
- console.log();
2349
- const choice = await (0, import_prompts4.select)({
2350
- message: `Manage ${options.itemsNoun}:`,
2351
- choices: [
2352
- {
2353
- name: "Open in system default editor ($EDITOR)",
2354
- value: "editor"
2355
- },
2356
- {
2357
- name: `Add or modify a ${options.itemNoun}`,
2358
- value: "upsert"
2359
- },
2360
- ...keys.length > 0 ? [
2361
- {
2362
- name: `Delete a ${options.itemNoun}`,
2363
- value: "delete"
2364
- }
2365
- ] : [],
2366
- {
2367
- name: `Paste multiline ${options.itemsNoun} into terminal`,
2368
- value: "paste"
2369
- },
2370
- ...keys.length > 0 ? [
2371
- {
2372
- name: `Clear all ${options.itemsNoun}`,
2373
- value: "clear"
2374
- }
2375
- ] : [],
2376
- {
2377
- name: `Done (finish editing ${options.itemsNoun})`,
2378
- value: "done"
2379
- }
2380
- ]
2381
- });
2382
- if (choice === "done") {
2383
- return items;
2384
- }
2385
- if (choice === "editor") {
2386
- const defaultText = options.formatText(items);
2387
- const text = await promptEditorText({
2388
- message: options.editorMessage,
2389
- postfix: options.editorPostfix,
2390
- defaultText
2391
- });
2392
- const parsed = options.parseText(text);
2393
- items = parsed;
2394
- logger.success(`${options.title} updated (${Object.keys(items).length} total)`);
2395
- } else if (choice === "upsert") {
2396
- const key = await (0, import_prompts4.input)({
2397
- message: options.keyPromptMessage,
2398
- validate: (val) => {
2399
- const trimmed = val.trim();
2400
- if (!trimmed) return `${options.itemNoun} name cannot be empty`;
2401
- if (/\s/.test(trimmed)) return `${options.itemNoun} name cannot contain spaces`;
2402
- return true;
2403
- }
2404
- });
2405
- const trimmedKey = key.trim();
2406
- const existingVal = items[trimmedKey];
2407
- const isSecret = options.isSecretKey(trimmedKey);
2408
- let newVal;
2409
- if (isSecret) {
2410
- newVal = await (0, import_prompts4.password)({
2411
- message: existingVal !== void 0 ? `New value for (${trimmedKey}) [leave empty to keep current]:` : `${options.valuePromptMessage} for (${trimmedKey}) [sensitive content masked]:`,
2412
- mask: "*"
2413
- });
2414
- if (existingVal !== void 0 && newVal === "") {
2415
- newVal = existingVal;
2416
- }
2417
- } else {
2418
- newVal = await (0, import_prompts4.input)({
2419
- message: `${options.valuePromptMessage} for (${trimmedKey}):`,
2420
- default: existingVal
2421
- });
2422
- }
2423
- items[trimmedKey] = newVal;
2424
- logger.success(`${existingVal !== void 0 ? "Updated" : "Added"}: ${import_picocolors8.default.cyan(trimmedKey)}`);
2425
- } else if (choice === "delete") {
2426
- const toDelete = await (0, import_prompts4.select)({
2427
- message: `Select ${options.itemNoun} to delete:`,
2428
- choices: [
2429
- ...keys.map((k) => ({ name: k, value: k })),
2430
- { name: "Cancel", value: "__cancel__" }
2431
- ]
2432
- });
2433
- if (toDelete !== "__cancel__") {
2434
- delete items[toDelete];
2435
- logger.success(`Deleted: ${import_picocolors8.default.cyan(toDelete)}`);
2436
- }
2437
- } else if (choice === "paste") {
2438
- const pasted = await readMultilineTextFromTerminal(options.pasteMessage);
2439
- const parsed = options.parseText(pasted);
2440
- const count = Object.keys(parsed).length;
2441
- if (count === 0) {
2442
- logger.warn(`No valid ${options.itemsNoun} recognized`);
2443
- } else {
2444
- if (keys.length > 0) {
2445
- const pasteMode = await (0, import_prompts4.select)({
2446
- message: `How to apply pasted ${options.itemsNoun}?`,
2447
- choices: [
2448
- { name: `Merge with existing ${options.itemsNoun}`, value: "merge" },
2449
- { name: `Replace all existing ${options.itemsNoun}`, value: "replace" }
2450
- ]
2451
- });
2452
- if (pasteMode === "replace") {
2453
- items = parsed;
2454
- } else {
2455
- Object.assign(items, parsed);
2456
- }
2457
- } else {
2458
- items = parsed;
2459
- }
2460
- logger.success(`Successfully applied ${import_picocolors8.default.cyan(String(count))} ${options.itemsNoun}`);
2461
- }
2462
- } else if (choice === "clear") {
2463
- const confirmClear = await (0, import_prompts4.confirm)({
2464
- message: `Are you sure you want to clear all ${options.itemsNoun}?`,
2465
- default: false
2466
- });
2467
- if (confirmClear) {
2468
- items = {};
2469
- logger.success(`Cleared all ${options.itemsNoun}`);
2470
- }
2471
- }
2472
- }
2473
- };
2474
-
2475
- // src/interactive/prompts/env.ts
2476
- var formatEnvText = (env) => {
2477
- return Object.entries(env).map(([key, value]) => {
2478
- if (/[\s"']/.test(value)) {
2479
- return `${key}="${value.replace(/"/g, '\\"')}"`;
2480
- }
2481
- return `${key}=${value}`;
2482
- }).join("\n");
2483
- };
2484
- var parseEnvText = (rawText) => {
2485
- const result = {};
2486
- const lines = rawText.split(/\r?\n/);
2487
- for (const rawLine of lines) {
2488
- let line = rawLine.trim();
2489
- if (!line || line.startsWith("#")) continue;
2490
- if (line.startsWith("export ")) {
2491
- line = line.slice(7).trim();
2492
- }
2493
- const eqIndex = line.indexOf("=");
2494
- if (eqIndex === -1) continue;
2495
- const key = line.slice(0, eqIndex).trim();
2496
- let value = line.slice(eqIndex + 1).trim();
2497
- if (!key) continue;
2498
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
2499
- value = value.slice(1, -1);
2500
- }
2501
- result[key] = value;
2502
- }
2503
- return result;
2504
- };
2505
- var promptEnvConfig = async (initialEnv = {}) => {
2506
- const env = { ...initialEnv };
2507
- const initialCount = Object.keys(env).length;
2508
- if (initialCount > 0) {
2509
- logger.info(`Includes ${import_picocolors9.default.cyan(String(initialCount))} preset environment variables`);
2510
- }
2511
- const mode = await (0, import_prompts5.select)({
2512
- message: "Configure environment variables?",
2513
- choices: [
2514
- {
2515
- name: "Skip / None",
2516
- value: "skip"
2517
- },
2518
- {
2519
- name: "Paste multiline .env text into terminal",
2520
- value: "paste"
2521
- },
2522
- {
2523
- name: "Open in system default editor ($EDITOR)",
2524
- value: "editor"
2525
- },
2526
- {
2527
- name: "Enter key-value pairs one by one",
2528
- value: "manual"
2529
- }
2530
- ]
2531
- });
2532
- if (mode === "skip") {
2533
- return env;
2534
- }
2535
- if (mode === "paste" || mode === "editor") {
2536
- const pasted = mode === "editor" ? await promptEditorText({
2537
- message: "Paste or edit environment variables in editor, then save and exit:",
2538
- postfix: ".env",
2539
- defaultText: formatEnvText(env)
2540
- }) : await readMultilineTextFromTerminal("Paste .env formatted content (multiline supported):");
2541
- const parsed = parseEnvText(pasted);
2542
- const count = Object.keys(parsed).length;
2543
- if (count === 0) {
2544
- logger.warn("No valid KEY=VALUE pairs recognized");
2545
- } else {
2546
- Object.assign(env, parsed);
2547
- logger.success(`Successfully parsed ${import_picocolors9.default.cyan(String(count))} environment variables:`);
2548
- for (const [k, v] of Object.entries(parsed)) {
2549
- console.log(` ${import_picocolors9.default.bold(k)}=${import_picocolors9.default.dim(maskSecretValue(k, v))}`);
2550
- }
2551
- }
2552
- return env;
2553
- }
2554
- logger.info("Entering environment variables (leave key empty and press enter to finish):");
2555
- while (true) {
2556
- const key = await (0, import_prompts5.input)({
2557
- message: "Variable name (Key, leave empty to finish):",
2558
- validate: (val2) => {
2559
- const trimmed = val2.trim();
2560
- if (!trimmed) return true;
2561
- if (/\s/.test(trimmed)) return "Variable name cannot contain spaces";
2562
- return true;
2563
- }
2564
- });
2565
- const trimmedKey = key.trim();
2566
- if (!trimmedKey) break;
2567
- const isSecret = SECRET_KEY_PATTERN.test(trimmedKey);
2568
- let val;
2569
- if (isSecret) {
2570
- val = await (0, import_prompts5.password)({
2571
- message: `Value for (${trimmedKey}) [secret masked]:`,
2572
- mask: "*"
2573
- });
2574
- } else {
2575
- val = await (0, import_prompts5.input)({
2576
- message: `Value for (${trimmedKey}):`
2577
- });
2578
- }
2579
- env[trimmedKey] = val;
2580
- logger.success(`Added: ${import_picocolors9.default.cyan(trimmedKey)}`);
2581
- }
2582
- return env;
2583
- };
2584
- var promptEditEnvConfig = async (currentEnv = {}) => promptEditKeyValueConfig(currentEnv, {
2585
- title: "Environment Variables",
2586
- itemNoun: "variable",
2587
- itemsNoun: "environment variables",
2588
- separator: "=",
2589
- editorPostfix: ".env",
2590
- editorMessage: "Edit environment variables in editor, then save and exit:",
2591
- pasteMessage: "Paste .env formatted content (multiline supported):",
2592
- keyPromptMessage: "Variable name (Key):",
2593
- valuePromptMessage: "Value",
2594
- isSecretKey: (k) => SECRET_KEY_PATTERN.test(k),
2595
- maskValue: maskSecretValue,
2596
- formatText: formatEnvText,
2597
- parseText: parseEnvText
2598
- });
2599
-
2600
- // src/interactive/prompts/headers.ts
2601
- var import_prompts6 = require("@inquirer/prompts");
2602
- var import_picocolors10 = __toESM(require("picocolors"), 1);
2603
- var formatHeadersText = (headers) => {
2604
- return Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n");
2605
- };
2606
- var parseHeadersText = (rawText) => {
2607
- const result = {};
2608
- const lines = rawText.split(/\r?\n/);
2609
- for (const rawLine of lines) {
2610
- const line = rawLine.trim();
2611
- if (!line || line.startsWith("#")) continue;
2612
- const colonIndex = line.indexOf(":");
2613
- const equalIndex = line.indexOf("=");
2614
- let splitIndex = -1;
2615
- if (colonIndex !== -1 && equalIndex !== -1) {
2616
- splitIndex = Math.min(colonIndex, equalIndex);
2617
- } else if (colonIndex !== -1) {
2618
- splitIndex = colonIndex;
2619
- } else {
2620
- splitIndex = equalIndex;
2621
- }
2622
- if (splitIndex === -1) continue;
2623
- const key = line.slice(0, splitIndex).trim();
2624
- let value = line.slice(splitIndex + 1).trim();
2625
- if (!key) continue;
2626
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
2627
- value = value.slice(1, -1);
2628
- }
2629
- result[key] = value;
2630
- }
2631
- return result;
2632
- };
2633
- var promptHeadersConfig = async (initialHeaders = {}) => {
2634
- const headers = { ...initialHeaders };
2635
- const mode = await (0, import_prompts6.select)({
2636
- message: "Select HTTP headers configuration method:",
2637
- choices: [
2638
- {
2639
- name: "Paste multiline headers into terminal (Key: Value format)",
2640
- value: "paste"
2641
- },
2642
- {
2643
- name: "Open in system default editor ($EDITOR)",
2644
- value: "editor"
2645
- },
2646
- {
2647
- name: "Enter headers one by one (e.g. Authorization: Bearer ...)",
2648
- value: "manual"
2649
- },
2650
- {
2651
- name: "Skip / None",
2652
- value: "skip"
2653
- }
2654
- ]
2655
- });
2656
- if (mode === "skip") {
2657
- return headers;
2658
- }
2659
- if (mode === "paste" || mode === "editor") {
2660
- const pasted = mode === "editor" ? await promptEditorText({
2661
- message: "Paste or edit HTTP headers in editor, then save and exit:",
2662
- defaultText: formatHeadersText(headers)
2663
- }) : await readMultilineTextFromTerminal(
2664
- "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):"
2665
- );
2666
- const parsed = parseHeadersText(pasted);
2667
- const count = Object.keys(parsed).length;
2668
- if (count === 0) {
2669
- logger.warn("No valid Key: Value pairs recognized");
2670
- } else {
2671
- Object.assign(headers, parsed);
2672
- logger.success(`Successfully parsed ${import_picocolors10.default.cyan(String(count))} headers:`);
2673
- for (const [k, v] of Object.entries(parsed)) {
2674
- console.log(` ${import_picocolors10.default.bold(k)}: ${import_picocolors10.default.dim(maskSecretHeader(k, v))}`);
2675
- }
2676
- }
2677
- return headers;
2678
- }
2679
- logger.info("Entering HTTP headers (leave header name empty and press enter to finish):");
2680
- while (true) {
2681
- const name = await (0, import_prompts6.input)({
2682
- message: "Header name (e.g. Authorization, leave empty to finish):",
2683
- validate: (val2) => {
2684
- const trimmed = val2.trim();
2685
- if (!trimmed) return true;
2686
- if (/\s/.test(trimmed)) return "Header name cannot contain spaces";
2687
- return true;
2688
- }
2689
- });
2690
- const trimmedName = name.trim();
2691
- if (!trimmedName) break;
2692
- const isSecret = SECRET_HEADER_PATTERN.test(trimmedName);
2693
- let val;
2694
- if (isSecret) {
2695
- val = await (0, import_prompts6.password)({
2696
- message: `Header value for (${trimmedName}) [sensitive content masked]:`,
2697
- mask: "*"
2698
- });
2699
- } else {
2700
- val = await (0, import_prompts6.input)({
2701
- message: `Header value for (${trimmedName}):`
2702
- });
2703
- }
2704
- headers[trimmedName] = val;
2705
- logger.success(`Added: ${import_picocolors10.default.cyan(trimmedName)}`);
2706
- }
2707
- return headers;
2708
- };
2709
- var promptEditHeadersConfig = async (currentHeaders = {}) => promptEditKeyValueConfig(currentHeaders, {
2710
- title: "HTTP Headers",
2711
- itemNoun: "header",
2712
- itemsNoun: "HTTP headers",
2713
- separator: ":",
2714
- editorMessage: "Edit HTTP headers in editor, then save and exit:",
2715
- pasteMessage: "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):",
2716
- keyPromptMessage: "Header name (e.g. Authorization):",
2717
- valuePromptMessage: "Header value",
2718
- isSecretKey: (k) => SECRET_HEADER_PATTERN.test(k),
2719
- maskValue: maskSecretHeader,
2720
- formatText: formatHeadersText,
2721
- parseText: parseHeadersText
2722
- });
2723
-
2724
- // src/interactive/wizard-add.ts
2725
- var wizardAdd = async (initial = {}) => {
2726
- const cwd = initial.cwd ?? process.cwd();
2727
- logger.info(import_picocolors11.default.bold("Welcome to the MCP interactive add wizard"));
2728
- let source = initial.source;
2729
- if (!source) {
2730
- const sourceType = await (0, import_prompts7.select)({
2731
- message: "Select MCP server type:",
2732
- choices: [
2733
- {
2734
- name: "npm package (run via npx)",
2735
- value: "npm"
2736
- },
2737
- {
2738
- name: "Remote MCP server (via HTTP / SSE URL)",
2739
- value: "remote"
2740
- },
2741
- {
2742
- name: "Local command / script / Docker (stdio)",
2743
- value: "command"
2744
- }
2745
- ]
2746
- });
2747
- if (sourceType === "npm") {
2748
- source = await (0, import_prompts7.input)({
2749
- message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres):",
2750
- validate: (val) => val.trim() ? true : "Package name cannot be empty"
2751
- });
2752
- } else if (sourceType === "remote") {
2753
- source = await (0, import_prompts7.input)({
2754
- message: "Enter remote server URL (e.g. https://mcp.example.com/sse):",
2755
- validate: (val) => {
2756
- const trimmed = val.trim();
2757
- if (!trimmed) return "URL cannot be empty";
2758
- if (!/^https?:\/\//i.test(trimmed)) return "Please enter a valid URL starting with http:// or https://";
2759
- return true;
2760
- }
2761
- });
2762
- } else {
2763
- source = await (0, import_prompts7.input)({
2764
- message: "Enter command and arguments (e.g. python -m my_mcp_server or docker run ...):",
2765
- validate: (val) => val.trim() ? true : "Command cannot be empty"
2766
- });
2767
- }
2768
- }
2769
- source = source.trim();
2770
- const parsed = parseMcpSource(source);
2771
- let serverName = initial.name;
2772
- if (!serverName) {
2773
- serverName = await (0, import_prompts7.input)({
2774
- message: "MCP server name:",
2775
- default: parsed.inferredName,
2776
- validate: (val) => val.trim() ? true : "Server name cannot be empty"
2777
- });
2778
- }
2779
- serverName = serverName.trim();
2780
- let transport = initial.transport;
2781
- let headers = initial.headers ?? {};
2782
- if (parsed.type === "remote") {
2783
- if (!transport) {
2784
- const isSseUrl = /\/sse\b/i.test(parsed.value);
2785
- transport = await (0, import_prompts7.select)({
2786
- message: "Select remote transport protocol:",
2787
- choices: [
2788
- { name: "HTTP", value: "http" },
2789
- { name: "SSE (Server-Sent Events)", value: "sse" }
2790
- ],
2791
- default: isSseUrl ? "sse" : "http"
2792
- });
2793
- }
2794
- if (Object.keys(headers).length === 0) {
2795
- const needHeader = await (0, import_prompts7.confirm)({
2796
- message: "Configure HTTP headers (e.g. Authorization Bearer token)?",
2797
- default: false
2798
- });
2799
- if (needHeader) {
2800
- headers = await promptHeadersConfig();
2801
- }
2802
- }
2803
- }
2804
- const { global: isGlobal, agents: selectedAgents } = await promptScopeAndAgents({
2805
- cwd,
2806
- defaultGlobal: initial.global,
2807
- defaultAgents: initial.agents
2808
- });
2809
- let args = initial.args ?? [];
2810
- if (parsed.type !== "remote") {
2811
- args = await promptArgsConfig(args);
2812
- }
2813
- let env = initial.env ?? {};
2814
- if (parsed.type !== "remote") {
2815
- env = await promptEnvConfig(env);
2816
- }
2817
- console.log("\n" + import_picocolors11.default.cyan(import_picocolors11.default.bold("Configuration Preview:")));
2818
- console.log(` ${import_picocolors11.default.bold("Server Name:")} ${import_picocolors11.default.green(serverName)}`);
2819
- console.log(` ${import_picocolors11.default.bold("Server Type:")} ${import_picocolors11.default.magenta(parsed.type)}`);
2820
- console.log(` ${import_picocolors11.default.bold("Source/Command:")} ${import_picocolors11.default.dim(source)}`);
2821
- console.log(` ${import_picocolors11.default.bold("Scope:")} ${isGlobal ? import_picocolors11.default.yellow("Global") : import_picocolors11.default.blue("Project")}`);
2822
- console.log(` ${import_picocolors11.default.bold("Target Agents:")} ${import_picocolors11.default.cyan(selectedAgents.join(", "))}`);
2823
- if (args.length > 0) {
2824
- console.log(` ${import_picocolors11.default.bold("Arguments:")} ${import_picocolors11.default.dim(args.join(" "))}`);
2825
- }
2826
- if (transport) {
2827
- console.log(` ${import_picocolors11.default.bold("Transport:")} ${import_picocolors11.default.magenta(transport)}`);
2828
- }
2829
- const envKeys = Object.keys(env);
2830
- if (envKeys.length > 0) {
2831
- console.log(` ${import_picocolors11.default.bold("Environment Variables:")} ${import_picocolors11.default.dim(envKeys.join(", "))} (${envKeys.length})`);
2832
- }
2833
- const headerKeys = Object.keys(headers);
2834
- if (headerKeys.length > 0) {
2835
- console.log(` ${import_picocolors11.default.bold("Headers:")} ${import_picocolors11.default.dim(headerKeys.join(", "))} (${headerKeys.length})`);
2836
- }
2837
- console.log();
2838
- const proceed = await (0, import_prompts7.confirm)({
2839
- message: "Confirm installation with this configuration?",
2840
- default: true
2841
- });
2842
- if (!proceed) {
2843
- logger.warn("Operation cancelled");
2844
- return false;
2845
- }
2846
- const result = installMcpServer({
2847
- source,
2848
- name: serverName,
2849
- agents: selectedAgents,
2850
- args,
2851
- global: isGlobal,
2852
- cwd,
2853
- transport,
2854
- headers,
2855
- env
2856
- });
2857
- logger.info(
2858
- `Writing ${import_picocolors11.default.bold(result.serverName)} to ${import_picocolors11.default.cyan(String(result.results.length))} agent config files...`
2859
- );
2860
- let allSuccess = true;
2861
- for (const record of result.results) {
2862
- if (record.success) {
2863
- logger.success(
2864
- `${import_picocolors11.default.cyan(record.agent)}: Successfully written to ${import_picocolors11.default.dim(record.path)}${formatCoHostedBadge("configured", record.coConfiguredAgents)}`
2865
- );
2866
- } else {
2867
- allSuccess = false;
2868
- logger.error(`${import_picocolors11.default.cyan(record.agent)}: Failed to write - ${record.error}`);
2869
- }
2870
- }
2871
- if (allSuccess) {
2872
- logger.success(import_picocolors11.default.bold(`MCP server "${serverName}" configured successfully!`));
2873
- }
2874
- return allSuccess;
2875
- };
2876
-
2877
- // src/interactive/wizard-manage.ts
2878
- var import_prompts8 = require("@inquirer/prompts");
2879
- var import_picocolors13 = __toESM(require("picocolors"), 1);
2880
-
2881
- // src/utils/display-server-details.ts
2882
- var import_picocolors12 = __toESM(require("picocolors"), 1);
2883
- var displayServerDetails = ({
2884
- serverName,
2885
- config,
2886
- agents,
2887
- hasDivergence,
2888
- global: isGlobal,
2889
- titlePrefix = "MCP Server Details"
2890
- }) => {
2891
- console.log("\n" + import_picocolors12.default.cyan(import_picocolors12.default.bold(`${titlePrefix}: [${serverName}]`)));
2892
- if (isGlobal !== void 0) {
2893
- console.log(` ${import_picocolors12.default.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
2894
- }
2895
- if (agents && agents.length > 0) {
2896
- console.log(
2897
- ` ${import_picocolors12.default.bold("Configured Agents:")} ${import_picocolors12.default.green(agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
2898
- );
2899
- }
2900
- if (hasDivergence) {
2901
- console.log(
2902
- ` ${import_picocolors12.default.yellow(import_picocolors12.default.bold("Notice:"))} ${import_picocolors12.default.yellow("Configurations differ across installed agents. Showing configuration from the first agent.")}`
2903
- );
2904
- }
2905
- const isRemote = Boolean(config.url && config.url.length > 0);
2906
- if (isRemote) {
2907
- console.log(` ${import_picocolors12.default.bold("Transport:")} ${import_picocolors12.default.magenta(config.type ?? "http")}`);
2908
- console.log(` ${import_picocolors12.default.bold("URL:")} ${import_picocolors12.default.dim(config.url ?? "")}`);
2909
- const headerKeys = Object.keys(config.headers ?? {});
2910
- if (headerKeys.length > 0) {
2911
- console.log(` ${import_picocolors12.default.bold("Headers:")} ${import_picocolors12.default.cyan(String(headerKeys.length))}`);
2912
- for (const [k, v] of Object.entries(config.headers ?? {})) {
2913
- console.log(` ${import_picocolors12.default.bold(k)}: ${import_picocolors12.default.dim(maskSecretHeader(k, v))}`);
2914
- }
2915
- } else {
2916
- console.log(` ${import_picocolors12.default.bold("Headers:")} ${import_picocolors12.default.dim("(none)")}`);
2917
- }
2918
- } else {
2919
- console.log(` ${import_picocolors12.default.bold("Command:")} ${import_picocolors12.default.magenta(config.command ?? "")}`);
2920
- const argsStr = config.args && config.args.length > 0 ? config.args.join(" ") : "(none)";
2921
- console.log(` ${import_picocolors12.default.bold("Arguments:")} ${import_picocolors12.default.dim(argsStr)}`);
2922
- const envKeys = Object.keys(config.env ?? {});
2923
- if (envKeys.length > 0) {
2924
- console.log(` ${import_picocolors12.default.bold("Environment Variables:")} ${import_picocolors12.default.cyan(String(envKeys.length))}`);
2925
- for (const [k, v] of Object.entries(config.env ?? {})) {
2926
- console.log(` ${import_picocolors12.default.bold(k)}=${import_picocolors12.default.dim(maskSecretValue(k, v))}`);
2927
- }
2928
- } else {
2929
- console.log(` ${import_picocolors12.default.bold("Environment Variables:")} ${import_picocolors12.default.dim("(none)")}`);
2930
- }
2931
- }
2932
- console.log();
2933
- };
2934
-
2935
- // src/interactive/utils/group-installed-servers.ts
2936
- var normalizeServerConfig = parseServerConfig;
2937
- var groupInstalledServersByName = (installed) => {
2938
- const grouped = /* @__PURE__ */ new Map();
2939
- for (const item of installed) {
2940
- const itemConfig = normalizeServerConfig(item.config);
2941
- let entry = grouped.get(item.serverName);
2942
- if (!entry) {
2943
- entry = {
2944
- serverName: item.serverName,
2945
- agents: [],
2946
- paths: [],
2947
- config: itemConfig,
2948
- hasDivergence: false
2949
- };
2950
- grouped.set(item.serverName, entry);
2951
- } else if (!entry.hasDivergence) {
2952
- if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
2953
- entry.hasDivergence = true;
2954
- }
2955
- }
2956
- if (!entry.agents.includes(item.agent)) {
2957
- entry.agents.push(item.agent);
2958
- }
2959
- if (!entry.paths.includes(item.path)) {
2960
- entry.paths.push(item.path);
2961
- }
2962
- }
2963
- return grouped;
2964
- };
2965
-
2966
- // src/interactive/wizard-manage.ts
2967
- var promptSwitchServerType = async (currentConfig, serverName) => {
2968
- const isRemote = Boolean(currentConfig.url && currentConfig.url.length > 0);
2969
- if (isRemote) {
2970
- const newCmd = await (0, import_prompts8.input)({
2971
- message: "Executable command (e.g. node, npx):",
2972
- validate: (val) => val.trim() ? true : "Command cannot be empty"
2973
- });
2974
- const newArgs = await promptEditArgs([]);
2975
- const newEnv = await promptEditEnvConfig({});
2976
- logger.success(`Switched [${serverName}] configuration to stdio mode`);
2977
- return {
2978
- command: newCmd.trim(),
2979
- args: newArgs.length > 0 ? newArgs : void 0,
2980
- env: Object.keys(newEnv).length > 0 ? newEnv : void 0
2981
- };
2982
- }
2983
- const newUrl = await (0, import_prompts8.input)({
2984
- message: "Remote server URL:",
2985
- validate: (val) => {
2986
- const trimmed = val.trim();
2987
- if (!trimmed) return "URL cannot be empty";
2988
- if (!/^https?:\/\//i.test(trimmed)) {
2989
- return "Please enter a valid URL starting with http:// or https://";
2990
- }
2991
- return true;
2992
- }
2993
- });
2994
- const transport = await (0, import_prompts8.select)({
2995
- message: "Select remote transport protocol:",
2996
- choices: [
2997
- { name: "HTTP", value: "http" },
2998
- { name: "SSE (Server-Sent Events)", value: "sse" }
2999
- ],
3000
- default: "http"
3001
- });
3002
- const newHeaders = await promptEditHeadersConfig({});
3003
- logger.success(`Switched [${serverName}] configuration to remote mode`);
3004
- return {
3005
- url: newUrl.trim(),
3006
- type: transport,
3007
- headers: Object.keys(newHeaders).length > 0 ? newHeaders : void 0
3008
- };
3009
- };
3010
- var handleEditServerConfig = async (options) => {
3011
- const { targetGroup } = options;
3012
- const isGlobal = options.global ?? false;
3013
- const cwd = options.cwd ?? process.cwd();
3014
- const serverName = targetGroup.serverName;
3015
- let workingConfig = {
3016
- ...targetGroup.config,
3017
- args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
3018
- env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
3019
- headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
3020
- };
3021
- while (true) {
3022
- const isRemote = Boolean(workingConfig.url && workingConfig.url.length > 0);
3023
- displayServerDetails({
3024
- serverName,
3025
- config: workingConfig,
3026
- titlePrefix: "Edit Server Configuration"
3027
- });
3028
- const editChoices = isRemote ? [
3029
- { name: "Edit HTTP Headers (headers)", value: "headers" },
3030
- { name: "Edit Remote URL (url)", value: "url" },
3031
- { name: "Edit Transport Protocol (type)", value: "transport" },
3032
- { name: "Switch to local command (stdio)", value: "switch_type" },
3033
- { name: "Reset changes to original", value: "reset" },
3034
- { name: "Save and apply changes", value: "save" },
3035
- { name: "Cancel (discard changes)", value: "cancel" }
3036
- ] : [
3037
- { name: "Edit Environment Variables (env)", value: "env" },
3038
- { name: "Edit Command Arguments (args)", value: "args" },
3039
- { name: "Edit Executable Command (command)", value: "command" },
3040
- { name: "Switch to remote server (HTTP/SSE)", value: "switch_type" },
3041
- { name: "Reset changes to original", value: "reset" },
3042
- { name: "Save and apply changes", value: "save" },
3043
- { name: "Cancel (discard changes)", value: "cancel" }
3044
- ];
3045
- const editAction = await (0, import_prompts8.select)({
3046
- message: `What would you like to modify in [${serverName}]?`,
3047
- choices: editChoices
3048
- });
3049
- if (editAction === "cancel") {
3050
- logger.info("Modification cancelled; changes discarded");
3051
- return;
3052
- }
3053
- if (editAction === "reset") {
3054
- workingConfig = {
3055
- ...targetGroup.config,
3056
- args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
3057
- env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
3058
- headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
3059
- };
3060
- logger.info("Configuration reset to original");
3061
- continue;
3062
- }
3063
- if (editAction === "switch_type") {
3064
- workingConfig = await promptSwitchServerType(workingConfig, serverName);
3065
- continue;
3066
- }
3067
- if (editAction === "env") {
3068
- workingConfig.env = await promptEditEnvConfig(workingConfig.env ?? {});
3069
- } else if (editAction === "args") {
3070
- workingConfig.args = await promptEditArgs(workingConfig.args ?? []);
3071
- } else if (editAction === "command") {
3072
- const newCmd = await (0, import_prompts8.input)({
3073
- message: "Executable command:",
3074
- default: workingConfig.command,
3075
- validate: (val) => val.trim() ? true : "Command cannot be empty"
3076
- });
3077
- workingConfig.command = newCmd.trim();
3078
- } else if (editAction === "headers") {
3079
- workingConfig.headers = await promptEditHeadersConfig(workingConfig.headers ?? {});
3080
- } else if (editAction === "url") {
3081
- const newUrl = await (0, import_prompts8.input)({
3082
- message: "Remote server URL:",
3083
- default: workingConfig.url,
3084
- validate: (val) => {
3085
- const trimmed = val.trim();
3086
- if (!trimmed) return "URL cannot be empty";
3087
- if (!/^https?:\/\//i.test(trimmed)) {
3088
- return "Please enter a valid URL starting with http:// or https://";
3089
- }
3090
- return true;
3091
- }
3092
- });
3093
- workingConfig.url = newUrl.trim();
3094
- } else if (editAction === "transport") {
3095
- workingConfig.type = await (0, import_prompts8.select)({
3096
- message: "Select remote transport protocol:",
3097
- choices: [
3098
- { name: "HTTP", value: "http" },
3099
- { name: "SSE (Server-Sent Events)", value: "sse" }
3100
- ],
3101
- default: workingConfig.type === "sse" ? "sse" : "http"
3102
- });
3103
- } else if (editAction === "save") {
3104
- let targetAgents = targetGroup.agents;
3105
- if (targetGroup.agents.length > 1) {
3106
- const sortedAgents = sortAgentsWithClusters(targetGroup.agents, { global: isGlobal, cwd });
3107
- const choices = buildLinkedAgentChoices({
3108
- agents: sortedAgents,
3109
- checkedAgents: sortedAgents,
3110
- scopeOptions: { global: isGlobal, cwd }
3111
- });
3112
- targetAgents = await linkedCheckbox({
3113
- message: "Select agents to update configuration (Space to toggle):",
3114
- choices,
3115
- loop: false,
3116
- validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
3117
- });
3118
- if (targetAgents.length < targetGroup.agents.length) {
3119
- const unselected = targetGroup.agents.filter((a) => !targetAgents.includes(a));
3120
- const unselectedNames = unselected.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3121
- logger.info(
3122
- `Note: Updating only a subset of agents. Server configurations will diverge from: ${unselectedNames}.`
3123
- );
3124
- }
3125
- }
3126
- const requestedTransport = workingConfig.url ? workingConfig.type ?? "http" : "stdio";
3127
- const resolution = resolveTargetAgents({
3128
- requested: targetAgents,
3129
- global: isGlobal,
3130
- cwd,
3131
- transport: requestedTransport
3132
- });
3133
- if (resolution.incompatible.length > 0) {
3134
- for (const item of resolution.incompatible) {
3135
- logger.warn(`Skipping ${import_picocolors13.default.cyan(item.agent)}: ${item.reason}`);
3136
- }
3137
- }
3138
- if (resolution.compatibleAgents.length === 0) {
3139
- logger.error(
3140
- `None of the selected agents support ${requestedTransport} transport. Cannot update.`
3141
- );
3142
- continue;
3143
- }
3144
- const agentNames = resolution.compatibleAgents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3145
- const confirmed = await (0, import_prompts8.confirm)({
3146
- message: `Confirm updating configuration for [${serverName}] across: ${agentNames}?`,
3147
- default: true
3148
- });
3149
- if (!confirmed) {
3150
- logger.warn("Update cancelled");
3151
- continue;
3152
- }
3153
- const updateResult = updateMcpServer({
3154
- serverName,
3155
- config: workingConfig,
3156
- previousConfig: targetGroup.config,
3157
- agents: resolution.compatibleAgents,
3158
- global: isGlobal,
3159
- cwd
3160
- });
3161
- let updatedAny = false;
3162
- const succeededAgents = [];
3163
- for (const res of updateResult.results) {
3164
- if (res.success) {
3165
- updatedAny = true;
3166
- succeededAgents.push(res.agent);
3167
- logger.success(
3168
- `${import_picocolors13.default.cyan(res.agent)}: Successfully updated configuration in ${import_picocolors13.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
3169
- );
3170
- } else {
3171
- logger.error(`${import_picocolors13.default.cyan(res.agent)}: Update failed - ${res.error}`);
3172
- }
3173
- }
3174
- if (updatedAny) {
3175
- targetGroup.config = updateResult.config;
3176
- logger.success(`Configuration for [${serverName}] updated successfully!`);
3177
- return;
3178
- }
3179
- }
3180
- }
3181
- };
3182
- var wizardManage = async (options = {}) => {
3183
- const cwd = options.cwd ?? process.cwd();
3184
- const isGlobal = await promptScope({
3185
- cwd,
3186
- defaultGlobal: options.global,
3187
- message: "Select MCP scope to inspect and manage:"
3188
- });
3189
- const installed = listInstalledMcpServers({ global: isGlobal, cwd });
3190
- if (installed.length === 0) {
3191
- logger.warn(`No configured MCP servers found in ${isGlobal ? "global" : "project"} scope`);
3192
- return;
3193
- }
3194
- const grouped = groupInstalledServersByName(installed);
3195
- let pendingServerName = options.serverName;
3196
- const refreshGroupedServers = () => {
3197
- const freshInstalled = listInstalledMcpServers({ global: isGlobal, cwd });
3198
- const freshGrouped = groupInstalledServersByName(freshInstalled);
3199
- grouped.clear();
3200
- for (const [name, grp] of freshGrouped) {
3201
- grouped.set(name, grp);
3202
- }
3203
- };
3204
- while (true) {
3205
- let chosenServerName;
3206
- if (pendingServerName && grouped.has(pendingServerName)) {
3207
- chosenServerName = pendingServerName;
3208
- pendingServerName = void 0;
3209
- } else {
3210
- pendingServerName = void 0;
3211
- const choices = Array.from(grouped.values()).map((g) => {
3212
- const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
3213
- return {
3214
- name: `${import_picocolors13.default.bold(g.serverName)} ${import_picocolors13.default.dim(`(configured in: ${agentNames})`)}`,
3215
- value: g.serverName
3216
- };
3217
- });
3218
- choices.push({
3219
- name: `Back`,
3220
- value: "__back__"
3221
- });
3222
- chosenServerName = await (0, import_prompts8.select)({
3223
- message: "Select MCP server to manage or sync:",
3224
- choices
3225
- });
3226
- if (chosenServerName === "__back__") {
3227
- return;
3228
- }
3229
- }
3230
- const targetGroup = grouped.get(chosenServerName);
3231
- if (!targetGroup) continue;
3232
- displayServerDetails({
3233
- serverName: chosenServerName,
3234
- config: targetGroup.config,
3235
- agents: targetGroup.agents,
3236
- global: isGlobal,
3237
- hasDivergence: targetGroup.hasDivergence
3238
- });
3239
- const action = await (0, import_prompts8.select)({
3240
- message: `What would you like to do with [${chosenServerName}]?`,
3241
- choices: [
3242
- {
3243
- name: "Edit server configuration",
3244
- value: "edit"
3245
- },
3246
- {
3247
- name: "Sync / clone to other agents",
3248
- value: "sync"
3249
- },
3250
- {
3251
- name: "Back to list",
3252
- value: "back"
3253
- }
3254
- ]
3255
- });
3256
- if (action === "back") continue;
3257
- if (action === "edit") {
3258
- await handleEditServerConfig({
3259
- targetGroup,
3260
- global: isGlobal,
3261
- cwd
3262
- });
3263
- refreshGroupedServers();
3264
- continue;
3265
- }
3266
- if (action === "sync") {
3267
- const allAllowedAgents = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
3268
- const rawCandidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
3269
- if (rawCandidateAgents.length === 0) {
3270
- logger.info(
3271
- "All supported agents in this scope already have this MCP server configured; no sync needed"
3272
- );
3273
- continue;
3274
- }
3275
- const candidateAgents = sortAgentsWithClusters(rawCandidateAgents, { global: isGlobal, cwd });
3276
- const choices = buildLinkedAgentChoices({
3277
- agents: candidateAgents,
3278
- checkedAgents: [],
3279
- scopeOptions: { global: isGlobal, cwd }
3280
- });
3281
- const selectedToSync = await linkedCheckbox({
3282
- message: "Select target agents to sync to (Space to select):",
3283
- choices,
3284
- loop: false,
3285
- validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
3286
- });
3287
- const confirmed = await (0, import_prompts8.confirm)({
3288
- message: `Confirm syncing configuration of [${chosenServerName}] to: ${selectedToSync.join(", ")}?`,
3289
- default: true
3290
- });
3291
- if (!confirmed) {
3292
- logger.warn("Sync cancelled");
3293
- continue;
3294
- }
3295
- const syncResult = updateMcpServer({
3296
- serverName: chosenServerName,
3297
- config: targetGroup.config,
3298
- agents: selectedToSync,
3299
- global: isGlobal,
3300
- cwd
3301
- });
3302
- for (const item of syncResult.incompatible) {
3303
- logger.warn(`Skipping ${import_picocolors13.default.cyan(item.agent)}: ${item.reason}`);
3304
- }
3305
- for (const res of syncResult.results) {
3306
- if (syncResult.incompatible.some((i) => i.agent === res.agent)) {
3307
- continue;
3308
- }
3309
- if (res.success) {
3310
- logger.success(
3311
- `${import_picocolors13.default.cyan(res.agent)}: Successfully synced to ${import_picocolors13.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
3312
- );
3313
- targetGroup.agents.push(res.agent);
3314
- } else {
3315
- logger.error(`${import_picocolors13.default.cyan(res.agent)}: Sync failed - ${res.error}`);
3316
- }
3317
- }
3318
- refreshGroupedServers();
3319
- }
3320
- }
3321
- };
3322
-
3323
- // src/interactive/wizard-remove.ts
3324
- var import_prompts9 = require("@inquirer/prompts");
3325
- var import_picocolors14 = __toESM(require("picocolors"), 1);
3326
- var wizardRemove = async (options = {}) => {
3327
- const cwd = options.cwd ?? process.cwd();
3328
- const isGlobal = await promptScope({
3329
- cwd,
3330
- defaultGlobal: options.global,
3331
- message: "Select scope to remove MCP server from:"
3332
- });
3333
- const installed = listInstalledMcpServers({ global: isGlobal, cwd });
3334
- if (installed.length === 0) {
3335
- logger.warn(`No installed MCP servers found in ${isGlobal ? "global" : "project"} scope`);
3336
- return false;
3337
- }
3338
- const serverMap = groupInstalledServersByName(installed);
3339
- let serverName = options.name;
3340
- if (!serverName) {
3341
- const choices = Array.from(serverMap.values()).map((g) => ({
3342
- name: `${import_picocolors14.default.bold(g.serverName)} ${import_picocolors14.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
3343
- value: g.serverName
3344
- }));
3345
- serverName = await (0, import_prompts9.select)({
3346
- message: "Select MCP server to remove:",
3347
- choices
3348
- });
3349
- }
3350
- const rawInstalledAgents = serverMap.get(serverName)?.agents || [];
3351
- if (rawInstalledAgents.length === 0) {
3352
- logger.warn(`No agents found with [${serverName}] installed`);
3353
- return false;
3354
- }
3355
- const installedAgents = sortAgentsWithClusters(rawInstalledAgents, { global: isGlobal, cwd });
3356
- let targetAgents = options.agents;
3357
- if (!targetAgents || targetAgents.length === 0) {
3358
- const choices = buildLinkedAgentChoices({
3359
- agents: installedAgents,
3360
- checkedAgents: installedAgents,
3361
- scopeOptions: { global: isGlobal, cwd }
3362
- });
3363
- targetAgents = await linkedCheckbox({
3364
- message: `Select agents to remove [${serverName}] from:`,
3365
- choices,
3366
- validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
3367
- });
3368
- } else {
3369
- const validAgents = targetAgents.filter((agent) => installedAgents.includes(agent));
3370
- if (validAgents.length === 0) {
3371
- logger.warn(`None of the specified agents (${targetAgents.join(", ")}) have [${serverName}] installed`);
3372
- return false;
3373
- }
3374
- targetAgents = validAgents;
3375
- }
3376
- const confirmed = await (0, import_prompts9.confirm)({
3377
- message: `Confirm removing MCP server [${serverName}] from ${targetAgents.join(", ")}?`,
3378
- default: true
3379
- });
3380
- if (!confirmed) {
3381
- logger.warn("Operation cancelled");
3382
- return false;
3383
- }
3384
- const results = removeMcpServer({
3385
- name: serverName,
3386
- agents: targetAgents,
3387
- global: isGlobal,
3388
- cwd
3389
- });
3390
- let removedCount = 0;
3391
- for (const res of results) {
3392
- if (res.removed) {
3393
- logger.success(
3394
- `${import_picocolors14.default.cyan(res.agent)}: Successfully removed from ${import_picocolors14.default.dim(res.path)}${formatCoHostedBadge("affected", res.coAffectedAgents)}`
3395
- );
3396
- removedCount++;
3397
- } else if (res.error) {
3398
- logger.error(`${import_picocolors14.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
3399
- }
3400
- }
3401
- if (removedCount > 0) {
3402
- logger.success(`Successfully removed [${serverName}] from ${removedCount} agent(s)`);
3403
- return true;
3404
- }
3405
- logger.warn(`Failed to remove [${serverName}] from specified agents`);
3406
- return false;
3407
- };
3408
-
3409
- // src/interactive/main-menu.ts
3410
- var mainMenu = async () => {
3411
- console.log();
3412
- console.log(import_picocolors15.default.bold(import_picocolors15.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
3413
- console.log(import_picocolors15.default.dim("Cross-platform MCP server configuration & synchronization tool"));
3414
- console.log();
3415
- while (true) {
3416
- try {
3417
- const action = await (0, import_prompts10.select)({
3418
- message: "Select an action:",
3419
- choices: [
3420
- {
3421
- name: "Add MCP Server",
3422
- value: "add"
3423
- },
3424
- {
3425
- name: "Manage & Sync Installed MCP Servers",
3426
- value: "manage"
3427
- },
3428
- {
3429
- name: "Remove MCP Server",
3430
- value: "remove"
3431
- },
3432
- {
3433
- name: "Exit",
3434
- value: "exit"
3435
- }
3436
- ]
3437
- });
3438
- if (action === "exit") {
3439
- console.log(import_picocolors15.default.dim("Goodbye!"));
3440
- break;
3441
- }
3442
- if (action === "add") {
3443
- await wizardAdd();
3444
- } else if (action === "manage") {
3445
- await wizardManage();
3446
- } else if (action === "remove") {
3447
- await wizardRemove();
3448
- }
3449
- console.log();
3450
- } catch (error) {
3451
- if (error?.name === "ExitPromptError") {
3452
- console.log("\n" + import_picocolors15.default.dim("Exited."));
3453
- break;
3454
- }
3455
- throw error;
3456
- }
3457
- }
3458
- };
3459
-
3460
- // src/utils/resolve-transport.ts
3461
- var resolveTransport = (input7) => {
3462
- if (!input7) return void 0;
3463
- if (input7 === "http" || input7 === "sse") return input7;
3464
- throw new Error(`Unsupported transport "${input7}" (expected: http, sse)`);
3465
- };
3466
-
3467
- // src/cli/manage.ts
3468
- var import_commander = require("commander");
3469
- var import_picocolors16 = __toESM(require("picocolors"), 1);
3470
-
3471
- // src/utils/parse-key-value-list.ts
3472
- var parseKeyValueList = (entries, separator) => {
3473
- if (!entries || entries.length === 0) return {};
3474
- const result = {};
3475
- for (const entry of entries) {
3476
- const splitIndex = entry.indexOf(separator);
3477
- if (splitIndex === -1) {
3478
- throw new Error(`Invalid entry "${entry}": expected "${separator}" separator`);
3479
- }
3480
- const key = entry.slice(0, splitIndex).trim();
3481
- const value = entry.slice(splitIndex + separator.length).trim();
3482
- if (!key) throw new Error(`Invalid entry "${entry}": empty key`);
3483
- result[key] = value;
3484
- }
3485
- return result;
3486
- };
3487
-
3488
- // src/cli/manage.ts
3489
- var requireTargetServerGroup = (serverName, scope) => {
3490
- const installed = listInstalledMcpServers(scope);
3491
- const grouped = groupInstalledServersByName(installed);
3492
- const targetGroup = grouped.get(serverName);
3493
- if (!targetGroup) {
3494
- logger.error(
3495
- `MCP server "${serverName}" is not configured in ${scope.global ? "global" : "project"} scope.`
3496
- );
3497
- process.exitCode = 1;
3498
- return void 0;
3499
- }
3500
- return targetGroup;
3501
- };
3502
- var mcpManageCommand = new import_commander.Command("manage").description("Inspect, modify, and sync installed MCP servers across coding agents").argument("[server-name]", "Optional server name to inspect or manage").option("-a, --agent <agents...>", "Target specific agents for update").option("-g, --global", "Manage global scope servers instead of project").option("-t, --transport <type>", "Transport type for remote servers (http or sse)").option("--header <header...>", "HTTP header (Header: Value), repeatable").option("--clear-headers", "Clear all HTTP headers for remote servers").option("--env <env...>", "Env var for stdio servers (KEY=VALUE), repeatable").option("--clear-env", "Clear all environment variables for stdio servers").option("--args <args...>", "CLI arguments for stdio/package servers").option("--clear-args", "Clear all arguments for stdio/package servers").option("--command <command>", "Executable command for stdio servers").option("--url <url>", "Remote endpoint URL").option("-y, --yes", "Skip interactive prompts").action(async (serverName, options) => {
3503
- try {
3504
- const cwd = process.cwd();
3505
- const isGlobal = Boolean(options.global);
3506
- const hasModifications = options.command !== void 0 || options.args !== void 0 || Boolean(options.clearArgs) || options.env !== void 0 || Boolean(options.clearEnv) || options.header !== void 0 || Boolean(options.clearHeaders) || options.url !== void 0 || options.transport !== void 0;
3507
- const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
3508
- if (hasModifications) {
3509
- if (options.url !== void 0 && options.command !== void 0) {
3510
- logger.error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
3511
- process.exitCode = 1;
3512
- return;
3513
- }
3514
- if (!serverName) {
3515
- logger.error('Missing required argument: "server-name" when passing modification flags.');
3516
- process.exitCode = 1;
3517
- return;
3518
- }
3519
- const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
3520
- if (!targetGroup) {
3521
- return;
3522
- }
3523
- const isCurrentRemote = Boolean(targetGroup.config.url && targetGroup.config.url.length > 0);
3524
- const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
3525
- if (willBeRemote) {
3526
- const ignoredStdioFlags = [];
3527
- if (options.env !== void 0) ignoredStdioFlags.push("--env");
3528
- if (options.clearEnv) ignoredStdioFlags.push("--clear-env");
3529
- if (options.args !== void 0) ignoredStdioFlags.push("--args");
3530
- if (options.clearArgs) ignoredStdioFlags.push("--clear-args");
3531
- if (ignoredStdioFlags.length > 0) {
3532
- const hint = options.url !== void 0 ? "When configuring a remote server, stdio flags are ignored." : "Use --command to switch to stdio mode.";
3533
- logger.warn(
3534
- `Server "${serverName}" is a remote server. The following stdio flags will be ignored: ${ignoredStdioFlags.join(", ")}. ${hint}`
3535
- );
3536
- }
3537
- } else {
3538
- const ignoredRemoteFlags = [];
3539
- if (options.header !== void 0) ignoredRemoteFlags.push("--header");
3540
- if (options.clearHeaders) ignoredRemoteFlags.push("--clear-headers");
3541
- if (options.transport !== void 0) ignoredRemoteFlags.push("--transport");
3542
- if (ignoredRemoteFlags.length > 0) {
3543
- const hint = options.command !== void 0 ? "When configuring a stdio server, remote flags are ignored." : "Use --url to switch to remote mode.";
3544
- logger.warn(
3545
- `Server "${serverName}" is a stdio server. The following remote flags will be ignored: ${ignoredRemoteFlags.join(", ")}. ${hint}`
3546
- );
3547
- }
3548
- }
3549
- const incomingDelta = {};
3550
- if (options.command !== void 0) {
3551
- incomingDelta.command = options.command;
3552
- }
3553
- if (options.clearArgs) {
3554
- incomingDelta.args = void 0;
3555
- }
3556
- if (options.args !== void 0) {
3557
- incomingDelta.args = options.args;
3558
- }
3559
- if (options.url !== void 0) {
3560
- incomingDelta.url = options.url;
3561
- }
3562
- if (options.transport !== void 0) {
3563
- incomingDelta.type = resolveTransport(options.transport);
3564
- }
3565
- if (options.clearEnv) {
3566
- incomingDelta.env = void 0;
3567
- }
3568
- if (options.env !== void 0) {
3569
- const parsedEnv = parseKeyValueList(options.env, "=");
3570
- const baseEnv = options.clearEnv ? {} : targetGroup.config.env ?? {};
3571
- incomingDelta.env = { ...baseEnv, ...parsedEnv };
3572
- }
3573
- if (options.clearHeaders) {
3574
- incomingDelta.headers = void 0;
3575
- }
3576
- if (options.header !== void 0) {
3577
- const parsedHeaders = parseKeyValueList(options.header, ":");
3578
- const baseHeaders = options.clearHeaders ? {} : targetGroup.config.headers ?? {};
3579
- incomingDelta.headers = { ...baseHeaders, ...parsedHeaders };
3580
- }
3581
- let targetAgents = targetGroup.agents;
3582
- if (options.agent !== void 0) {
3583
- const parsed = parseMcpAgentList(options.agent);
3584
- if (!parsed || parsed.length === 0) {
3585
- logger.error(`No valid agents recognized from: "${options.agent.join(", ")}".`);
3586
- process.exitCode = 1;
3587
- return;
3588
- }
3589
- targetAgents = parsed;
3590
- }
3591
- const updateResult = updateMcpServer({
3592
- serverName,
3593
- config: incomingDelta,
3594
- previousConfig: targetGroup.config,
3595
- agents: targetAgents,
3596
- global: isGlobal,
3597
- cwd
3598
- });
3599
- for (const item of updateResult.incompatible) {
3600
- logger.warn(`Skipping ${import_picocolors16.default.cyan(item.agent)}: ${item.reason}`);
3601
- }
3602
- const attemptedResults = updateResult.results.filter(
3603
- (r) => !updateResult.incompatible.some((i) => i.agent === r.agent)
3604
- );
3605
- if (attemptedResults.length === 0) {
3606
- const requestedTransport = updateResult.config.url ? updateResult.config.type ?? "http" : "stdio";
3607
- logger.error(
3608
- `None of the target agents support ${requestedTransport} transport. Update aborted.`
3609
- );
3610
- process.exitCode = 1;
3611
- return;
3612
- }
3613
- logger.info(
3614
- `Updating ${import_picocolors16.default.bold(serverName)} across ${import_picocolors16.default.cyan(String(attemptedResults.length))} agent(s)...`
3615
- );
3616
- let allSuccess = true;
3617
- for (const res of attemptedResults) {
3618
- if (res.success) {
3619
- logger.success(`${import_picocolors16.default.cyan(res.agent)}: Successfully updated in ${import_picocolors16.default.dim(res.path)}`);
3620
- logCoHostedNotice("configured", res.coConfiguredAgents);
3621
- } else {
3622
- allSuccess = false;
3623
- logger.error(`${import_picocolors16.default.cyan(res.agent)}: Update failed - ${res.error}`);
3624
- }
3625
- }
3626
- if (!allSuccess) {
3627
- process.exitCode = 1;
3628
- }
3629
- return;
3630
- }
3631
- if (!isInteractive) {
3632
- if (!serverName) {
3633
- logger.error(
3634
- 'Missing required argument: "server-name" for non-interactive manage command. Specify a server name or use interactive terminal.'
3635
- );
3636
- process.exitCode = 1;
3637
- return;
3638
- }
3639
- const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
3640
- if (!targetGroup) {
3641
- return;
3642
- }
3643
- displayServerDetails({
3644
- serverName,
3645
- config: targetGroup.config,
3646
- agents: targetGroup.agents,
3647
- global: isGlobal,
3648
- hasDivergence: targetGroup.hasDivergence
3649
- });
3650
- return;
3651
- }
3652
- await wizardManage({
3653
- global: options.global,
3654
- serverName
3655
- });
3656
- } catch (error) {
3657
- if (error && typeof error === "object" && "name" in error && error.name === "ExitPromptError") {
3658
- process.exit(0);
3659
- }
3660
- logger.error(toErrorMessage(error));
3661
- process.exitCode = 1;
3662
- }
3663
- });
3664
1929
  // Annotate the CommonJS export names for ESM import in node:
3665
1930
  0 && (module.exports = {
3666
1931
  AgentConfigStore,
3667
1932
  DEFAULT_REMOTE_TRANSPORT,
3668
- NPX_COMMAND,
3669
- NPX_DASH_Y,
3670
1933
  SECRET_HEADER_PATTERN,
3671
1934
  SECRET_KEY_PATTERN,
3672
- add,
3673
1935
  agentConfigStore,
3674
- buildLinkedAgentChoices,
1936
+ applyServerConfigDelta,
3675
1937
  buildMcpServerConfig,
3676
1938
  createAgentTransform,
3677
1939
  detectGloballyInstalledMcpAgents,
3678
1940
  detectProjectInstalledMcpAgents,
3679
1941
  detectUpdateTransition,
3680
- displayServerDetails,
3681
1942
  extractPackageName,
3682
- formatArgsString,
3683
- formatEnvText,
3684
- formatHeadersText,
3685
1943
  getCandidateAgentsForScope,
3686
1944
  getCoHostedAgents,
3687
1945
  getMcpAgentConfig,
3688
1946
  getMcpAgentTypes,
3689
1947
  getMcpAgentsSupportingProjectScope,
3690
- groupInstalledServersByName,
3691
- install,
3692
1948
  installMcpServer,
3693
1949
  installMcpServerForAgent,
3694
1950
  installMcpServerForAgents,
@@ -3698,53 +1954,21 @@ var mcpManageCommand = new import_commander.Command("manage").description("Inspe
3698
1954
  isRemoteMcpSource,
3699
1955
  isRemoteServerConfig,
3700
1956
  isStdioServerConfig,
3701
- linkedCheckbox,
3702
- list,
3703
1957
  listInstalledMcpServers,
3704
- listServersInConfigFile,
3705
- mainMenu,
3706
1958
  maskSecretHeader,
3707
1959
  maskSecretValue,
3708
- mcpAgentAliases,
3709
- mcpAgents,
3710
- mcpManageCommand,
3711
- normalizeServerConfig,
3712
- parseArgsString,
3713
- parseEnvText,
3714
- parseHeadersText,
3715
1960
  parseMcpSource,
3716
1961
  parseServerConfig,
3717
- parseSource,
3718
- promptArgsConfig,
3719
- promptEditArgs,
3720
- promptEditEnvConfig,
3721
- promptEditHeadersConfig,
3722
- promptEditKeyValueConfig,
3723
- promptEnvConfig,
3724
- promptHeadersConfig,
3725
- promptScope,
3726
- promptScopeAndAgents,
3727
- promptSwitchServerType,
3728
- readConfigFile,
3729
- remove,
1962
+ queryGroupedInstalledServers,
3730
1963
  removeMcpServer,
3731
- removeMcpServerFromAgent,
3732
- removeServerFromConfigFile,
3733
1964
  resolveConfigClusters,
3734
1965
  resolveMcpAgentAlias,
3735
1966
  resolveMcpConfigTarget,
3736
1967
  resolveTargetAgents,
3737
- resolveTransport,
3738
1968
  sanitizeUpdatedServerConfig,
3739
- sortAgentsWithClusters,
1969
+ sortAgentsByClusters,
3740
1970
  toRemoteServerConfig,
3741
1971
  toStdioServerConfig,
3742
- transformServerConfig,
3743
1972
  transformServerConfigForAgent,
3744
- update,
3745
- updateMcpServer,
3746
- wizardAdd,
3747
- wizardManage,
3748
- wizardRemove,
3749
- writeServerToConfigFile
1973
+ updateMcpServer
3750
1974
  });