@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/LICENSE +21 -0
- package/README.md +22 -14
- package/dist/chunk-AWD3VQ3S.js +1624 -0
- package/dist/chunk-O3BRJFEC.js +1898 -0
- package/dist/cli.cjs +1057 -979
- package/dist/cli.js +188 -17
- package/dist/index.cjs +702 -2478
- package/dist/index.d.cts +152 -452
- package/dist/index.d.ts +152 -452
- package/dist/index.js +9 -85
- package/dist/{chunk-BUKA3NPJ.js → interactive.cjs} +1555 -1779
- package/dist/interactive.d.cts +228 -0
- package/dist/interactive.d.ts +228 -0
- package/dist/interactive.js +53 -0
- package/dist/types-lEP3zGvf.d.cts +128 -0
- package/dist/types-lEP3zGvf.d.ts +128 -0
- package/package.json +31 -1
package/dist/cli.cjs
CHANGED
|
@@ -26,8 +26,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
var import_commander5 = require("commander");
|
|
27
27
|
|
|
28
28
|
// src/cli/add.ts
|
|
29
|
-
var
|
|
30
|
-
var
|
|
29
|
+
var import_commander = require("commander");
|
|
30
|
+
var import_picocolors12 = __toESM(require("picocolors"), 1);
|
|
31
31
|
|
|
32
32
|
// src/agents.ts
|
|
33
33
|
var import_node_fs = require("fs");
|
|
@@ -537,7 +537,7 @@ var KNOWN_COMMAND_RUNNERS = /* @__PURE__ */ new Set([
|
|
|
537
537
|
]);
|
|
538
538
|
var SCRIPT_EXTENSION_REGEX = /\.(?:js|ts|mjs|cjs|py|sh|rb|go)$/i;
|
|
539
539
|
|
|
540
|
-
// src/
|
|
540
|
+
// src/server-config.ts
|
|
541
541
|
var buildMcpServerConfig = (parsed, options = {}) => {
|
|
542
542
|
if (parsed.type === "remote") {
|
|
543
543
|
const config2 = {
|
|
@@ -575,8 +575,6 @@ var buildMcpServerConfig = (parsed, options = {}) => {
|
|
|
575
575
|
}
|
|
576
576
|
return config;
|
|
577
577
|
};
|
|
578
|
-
|
|
579
|
-
// src/parse-server-config.ts
|
|
580
578
|
var parseServerConfig = (raw) => {
|
|
581
579
|
if (!raw || typeof raw !== "object") return {};
|
|
582
580
|
const data = raw;
|
|
@@ -603,6 +601,236 @@ var parseServerConfig = (raw) => {
|
|
|
603
601
|
}
|
|
604
602
|
return {};
|
|
605
603
|
};
|
|
604
|
+
var toRemoteServerConfig = (config, defaultTransport = "http") => {
|
|
605
|
+
const {
|
|
606
|
+
command: _droppedCommand,
|
|
607
|
+
args: _droppedArgs,
|
|
608
|
+
env: _droppedEnv,
|
|
609
|
+
...remoteConfig
|
|
610
|
+
} = config;
|
|
611
|
+
return {
|
|
612
|
+
...remoteConfig,
|
|
613
|
+
type: remoteConfig.type ?? defaultTransport
|
|
614
|
+
};
|
|
615
|
+
};
|
|
616
|
+
var toStdioServerConfig = (config) => {
|
|
617
|
+
const {
|
|
618
|
+
url: _droppedUrl,
|
|
619
|
+
type: _droppedType,
|
|
620
|
+
headers: _droppedHeaders,
|
|
621
|
+
...stdioConfig
|
|
622
|
+
} = config;
|
|
623
|
+
return stdioConfig;
|
|
624
|
+
};
|
|
625
|
+
var detectUpdateTransition = (incoming, previous) => {
|
|
626
|
+
if (!previous) {
|
|
627
|
+
return incoming.url ? "switch-to-remote" : "switch-to-stdio";
|
|
628
|
+
}
|
|
629
|
+
const previousIsRemote = Boolean(previous.url && previous.url.length > 0);
|
|
630
|
+
if (previousIsRemote) {
|
|
631
|
+
if (incoming.command && !incoming.url) {
|
|
632
|
+
return "switch-to-stdio";
|
|
633
|
+
}
|
|
634
|
+
return "merge-remote";
|
|
635
|
+
}
|
|
636
|
+
if (incoming.url && !incoming.command) {
|
|
637
|
+
return "switch-to-remote";
|
|
638
|
+
}
|
|
639
|
+
return "merge-stdio";
|
|
640
|
+
};
|
|
641
|
+
var sanitizeUpdatedServerConfig = (incoming, previous) => {
|
|
642
|
+
const transition = detectUpdateTransition(incoming, previous);
|
|
643
|
+
const targetTransport = incoming.type ?? previous?.type ?? "http";
|
|
644
|
+
switch (transition) {
|
|
645
|
+
case "switch-to-remote": {
|
|
646
|
+
const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
|
|
647
|
+
return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
|
|
648
|
+
}
|
|
649
|
+
case "switch-to-stdio": {
|
|
650
|
+
const cleanBase = previous ? toStdioServerConfig(previous) : {};
|
|
651
|
+
return toStdioServerConfig({ ...cleanBase, ...incoming });
|
|
652
|
+
}
|
|
653
|
+
case "merge-remote": {
|
|
654
|
+
return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
|
|
655
|
+
}
|
|
656
|
+
case "merge-stdio":
|
|
657
|
+
default: {
|
|
658
|
+
return toStdioServerConfig({ ...previous, ...incoming });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
var applyServerConfigDelta = (previousConfig, options) => {
|
|
663
|
+
if (options.url !== void 0 && options.command !== void 0) {
|
|
664
|
+
throw new Error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
|
|
665
|
+
}
|
|
666
|
+
const isCurrentRemote = Boolean(previousConfig?.url && previousConfig.url.length > 0);
|
|
667
|
+
const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
|
|
668
|
+
const protocol = willBeRemote ? "remote" : "stdio";
|
|
669
|
+
const ignoredFlags = [];
|
|
670
|
+
if (willBeRemote) {
|
|
671
|
+
if (options.env !== void 0) ignoredFlags.push("--env");
|
|
672
|
+
if (options.clearEnv) ignoredFlags.push("--clear-env");
|
|
673
|
+
if (options.args !== void 0) ignoredFlags.push("--args");
|
|
674
|
+
if (options.clearArgs) ignoredFlags.push("--clear-args");
|
|
675
|
+
} else {
|
|
676
|
+
if (options.headers !== void 0) ignoredFlags.push("--header");
|
|
677
|
+
if (options.clearHeaders) ignoredFlags.push("--clear-headers");
|
|
678
|
+
if (options.transport !== void 0) ignoredFlags.push("--transport");
|
|
679
|
+
}
|
|
680
|
+
const incomingDelta = {};
|
|
681
|
+
if (options.command !== void 0) {
|
|
682
|
+
incomingDelta.command = options.command;
|
|
683
|
+
}
|
|
684
|
+
if (options.clearArgs) {
|
|
685
|
+
incomingDelta.args = void 0;
|
|
686
|
+
}
|
|
687
|
+
if (options.args !== void 0) {
|
|
688
|
+
incomingDelta.args = options.args;
|
|
689
|
+
}
|
|
690
|
+
if (options.url !== void 0) {
|
|
691
|
+
incomingDelta.url = options.url;
|
|
692
|
+
}
|
|
693
|
+
if (options.transport !== void 0) {
|
|
694
|
+
incomingDelta.type = options.transport;
|
|
695
|
+
}
|
|
696
|
+
if (options.clearEnv) {
|
|
697
|
+
incomingDelta.env = void 0;
|
|
698
|
+
}
|
|
699
|
+
if (options.env !== void 0) {
|
|
700
|
+
const baseEnv = options.clearEnv ? {} : previousConfig?.env ?? {};
|
|
701
|
+
incomingDelta.env = { ...baseEnv, ...options.env };
|
|
702
|
+
}
|
|
703
|
+
if (options.clearHeaders) {
|
|
704
|
+
incomingDelta.headers = void 0;
|
|
705
|
+
}
|
|
706
|
+
if (options.headers !== void 0) {
|
|
707
|
+
const baseHeaders = options.clearHeaders ? {} : previousConfig?.headers ?? {};
|
|
708
|
+
incomingDelta.headers = { ...baseHeaders, ...options.headers };
|
|
709
|
+
}
|
|
710
|
+
const transition = detectUpdateTransition(incomingDelta, previousConfig);
|
|
711
|
+
const cleanConfig = sanitizeUpdatedServerConfig(incomingDelta, previousConfig);
|
|
712
|
+
return {
|
|
713
|
+
config: cleanConfig,
|
|
714
|
+
transition,
|
|
715
|
+
protocol,
|
|
716
|
+
ignoredFlags
|
|
717
|
+
};
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// src/source-parser.ts
|
|
721
|
+
var REMOTE_URL_REGEX = /^https?:\/\//i;
|
|
722
|
+
var HAS_WHITESPACE_REGEX = /\s/;
|
|
723
|
+
var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
|
|
724
|
+
var PATH_SEPARATOR_REGEX = /[/\\]/;
|
|
725
|
+
var stripVersionSuffix = (input7) => {
|
|
726
|
+
if (input7.startsWith("@")) {
|
|
727
|
+
const secondAtIndex = input7.indexOf("@", 1);
|
|
728
|
+
if (secondAtIndex > 0) return input7.slice(0, secondAtIndex);
|
|
729
|
+
return input7;
|
|
730
|
+
}
|
|
731
|
+
const atIndex = input7.lastIndexOf("@");
|
|
732
|
+
if (atIndex > 0) return input7.slice(0, atIndex);
|
|
733
|
+
return input7;
|
|
734
|
+
};
|
|
735
|
+
var stripScopePrefix = (input7) => {
|
|
736
|
+
if (!input7.startsWith("@") || !input7.includes("/")) return input7;
|
|
737
|
+
const parts = input7.split("/");
|
|
738
|
+
return parts[1] || input7;
|
|
739
|
+
};
|
|
740
|
+
var stripPathPrefix = (input7) => {
|
|
741
|
+
if (!PATH_SEPARATOR_REGEX.test(input7)) return input7;
|
|
742
|
+
const segments = input7.split(PATH_SEPARATOR_REGEX);
|
|
743
|
+
const basename = segments[segments.length - 1];
|
|
744
|
+
return basename || input7;
|
|
745
|
+
};
|
|
746
|
+
var extractPackageName = (input7) => {
|
|
747
|
+
let name = stripVersionSuffix(input7);
|
|
748
|
+
name = stripScopePrefix(name);
|
|
749
|
+
name = stripPathPrefix(name);
|
|
750
|
+
name = name.replace(SCRIPT_EXTENSION_REGEX, "");
|
|
751
|
+
for (const prefix of PACKAGE_NAME_PREFIX_STRIP) {
|
|
752
|
+
if (name.startsWith(prefix)) {
|
|
753
|
+
name = name.slice(prefix.length);
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
for (const suffix of PACKAGE_NAME_SUFFIX_STRIP) {
|
|
758
|
+
if (name.endsWith(suffix)) {
|
|
759
|
+
name = name.slice(0, -suffix.length);
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return name || MCP_DEFAULT_SERVER_NAME;
|
|
764
|
+
};
|
|
765
|
+
var inferNameFromUrl = (input7) => {
|
|
766
|
+
try {
|
|
767
|
+
const url = new URL(input7);
|
|
768
|
+
const host = url.hostname;
|
|
769
|
+
const labels = host.split(".").filter((segment) => segment.length > 0);
|
|
770
|
+
if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
|
|
771
|
+
const meaningfulLabels = labels.filter((label) => {
|
|
772
|
+
const lower = label.toLowerCase();
|
|
773
|
+
if (COMMON_TLD_LABELS.has(lower)) return false;
|
|
774
|
+
if (GENERIC_HOST_PREFIXES.has(lower)) return false;
|
|
775
|
+
return true;
|
|
776
|
+
});
|
|
777
|
+
if (meaningfulLabels.length > 0) return meaningfulLabels[0];
|
|
778
|
+
if (labels.length >= 2) return labels[labels.length - 2];
|
|
779
|
+
return labels[labels.length - 1] || MCP_DEFAULT_SERVER_NAME;
|
|
780
|
+
} catch {
|
|
781
|
+
return MCP_DEFAULT_SERVER_NAME;
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
var inferNameFromCommand = (command) => {
|
|
785
|
+
const tokens = command.trim().split(/\s+/);
|
|
786
|
+
const runnerBase = tokens[0]?.split(PATH_SEPARATOR_REGEX).pop() ?? "";
|
|
787
|
+
const startIndex = KNOWN_COMMAND_RUNNERS.has(runnerBase) ? 1 : 0;
|
|
788
|
+
for (let tokenIndex = startIndex; tokenIndex < tokens.length; tokenIndex += 1) {
|
|
789
|
+
const token = tokens[tokenIndex];
|
|
790
|
+
if (!token || token.startsWith("-")) continue;
|
|
791
|
+
return extractPackageName(token);
|
|
792
|
+
}
|
|
793
|
+
const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
|
|
794
|
+
return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
|
|
795
|
+
};
|
|
796
|
+
var parseMcpSource = (input7) => {
|
|
797
|
+
const trimmed = input7.trim();
|
|
798
|
+
if (trimmed.length === 0) {
|
|
799
|
+
throw new Error(
|
|
800
|
+
"Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
if (REMOTE_URL_REGEX.test(trimmed)) {
|
|
804
|
+
return {
|
|
805
|
+
type: "remote",
|
|
806
|
+
value: trimmed,
|
|
807
|
+
inferredName: inferNameFromUrl(trimmed)
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
if (HAS_WHITESPACE_REGEX.test(trimmed)) {
|
|
811
|
+
return {
|
|
812
|
+
type: "command",
|
|
813
|
+
value: trimmed,
|
|
814
|
+
inferredName: inferNameFromCommand(trimmed)
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
if (PACKAGE_NAME_REGEX.test(trimmed)) {
|
|
818
|
+
return {
|
|
819
|
+
type: "package",
|
|
820
|
+
value: trimmed,
|
|
821
|
+
inferredName: extractPackageName(trimmed)
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
return {
|
|
825
|
+
type: "command",
|
|
826
|
+
value: trimmed,
|
|
827
|
+
inferredName: inferNameFromCommand(trimmed)
|
|
828
|
+
};
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
// src/config-store.ts
|
|
832
|
+
var import_node_fs6 = require("fs");
|
|
833
|
+
var import_node_path3 = require("path");
|
|
606
834
|
|
|
607
835
|
// src/utils/is-plain-object.ts
|
|
608
836
|
var isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -887,220 +1115,45 @@ var listServersInConfigFile = (filePath, format, dottedKey) => {
|
|
|
887
1115
|
return isPlainObject(entries) ? entries : {};
|
|
888
1116
|
};
|
|
889
1117
|
|
|
890
|
-
// src/
|
|
891
|
-
var
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
serverName
|
|
930
|
-
);
|
|
931
|
-
}
|
|
932
|
-
listServers(target) {
|
|
933
|
-
if (!target.dottedKey) return {};
|
|
934
|
-
return listServersInConfigFile(target.filePath, target.format, target.dottedKey);
|
|
935
|
-
}
|
|
936
|
-
};
|
|
937
|
-
var AgentConfigStore = class {
|
|
938
|
-
constructor(adapter = new FsConfigStoreAdapter()) {
|
|
939
|
-
this.adapter = adapter;
|
|
940
|
-
}
|
|
941
|
-
adapter;
|
|
942
|
-
getAdapter() {
|
|
943
|
-
return this.adapter;
|
|
944
|
-
}
|
|
945
|
-
resolveTarget(agent, options = {}) {
|
|
946
|
-
const agentConfig = typeof agent === "string" ? getMcpAgentConfig(agent) : agent;
|
|
947
|
-
const target = resolveMcpConfigTarget(agentConfig, options);
|
|
948
|
-
return { agent: agentConfig, target };
|
|
949
|
-
}
|
|
950
|
-
resolveDescriptor(agent, options = {}) {
|
|
951
|
-
const { agent: agentConfig, target } = this.resolveTarget(agent, options);
|
|
952
|
-
return {
|
|
953
|
-
filePath: target.configPath,
|
|
954
|
-
format: agentConfig.format,
|
|
955
|
-
dottedKey: target.configKey
|
|
956
|
-
};
|
|
957
|
-
}
|
|
958
|
-
writeServer(agent, serverName, serverConfig, options = {}) {
|
|
959
|
-
const descriptor = this.resolveDescriptor(agent, options);
|
|
960
|
-
this.adapter.writeServer(descriptor, serverName, serverConfig);
|
|
961
|
-
return { path: descriptor.filePath };
|
|
962
|
-
}
|
|
963
|
-
removeServer(agent, serverName, options = {}) {
|
|
964
|
-
const descriptor = this.resolveDescriptor(agent, options);
|
|
965
|
-
if (!this.adapter.exists(descriptor.filePath)) {
|
|
966
|
-
return { path: descriptor.filePath, removed: false };
|
|
967
|
-
}
|
|
968
|
-
const removed = this.adapter.removeServer(descriptor, serverName);
|
|
969
|
-
return { path: descriptor.filePath, removed };
|
|
970
|
-
}
|
|
971
|
-
listServers(agent, options = {}) {
|
|
972
|
-
const descriptor = this.resolveDescriptor(agent, options);
|
|
973
|
-
if (!this.adapter.exists(descriptor.filePath)) {
|
|
974
|
-
return { path: descriptor.filePath, exists: false, servers: {} };
|
|
975
|
-
}
|
|
976
|
-
const servers = this.adapter.listServers(descriptor);
|
|
977
|
-
return { path: descriptor.filePath, exists: true, servers };
|
|
978
|
-
}
|
|
979
|
-
read(agent, options = {}) {
|
|
980
|
-
const descriptor = this.resolveDescriptor(agent, options);
|
|
981
|
-
if (!this.adapter.exists(descriptor.filePath)) {
|
|
982
|
-
return {};
|
|
983
|
-
}
|
|
984
|
-
return this.adapter.read(descriptor);
|
|
985
|
-
}
|
|
986
|
-
readServer(agent, serverName, options = {}) {
|
|
987
|
-
const { exists, servers } = this.listServers(agent, options);
|
|
988
|
-
if (!exists) return void 0;
|
|
989
|
-
return servers[serverName];
|
|
990
|
-
}
|
|
991
|
-
};
|
|
992
|
-
var agentConfigStore = new AgentConfigStore();
|
|
993
|
-
|
|
994
|
-
// src/utils/to-error-message.ts
|
|
995
|
-
var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
|
|
996
|
-
|
|
997
|
-
// src/resolve-config-clusters.ts
|
|
998
|
-
var getCandidateAgentsForScope = (options = {}) => {
|
|
999
|
-
return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
1000
|
-
};
|
|
1001
|
-
var getCoHostedAgents = (agentType, options = {}) => {
|
|
1002
|
-
const currentAgent = getMcpAgentConfig(agentType);
|
|
1003
|
-
const currentTarget = resolveMcpConfigTarget(currentAgent, options);
|
|
1004
|
-
const candidates = getCandidateAgentsForScope(options);
|
|
1005
|
-
const coHosted = [];
|
|
1006
|
-
for (const candidateType of candidates) {
|
|
1007
|
-
if (candidateType === agentType) continue;
|
|
1008
|
-
const candidateConfig = getMcpAgentConfig(candidateType);
|
|
1009
|
-
const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
|
|
1010
|
-
if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
|
|
1011
|
-
coHosted.push(candidateType);
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
return coHosted;
|
|
1015
|
-
};
|
|
1016
|
-
var resolveConfigClusters = (agentTypes, options = {}) => {
|
|
1017
|
-
const clustersByPath = /* @__PURE__ */ new Map();
|
|
1018
|
-
for (const agentType of agentTypes) {
|
|
1019
|
-
const agentConfig = getMcpAgentConfig(agentType);
|
|
1020
|
-
const target = resolveMcpConfigTarget(agentConfig, options);
|
|
1021
|
-
let keyMap = clustersByPath.get(target.configPath);
|
|
1022
|
-
if (!keyMap) {
|
|
1023
|
-
keyMap = /* @__PURE__ */ new Map();
|
|
1024
|
-
clustersByPath.set(target.configPath, keyMap);
|
|
1025
|
-
}
|
|
1026
|
-
let cluster = keyMap.get(target.configKey);
|
|
1027
|
-
if (!cluster) {
|
|
1028
|
-
const allCoHosted = getCoHostedAgents(agentType, options);
|
|
1029
|
-
cluster = {
|
|
1030
|
-
configPath: target.configPath,
|
|
1031
|
-
configKey: target.configKey,
|
|
1032
|
-
targetAgents: [],
|
|
1033
|
-
coHostedAgents: allCoHosted
|
|
1034
|
-
};
|
|
1035
|
-
keyMap.set(target.configKey, cluster);
|
|
1036
|
-
}
|
|
1037
|
-
if (!cluster.targetAgents.includes(agentType)) {
|
|
1038
|
-
cluster.targetAgents.push(agentType);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
const clusters = [];
|
|
1042
|
-
for (const keyMap of clustersByPath.values()) {
|
|
1043
|
-
for (const cluster of keyMap.values()) {
|
|
1044
|
-
cluster.coHostedAgents = cluster.coHostedAgents.filter(
|
|
1045
|
-
(co) => !cluster.targetAgents.includes(co)
|
|
1046
|
-
);
|
|
1047
|
-
clusters.push(cluster);
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
return clusters;
|
|
1051
|
-
};
|
|
1052
|
-
var sortAgentsWithClusters = (agentTypes, options = {}) => {
|
|
1053
|
-
const clusters = resolveConfigClusters(agentTypes, options);
|
|
1054
|
-
const sorted = [];
|
|
1055
|
-
for (const cluster of clusters) {
|
|
1056
|
-
for (const agent of cluster.targetAgents) {
|
|
1057
|
-
if (!sorted.includes(agent)) {
|
|
1058
|
-
sorted.push(agent);
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
return sorted;
|
|
1063
|
-
};
|
|
1064
|
-
|
|
1065
|
-
// src/transforms/index.ts
|
|
1066
|
-
var DIALECT_PRESETS = {
|
|
1067
|
-
vscode: {
|
|
1068
|
-
stdioTransport: "type-stdio",
|
|
1069
|
-
remoteTransport: "type-http-sse"
|
|
1070
|
-
},
|
|
1071
|
-
augment: {
|
|
1072
|
-
stdioTransport: "none",
|
|
1073
|
-
remoteTransport: "type-http-sse"
|
|
1074
|
-
},
|
|
1075
|
-
amp: {
|
|
1076
|
-
stdioTransport: "none",
|
|
1077
|
-
remoteTransport: "none"
|
|
1078
|
-
},
|
|
1079
|
-
trae: {
|
|
1080
|
-
stdioTransport: "none",
|
|
1081
|
-
remoteTransport: "sse-only-type"
|
|
1082
|
-
},
|
|
1083
|
-
grok: {
|
|
1084
|
-
stdioTransport: "none",
|
|
1085
|
-
remoteTransport: "sse-only-type"
|
|
1086
|
-
},
|
|
1087
|
-
cline: {
|
|
1088
|
-
stdioTransport: "none",
|
|
1089
|
-
remoteTransport: "streamableHttp"
|
|
1090
|
-
},
|
|
1091
|
-
goose: {
|
|
1092
|
-
stdioTransport: "type-stdio",
|
|
1093
|
-
remoteTransport: "streamable_http",
|
|
1094
|
-
commandField: "cmd",
|
|
1095
|
-
envField: "envs",
|
|
1096
|
-
urlField: "uri",
|
|
1097
|
-
defaultEnvEmpty: true,
|
|
1098
|
-
defaultHeadersEmpty: true,
|
|
1099
|
-
includeServerName: true,
|
|
1100
|
-
timeoutSeconds: GOOSE_TIMEOUT_SECONDS,
|
|
1101
|
-
extraFields: {
|
|
1102
|
-
description: "",
|
|
1103
|
-
enabled: true
|
|
1118
|
+
// src/transforms/index.ts
|
|
1119
|
+
var DIALECT_PRESETS = {
|
|
1120
|
+
vscode: {
|
|
1121
|
+
stdioTransport: "type-stdio",
|
|
1122
|
+
remoteTransport: "type-http-sse"
|
|
1123
|
+
},
|
|
1124
|
+
augment: {
|
|
1125
|
+
stdioTransport: "none",
|
|
1126
|
+
remoteTransport: "type-http-sse"
|
|
1127
|
+
},
|
|
1128
|
+
amp: {
|
|
1129
|
+
stdioTransport: "none",
|
|
1130
|
+
remoteTransport: "none"
|
|
1131
|
+
},
|
|
1132
|
+
trae: {
|
|
1133
|
+
stdioTransport: "none",
|
|
1134
|
+
remoteTransport: "sse-only-type"
|
|
1135
|
+
},
|
|
1136
|
+
grok: {
|
|
1137
|
+
stdioTransport: "none",
|
|
1138
|
+
remoteTransport: "sse-only-type"
|
|
1139
|
+
},
|
|
1140
|
+
cline: {
|
|
1141
|
+
stdioTransport: "none",
|
|
1142
|
+
remoteTransport: "streamableHttp"
|
|
1143
|
+
},
|
|
1144
|
+
goose: {
|
|
1145
|
+
stdioTransport: "type-stdio",
|
|
1146
|
+
remoteTransport: "streamable_http",
|
|
1147
|
+
commandField: "cmd",
|
|
1148
|
+
envField: "envs",
|
|
1149
|
+
urlField: "uri",
|
|
1150
|
+
defaultEnvEmpty: true,
|
|
1151
|
+
defaultHeadersEmpty: true,
|
|
1152
|
+
includeServerName: true,
|
|
1153
|
+
timeoutSeconds: GOOSE_TIMEOUT_SECONDS,
|
|
1154
|
+
extraFields: {
|
|
1155
|
+
description: "",
|
|
1156
|
+
enabled: true
|
|
1104
1157
|
}
|
|
1105
1158
|
},
|
|
1106
1159
|
"kimi-code": {
|
|
@@ -1269,246 +1322,392 @@ var transformServerConfigForAgent = (agent, serverName, config, context = { glob
|
|
|
1269
1322
|
return config;
|
|
1270
1323
|
};
|
|
1271
1324
|
|
|
1272
|
-
// src/
|
|
1273
|
-
var
|
|
1274
|
-
|
|
1275
|
-
|
|
1325
|
+
// src/utils/to-error-message.ts
|
|
1326
|
+
var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
|
|
1327
|
+
|
|
1328
|
+
// src/config-store.ts
|
|
1329
|
+
var resolveMcpConfigTarget = (agent, options = {}) => {
|
|
1276
1330
|
const isGlobal = options.global ?? false;
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
|
|
1282
|
-
global: isGlobal
|
|
1283
|
-
});
|
|
1284
|
-
agentConfigStore.writeServer(primaryAgent, serverName, transformed, options);
|
|
1285
|
-
for (const agentType of cluster.targetAgents) {
|
|
1286
|
-
resultsByAgent.set(agentType, {
|
|
1287
|
-
agent: agentType,
|
|
1288
|
-
success: true,
|
|
1289
|
-
path: cluster.configPath,
|
|
1290
|
-
coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1293
|
-
} catch (error) {
|
|
1294
|
-
const errorMsg = toErrorMessage(error);
|
|
1295
|
-
for (const agentType of cluster.targetAgents) {
|
|
1296
|
-
resultsByAgent.set(agentType, {
|
|
1297
|
-
agent: agentType,
|
|
1298
|
-
success: false,
|
|
1299
|
-
path: cluster.configPath,
|
|
1300
|
-
error: errorMsg
|
|
1301
|
-
});
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
return agentTypes.map((agentType) => resultsByAgent.get(agentType));
|
|
1306
|
-
};
|
|
1307
|
-
var installToCompatibleAgents = (serverName, serverConfig, options) => {
|
|
1308
|
-
const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
|
|
1309
|
-
const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
|
|
1310
|
-
const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
|
|
1311
|
-
const installedResults = installMcpServerForAgents(serverName, serverConfig, compatibleAgents, {
|
|
1312
|
-
global: isGlobal,
|
|
1313
|
-
cwd
|
|
1314
|
-
});
|
|
1315
|
-
const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
|
|
1316
|
-
return allAgents.map((agentType) => {
|
|
1317
|
-
const incompatibleReason = incompatibleMap.get(agentType);
|
|
1318
|
-
if (incompatibleReason) {
|
|
1319
|
-
return {
|
|
1320
|
-
agent: agentType,
|
|
1321
|
-
success: false,
|
|
1322
|
-
path: "",
|
|
1323
|
-
error: incompatibleReason
|
|
1324
|
-
};
|
|
1325
|
-
}
|
|
1326
|
-
return installedMap.get(agentType);
|
|
1327
|
-
});
|
|
1331
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1332
|
+
const configPath = agent.resolveConfigPath ? agent.resolveConfigPath({ global: isGlobal, cwd }) : !isGlobal && agent.projectConfigPath ? (0, import_node_path3.join)(cwd, agent.projectConfigPath) : agent.globalConfigPath;
|
|
1333
|
+
const configKey = !isGlobal && agent.projectConfigKey ? agent.projectConfigKey : agent.configKey;
|
|
1334
|
+
return { configPath, configKey };
|
|
1328
1335
|
};
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
var parseMcpAgentList = (input7) => {
|
|
1332
|
-
if (!input7 || input7.length === 0) return void 0;
|
|
1333
|
-
if (input7.includes("*")) return getMcpAgentTypes();
|
|
1334
|
-
const resolved = [];
|
|
1335
|
-
for (const value of input7) {
|
|
1336
|
-
const agentType = resolveMcpAgentAlias(value);
|
|
1337
|
-
if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
|
|
1338
|
-
resolved.push(agentType);
|
|
1339
|
-
}
|
|
1340
|
-
return resolved;
|
|
1336
|
+
var getCandidateAgentsForScope = (options = {}) => {
|
|
1337
|
+
return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
1341
1338
|
};
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
if (!input7 || input7.length === 0) return void 0;
|
|
1346
|
-
const rawList = [...input7];
|
|
1347
|
-
if (rawList.every((item) => isMcpAgentType(item))) {
|
|
1348
|
-
return rawList;
|
|
1339
|
+
var FsConfigStoreAdapter = class {
|
|
1340
|
+
exists(filePath) {
|
|
1341
|
+
return (0, import_node_fs6.existsSync)(filePath);
|
|
1349
1342
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
var resolveTargetAgents = (query = {}) => {
|
|
1353
|
-
const cwd = query.cwd ?? process.cwd();
|
|
1354
|
-
const isGlobal = query.global ?? false;
|
|
1355
|
-
let explicitAgents = normalizeRequestedAgents(query.requested);
|
|
1356
|
-
if (query.all) {
|
|
1357
|
-
explicitAgents = getMcpAgentTypes();
|
|
1343
|
+
read(target) {
|
|
1344
|
+
return readConfigFile(target.filePath, target.format);
|
|
1358
1345
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
const allAgents = candidateAgents.filter(
|
|
1363
|
-
(type, index) => candidateAgents.indexOf(type) === index
|
|
1364
|
-
);
|
|
1365
|
-
const incompatible = [];
|
|
1366
|
-
const compatibleAgents = [];
|
|
1367
|
-
for (const agentType of allAgents) {
|
|
1368
|
-
const config = getMcpAgentConfig(agentType);
|
|
1369
|
-
if (query.transport && !isMcpTransportSupported(config, query.transport)) {
|
|
1370
|
-
incompatible.push({
|
|
1371
|
-
agent: agentType,
|
|
1372
|
-
reason: config.unsupportedTransportMessage ?? `agent ${agentType} only supports ${config.supportedTransports.join(", ")} transport (attempted ${query.transport})`
|
|
1373
|
-
});
|
|
1374
|
-
} else {
|
|
1375
|
-
compatibleAgents.push(agentType);
|
|
1346
|
+
writeServer(target, serverName, serverConfig) {
|
|
1347
|
+
if (!target.dottedKey) {
|
|
1348
|
+
throw new Error(`Cannot write server: missing dottedKey for ${target.filePath}`);
|
|
1376
1349
|
}
|
|
1350
|
+
writeServerToConfigFile(
|
|
1351
|
+
target.filePath,
|
|
1352
|
+
target.format,
|
|
1353
|
+
target.dottedKey,
|
|
1354
|
+
serverName,
|
|
1355
|
+
serverConfig
|
|
1356
|
+
);
|
|
1377
1357
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
diagnostic = "No valid target agents specified.";
|
|
1387
|
-
}
|
|
1358
|
+
removeServer(target, serverName) {
|
|
1359
|
+
if (!target.dottedKey) return false;
|
|
1360
|
+
return removeServerFromConfigFile(
|
|
1361
|
+
target.filePath,
|
|
1362
|
+
target.format,
|
|
1363
|
+
target.dottedKey,
|
|
1364
|
+
serverName
|
|
1365
|
+
);
|
|
1388
1366
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
allAgents,
|
|
1393
|
-
candidateAgents: allAgents,
|
|
1394
|
-
detected,
|
|
1395
|
-
isDetected,
|
|
1396
|
-
incompatible,
|
|
1397
|
-
diagnostic
|
|
1398
|
-
};
|
|
1399
|
-
};
|
|
1400
|
-
|
|
1401
|
-
// src/source-parser.ts
|
|
1402
|
-
var REMOTE_URL_REGEX = /^https?:\/\//i;
|
|
1403
|
-
var HAS_WHITESPACE_REGEX = /\s/;
|
|
1404
|
-
var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
|
|
1405
|
-
var PATH_SEPARATOR_REGEX = /[/\\]/;
|
|
1406
|
-
var stripVersionSuffix = (input7) => {
|
|
1407
|
-
if (input7.startsWith("@")) {
|
|
1408
|
-
const secondAtIndex = input7.indexOf("@", 1);
|
|
1409
|
-
if (secondAtIndex > 0) return input7.slice(0, secondAtIndex);
|
|
1410
|
-
return input7;
|
|
1367
|
+
listServers(target) {
|
|
1368
|
+
if (!target.dottedKey) return {};
|
|
1369
|
+
return listServersInConfigFile(target.filePath, target.format, target.dottedKey);
|
|
1411
1370
|
}
|
|
1412
|
-
const atIndex = input7.lastIndexOf("@");
|
|
1413
|
-
if (atIndex > 0) return input7.slice(0, atIndex);
|
|
1414
|
-
return input7;
|
|
1415
|
-
};
|
|
1416
|
-
var stripScopePrefix = (input7) => {
|
|
1417
|
-
if (!input7.startsWith("@") || !input7.includes("/")) return input7;
|
|
1418
|
-
const parts = input7.split("/");
|
|
1419
|
-
return parts[1] || input7;
|
|
1420
1371
|
};
|
|
1421
|
-
var
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1372
|
+
var AgentConfigStore = class {
|
|
1373
|
+
constructor(adapter = new FsConfigStoreAdapter()) {
|
|
1374
|
+
this.adapter = adapter;
|
|
1375
|
+
}
|
|
1376
|
+
adapter;
|
|
1377
|
+
getAdapter() {
|
|
1378
|
+
return this.adapter;
|
|
1379
|
+
}
|
|
1380
|
+
// ---- Single-agent primitives ----
|
|
1381
|
+
resolveTarget(agent, options = {}) {
|
|
1382
|
+
const agentConfig = typeof agent === "string" ? getMcpAgentConfig(agent) : agent;
|
|
1383
|
+
const target = resolveMcpConfigTarget(agentConfig, options);
|
|
1384
|
+
return { agent: agentConfig, target };
|
|
1385
|
+
}
|
|
1386
|
+
resolveDescriptor(agent, options = {}) {
|
|
1387
|
+
const { agent: agentConfig, target } = this.resolveTarget(agent, options);
|
|
1388
|
+
return {
|
|
1389
|
+
filePath: target.configPath,
|
|
1390
|
+
format: agentConfig.format,
|
|
1391
|
+
dottedKey: target.configKey
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
writeServer(agent, serverName, serverConfig, options = {}) {
|
|
1395
|
+
const descriptor = this.resolveDescriptor(agent, options);
|
|
1396
|
+
this.adapter.writeServer(descriptor, serverName, serverConfig);
|
|
1397
|
+
return { path: descriptor.filePath };
|
|
1398
|
+
}
|
|
1399
|
+
removeServer(agent, serverName, options = {}) {
|
|
1400
|
+
const descriptor = this.resolveDescriptor(agent, options);
|
|
1401
|
+
if (!this.adapter.exists(descriptor.filePath)) {
|
|
1402
|
+
return { path: descriptor.filePath, removed: false };
|
|
1436
1403
|
}
|
|
1404
|
+
const removed = this.adapter.removeServer(descriptor, serverName);
|
|
1405
|
+
return { path: descriptor.filePath, removed };
|
|
1437
1406
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1407
|
+
listServers(agent, options = {}) {
|
|
1408
|
+
const descriptor = this.resolveDescriptor(agent, options);
|
|
1409
|
+
if (!this.adapter.exists(descriptor.filePath)) {
|
|
1410
|
+
return { path: descriptor.filePath, exists: false, servers: {} };
|
|
1442
1411
|
}
|
|
1412
|
+
const servers = this.adapter.listServers(descriptor);
|
|
1413
|
+
return { path: descriptor.filePath, exists: true, servers };
|
|
1443
1414
|
}
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
if (
|
|
1455
|
-
|
|
1456
|
-
|
|
1415
|
+
/**
|
|
1416
|
+
* Batch lists servers for multiple agents, caching adapter reads for co-hosted
|
|
1417
|
+
* agents that share the exact same physical configuration file and dotted key.
|
|
1418
|
+
*/
|
|
1419
|
+
listServersForAgents(agents, options = {}) {
|
|
1420
|
+
const readCache = /* @__PURE__ */ new Map();
|
|
1421
|
+
return agents.map((agentType) => {
|
|
1422
|
+
const descriptor = this.resolveDescriptor(agentType, options);
|
|
1423
|
+
const cacheKey = `${descriptor.filePath}::${descriptor.dottedKey ?? ""}`;
|
|
1424
|
+
let cached = readCache.get(cacheKey);
|
|
1425
|
+
if (!cached) {
|
|
1426
|
+
cached = this.listServers(agentType, options);
|
|
1427
|
+
readCache.set(cacheKey, cached);
|
|
1428
|
+
}
|
|
1429
|
+
return {
|
|
1430
|
+
agent: agentType,
|
|
1431
|
+
path: cached.path,
|
|
1432
|
+
exists: cached.exists,
|
|
1433
|
+
servers: cached.servers
|
|
1434
|
+
};
|
|
1457
1435
|
});
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1436
|
+
}
|
|
1437
|
+
read(agent, options = {}) {
|
|
1438
|
+
const descriptor = this.resolveDescriptor(agent, options);
|
|
1439
|
+
if (!this.adapter.exists(descriptor.filePath)) {
|
|
1440
|
+
return {};
|
|
1441
|
+
}
|
|
1442
|
+
return this.adapter.read(descriptor);
|
|
1443
|
+
}
|
|
1444
|
+
readServer(agent, serverName, options = {}) {
|
|
1445
|
+
const { exists, servers } = this.listServers(agent, options);
|
|
1446
|
+
if (!exists) return void 0;
|
|
1447
|
+
return servers[serverName];
|
|
1448
|
+
}
|
|
1449
|
+
// ---- Cluster & co-hosted awareness ----
|
|
1450
|
+
/**
|
|
1451
|
+
* Returns all other agents sharing the exact same physical configuration target
|
|
1452
|
+
* (same configPath and configKey) for the given scope.
|
|
1453
|
+
*/
|
|
1454
|
+
getCoHostedAgents(agentType, options = {}) {
|
|
1455
|
+
const currentAgent = getMcpAgentConfig(agentType);
|
|
1456
|
+
const currentTarget = resolveMcpConfigTarget(currentAgent, options);
|
|
1457
|
+
const candidates = getCandidateAgentsForScope(options);
|
|
1458
|
+
const coHosted = [];
|
|
1459
|
+
for (const candidateType of candidates) {
|
|
1460
|
+
if (candidateType === agentType) continue;
|
|
1461
|
+
const candidateConfig = getMcpAgentConfig(candidateType);
|
|
1462
|
+
const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
|
|
1463
|
+
if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
|
|
1464
|
+
coHosted.push(candidateType);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
return coHosted;
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Resolves configuration clusters for a given list of requested agents.
|
|
1471
|
+
* Groups requested agents sharing the same physical config path, and tracks
|
|
1472
|
+
* any remaining co-hosted agents sharing the same target that were not in the request.
|
|
1473
|
+
*/
|
|
1474
|
+
resolveConfigClusters(agentTypes, options = {}) {
|
|
1475
|
+
const clustersByPath = /* @__PURE__ */ new Map();
|
|
1476
|
+
for (const agentType of agentTypes) {
|
|
1477
|
+
const agentConfig = getMcpAgentConfig(agentType);
|
|
1478
|
+
const target = resolveMcpConfigTarget(agentConfig, options);
|
|
1479
|
+
let keyMap = clustersByPath.get(target.configPath);
|
|
1480
|
+
if (!keyMap) {
|
|
1481
|
+
keyMap = /* @__PURE__ */ new Map();
|
|
1482
|
+
clustersByPath.set(target.configPath, keyMap);
|
|
1483
|
+
}
|
|
1484
|
+
let cluster = keyMap.get(target.configKey);
|
|
1485
|
+
if (!cluster) {
|
|
1486
|
+
const allCoHosted = this.getCoHostedAgents(agentType, options);
|
|
1487
|
+
cluster = {
|
|
1488
|
+
configPath: target.configPath,
|
|
1489
|
+
configKey: target.configKey,
|
|
1490
|
+
targetAgents: [],
|
|
1491
|
+
coHostedAgents: allCoHosted
|
|
1492
|
+
};
|
|
1493
|
+
keyMap.set(target.configKey, cluster);
|
|
1494
|
+
}
|
|
1495
|
+
if (!cluster.targetAgents.includes(agentType)) {
|
|
1496
|
+
cluster.targetAgents.push(agentType);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
const clusters = [];
|
|
1500
|
+
for (const keyMap of clustersByPath.values()) {
|
|
1501
|
+
for (const cluster of keyMap.values()) {
|
|
1502
|
+
cluster.coHostedAgents = cluster.coHostedAgents.filter(
|
|
1503
|
+
(co) => !cluster.targetAgents.includes(co)
|
|
1504
|
+
);
|
|
1505
|
+
clusters.push(cluster);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
return clusters;
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Sorts agent types so that agents sharing the same physical config target
|
|
1512
|
+
* appear adjacent to each other in the returned array.
|
|
1513
|
+
*/
|
|
1514
|
+
sortAgentsByClusters(agentTypes, options = {}) {
|
|
1515
|
+
const clusters = this.resolveConfigClusters(agentTypes, options);
|
|
1516
|
+
const sorted = [];
|
|
1517
|
+
for (const cluster of clusters) {
|
|
1518
|
+
for (const agent of cluster.targetAgents) {
|
|
1519
|
+
if (!sorted.includes(agent)) {
|
|
1520
|
+
sorted.push(agent);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
return sorted;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Alias for sortAgentsByClusters for backward compatibility.
|
|
1528
|
+
*/
|
|
1529
|
+
sortAgentsWithClusters(agentTypes, options = {}) {
|
|
1530
|
+
return this.sortAgentsByClusters(agentTypes, options);
|
|
1531
|
+
}
|
|
1532
|
+
// ---- Batch operations with automatic clustering & dialect transforms ----
|
|
1533
|
+
/**
|
|
1534
|
+
* Batch write: transforms and writes a server config to multiple agents,
|
|
1535
|
+
* with automatic cluster deduplication, dialect transform, and co-hosted awareness.
|
|
1536
|
+
* Callers pass standard McpServerConfig; the Store applies per-Agent dialect transforms
|
|
1537
|
+
* internally before persisting.
|
|
1538
|
+
*/
|
|
1539
|
+
writeServers(agents, serverName, serverConfig, options = {}) {
|
|
1540
|
+
const clusters = this.resolveConfigClusters(agents, options);
|
|
1541
|
+
const resultsByAgent = /* @__PURE__ */ new Map();
|
|
1542
|
+
const isGlobal = options.global ?? false;
|
|
1543
|
+
for (const cluster of clusters) {
|
|
1544
|
+
const primaryAgentType = cluster.targetAgents[0];
|
|
1545
|
+
const primaryAgent = getMcpAgentConfig(primaryAgentType);
|
|
1546
|
+
try {
|
|
1547
|
+
const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
|
|
1548
|
+
global: isGlobal
|
|
1549
|
+
});
|
|
1550
|
+
const descriptor = this.resolveDescriptor(primaryAgent, options);
|
|
1551
|
+
this.adapter.writeServer(descriptor, serverName, transformed);
|
|
1552
|
+
for (const agentType of cluster.targetAgents) {
|
|
1553
|
+
resultsByAgent.set(agentType, {
|
|
1554
|
+
agent: agentType,
|
|
1555
|
+
success: true,
|
|
1556
|
+
path: cluster.configPath,
|
|
1557
|
+
coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
} catch (error) {
|
|
1561
|
+
const errorMsg = toErrorMessage(error);
|
|
1562
|
+
for (const agentType of cluster.targetAgents) {
|
|
1563
|
+
resultsByAgent.set(agentType, {
|
|
1564
|
+
agent: agentType,
|
|
1565
|
+
success: false,
|
|
1566
|
+
path: cluster.configPath,
|
|
1567
|
+
error: errorMsg
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
return agents.map((agentType) => resultsByAgent.get(agentType));
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Batch remove: removes a server from multiple agents' configs,
|
|
1576
|
+
* with automatic cluster deduplication and co-hosted awareness.
|
|
1577
|
+
*/
|
|
1578
|
+
removeServers(agents, serverName, options = {}) {
|
|
1579
|
+
const clusters = this.resolveConfigClusters(agents, options);
|
|
1580
|
+
const resultsByAgent = /* @__PURE__ */ new Map();
|
|
1581
|
+
for (const cluster of clusters) {
|
|
1582
|
+
const primaryAgentType = cluster.targetAgents[0];
|
|
1583
|
+
const primaryAgent = getMcpAgentConfig(primaryAgentType);
|
|
1584
|
+
try {
|
|
1585
|
+
const { removed } = this.removeServer(primaryAgent, serverName, options);
|
|
1586
|
+
for (const agentType of cluster.targetAgents) {
|
|
1587
|
+
resultsByAgent.set(agentType, {
|
|
1588
|
+
agent: agentType,
|
|
1589
|
+
path: cluster.configPath,
|
|
1590
|
+
removed,
|
|
1591
|
+
coAffectedAgents: removed && cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
} catch (error) {
|
|
1595
|
+
const errorMsg = toErrorMessage(error);
|
|
1596
|
+
for (const agentType of cluster.targetAgents) {
|
|
1597
|
+
resultsByAgent.set(agentType, {
|
|
1598
|
+
agent: agentType,
|
|
1599
|
+
path: cluster.configPath,
|
|
1600
|
+
removed: false,
|
|
1601
|
+
error: errorMsg
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
return agents.map((agentType) => resultsByAgent.get(agentType));
|
|
1463
1607
|
}
|
|
1464
1608
|
};
|
|
1465
|
-
var
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1609
|
+
var agentConfigStore = new AgentConfigStore();
|
|
1610
|
+
|
|
1611
|
+
// src/utils/parse-mcp-agent-list.ts
|
|
1612
|
+
var parseMcpAgentList = (input7) => {
|
|
1613
|
+
if (!input7 || input7.length === 0) return void 0;
|
|
1614
|
+
if (input7.includes("*")) return getMcpAgentTypes();
|
|
1615
|
+
const resolved = [];
|
|
1616
|
+
for (const value of input7) {
|
|
1617
|
+
const agentType = resolveMcpAgentAlias(value);
|
|
1618
|
+
if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
|
|
1619
|
+
resolved.push(agentType);
|
|
1473
1620
|
}
|
|
1474
|
-
|
|
1475
|
-
return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
|
|
1621
|
+
return resolved;
|
|
1476
1622
|
};
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1623
|
+
|
|
1624
|
+
// src/resolve-target-agents.ts
|
|
1625
|
+
var normalizeRequestedAgents = (input7) => {
|
|
1626
|
+
if (!input7 || input7.length === 0) return void 0;
|
|
1627
|
+
const rawList = [...input7];
|
|
1628
|
+
if (rawList.every((item) => isMcpAgentType(item))) {
|
|
1629
|
+
return rawList;
|
|
1483
1630
|
}
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1631
|
+
return parseMcpAgentList(rawList);
|
|
1632
|
+
};
|
|
1633
|
+
var resolveTargetAgents = (query = {}) => {
|
|
1634
|
+
const cwd = query.cwd ?? process.cwd();
|
|
1635
|
+
const isGlobal = query.global ?? false;
|
|
1636
|
+
let explicitAgents = normalizeRequestedAgents(query.requested);
|
|
1637
|
+
if (query.all) {
|
|
1638
|
+
explicitAgents = getMcpAgentTypes();
|
|
1490
1639
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1640
|
+
const isDetected = !explicitAgents || explicitAgents.length === 0;
|
|
1641
|
+
const detected = isGlobal ? detectGloballyInstalledMcpAgents() : detectProjectInstalledMcpAgents(cwd);
|
|
1642
|
+
const candidateAgents = isDetected ? detected : explicitAgents ?? [];
|
|
1643
|
+
const allAgents = candidateAgents.filter(
|
|
1644
|
+
(type, index) => candidateAgents.indexOf(type) === index
|
|
1645
|
+
);
|
|
1646
|
+
const incompatible = [];
|
|
1647
|
+
const compatibleAgents = [];
|
|
1648
|
+
for (const agentType of allAgents) {
|
|
1649
|
+
const config = getMcpAgentConfig(agentType);
|
|
1650
|
+
if (query.transport && !isMcpTransportSupported(config, query.transport)) {
|
|
1651
|
+
incompatible.push({
|
|
1652
|
+
agent: agentType,
|
|
1653
|
+
reason: config.unsupportedTransportMessage ?? `agent ${agentType} only supports ${config.supportedTransports.join(", ")} transport (attempted ${query.transport})`
|
|
1654
|
+
});
|
|
1655
|
+
} else {
|
|
1656
|
+
compatibleAgents.push(agentType);
|
|
1657
|
+
}
|
|
1497
1658
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1659
|
+
let diagnostic;
|
|
1660
|
+
if (compatibleAgents.length === 0) {
|
|
1661
|
+
if (isDetected) {
|
|
1662
|
+
diagnostic = `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass -a <agent> (e.g. -a cursor) or --all to install.`;
|
|
1663
|
+
} else if (allAgents.length > 0 && incompatible.length > 0 && query.transport) {
|
|
1664
|
+
const list = incompatible.map((item) => `${item.agent} (${item.reason})`).join(", ");
|
|
1665
|
+
diagnostic = `None of the selected agents support ${query.transport} transport: ${list}`;
|
|
1666
|
+
} else {
|
|
1667
|
+
diagnostic = "No valid target agents specified.";
|
|
1668
|
+
}
|
|
1504
1669
|
}
|
|
1505
1670
|
return {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1671
|
+
agents: compatibleAgents,
|
|
1672
|
+
compatibleAgents,
|
|
1673
|
+
allAgents,
|
|
1674
|
+
candidateAgents: allAgents,
|
|
1675
|
+
detected,
|
|
1676
|
+
isDetected,
|
|
1677
|
+
incompatible,
|
|
1678
|
+
diagnostic
|
|
1509
1679
|
};
|
|
1510
1680
|
};
|
|
1511
1681
|
|
|
1682
|
+
// src/install-compat.ts
|
|
1683
|
+
var installToCompatibleAgents = (serverName, serverConfig, options) => {
|
|
1684
|
+
const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
|
|
1685
|
+
const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
|
|
1686
|
+
const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
|
|
1687
|
+
const installedResults = agentConfigStore.writeServers(
|
|
1688
|
+
compatibleAgents,
|
|
1689
|
+
serverName,
|
|
1690
|
+
serverConfig,
|
|
1691
|
+
{
|
|
1692
|
+
global: isGlobal,
|
|
1693
|
+
cwd
|
|
1694
|
+
}
|
|
1695
|
+
);
|
|
1696
|
+
const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
|
|
1697
|
+
return allAgents.map((agentType) => {
|
|
1698
|
+
const incompatibleReason = incompatibleMap.get(agentType);
|
|
1699
|
+
if (incompatibleReason) {
|
|
1700
|
+
return {
|
|
1701
|
+
agent: agentType,
|
|
1702
|
+
success: false,
|
|
1703
|
+
path: "",
|
|
1704
|
+
error: incompatibleReason
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
return installedMap.get(agentType);
|
|
1708
|
+
});
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1512
1711
|
// src/install-mcp-server.ts
|
|
1513
1712
|
var installMcpServer = (options) => {
|
|
1514
1713
|
const parsed = parseMcpSource(options.source);
|
|
@@ -1541,15 +1740,14 @@ var installMcpServer = (options) => {
|
|
|
1541
1740
|
var listInstalledMcpServers = (options = {}) => {
|
|
1542
1741
|
const agentTypes = options.agents ?? getMcpAgentTypes();
|
|
1543
1742
|
const collected = [];
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
for (const [serverName, rawConfig] of Object.entries(servers)) {
|
|
1743
|
+
const results = agentConfigStore.listServersForAgents(agentTypes, options);
|
|
1744
|
+
for (const item of results) {
|
|
1745
|
+
if (!item.exists) continue;
|
|
1746
|
+
for (const [serverName, rawConfig] of Object.entries(item.servers)) {
|
|
1549
1747
|
collected.push({
|
|
1550
1748
|
serverName,
|
|
1551
|
-
agent:
|
|
1552
|
-
path,
|
|
1749
|
+
agent: item.agent,
|
|
1750
|
+
path: item.path,
|
|
1553
1751
|
config: rawConfig,
|
|
1554
1752
|
serverConfig: parseServerConfig(rawConfig)
|
|
1555
1753
|
});
|
|
@@ -1557,110 +1755,40 @@ var listInstalledMcpServers = (options = {}) => {
|
|
|
1557
1755
|
}
|
|
1558
1756
|
return collected;
|
|
1559
1757
|
};
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
try {
|
|
1578
|
-
const { removed } = agentConfigStore.removeServer(primaryAgent, options.name, {
|
|
1579
|
-
global: options.global,
|
|
1580
|
-
cwd: options.cwd
|
|
1581
|
-
});
|
|
1582
|
-
if (removed) {
|
|
1583
|
-
for (const agentType of cluster.targetAgents) {
|
|
1584
|
-
results.push({
|
|
1585
|
-
agent: agentType,
|
|
1586
|
-
path: cluster.configPath,
|
|
1587
|
-
removed: true,
|
|
1588
|
-
coAffectedAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
|
|
1589
|
-
});
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
} catch (error) {
|
|
1593
|
-
const errorMsg = toErrorMessage(error);
|
|
1594
|
-
for (const agentType of cluster.targetAgents) {
|
|
1595
|
-
results.push({
|
|
1596
|
-
agent: agentType,
|
|
1597
|
-
path: cluster.configPath,
|
|
1598
|
-
removed: false,
|
|
1599
|
-
error: errorMsg
|
|
1600
|
-
});
|
|
1758
|
+
var groupInstalledServersByName = (installed) => {
|
|
1759
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1760
|
+
for (const item of installed) {
|
|
1761
|
+
const itemConfig = item.serverConfig ?? parseServerConfig(item.config);
|
|
1762
|
+
let entry = grouped.get(item.serverName);
|
|
1763
|
+
if (!entry) {
|
|
1764
|
+
entry = {
|
|
1765
|
+
serverName: item.serverName,
|
|
1766
|
+
agents: [],
|
|
1767
|
+
paths: [],
|
|
1768
|
+
config: itemConfig,
|
|
1769
|
+
hasDivergence: false
|
|
1770
|
+
};
|
|
1771
|
+
grouped.set(item.serverName, entry);
|
|
1772
|
+
} else if (!entry.hasDivergence) {
|
|
1773
|
+
if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
|
|
1774
|
+
entry.hasDivergence = true;
|
|
1601
1775
|
}
|
|
1602
1776
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
};
|
|
1606
|
-
|
|
1607
|
-
// src/update-mcp-server.ts
|
|
1608
|
-
var toRemoteServerConfig = (config, defaultTransport = "http") => {
|
|
1609
|
-
const {
|
|
1610
|
-
command: _droppedCommand,
|
|
1611
|
-
args: _droppedArgs,
|
|
1612
|
-
env: _droppedEnv,
|
|
1613
|
-
...remoteConfig
|
|
1614
|
-
} = config;
|
|
1615
|
-
return {
|
|
1616
|
-
...remoteConfig,
|
|
1617
|
-
type: remoteConfig.type ?? defaultTransport
|
|
1618
|
-
};
|
|
1619
|
-
};
|
|
1620
|
-
var toStdioServerConfig = (config) => {
|
|
1621
|
-
const {
|
|
1622
|
-
url: _droppedUrl,
|
|
1623
|
-
type: _droppedType,
|
|
1624
|
-
headers: _droppedHeaders,
|
|
1625
|
-
...stdioConfig
|
|
1626
|
-
} = config;
|
|
1627
|
-
return stdioConfig;
|
|
1628
|
-
};
|
|
1629
|
-
var detectUpdateTransition = (incoming, previous) => {
|
|
1630
|
-
if (!previous) {
|
|
1631
|
-
return incoming.url ? "switch-to-remote" : "switch-to-stdio";
|
|
1632
|
-
}
|
|
1633
|
-
if (incoming.url && !incoming.command) {
|
|
1634
|
-
return "switch-to-remote";
|
|
1635
|
-
}
|
|
1636
|
-
if (incoming.command && !incoming.url) {
|
|
1637
|
-
return "switch-to-stdio";
|
|
1638
|
-
}
|
|
1639
|
-
if (incoming.url || !incoming.command && previous.url) {
|
|
1640
|
-
return "merge-remote";
|
|
1641
|
-
}
|
|
1642
|
-
return "merge-stdio";
|
|
1643
|
-
};
|
|
1644
|
-
var sanitizeUpdatedServerConfig = (incoming, previous) => {
|
|
1645
|
-
const transition = detectUpdateTransition(incoming, previous);
|
|
1646
|
-
const targetTransport = incoming.type ?? previous?.type ?? "http";
|
|
1647
|
-
switch (transition) {
|
|
1648
|
-
case "switch-to-remote": {
|
|
1649
|
-
const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
|
|
1650
|
-
return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
|
|
1651
|
-
}
|
|
1652
|
-
case "switch-to-stdio": {
|
|
1653
|
-
const cleanBase = previous ? toStdioServerConfig(previous) : {};
|
|
1654
|
-
return toStdioServerConfig({ ...cleanBase, ...incoming });
|
|
1655
|
-
}
|
|
1656
|
-
case "merge-remote": {
|
|
1657
|
-
return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
|
|
1777
|
+
if (!entry.agents.includes(item.agent)) {
|
|
1778
|
+
entry.agents.push(item.agent);
|
|
1658
1779
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1780
|
+
if (!entry.paths.includes(item.path)) {
|
|
1781
|
+
entry.paths.push(item.path);
|
|
1661
1782
|
}
|
|
1662
1783
|
}
|
|
1784
|
+
return grouped;
|
|
1663
1785
|
};
|
|
1786
|
+
var queryGroupedInstalledServers = (options = {}) => {
|
|
1787
|
+
const installed = listInstalledMcpServers(options);
|
|
1788
|
+
return groupInstalledServersByName(installed);
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
// src/update-mcp-server.ts
|
|
1664
1792
|
var updateMcpServer = (options) => {
|
|
1665
1793
|
const isGlobal = options.global ?? false;
|
|
1666
1794
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -1703,9 +1831,36 @@ var updateMcpServer = (options) => {
|
|
|
1703
1831
|
};
|
|
1704
1832
|
};
|
|
1705
1833
|
|
|
1706
|
-
// src/
|
|
1707
|
-
var
|
|
1708
|
-
|
|
1834
|
+
// src/remove.ts
|
|
1835
|
+
var removeMcpServer = (options) => {
|
|
1836
|
+
const { allAgents } = resolveTargetAgents({
|
|
1837
|
+
requested: options.agents,
|
|
1838
|
+
all: !options.agents,
|
|
1839
|
+
global: options.global,
|
|
1840
|
+
cwd: options.cwd
|
|
1841
|
+
});
|
|
1842
|
+
const storeResults = agentConfigStore.removeServers(allAgents, options.name, {
|
|
1843
|
+
global: options.global,
|
|
1844
|
+
cwd: options.cwd
|
|
1845
|
+
});
|
|
1846
|
+
return storeResults.filter((r) => r.removed || Boolean(r.error));
|
|
1847
|
+
};
|
|
1848
|
+
|
|
1849
|
+
// src/utils/mask-secret.ts
|
|
1850
|
+
var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
|
|
1851
|
+
var maskSecretValue = (key, value) => {
|
|
1852
|
+
if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
|
|
1853
|
+
return value;
|
|
1854
|
+
}
|
|
1855
|
+
return `${value.slice(0, 2)}***${value.slice(-2)}`;
|
|
1856
|
+
};
|
|
1857
|
+
var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
|
|
1858
|
+
var maskSecretHeader = (key, value) => {
|
|
1859
|
+
if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
|
|
1860
|
+
return value;
|
|
1861
|
+
}
|
|
1862
|
+
return `${value.slice(0, 4)}***${value.slice(-3)}`;
|
|
1863
|
+
};
|
|
1709
1864
|
|
|
1710
1865
|
// src/interactive/wizard-add.ts
|
|
1711
1866
|
var import_prompts7 = require("@inquirer/prompts");
|
|
@@ -1754,7 +1909,7 @@ var buildLinkedAgentChoices = (options) => {
|
|
|
1754
1909
|
const { agents, checkedAgents, detectedAgents = [], scopeOptions = {} } = options;
|
|
1755
1910
|
const alignedCheckedSet = new Set(checkedAgents);
|
|
1756
1911
|
for (const agent of checkedAgents) {
|
|
1757
|
-
const coHosted = getCoHostedAgents(agent, scopeOptions);
|
|
1912
|
+
const coHosted = agentConfigStore.getCoHostedAgents(agent, scopeOptions);
|
|
1758
1913
|
for (const co of coHosted) {
|
|
1759
1914
|
if (agents.includes(co)) {
|
|
1760
1915
|
alignedCheckedSet.add(co);
|
|
@@ -1765,7 +1920,7 @@ var buildLinkedAgentChoices = (options) => {
|
|
|
1765
1920
|
const config = getMcpAgentConfig(agent);
|
|
1766
1921
|
const displayName = config?.displayName ?? agent;
|
|
1767
1922
|
const isDetected = detectedAgents.includes(agent);
|
|
1768
|
-
const coHosted = getCoHostedAgents(agent, scopeOptions).filter(
|
|
1923
|
+
const coHosted = agentConfigStore.getCoHostedAgents(agent, scopeOptions).filter(
|
|
1769
1924
|
(co) => agents.includes(co)
|
|
1770
1925
|
);
|
|
1771
1926
|
const detectedBadge = isDetected ? import_picocolors3.default.green(" [detected]") : "";
|
|
@@ -2029,7 +2184,10 @@ var promptScopeAndAgents = async (options = {}) => {
|
|
|
2029
2184
|
});
|
|
2030
2185
|
const detected = resolution.detected;
|
|
2031
2186
|
const rawAvailable = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
2032
|
-
const availableAgentTypes =
|
|
2187
|
+
const availableAgentTypes = agentConfigStore.sortAgentsByClusters(rawAvailable, {
|
|
2188
|
+
global: isGlobal,
|
|
2189
|
+
cwd
|
|
2190
|
+
});
|
|
2033
2191
|
if (detected.length > 0) {
|
|
2034
2192
|
logger.info(
|
|
2035
2193
|
`Detected configured agents: ${import_picocolors6.default.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
|
|
@@ -2109,22 +2267,6 @@ var promptEditArgs = async (currentArgs = []) => {
|
|
|
2109
2267
|
var import_prompts5 = require("@inquirer/prompts");
|
|
2110
2268
|
var import_picocolors9 = __toESM(require("picocolors"), 1);
|
|
2111
2269
|
|
|
2112
|
-
// src/utils/mask-secret.ts
|
|
2113
|
-
var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
|
|
2114
|
-
var maskSecretValue = (key, value) => {
|
|
2115
|
-
if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
|
|
2116
|
-
return value;
|
|
2117
|
-
}
|
|
2118
|
-
return `${value.slice(0, 2)}***${value.slice(-2)}`;
|
|
2119
|
-
};
|
|
2120
|
-
var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
|
|
2121
|
-
var maskSecretHeader = (key, value) => {
|
|
2122
|
-
if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
|
|
2123
|
-
return value;
|
|
2124
|
-
}
|
|
2125
|
-
return `${value.slice(0, 4)}***${value.slice(-3)}`;
|
|
2126
|
-
};
|
|
2127
|
-
|
|
2128
2270
|
// src/interactive/prompts/kv.ts
|
|
2129
2271
|
var import_prompts4 = require("@inquirer/prompts");
|
|
2130
2272
|
var import_picocolors8 = __toESM(require("picocolors"), 1);
|
|
@@ -2728,18 +2870,166 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2728
2870
|
logger.error(`${import_picocolors11.default.cyan(record.agent)}: Failed to write - ${record.error}`);
|
|
2729
2871
|
}
|
|
2730
2872
|
}
|
|
2731
|
-
if (allSuccess) {
|
|
2732
|
-
logger.success(import_picocolors11.default.bold(`MCP server "${serverName}" configured successfully!`));
|
|
2873
|
+
if (allSuccess) {
|
|
2874
|
+
logger.success(import_picocolors11.default.bold(`MCP server "${serverName}" configured successfully!`));
|
|
2875
|
+
}
|
|
2876
|
+
return allSuccess;
|
|
2877
|
+
};
|
|
2878
|
+
|
|
2879
|
+
// src/utils/format-agent-list.ts
|
|
2880
|
+
var formatAgentList = (agentList, emptyLabel = "(none)") => agentList.length === 0 ? emptyLabel : agentList.join(", ");
|
|
2881
|
+
|
|
2882
|
+
// src/utils/parse-key-value-list.ts
|
|
2883
|
+
var parseKeyValueList = (entries, separator) => {
|
|
2884
|
+
if (!entries || entries.length === 0) return {};
|
|
2885
|
+
const result = {};
|
|
2886
|
+
for (const entry of entries) {
|
|
2887
|
+
const splitIndex = entry.indexOf(separator);
|
|
2888
|
+
if (splitIndex === -1) {
|
|
2889
|
+
throw new Error(`Invalid entry "${entry}": expected "${separator}" separator`);
|
|
2890
|
+
}
|
|
2891
|
+
const key = entry.slice(0, splitIndex).trim();
|
|
2892
|
+
const value = entry.slice(splitIndex + separator.length).trim();
|
|
2893
|
+
if (!key) throw new Error(`Invalid entry "${entry}": empty key`);
|
|
2894
|
+
result[key] = value;
|
|
2895
|
+
}
|
|
2896
|
+
return result;
|
|
2897
|
+
};
|
|
2898
|
+
|
|
2899
|
+
// src/utils/resolve-transport.ts
|
|
2900
|
+
var resolveTransport = (input7) => {
|
|
2901
|
+
if (!input7) return void 0;
|
|
2902
|
+
if (input7 === "http" || input7 === "sse") return input7;
|
|
2903
|
+
throw new Error(`Unsupported transport "${input7}" (expected: http, sse)`);
|
|
2904
|
+
};
|
|
2905
|
+
|
|
2906
|
+
// src/cli/add.ts
|
|
2907
|
+
var mcpAddCommand = new import_commander.Command("add").description("Add an MCP server to coding agents").argument("[source]", "Remote URL, npm package, or command line").option("-a, --agent <agents...>", "Target specific agents (use '*' for all)").option("-g, --global", "Install to user-level config instead of project").option("-t, --transport <type>", "Transport type for remote servers (http or sse)").option("--header <header...>", "HTTP header (Key: Value), repeatable").option("--env <env...>", "Env var for stdio servers (KEY=VALUE), repeatable").option("--args <args...>", "CLI arguments for stdio/package servers").option("-n, --name <name>", "Server name override").option("-y, --yes", "Skip all prompts").option("--all", "Install to all supported agents").action(async (source, options) => {
|
|
2908
|
+
try {
|
|
2909
|
+
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
2910
|
+
if (!source) {
|
|
2911
|
+
if (isInteractive) {
|
|
2912
|
+
const success = await wizardAdd({
|
|
2913
|
+
name: options.name,
|
|
2914
|
+
global: options.global,
|
|
2915
|
+
args: options.args,
|
|
2916
|
+
transport: resolveTransport(options.transport),
|
|
2917
|
+
headers: parseKeyValueList(options.header, ":"),
|
|
2918
|
+
env: parseKeyValueList(options.env, "="),
|
|
2919
|
+
agents: options.all ? getMcpAgentTypes() : parseMcpAgentList(options.agent)
|
|
2920
|
+
});
|
|
2921
|
+
if (!success) process.exitCode = 1;
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
logger.error('Missing required argument: "source" (e.g. mcps add @modelcontextprotocol/server-filesystem)');
|
|
2925
|
+
process.exitCode = 1;
|
|
2926
|
+
return;
|
|
2927
|
+
}
|
|
2928
|
+
const parsed = parseMcpSource(source);
|
|
2929
|
+
const cwd = process.cwd();
|
|
2930
|
+
const isGlobal = Boolean(options.global);
|
|
2931
|
+
const explicitTransport = resolveTransport(options.transport);
|
|
2932
|
+
const transport = explicitTransport ?? (parsed.type === "remote" ? "http" : "stdio");
|
|
2933
|
+
const resolvedTargets = resolveTargetAgents({
|
|
2934
|
+
requested: options.agent,
|
|
2935
|
+
all: options.all,
|
|
2936
|
+
global: isGlobal,
|
|
2937
|
+
cwd,
|
|
2938
|
+
transport
|
|
2939
|
+
});
|
|
2940
|
+
if (resolvedTargets.agents.length === 0) {
|
|
2941
|
+
const message = resolvedTargets.diagnostic ?? `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass ${import_picocolors12.default.cyan("-a <agent>")} (e.g. ${import_picocolors12.default.cyan("-a cursor")}) or ${import_picocolors12.default.cyan("--all")} to install.`;
|
|
2942
|
+
logger.warn(message);
|
|
2943
|
+
process.exitCode = 1;
|
|
2944
|
+
return;
|
|
2945
|
+
}
|
|
2946
|
+
if (resolvedTargets.isDetected) {
|
|
2947
|
+
logger.info(
|
|
2948
|
+
`Detected ${isGlobal ? "global" : "project"} agents: ${import_picocolors12.default.cyan(formatAgentList(resolvedTargets.detected, "(none detected)"))}`
|
|
2949
|
+
);
|
|
2950
|
+
if (resolvedTargets.incompatible.length > 0) {
|
|
2951
|
+
const skippedList = resolvedTargets.incompatible.map((item) => `${item.agent} (${item.reason})`).join(", ");
|
|
2952
|
+
logger.info(
|
|
2953
|
+
`Skipping detected agents incompatible with ${transport}: ${import_picocolors12.default.yellow(skippedList)}`
|
|
2954
|
+
);
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
const targetAgents = resolvedTargets.isDetected ? resolvedTargets.agents : resolvedTargets.allAgents;
|
|
2958
|
+
const result = installMcpServer({
|
|
2959
|
+
source,
|
|
2960
|
+
name: options.name,
|
|
2961
|
+
agents: targetAgents,
|
|
2962
|
+
args: options.args,
|
|
2963
|
+
global: isGlobal,
|
|
2964
|
+
cwd,
|
|
2965
|
+
transport: explicitTransport,
|
|
2966
|
+
headers: parseKeyValueList(options.header, ":"),
|
|
2967
|
+
env: parseKeyValueList(options.env, "=")
|
|
2968
|
+
});
|
|
2969
|
+
logger.info(
|
|
2970
|
+
`Installing ${import_picocolors12.default.bold(result.serverName)} (${import_picocolors12.default.cyan(parsed.type)}) to ${import_picocolors12.default.cyan(String(result.results.length))} agent(s)`
|
|
2971
|
+
);
|
|
2972
|
+
for (const record of result.results) {
|
|
2973
|
+
if (record.success) {
|
|
2974
|
+
logger.success(`${import_picocolors12.default.cyan(record.agent)} ${import_picocolors12.default.dim(record.path)}`);
|
|
2975
|
+
logCoHostedNotice("configured", record.coConfiguredAgents);
|
|
2976
|
+
} else {
|
|
2977
|
+
logger.error(`${import_picocolors12.default.cyan(record.agent)}: ${record.error}`);
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
if (result.results.some((record) => !record.success)) process.exitCode = 1;
|
|
2981
|
+
} catch (error) {
|
|
2982
|
+
logger.error(toErrorMessage(error));
|
|
2983
|
+
process.exitCode = 1;
|
|
2984
|
+
}
|
|
2985
|
+
});
|
|
2986
|
+
|
|
2987
|
+
// src/cli/list.ts
|
|
2988
|
+
var import_commander2 = require("commander");
|
|
2989
|
+
var import_picocolors13 = __toESM(require("picocolors"), 1);
|
|
2990
|
+
var mcpListCommand = new import_commander2.Command("list").alias("ls").description("List installed MCP servers across agents").option("-g, --global", "List global configs instead of project").option("-a, --agent <agents...>", "Filter by specific agents").option("--json", "Output as JSON").action((options) => {
|
|
2991
|
+
try {
|
|
2992
|
+
const entries = listInstalledMcpServers({
|
|
2993
|
+
global: Boolean(options.global),
|
|
2994
|
+
cwd: process.cwd(),
|
|
2995
|
+
agents: parseMcpAgentList(options.agent)
|
|
2996
|
+
});
|
|
2997
|
+
if (options.json) {
|
|
2998
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
if (entries.length === 0) {
|
|
3002
|
+
logger.warn("No MCP servers installed");
|
|
3003
|
+
return;
|
|
3004
|
+
}
|
|
3005
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3006
|
+
for (const entry of entries) {
|
|
3007
|
+
const existing = grouped.get(entry.serverName) ?? [];
|
|
3008
|
+
existing.push(entry);
|
|
3009
|
+
grouped.set(entry.serverName, existing);
|
|
3010
|
+
}
|
|
3011
|
+
for (const [serverName, group] of grouped) {
|
|
3012
|
+
const agentLabels = group.map((record) => record.agent).join(", ");
|
|
3013
|
+
console.log(` ${import_picocolors13.default.bold(serverName)} ${import_picocolors13.default.dim(`[${agentLabels}]`)}`);
|
|
3014
|
+
const firstPath = group[0]?.path;
|
|
3015
|
+
if (firstPath) console.log(` ${import_picocolors13.default.dim(firstPath)}`);
|
|
3016
|
+
}
|
|
3017
|
+
} catch (error) {
|
|
3018
|
+
logger.error(toErrorMessage(error));
|
|
3019
|
+
process.exitCode = 1;
|
|
2733
3020
|
}
|
|
2734
|
-
|
|
2735
|
-
|
|
3021
|
+
});
|
|
3022
|
+
|
|
3023
|
+
// src/cli/manage.ts
|
|
3024
|
+
var import_commander3 = require("commander");
|
|
3025
|
+
var import_picocolors16 = __toESM(require("picocolors"), 1);
|
|
2736
3026
|
|
|
2737
3027
|
// src/interactive/wizard-manage.ts
|
|
2738
3028
|
var import_prompts8 = require("@inquirer/prompts");
|
|
2739
|
-
var
|
|
3029
|
+
var import_picocolors15 = __toESM(require("picocolors"), 1);
|
|
2740
3030
|
|
|
2741
3031
|
// src/utils/display-server-details.ts
|
|
2742
|
-
var
|
|
3032
|
+
var import_picocolors14 = __toESM(require("picocolors"), 1);
|
|
2743
3033
|
var displayServerDetails = ({
|
|
2744
3034
|
serverName,
|
|
2745
3035
|
config,
|
|
@@ -2748,81 +3038,50 @@ var displayServerDetails = ({
|
|
|
2748
3038
|
global: isGlobal,
|
|
2749
3039
|
titlePrefix = "MCP Server Details"
|
|
2750
3040
|
}) => {
|
|
2751
|
-
console.log("\n" +
|
|
3041
|
+
console.log("\n" + import_picocolors14.default.cyan(import_picocolors14.default.bold(`${titlePrefix}: [${serverName}]`)));
|
|
2752
3042
|
if (isGlobal !== void 0) {
|
|
2753
|
-
console.log(` ${
|
|
3043
|
+
console.log(` ${import_picocolors14.default.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
|
|
2754
3044
|
}
|
|
2755
3045
|
if (agents && agents.length > 0) {
|
|
2756
3046
|
console.log(
|
|
2757
|
-
` ${
|
|
3047
|
+
` ${import_picocolors14.default.bold("Configured Agents:")} ${import_picocolors14.default.green(agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
|
|
2758
3048
|
);
|
|
2759
3049
|
}
|
|
2760
3050
|
if (hasDivergence) {
|
|
2761
3051
|
console.log(
|
|
2762
|
-
` ${
|
|
3052
|
+
` ${import_picocolors14.default.yellow(import_picocolors14.default.bold("Notice:"))} ${import_picocolors14.default.yellow("Configurations differ across installed agents. Showing configuration from the first agent.")}`
|
|
2763
3053
|
);
|
|
2764
3054
|
}
|
|
2765
3055
|
const isRemote = Boolean(config.url && config.url.length > 0);
|
|
2766
3056
|
if (isRemote) {
|
|
2767
|
-
console.log(` ${
|
|
2768
|
-
console.log(` ${
|
|
3057
|
+
console.log(` ${import_picocolors14.default.bold("Transport:")} ${import_picocolors14.default.magenta(config.type ?? "http")}`);
|
|
3058
|
+
console.log(` ${import_picocolors14.default.bold("URL:")} ${import_picocolors14.default.dim(config.url ?? "")}`);
|
|
2769
3059
|
const headerKeys = Object.keys(config.headers ?? {});
|
|
2770
3060
|
if (headerKeys.length > 0) {
|
|
2771
|
-
console.log(` ${
|
|
3061
|
+
console.log(` ${import_picocolors14.default.bold("Headers:")} ${import_picocolors14.default.cyan(String(headerKeys.length))}`);
|
|
2772
3062
|
for (const [k, v] of Object.entries(config.headers ?? {})) {
|
|
2773
|
-
console.log(` ${
|
|
3063
|
+
console.log(` ${import_picocolors14.default.bold(k)}: ${import_picocolors14.default.dim(maskSecretHeader(k, v))}`);
|
|
2774
3064
|
}
|
|
2775
3065
|
} else {
|
|
2776
|
-
console.log(` ${
|
|
3066
|
+
console.log(` ${import_picocolors14.default.bold("Headers:")} ${import_picocolors14.default.dim("(none)")}`);
|
|
2777
3067
|
}
|
|
2778
3068
|
} else {
|
|
2779
|
-
console.log(` ${
|
|
3069
|
+
console.log(` ${import_picocolors14.default.bold("Command:")} ${import_picocolors14.default.magenta(config.command ?? "")}`);
|
|
2780
3070
|
const argsStr = config.args && config.args.length > 0 ? config.args.join(" ") : "(none)";
|
|
2781
|
-
console.log(` ${
|
|
3071
|
+
console.log(` ${import_picocolors14.default.bold("Arguments:")} ${import_picocolors14.default.dim(argsStr)}`);
|
|
2782
3072
|
const envKeys = Object.keys(config.env ?? {});
|
|
2783
3073
|
if (envKeys.length > 0) {
|
|
2784
|
-
console.log(` ${
|
|
3074
|
+
console.log(` ${import_picocolors14.default.bold("Environment Variables:")} ${import_picocolors14.default.cyan(String(envKeys.length))}`);
|
|
2785
3075
|
for (const [k, v] of Object.entries(config.env ?? {})) {
|
|
2786
|
-
console.log(` ${
|
|
3076
|
+
console.log(` ${import_picocolors14.default.bold(k)}=${import_picocolors14.default.dim(maskSecretValue(k, v))}`);
|
|
2787
3077
|
}
|
|
2788
3078
|
} else {
|
|
2789
|
-
console.log(` ${
|
|
3079
|
+
console.log(` ${import_picocolors14.default.bold("Environment Variables:")} ${import_picocolors14.default.dim("(none)")}`);
|
|
2790
3080
|
}
|
|
2791
3081
|
}
|
|
2792
3082
|
console.log();
|
|
2793
3083
|
};
|
|
2794
3084
|
|
|
2795
|
-
// src/interactive/utils/group-installed-servers.ts
|
|
2796
|
-
var normalizeServerConfig = parseServerConfig;
|
|
2797
|
-
var groupInstalledServersByName = (installed) => {
|
|
2798
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
2799
|
-
for (const item of installed) {
|
|
2800
|
-
const itemConfig = normalizeServerConfig(item.config);
|
|
2801
|
-
let entry = grouped.get(item.serverName);
|
|
2802
|
-
if (!entry) {
|
|
2803
|
-
entry = {
|
|
2804
|
-
serverName: item.serverName,
|
|
2805
|
-
agents: [],
|
|
2806
|
-
paths: [],
|
|
2807
|
-
config: itemConfig,
|
|
2808
|
-
hasDivergence: false
|
|
2809
|
-
};
|
|
2810
|
-
grouped.set(item.serverName, entry);
|
|
2811
|
-
} else if (!entry.hasDivergence) {
|
|
2812
|
-
if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
|
|
2813
|
-
entry.hasDivergence = true;
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
2816
|
-
if (!entry.agents.includes(item.agent)) {
|
|
2817
|
-
entry.agents.push(item.agent);
|
|
2818
|
-
}
|
|
2819
|
-
if (!entry.paths.includes(item.path)) {
|
|
2820
|
-
entry.paths.push(item.path);
|
|
2821
|
-
}
|
|
2822
|
-
}
|
|
2823
|
-
return grouped;
|
|
2824
|
-
};
|
|
2825
|
-
|
|
2826
3085
|
// src/interactive/wizard-manage.ts
|
|
2827
3086
|
var promptSwitchServerType = async (currentConfig, serverName) => {
|
|
2828
3087
|
const isRemote = Boolean(currentConfig.url && currentConfig.url.length > 0);
|
|
@@ -2834,11 +3093,11 @@ var promptSwitchServerType = async (currentConfig, serverName) => {
|
|
|
2834
3093
|
const newArgs = await promptEditArgs([]);
|
|
2835
3094
|
const newEnv = await promptEditEnvConfig({});
|
|
2836
3095
|
logger.success(`Switched [${serverName}] configuration to stdio mode`);
|
|
2837
|
-
return {
|
|
3096
|
+
return toStdioServerConfig({
|
|
2838
3097
|
command: newCmd.trim(),
|
|
2839
3098
|
args: newArgs.length > 0 ? newArgs : void 0,
|
|
2840
3099
|
env: Object.keys(newEnv).length > 0 ? newEnv : void 0
|
|
2841
|
-
};
|
|
3100
|
+
});
|
|
2842
3101
|
}
|
|
2843
3102
|
const newUrl = await (0, import_prompts8.input)({
|
|
2844
3103
|
message: "Remote server URL:",
|
|
@@ -2861,11 +3120,13 @@ var promptSwitchServerType = async (currentConfig, serverName) => {
|
|
|
2861
3120
|
});
|
|
2862
3121
|
const newHeaders = await promptEditHeadersConfig({});
|
|
2863
3122
|
logger.success(`Switched [${serverName}] configuration to remote mode`);
|
|
2864
|
-
return
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
3123
|
+
return toRemoteServerConfig(
|
|
3124
|
+
{
|
|
3125
|
+
url: newUrl.trim(),
|
|
3126
|
+
headers: Object.keys(newHeaders).length > 0 ? newHeaders : void 0
|
|
3127
|
+
},
|
|
3128
|
+
transport
|
|
3129
|
+
);
|
|
2869
3130
|
};
|
|
2870
3131
|
var handleEditServerConfig = async (options) => {
|
|
2871
3132
|
const { targetGroup } = options;
|
|
@@ -2963,7 +3224,7 @@ var handleEditServerConfig = async (options) => {
|
|
|
2963
3224
|
} else if (editAction === "save") {
|
|
2964
3225
|
let targetAgents = targetGroup.agents;
|
|
2965
3226
|
if (targetGroup.agents.length > 1) {
|
|
2966
|
-
const sortedAgents =
|
|
3227
|
+
const sortedAgents = agentConfigStore.sortAgentsByClusters(targetGroup.agents, { global: isGlobal, cwd });
|
|
2967
3228
|
const choices = buildLinkedAgentChoices({
|
|
2968
3229
|
agents: sortedAgents,
|
|
2969
3230
|
checkedAgents: sortedAgents,
|
|
@@ -2992,7 +3253,7 @@ var handleEditServerConfig = async (options) => {
|
|
|
2992
3253
|
});
|
|
2993
3254
|
if (resolution.incompatible.length > 0) {
|
|
2994
3255
|
for (const item of resolution.incompatible) {
|
|
2995
|
-
logger.warn(`Skipping ${
|
|
3256
|
+
logger.warn(`Skipping ${import_picocolors15.default.cyan(item.agent)}: ${item.reason}`);
|
|
2996
3257
|
}
|
|
2997
3258
|
}
|
|
2998
3259
|
if (resolution.compatibleAgents.length === 0) {
|
|
@@ -3025,10 +3286,10 @@ var handleEditServerConfig = async (options) => {
|
|
|
3025
3286
|
updatedAny = true;
|
|
3026
3287
|
succeededAgents.push(res.agent);
|
|
3027
3288
|
logger.success(
|
|
3028
|
-
`${
|
|
3289
|
+
`${import_picocolors15.default.cyan(res.agent)}: Successfully updated configuration in ${import_picocolors15.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
|
|
3029
3290
|
);
|
|
3030
3291
|
} else {
|
|
3031
|
-
logger.error(`${
|
|
3292
|
+
logger.error(`${import_picocolors15.default.cyan(res.agent)}: Update failed - ${res.error}`);
|
|
3032
3293
|
}
|
|
3033
3294
|
}
|
|
3034
3295
|
if (updatedAny) {
|
|
@@ -3046,16 +3307,14 @@ var wizardManage = async (options = {}) => {
|
|
|
3046
3307
|
defaultGlobal: options.global,
|
|
3047
3308
|
message: "Select MCP scope to inspect and manage:"
|
|
3048
3309
|
});
|
|
3049
|
-
const
|
|
3050
|
-
if (
|
|
3310
|
+
const grouped = queryGroupedInstalledServers({ global: isGlobal, cwd });
|
|
3311
|
+
if (grouped.size === 0) {
|
|
3051
3312
|
logger.warn(`No configured MCP servers found in ${isGlobal ? "global" : "project"} scope`);
|
|
3052
3313
|
return;
|
|
3053
3314
|
}
|
|
3054
|
-
const grouped = groupInstalledServersByName(installed);
|
|
3055
3315
|
let pendingServerName = options.serverName;
|
|
3056
3316
|
const refreshGroupedServers = () => {
|
|
3057
|
-
const
|
|
3058
|
-
const freshGrouped = groupInstalledServersByName(freshInstalled);
|
|
3317
|
+
const freshGrouped = queryGroupedInstalledServers({ global: isGlobal, cwd });
|
|
3059
3318
|
grouped.clear();
|
|
3060
3319
|
for (const [name, grp] of freshGrouped) {
|
|
3061
3320
|
grouped.set(name, grp);
|
|
@@ -3071,7 +3330,7 @@ var wizardManage = async (options = {}) => {
|
|
|
3071
3330
|
const choices = Array.from(grouped.values()).map((g) => {
|
|
3072
3331
|
const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
3073
3332
|
return {
|
|
3074
|
-
name: `${
|
|
3333
|
+
name: `${import_picocolors15.default.bold(g.serverName)} ${import_picocolors15.default.dim(`(configured in: ${agentNames})`)}`,
|
|
3075
3334
|
value: g.serverName
|
|
3076
3335
|
};
|
|
3077
3336
|
});
|
|
@@ -3132,7 +3391,7 @@ var wizardManage = async (options = {}) => {
|
|
|
3132
3391
|
);
|
|
3133
3392
|
continue;
|
|
3134
3393
|
}
|
|
3135
|
-
const candidateAgents =
|
|
3394
|
+
const candidateAgents = agentConfigStore.sortAgentsByClusters(rawCandidateAgents, { global: isGlobal, cwd });
|
|
3136
3395
|
const choices = buildLinkedAgentChoices({
|
|
3137
3396
|
agents: candidateAgents,
|
|
3138
3397
|
checkedAgents: [],
|
|
@@ -3160,7 +3419,7 @@ var wizardManage = async (options = {}) => {
|
|
|
3160
3419
|
cwd
|
|
3161
3420
|
});
|
|
3162
3421
|
for (const item of syncResult.incompatible) {
|
|
3163
|
-
logger.warn(`Skipping ${
|
|
3422
|
+
logger.warn(`Skipping ${import_picocolors15.default.cyan(item.agent)}: ${item.reason}`);
|
|
3164
3423
|
}
|
|
3165
3424
|
for (const res of syncResult.results) {
|
|
3166
3425
|
if (syncResult.incompatible.some((i) => i.agent === res.agent)) {
|
|
@@ -3168,187 +3427,21 @@ var wizardManage = async (options = {}) => {
|
|
|
3168
3427
|
}
|
|
3169
3428
|
if (res.success) {
|
|
3170
3429
|
logger.success(
|
|
3171
|
-
`${
|
|
3430
|
+
`${import_picocolors15.default.cyan(res.agent)}: Successfully synced to ${import_picocolors15.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
|
|
3172
3431
|
);
|
|
3173
3432
|
targetGroup.agents.push(res.agent);
|
|
3174
3433
|
} else {
|
|
3175
|
-
logger.error(`${
|
|
3434
|
+
logger.error(`${import_picocolors15.default.cyan(res.agent)}: Sync failed - ${res.error}`);
|
|
3176
3435
|
}
|
|
3177
3436
|
}
|
|
3178
|
-
refreshGroupedServers();
|
|
3179
|
-
}
|
|
3180
|
-
}
|
|
3181
|
-
};
|
|
3182
|
-
|
|
3183
|
-
// src/interactive/wizard-remove.ts
|
|
3184
|
-
var import_prompts9 = require("@inquirer/prompts");
|
|
3185
|
-
var import_picocolors14 = __toESM(require("picocolors"), 1);
|
|
3186
|
-
var wizardRemove = async (options = {}) => {
|
|
3187
|
-
const cwd = options.cwd ?? process.cwd();
|
|
3188
|
-
const isGlobal = await promptScope({
|
|
3189
|
-
cwd,
|
|
3190
|
-
defaultGlobal: options.global,
|
|
3191
|
-
message: "Select scope to remove MCP server from:"
|
|
3192
|
-
});
|
|
3193
|
-
const installed = listInstalledMcpServers({ global: isGlobal, cwd });
|
|
3194
|
-
if (installed.length === 0) {
|
|
3195
|
-
logger.warn(`No installed MCP servers found in ${isGlobal ? "global" : "project"} scope`);
|
|
3196
|
-
return false;
|
|
3197
|
-
}
|
|
3198
|
-
const serverMap = groupInstalledServersByName(installed);
|
|
3199
|
-
let serverName = options.name;
|
|
3200
|
-
if (!serverName) {
|
|
3201
|
-
const choices = Array.from(serverMap.values()).map((g) => ({
|
|
3202
|
-
name: `${import_picocolors14.default.bold(g.serverName)} ${import_picocolors14.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
|
|
3203
|
-
value: g.serverName
|
|
3204
|
-
}));
|
|
3205
|
-
serverName = await (0, import_prompts9.select)({
|
|
3206
|
-
message: "Select MCP server to remove:",
|
|
3207
|
-
choices
|
|
3208
|
-
});
|
|
3209
|
-
}
|
|
3210
|
-
const rawInstalledAgents = serverMap.get(serverName)?.agents || [];
|
|
3211
|
-
if (rawInstalledAgents.length === 0) {
|
|
3212
|
-
logger.warn(`No agents found with [${serverName}] installed`);
|
|
3213
|
-
return false;
|
|
3214
|
-
}
|
|
3215
|
-
const installedAgents = sortAgentsWithClusters(rawInstalledAgents, { global: isGlobal, cwd });
|
|
3216
|
-
let targetAgents = options.agents;
|
|
3217
|
-
if (!targetAgents || targetAgents.length === 0) {
|
|
3218
|
-
const choices = buildLinkedAgentChoices({
|
|
3219
|
-
agents: installedAgents,
|
|
3220
|
-
checkedAgents: installedAgents,
|
|
3221
|
-
scopeOptions: { global: isGlobal, cwd }
|
|
3222
|
-
});
|
|
3223
|
-
targetAgents = await linkedCheckbox({
|
|
3224
|
-
message: `Select agents to remove [${serverName}] from:`,
|
|
3225
|
-
choices,
|
|
3226
|
-
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
3227
|
-
});
|
|
3228
|
-
} else {
|
|
3229
|
-
const validAgents = targetAgents.filter((agent) => installedAgents.includes(agent));
|
|
3230
|
-
if (validAgents.length === 0) {
|
|
3231
|
-
logger.warn(`None of the specified agents (${targetAgents.join(", ")}) have [${serverName}] installed`);
|
|
3232
|
-
return false;
|
|
3233
|
-
}
|
|
3234
|
-
targetAgents = validAgents;
|
|
3235
|
-
}
|
|
3236
|
-
const confirmed = await (0, import_prompts9.confirm)({
|
|
3237
|
-
message: `Confirm removing MCP server [${serverName}] from ${targetAgents.join(", ")}?`,
|
|
3238
|
-
default: true
|
|
3239
|
-
});
|
|
3240
|
-
if (!confirmed) {
|
|
3241
|
-
logger.warn("Operation cancelled");
|
|
3242
|
-
return false;
|
|
3243
|
-
}
|
|
3244
|
-
const results = removeMcpServer({
|
|
3245
|
-
name: serverName,
|
|
3246
|
-
agents: targetAgents,
|
|
3247
|
-
global: isGlobal,
|
|
3248
|
-
cwd
|
|
3249
|
-
});
|
|
3250
|
-
let removedCount = 0;
|
|
3251
|
-
for (const res of results) {
|
|
3252
|
-
if (res.removed) {
|
|
3253
|
-
logger.success(
|
|
3254
|
-
`${import_picocolors14.default.cyan(res.agent)}: Successfully removed from ${import_picocolors14.default.dim(res.path)}${formatCoHostedBadge("affected", res.coAffectedAgents)}`
|
|
3255
|
-
);
|
|
3256
|
-
removedCount++;
|
|
3257
|
-
} else if (res.error) {
|
|
3258
|
-
logger.error(`${import_picocolors14.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
|
|
3259
|
-
}
|
|
3260
|
-
}
|
|
3261
|
-
if (removedCount > 0) {
|
|
3262
|
-
logger.success(`Successfully removed [${serverName}] from ${removedCount} agent(s)`);
|
|
3263
|
-
return true;
|
|
3264
|
-
}
|
|
3265
|
-
logger.warn(`Failed to remove [${serverName}] from specified agents`);
|
|
3266
|
-
return false;
|
|
3267
|
-
};
|
|
3268
|
-
|
|
3269
|
-
// src/interactive/main-menu.ts
|
|
3270
|
-
var mainMenu = async () => {
|
|
3271
|
-
console.log();
|
|
3272
|
-
console.log(import_picocolors15.default.bold(import_picocolors15.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
|
|
3273
|
-
console.log(import_picocolors15.default.dim("Cross-platform MCP server configuration & synchronization tool"));
|
|
3274
|
-
console.log();
|
|
3275
|
-
while (true) {
|
|
3276
|
-
try {
|
|
3277
|
-
const action = await (0, import_prompts10.select)({
|
|
3278
|
-
message: "Select an action:",
|
|
3279
|
-
choices: [
|
|
3280
|
-
{
|
|
3281
|
-
name: "Add MCP Server",
|
|
3282
|
-
value: "add"
|
|
3283
|
-
},
|
|
3284
|
-
{
|
|
3285
|
-
name: "Manage & Sync Installed MCP Servers",
|
|
3286
|
-
value: "manage"
|
|
3287
|
-
},
|
|
3288
|
-
{
|
|
3289
|
-
name: "Remove MCP Server",
|
|
3290
|
-
value: "remove"
|
|
3291
|
-
},
|
|
3292
|
-
{
|
|
3293
|
-
name: "Exit",
|
|
3294
|
-
value: "exit"
|
|
3295
|
-
}
|
|
3296
|
-
]
|
|
3297
|
-
});
|
|
3298
|
-
if (action === "exit") {
|
|
3299
|
-
console.log(import_picocolors15.default.dim("Goodbye!"));
|
|
3300
|
-
break;
|
|
3301
|
-
}
|
|
3302
|
-
if (action === "add") {
|
|
3303
|
-
await wizardAdd();
|
|
3304
|
-
} else if (action === "manage") {
|
|
3305
|
-
await wizardManage();
|
|
3306
|
-
} else if (action === "remove") {
|
|
3307
|
-
await wizardRemove();
|
|
3308
|
-
}
|
|
3309
|
-
console.log();
|
|
3310
|
-
} catch (error) {
|
|
3311
|
-
if (error?.name === "ExitPromptError") {
|
|
3312
|
-
console.log("\n" + import_picocolors15.default.dim("Exited."));
|
|
3313
|
-
break;
|
|
3314
|
-
}
|
|
3315
|
-
throw error;
|
|
3316
|
-
}
|
|
3317
|
-
}
|
|
3318
|
-
};
|
|
3319
|
-
|
|
3320
|
-
// src/utils/resolve-transport.ts
|
|
3321
|
-
var resolveTransport = (input7) => {
|
|
3322
|
-
if (!input7) return void 0;
|
|
3323
|
-
if (input7 === "http" || input7 === "sse") return input7;
|
|
3324
|
-
throw new Error(`Unsupported transport "${input7}" (expected: http, sse)`);
|
|
3325
|
-
};
|
|
3326
|
-
|
|
3327
|
-
// src/cli/manage.ts
|
|
3328
|
-
var import_commander = require("commander");
|
|
3329
|
-
var import_picocolors16 = __toESM(require("picocolors"), 1);
|
|
3330
|
-
|
|
3331
|
-
// src/utils/parse-key-value-list.ts
|
|
3332
|
-
var parseKeyValueList = (entries, separator) => {
|
|
3333
|
-
if (!entries || entries.length === 0) return {};
|
|
3334
|
-
const result = {};
|
|
3335
|
-
for (const entry of entries) {
|
|
3336
|
-
const splitIndex = entry.indexOf(separator);
|
|
3337
|
-
if (splitIndex === -1) {
|
|
3338
|
-
throw new Error(`Invalid entry "${entry}": expected "${separator}" separator`);
|
|
3437
|
+
refreshGroupedServers();
|
|
3339
3438
|
}
|
|
3340
|
-
const key = entry.slice(0, splitIndex).trim();
|
|
3341
|
-
const value = entry.slice(splitIndex + separator.length).trim();
|
|
3342
|
-
if (!key) throw new Error(`Invalid entry "${entry}": empty key`);
|
|
3343
|
-
result[key] = value;
|
|
3344
3439
|
}
|
|
3345
|
-
return result;
|
|
3346
3440
|
};
|
|
3347
3441
|
|
|
3348
3442
|
// src/cli/manage.ts
|
|
3349
3443
|
var requireTargetServerGroup = (serverName, scope) => {
|
|
3350
|
-
const
|
|
3351
|
-
const grouped = groupInstalledServersByName(installed);
|
|
3444
|
+
const grouped = queryGroupedInstalledServers(scope);
|
|
3352
3445
|
const targetGroup = grouped.get(serverName);
|
|
3353
3446
|
if (!targetGroup) {
|
|
3354
3447
|
logger.error(
|
|
@@ -3359,18 +3452,13 @@ var requireTargetServerGroup = (serverName, scope) => {
|
|
|
3359
3452
|
}
|
|
3360
3453
|
return targetGroup;
|
|
3361
3454
|
};
|
|
3362
|
-
var mcpManageCommand = new
|
|
3455
|
+
var mcpManageCommand = new import_commander3.Command("manage").description("Inspect, modify, and sync installed MCP servers across coding agents").argument("[server-name]", "Optional server name to inspect or manage").option("-a, --agent <agents...>", "Target specific agents for update").option("-g, --global", "Manage global scope servers instead of project").option("-t, --transport <type>", "Transport type for remote servers (http or sse)").option("--header <header...>", "HTTP header (Header: Value), repeatable").option("--clear-headers", "Clear all HTTP headers for remote servers").option("--env <env...>", "Env var for stdio servers (KEY=VALUE), repeatable").option("--clear-env", "Clear all environment variables for stdio servers").option("--args <args...>", "CLI arguments for stdio/package servers").option("--clear-args", "Clear all arguments for stdio/package servers").option("--command <command>", "Executable command for stdio servers").option("--url <url>", "Remote endpoint URL").option("-y, --yes", "Skip interactive prompts").action(async (serverName, options) => {
|
|
3363
3456
|
try {
|
|
3364
3457
|
const cwd = process.cwd();
|
|
3365
3458
|
const isGlobal = Boolean(options.global);
|
|
3366
3459
|
const hasModifications = options.command !== void 0 || options.args !== void 0 || Boolean(options.clearArgs) || options.env !== void 0 || Boolean(options.clearEnv) || options.header !== void 0 || Boolean(options.clearHeaders) || options.url !== void 0 || options.transport !== void 0;
|
|
3367
3460
|
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
3368
3461
|
if (hasModifications) {
|
|
3369
|
-
if (options.url !== void 0 && options.command !== void 0) {
|
|
3370
|
-
logger.error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
|
|
3371
|
-
process.exitCode = 1;
|
|
3372
|
-
return;
|
|
3373
|
-
}
|
|
3374
3462
|
if (!serverName) {
|
|
3375
3463
|
logger.error('Missing required argument: "server-name" when passing modification flags.');
|
|
3376
3464
|
process.exitCode = 1;
|
|
@@ -3380,64 +3468,34 @@ var mcpManageCommand = new import_commander.Command("manage").description("Inspe
|
|
|
3380
3468
|
if (!targetGroup) {
|
|
3381
3469
|
return;
|
|
3382
3470
|
}
|
|
3383
|
-
const
|
|
3384
|
-
const
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
if (ignoredRemoteFlags.length > 0) {
|
|
3403
|
-
const hint = options.command !== void 0 ? "When configuring a stdio server, remote flags are ignored." : "Use --url to switch to remote mode.";
|
|
3404
|
-
logger.warn(
|
|
3405
|
-
`Server "${serverName}" is a stdio server. The following remote flags will be ignored: ${ignoredRemoteFlags.join(", ")}. ${hint}`
|
|
3406
|
-
);
|
|
3407
|
-
}
|
|
3408
|
-
}
|
|
3409
|
-
const incomingDelta = {};
|
|
3410
|
-
if (options.command !== void 0) {
|
|
3411
|
-
incomingDelta.command = options.command;
|
|
3412
|
-
}
|
|
3413
|
-
if (options.clearArgs) {
|
|
3414
|
-
incomingDelta.args = void 0;
|
|
3415
|
-
}
|
|
3416
|
-
if (options.args !== void 0) {
|
|
3417
|
-
incomingDelta.args = options.args;
|
|
3418
|
-
}
|
|
3419
|
-
if (options.url !== void 0) {
|
|
3420
|
-
incomingDelta.url = options.url;
|
|
3421
|
-
}
|
|
3422
|
-
if (options.transport !== void 0) {
|
|
3423
|
-
incomingDelta.type = resolveTransport(options.transport);
|
|
3424
|
-
}
|
|
3425
|
-
if (options.clearEnv) {
|
|
3426
|
-
incomingDelta.env = void 0;
|
|
3427
|
-
}
|
|
3428
|
-
if (options.env !== void 0) {
|
|
3429
|
-
const parsedEnv = parseKeyValueList(options.env, "=");
|
|
3430
|
-
const baseEnv = options.clearEnv ? {} : targetGroup.config.env ?? {};
|
|
3431
|
-
incomingDelta.env = { ...baseEnv, ...parsedEnv };
|
|
3432
|
-
}
|
|
3433
|
-
if (options.clearHeaders) {
|
|
3434
|
-
incomingDelta.headers = void 0;
|
|
3471
|
+
const parsedEnv = options.env !== void 0 ? parseKeyValueList(options.env, "=") : void 0;
|
|
3472
|
+
const parsedHeaders = options.header !== void 0 ? parseKeyValueList(options.header, ":") : void 0;
|
|
3473
|
+
let deltaResult;
|
|
3474
|
+
try {
|
|
3475
|
+
deltaResult = applyServerConfigDelta(targetGroup.config, {
|
|
3476
|
+
command: options.command,
|
|
3477
|
+
args: options.args,
|
|
3478
|
+
clearArgs: options.clearArgs,
|
|
3479
|
+
env: parsedEnv,
|
|
3480
|
+
clearEnv: options.clearEnv,
|
|
3481
|
+
url: options.url,
|
|
3482
|
+
transport: resolveTransport(options.transport),
|
|
3483
|
+
headers: parsedHeaders,
|
|
3484
|
+
clearHeaders: options.clearHeaders
|
|
3485
|
+
});
|
|
3486
|
+
} catch (error) {
|
|
3487
|
+
logger.error(toErrorMessage(error));
|
|
3488
|
+
process.exitCode = 1;
|
|
3489
|
+
return;
|
|
3435
3490
|
}
|
|
3436
|
-
if (
|
|
3437
|
-
const
|
|
3438
|
-
const
|
|
3439
|
-
|
|
3491
|
+
if (deltaResult.ignoredFlags.length > 0) {
|
|
3492
|
+
const isRemote = deltaResult.protocol === "remote";
|
|
3493
|
+
const hint = isRemote ? options.url !== void 0 ? "When configuring a remote server, stdio flags are ignored." : "Use --command to switch to stdio mode." : options.command !== void 0 ? "When configuring a stdio server, remote flags are ignored." : "Use --url to switch to remote mode.";
|
|
3494
|
+
logger.warn(
|
|
3495
|
+
`Server "${serverName}" is a ${isRemote ? "remote" : "stdio"} server. The following ${isRemote ? "stdio" : "remote"} flags will be ignored: ${deltaResult.ignoredFlags.join(", ")}. ${hint}`
|
|
3496
|
+
);
|
|
3440
3497
|
}
|
|
3498
|
+
const incomingDelta = deltaResult.config;
|
|
3441
3499
|
let targetAgents = targetGroup.agents;
|
|
3442
3500
|
if (options.agent !== void 0) {
|
|
3443
3501
|
const parsed = parseMcpAgentList(options.agent);
|
|
@@ -3522,129 +3580,96 @@ var mcpManageCommand = new import_commander.Command("manage").description("Inspe
|
|
|
3522
3580
|
}
|
|
3523
3581
|
});
|
|
3524
3582
|
|
|
3525
|
-
// src/
|
|
3526
|
-
var
|
|
3583
|
+
// src/cli/remove.ts
|
|
3584
|
+
var import_commander4 = require("commander");
|
|
3585
|
+
var import_picocolors18 = __toESM(require("picocolors"), 1);
|
|
3527
3586
|
|
|
3528
|
-
// src/
|
|
3529
|
-
var
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
const isGlobal = Boolean(options.global);
|
|
3553
|
-
const explicitTransport = resolveTransport(options.transport);
|
|
3554
|
-
const transport = explicitTransport ?? (parsed.type === "remote" ? "http" : "stdio");
|
|
3555
|
-
const resolvedTargets = resolveTargetAgents({
|
|
3556
|
-
requested: options.agent,
|
|
3557
|
-
all: options.all,
|
|
3558
|
-
global: isGlobal,
|
|
3559
|
-
cwd,
|
|
3560
|
-
transport
|
|
3561
|
-
});
|
|
3562
|
-
if (resolvedTargets.agents.length === 0) {
|
|
3563
|
-
const message = resolvedTargets.diagnostic ?? `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass ${import_picocolors17.default.cyan("-a <agent>")} (e.g. ${import_picocolors17.default.cyan("-a cursor")}) or ${import_picocolors17.default.cyan("--all")} to install.`;
|
|
3564
|
-
logger.warn(message);
|
|
3565
|
-
process.exitCode = 1;
|
|
3566
|
-
return;
|
|
3567
|
-
}
|
|
3568
|
-
if (resolvedTargets.isDetected) {
|
|
3569
|
-
logger.info(
|
|
3570
|
-
`Detected ${isGlobal ? "global" : "project"} agents: ${import_picocolors17.default.cyan(formatAgentList(resolvedTargets.detected, "(none detected)"))}`
|
|
3571
|
-
);
|
|
3572
|
-
if (resolvedTargets.incompatible.length > 0) {
|
|
3573
|
-
const skippedList = resolvedTargets.incompatible.map((item) => `${item.agent} (${item.reason})`).join(", ");
|
|
3574
|
-
logger.info(
|
|
3575
|
-
`Skipping detected agents incompatible with ${transport}: ${import_picocolors17.default.yellow(skippedList)}`
|
|
3576
|
-
);
|
|
3577
|
-
}
|
|
3578
|
-
}
|
|
3579
|
-
const targetAgents = resolvedTargets.isDetected ? resolvedTargets.agents : resolvedTargets.allAgents;
|
|
3580
|
-
const result = installMcpServer({
|
|
3581
|
-
source,
|
|
3582
|
-
name: options.name,
|
|
3583
|
-
agents: targetAgents,
|
|
3584
|
-
args: options.args,
|
|
3585
|
-
global: isGlobal,
|
|
3586
|
-
cwd,
|
|
3587
|
-
transport: explicitTransport,
|
|
3588
|
-
headers: parseKeyValueList(options.header, ":"),
|
|
3589
|
-
env: parseKeyValueList(options.env, "=")
|
|
3587
|
+
// src/interactive/wizard-remove.ts
|
|
3588
|
+
var import_prompts9 = require("@inquirer/prompts");
|
|
3589
|
+
var import_picocolors17 = __toESM(require("picocolors"), 1);
|
|
3590
|
+
var wizardRemove = async (options = {}) => {
|
|
3591
|
+
const cwd = options.cwd ?? process.cwd();
|
|
3592
|
+
const isGlobal = await promptScope({
|
|
3593
|
+
cwd,
|
|
3594
|
+
defaultGlobal: options.global,
|
|
3595
|
+
message: "Select scope to remove MCP server from:"
|
|
3596
|
+
});
|
|
3597
|
+
const serverMap = queryGroupedInstalledServers({ global: isGlobal, cwd });
|
|
3598
|
+
if (serverMap.size === 0) {
|
|
3599
|
+
logger.warn(`No installed MCP servers found in ${isGlobal ? "global" : "project"} scope`);
|
|
3600
|
+
return false;
|
|
3601
|
+
}
|
|
3602
|
+
let serverName = options.name;
|
|
3603
|
+
if (!serverName) {
|
|
3604
|
+
const choices = Array.from(serverMap.values()).map((g) => ({
|
|
3605
|
+
name: `${import_picocolors17.default.bold(g.serverName)} ${import_picocolors17.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
|
|
3606
|
+
value: g.serverName
|
|
3607
|
+
}));
|
|
3608
|
+
serverName = await (0, import_prompts9.select)({
|
|
3609
|
+
message: "Select MCP server to remove:",
|
|
3610
|
+
choices
|
|
3590
3611
|
});
|
|
3591
|
-
logger.info(
|
|
3592
|
-
`Installing ${import_picocolors17.default.bold(result.serverName)} (${import_picocolors17.default.cyan(parsed.type)}) to ${import_picocolors17.default.cyan(String(result.results.length))} agent(s)`
|
|
3593
|
-
);
|
|
3594
|
-
for (const record of result.results) {
|
|
3595
|
-
if (record.success) {
|
|
3596
|
-
logger.success(`${import_picocolors17.default.cyan(record.agent)} ${import_picocolors17.default.dim(record.path)}`);
|
|
3597
|
-
logCoHostedNotice("configured", record.coConfiguredAgents);
|
|
3598
|
-
} else {
|
|
3599
|
-
logger.error(`${import_picocolors17.default.cyan(record.agent)}: ${record.error}`);
|
|
3600
|
-
}
|
|
3601
|
-
}
|
|
3602
|
-
if (result.results.some((record) => !record.success)) process.exitCode = 1;
|
|
3603
|
-
} catch (error) {
|
|
3604
|
-
logger.error(toErrorMessage(error));
|
|
3605
|
-
process.exitCode = 1;
|
|
3606
3612
|
}
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3613
|
+
const rawInstalledAgents = serverMap.get(serverName)?.agents || [];
|
|
3614
|
+
if (rawInstalledAgents.length === 0) {
|
|
3615
|
+
logger.warn(`No agents found with [${serverName}] installed`);
|
|
3616
|
+
return false;
|
|
3617
|
+
}
|
|
3618
|
+
const installedAgents = agentConfigStore.sortAgentsByClusters(rawInstalledAgents, { global: isGlobal, cwd });
|
|
3619
|
+
let targetAgents = options.agents;
|
|
3620
|
+
if (!targetAgents || targetAgents.length === 0) {
|
|
3621
|
+
const choices = buildLinkedAgentChoices({
|
|
3622
|
+
agents: installedAgents,
|
|
3623
|
+
checkedAgents: installedAgents,
|
|
3624
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
3618
3625
|
});
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
const existing = grouped.get(entry.serverName) ?? [];
|
|
3630
|
-
existing.push(entry);
|
|
3631
|
-
grouped.set(entry.serverName, existing);
|
|
3626
|
+
targetAgents = await linkedCheckbox({
|
|
3627
|
+
message: `Select agents to remove [${serverName}] from:`,
|
|
3628
|
+
choices,
|
|
3629
|
+
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
3630
|
+
});
|
|
3631
|
+
} else {
|
|
3632
|
+
const validAgents = targetAgents.filter((agent) => installedAgents.includes(agent));
|
|
3633
|
+
if (validAgents.length === 0) {
|
|
3634
|
+
logger.warn(`None of the specified agents (${targetAgents.join(", ")}) have [${serverName}] installed`);
|
|
3635
|
+
return false;
|
|
3632
3636
|
}
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3637
|
+
targetAgents = validAgents;
|
|
3638
|
+
}
|
|
3639
|
+
const confirmed = await (0, import_prompts9.confirm)({
|
|
3640
|
+
message: `Confirm removing MCP server [${serverName}] from ${targetAgents.join(", ")}?`,
|
|
3641
|
+
default: true
|
|
3642
|
+
});
|
|
3643
|
+
if (!confirmed) {
|
|
3644
|
+
logger.warn("Operation cancelled");
|
|
3645
|
+
return false;
|
|
3646
|
+
}
|
|
3647
|
+
const results = removeMcpServer({
|
|
3648
|
+
name: serverName,
|
|
3649
|
+
agents: targetAgents,
|
|
3650
|
+
global: isGlobal,
|
|
3651
|
+
cwd
|
|
3652
|
+
});
|
|
3653
|
+
let removedCount = 0;
|
|
3654
|
+
for (const res of results) {
|
|
3655
|
+
if (res.removed) {
|
|
3656
|
+
logger.success(
|
|
3657
|
+
`${import_picocolors17.default.cyan(res.agent)}: Successfully removed from ${import_picocolors17.default.dim(res.path)}${formatCoHostedBadge("affected", res.coAffectedAgents)}`
|
|
3658
|
+
);
|
|
3659
|
+
removedCount++;
|
|
3660
|
+
} else if (res.error) {
|
|
3661
|
+
logger.error(`${import_picocolors17.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
|
|
3638
3662
|
}
|
|
3639
|
-
} catch (error) {
|
|
3640
|
-
logger.error(toErrorMessage(error));
|
|
3641
|
-
process.exitCode = 1;
|
|
3642
3663
|
}
|
|
3643
|
-
|
|
3664
|
+
if (removedCount > 0) {
|
|
3665
|
+
logger.success(`Successfully removed [${serverName}] from ${removedCount} agent(s)`);
|
|
3666
|
+
return true;
|
|
3667
|
+
}
|
|
3668
|
+
logger.warn(`Failed to remove [${serverName}] from specified agents`);
|
|
3669
|
+
return false;
|
|
3670
|
+
};
|
|
3644
3671
|
|
|
3645
3672
|
// src/cli/remove.ts
|
|
3646
|
-
var import_commander4 = require("commander");
|
|
3647
|
-
var import_picocolors19 = __toESM(require("picocolors"), 1);
|
|
3648
3673
|
var mcpRemoveCommand = new import_commander4.Command("remove").alias("rm").description("Remove an MCP server from agent configs").argument("[name]", "Server name").option("-g, --global", "Remove from global scope").option("-a, --agent <agents...>", "Filter by specific agents (use '*' for all)").option("-y, --yes", "Skip confirmation prompts").action(async (name, options) => {
|
|
3649
3674
|
try {
|
|
3650
3675
|
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
@@ -3668,17 +3693,17 @@ var mcpRemoveCommand = new import_commander4.Command("remove").alias("rm").descr
|
|
|
3668
3693
|
cwd: process.cwd()
|
|
3669
3694
|
});
|
|
3670
3695
|
if (results.length === 0) {
|
|
3671
|
-
logger.warn(`No agent config contained ${
|
|
3696
|
+
logger.warn(`No agent config contained ${import_picocolors18.default.bold(name)}`);
|
|
3672
3697
|
return;
|
|
3673
3698
|
}
|
|
3674
3699
|
for (const record of results) {
|
|
3675
3700
|
if (record.removed) {
|
|
3676
3701
|
logger.success(
|
|
3677
|
-
`${
|
|
3702
|
+
`${import_picocolors18.default.cyan(record.agent)} removed ${import_picocolors18.default.bold(name)} ${import_picocolors18.default.dim(record.path)}`
|
|
3678
3703
|
);
|
|
3679
3704
|
logCoHostedNotice("affected", record.coAffectedAgents);
|
|
3680
3705
|
} else {
|
|
3681
|
-
logger.error(`${
|
|
3706
|
+
logger.error(`${import_picocolors18.default.cyan(record.agent)}: ${record.error ?? "not found"}`);
|
|
3682
3707
|
}
|
|
3683
3708
|
}
|
|
3684
3709
|
} catch (error) {
|
|
@@ -3687,8 +3712,61 @@ var mcpRemoveCommand = new import_commander4.Command("remove").alias("rm").descr
|
|
|
3687
3712
|
}
|
|
3688
3713
|
});
|
|
3689
3714
|
|
|
3715
|
+
// src/interactive/main-menu.ts
|
|
3716
|
+
var import_prompts10 = require("@inquirer/prompts");
|
|
3717
|
+
var import_picocolors19 = __toESM(require("picocolors"), 1);
|
|
3718
|
+
var mainMenu = async () => {
|
|
3719
|
+
console.log();
|
|
3720
|
+
console.log(import_picocolors19.default.bold(import_picocolors19.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
|
|
3721
|
+
console.log(import_picocolors19.default.dim("Cross-platform MCP server configuration & synchronization tool"));
|
|
3722
|
+
console.log();
|
|
3723
|
+
while (true) {
|
|
3724
|
+
try {
|
|
3725
|
+
const action = await (0, import_prompts10.select)({
|
|
3726
|
+
message: "Select an action:",
|
|
3727
|
+
choices: [
|
|
3728
|
+
{
|
|
3729
|
+
name: "Add MCP Server",
|
|
3730
|
+
value: "add"
|
|
3731
|
+
},
|
|
3732
|
+
{
|
|
3733
|
+
name: "Manage & Sync Installed MCP Servers",
|
|
3734
|
+
value: "manage"
|
|
3735
|
+
},
|
|
3736
|
+
{
|
|
3737
|
+
name: "Remove MCP Server",
|
|
3738
|
+
value: "remove"
|
|
3739
|
+
},
|
|
3740
|
+
{
|
|
3741
|
+
name: "Exit",
|
|
3742
|
+
value: "exit"
|
|
3743
|
+
}
|
|
3744
|
+
]
|
|
3745
|
+
});
|
|
3746
|
+
if (action === "exit") {
|
|
3747
|
+
console.log(import_picocolors19.default.dim("Goodbye!"));
|
|
3748
|
+
break;
|
|
3749
|
+
}
|
|
3750
|
+
if (action === "add") {
|
|
3751
|
+
await wizardAdd();
|
|
3752
|
+
} else if (action === "manage") {
|
|
3753
|
+
await wizardManage();
|
|
3754
|
+
} else if (action === "remove") {
|
|
3755
|
+
await wizardRemove();
|
|
3756
|
+
}
|
|
3757
|
+
console.log();
|
|
3758
|
+
} catch (error) {
|
|
3759
|
+
if (error?.name === "ExitPromptError") {
|
|
3760
|
+
console.log("\n" + import_picocolors19.default.dim("Exited."));
|
|
3761
|
+
break;
|
|
3762
|
+
}
|
|
3763
|
+
throw error;
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
};
|
|
3767
|
+
|
|
3690
3768
|
// src/cli.ts
|
|
3691
|
-
var VERSION = "0.1.0
|
|
3769
|
+
var VERSION = "0.1.0";
|
|
3692
3770
|
process.on("SIGINT", () => process.exit(0));
|
|
3693
3771
|
process.on("SIGTERM", () => process.exit(0));
|
|
3694
3772
|
var program = new import_commander5.Command().name("mcps").description("Install, list, and remove MCP servers across AI coding agents").version(VERSION, "-v, --version", "display the version number");
|