@wuyax/mcps 0.1.0-beta.2 → 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 +244 -20
- package/dist/chunk-AWD3VQ3S.js +1624 -0
- package/dist/chunk-O3BRJFEC.js +1898 -0
- package/dist/cli.cjs +1553 -852
- package/dist/cli.js +186 -16
- package/dist/index.cjs +678 -1755
- package/dist/index.d.cts +162 -299
- package/dist/index.d.ts +162 -299
- package/dist/index.js +29 -74
- package/dist/{chunk-7V5XL4RI.js → interactive.cjs} +1649 -1172
- 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 +33 -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,113 +1115,6 @@ var listServersInConfigFile = (filePath, format, dottedKey) => {
|
|
|
887
1115
|
return isPlainObject(entries) ? entries : {};
|
|
888
1116
|
};
|
|
889
1117
|
|
|
890
|
-
// src/config-store.ts
|
|
891
|
-
var import_node_fs6 = require("fs");
|
|
892
|
-
|
|
893
|
-
// src/resolve-config-target.ts
|
|
894
|
-
var import_node_path3 = require("path");
|
|
895
|
-
var resolveMcpConfigTarget = (agent, options = {}) => {
|
|
896
|
-
const isGlobal = options.global ?? false;
|
|
897
|
-
const cwd = options.cwd ?? process.cwd();
|
|
898
|
-
const configPath = agent.resolveConfigPath ? agent.resolveConfigPath({ global: isGlobal, cwd }) : !isGlobal && agent.projectConfigPath ? (0, import_node_path3.join)(cwd, agent.projectConfigPath) : agent.globalConfigPath;
|
|
899
|
-
const configKey = !isGlobal && agent.projectConfigKey ? agent.projectConfigKey : agent.configKey;
|
|
900
|
-
return { configPath, configKey };
|
|
901
|
-
};
|
|
902
|
-
|
|
903
|
-
// src/config-store.ts
|
|
904
|
-
var FsConfigStoreAdapter = class {
|
|
905
|
-
exists(filePath) {
|
|
906
|
-
return (0, import_node_fs6.existsSync)(filePath);
|
|
907
|
-
}
|
|
908
|
-
read(target) {
|
|
909
|
-
return readConfigFile(target.filePath, target.format);
|
|
910
|
-
}
|
|
911
|
-
writeServer(target, serverName, serverConfig) {
|
|
912
|
-
if (!target.dottedKey) {
|
|
913
|
-
throw new Error(`Cannot write server: missing dottedKey for ${target.filePath}`);
|
|
914
|
-
}
|
|
915
|
-
writeServerToConfigFile(
|
|
916
|
-
target.filePath,
|
|
917
|
-
target.format,
|
|
918
|
-
target.dottedKey,
|
|
919
|
-
serverName,
|
|
920
|
-
serverConfig
|
|
921
|
-
);
|
|
922
|
-
}
|
|
923
|
-
removeServer(target, serverName) {
|
|
924
|
-
if (!target.dottedKey) return false;
|
|
925
|
-
return removeServerFromConfigFile(
|
|
926
|
-
target.filePath,
|
|
927
|
-
target.format,
|
|
928
|
-
target.dottedKey,
|
|
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
1118
|
// src/transforms/index.ts
|
|
998
1119
|
var DIALECT_PRESETS = {
|
|
999
1120
|
vscode: {
|
|
@@ -1201,30 +1322,295 @@ var transformServerConfigForAgent = (agent, serverName, config, context = { glob
|
|
|
1201
1322
|
return config;
|
|
1202
1323
|
};
|
|
1203
1324
|
|
|
1204
|
-
// src/
|
|
1205
|
-
var
|
|
1206
|
-
const agent = getMcpAgentConfig(agentType);
|
|
1207
|
-
const isGlobal = options.global ?? false;
|
|
1208
|
-
const { target } = agentConfigStore.resolveTarget(agent, options);
|
|
1209
|
-
try {
|
|
1210
|
-
const transformed = transformServerConfigForAgent(agent, serverName, serverConfig, {
|
|
1211
|
-
global: isGlobal
|
|
1212
|
-
});
|
|
1213
|
-
agentConfigStore.writeServer(agent, serverName, transformed, options);
|
|
1214
|
-
return { agent: agentType, success: true, path: target.configPath };
|
|
1215
|
-
} catch (error) {
|
|
1216
|
-
return {
|
|
1217
|
-
agent: agentType,
|
|
1218
|
-
success: false,
|
|
1219
|
-
path: target.configPath,
|
|
1220
|
-
error: toErrorMessage(error)
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
};
|
|
1325
|
+
// src/utils/to-error-message.ts
|
|
1326
|
+
var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
|
|
1224
1327
|
|
|
1225
|
-
// src/
|
|
1226
|
-
var
|
|
1227
|
-
|
|
1328
|
+
// src/config-store.ts
|
|
1329
|
+
var resolveMcpConfigTarget = (agent, options = {}) => {
|
|
1330
|
+
const isGlobal = options.global ?? false;
|
|
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 };
|
|
1335
|
+
};
|
|
1336
|
+
var getCandidateAgentsForScope = (options = {}) => {
|
|
1337
|
+
return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
1338
|
+
};
|
|
1339
|
+
var FsConfigStoreAdapter = class {
|
|
1340
|
+
exists(filePath) {
|
|
1341
|
+
return (0, import_node_fs6.existsSync)(filePath);
|
|
1342
|
+
}
|
|
1343
|
+
read(target) {
|
|
1344
|
+
return readConfigFile(target.filePath, target.format);
|
|
1345
|
+
}
|
|
1346
|
+
writeServer(target, serverName, serverConfig) {
|
|
1347
|
+
if (!target.dottedKey) {
|
|
1348
|
+
throw new Error(`Cannot write server: missing dottedKey for ${target.filePath}`);
|
|
1349
|
+
}
|
|
1350
|
+
writeServerToConfigFile(
|
|
1351
|
+
target.filePath,
|
|
1352
|
+
target.format,
|
|
1353
|
+
target.dottedKey,
|
|
1354
|
+
serverName,
|
|
1355
|
+
serverConfig
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
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
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
listServers(target) {
|
|
1368
|
+
if (!target.dottedKey) return {};
|
|
1369
|
+
return listServersInConfigFile(target.filePath, target.format, target.dottedKey);
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
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 };
|
|
1403
|
+
}
|
|
1404
|
+
const removed = this.adapter.removeServer(descriptor, serverName);
|
|
1405
|
+
return { path: descriptor.filePath, removed };
|
|
1406
|
+
}
|
|
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: {} };
|
|
1411
|
+
}
|
|
1412
|
+
const servers = this.adapter.listServers(descriptor);
|
|
1413
|
+
return { path: descriptor.filePath, exists: true, servers };
|
|
1414
|
+
}
|
|
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
|
+
};
|
|
1435
|
+
});
|
|
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));
|
|
1607
|
+
}
|
|
1608
|
+
};
|
|
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;
|
|
1228
1614
|
if (input7.includes("*")) return getMcpAgentTypes();
|
|
1229
1615
|
const resolved = [];
|
|
1230
1616
|
for (const value of input7) {
|
|
@@ -1293,115 +1679,33 @@ var resolveTargetAgents = (query = {}) => {
|
|
|
1293
1679
|
};
|
|
1294
1680
|
};
|
|
1295
1681
|
|
|
1296
|
-
// src/
|
|
1297
|
-
var
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
if (atIndex > 0) return input7.slice(0, atIndex);
|
|
1309
|
-
return input7;
|
|
1310
|
-
};
|
|
1311
|
-
var stripScopePrefix = (input7) => {
|
|
1312
|
-
if (!input7.startsWith("@") || !input7.includes("/")) return input7;
|
|
1313
|
-
const parts = input7.split("/");
|
|
1314
|
-
return parts[1] || input7;
|
|
1315
|
-
};
|
|
1316
|
-
var stripPathPrefix = (input7) => {
|
|
1317
|
-
if (!PATH_SEPARATOR_REGEX.test(input7)) return input7;
|
|
1318
|
-
const segments = input7.split(PATH_SEPARATOR_REGEX);
|
|
1319
|
-
const basename = segments[segments.length - 1];
|
|
1320
|
-
return basename || input7;
|
|
1321
|
-
};
|
|
1322
|
-
var extractPackageName = (input7) => {
|
|
1323
|
-
let name = stripVersionSuffix(input7);
|
|
1324
|
-
name = stripScopePrefix(name);
|
|
1325
|
-
name = stripPathPrefix(name);
|
|
1326
|
-
name = name.replace(SCRIPT_EXTENSION_REGEX, "");
|
|
1327
|
-
for (const prefix of PACKAGE_NAME_PREFIX_STRIP) {
|
|
1328
|
-
if (name.startsWith(prefix)) {
|
|
1329
|
-
name = name.slice(prefix.length);
|
|
1330
|
-
break;
|
|
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
|
|
1331
1694
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
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
|
+
};
|
|
1337
1706
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
};
|
|
1341
|
-
var inferNameFromUrl = (input7) => {
|
|
1342
|
-
try {
|
|
1343
|
-
const url = new URL(input7);
|
|
1344
|
-
const host = url.hostname;
|
|
1345
|
-
const labels = host.split(".").filter((segment) => segment.length > 0);
|
|
1346
|
-
if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
|
|
1347
|
-
const meaningfulLabels = labels.filter((label) => {
|
|
1348
|
-
const lower = label.toLowerCase();
|
|
1349
|
-
if (COMMON_TLD_LABELS.has(lower)) return false;
|
|
1350
|
-
if (GENERIC_HOST_PREFIXES.has(lower)) return false;
|
|
1351
|
-
return true;
|
|
1352
|
-
});
|
|
1353
|
-
if (meaningfulLabels.length > 0) return meaningfulLabels[0];
|
|
1354
|
-
if (labels.length >= 2) return labels[labels.length - 2];
|
|
1355
|
-
return labels[labels.length - 1] || MCP_DEFAULT_SERVER_NAME;
|
|
1356
|
-
} catch {
|
|
1357
|
-
return MCP_DEFAULT_SERVER_NAME;
|
|
1358
|
-
}
|
|
1359
|
-
};
|
|
1360
|
-
var inferNameFromCommand = (command) => {
|
|
1361
|
-
const tokens = command.trim().split(/\s+/);
|
|
1362
|
-
const runnerBase = tokens[0]?.split(PATH_SEPARATOR_REGEX).pop() ?? "";
|
|
1363
|
-
const startIndex = KNOWN_COMMAND_RUNNERS.has(runnerBase) ? 1 : 0;
|
|
1364
|
-
for (let tokenIndex = startIndex; tokenIndex < tokens.length; tokenIndex += 1) {
|
|
1365
|
-
const token = tokens[tokenIndex];
|
|
1366
|
-
if (!token || token.startsWith("-")) continue;
|
|
1367
|
-
return extractPackageName(token);
|
|
1368
|
-
}
|
|
1369
|
-
const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
|
|
1370
|
-
return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
|
|
1371
|
-
};
|
|
1372
|
-
var parseMcpSource = (input7) => {
|
|
1373
|
-
const trimmed = input7.trim();
|
|
1374
|
-
if (trimmed.length === 0) {
|
|
1375
|
-
throw new Error(
|
|
1376
|
-
"Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
|
|
1377
|
-
);
|
|
1378
|
-
}
|
|
1379
|
-
if (REMOTE_URL_REGEX.test(trimmed)) {
|
|
1380
|
-
return {
|
|
1381
|
-
type: "remote",
|
|
1382
|
-
value: trimmed,
|
|
1383
|
-
inferredName: inferNameFromUrl(trimmed)
|
|
1384
|
-
};
|
|
1385
|
-
}
|
|
1386
|
-
if (HAS_WHITESPACE_REGEX.test(trimmed)) {
|
|
1387
|
-
return {
|
|
1388
|
-
type: "command",
|
|
1389
|
-
value: trimmed,
|
|
1390
|
-
inferredName: inferNameFromCommand(trimmed)
|
|
1391
|
-
};
|
|
1392
|
-
}
|
|
1393
|
-
if (PACKAGE_NAME_REGEX.test(trimmed)) {
|
|
1394
|
-
return {
|
|
1395
|
-
type: "package",
|
|
1396
|
-
value: trimmed,
|
|
1397
|
-
inferredName: extractPackageName(trimmed)
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
return {
|
|
1401
|
-
type: "command",
|
|
1402
|
-
value: trimmed,
|
|
1403
|
-
inferredName: inferNameFromCommand(trimmed)
|
|
1404
|
-
};
|
|
1707
|
+
return installedMap.get(agentType);
|
|
1708
|
+
});
|
|
1405
1709
|
};
|
|
1406
1710
|
|
|
1407
1711
|
// src/install-mcp-server.ts
|
|
@@ -1418,64 +1722,116 @@ var installMcpServer = (options) => {
|
|
|
1418
1722
|
});
|
|
1419
1723
|
const requestedTransport = parsed.type === "remote" ? serverConfig.type ?? "http" : "stdio";
|
|
1420
1724
|
const { allAgents, incompatible } = resolveTargetAgents({
|
|
1421
|
-
requested: options.agents,
|
|
1725
|
+
requested: options.agents,
|
|
1726
|
+
global: isGlobal,
|
|
1727
|
+
cwd,
|
|
1728
|
+
transport: requestedTransport
|
|
1729
|
+
});
|
|
1730
|
+
const results = installToCompatibleAgents(serverName, serverConfig, {
|
|
1731
|
+
allAgents,
|
|
1732
|
+
incompatible,
|
|
1733
|
+
global: isGlobal,
|
|
1734
|
+
cwd
|
|
1735
|
+
});
|
|
1736
|
+
return { serverName, config: serverConfig, results };
|
|
1737
|
+
};
|
|
1738
|
+
|
|
1739
|
+
// src/list.ts
|
|
1740
|
+
var listInstalledMcpServers = (options = {}) => {
|
|
1741
|
+
const agentTypes = options.agents ?? getMcpAgentTypes();
|
|
1742
|
+
const collected = [];
|
|
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)) {
|
|
1747
|
+
collected.push({
|
|
1748
|
+
serverName,
|
|
1749
|
+
agent: item.agent,
|
|
1750
|
+
path: item.path,
|
|
1751
|
+
config: rawConfig,
|
|
1752
|
+
serverConfig: parseServerConfig(rawConfig)
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return collected;
|
|
1757
|
+
};
|
|
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;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
if (!entry.agents.includes(item.agent)) {
|
|
1778
|
+
entry.agents.push(item.agent);
|
|
1779
|
+
}
|
|
1780
|
+
if (!entry.paths.includes(item.path)) {
|
|
1781
|
+
entry.paths.push(item.path);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
return grouped;
|
|
1785
|
+
};
|
|
1786
|
+
var queryGroupedInstalledServers = (options = {}) => {
|
|
1787
|
+
const installed = listInstalledMcpServers(options);
|
|
1788
|
+
return groupInstalledServersByName(installed);
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
// src/update-mcp-server.ts
|
|
1792
|
+
var updateMcpServer = (options) => {
|
|
1793
|
+
const isGlobal = options.global ?? false;
|
|
1794
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1795
|
+
let previousConfig = options.previousConfig;
|
|
1796
|
+
if (!previousConfig) {
|
|
1797
|
+
const existing = listInstalledMcpServers({
|
|
1798
|
+
global: isGlobal,
|
|
1799
|
+
cwd,
|
|
1800
|
+
agents: options.agents
|
|
1801
|
+
});
|
|
1802
|
+
const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
|
|
1803
|
+
if (found) {
|
|
1804
|
+
previousConfig = found.serverConfig;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
|
|
1808
|
+
let targetAgents = options.agents;
|
|
1809
|
+
if (!targetAgents || targetAgents.length === 0) {
|
|
1810
|
+
const existing = listInstalledMcpServers({ global: isGlobal, cwd });
|
|
1811
|
+
targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
|
|
1812
|
+
}
|
|
1813
|
+
const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
|
|
1814
|
+
const { allAgents, incompatible } = resolveTargetAgents({
|
|
1815
|
+
requested: targetAgents,
|
|
1422
1816
|
global: isGlobal,
|
|
1423
1817
|
cwd,
|
|
1424
1818
|
transport: requestedTransport
|
|
1425
1819
|
});
|
|
1426
|
-
const
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
agent: agentType,
|
|
1432
|
-
success: false,
|
|
1433
|
-
path: "",
|
|
1434
|
-
error: incompatibleReason
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
return installMcpServerForAgent(serverName, serverConfig, agentType, { global: isGlobal, cwd });
|
|
1820
|
+
const results = installToCompatibleAgents(options.serverName, serverConfig, {
|
|
1821
|
+
allAgents,
|
|
1822
|
+
incompatible,
|
|
1823
|
+
global: isGlobal,
|
|
1824
|
+
cwd
|
|
1438
1825
|
});
|
|
1439
|
-
return {
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
const collected = [];
|
|
1446
|
-
for (const agentType of agentTypes) {
|
|
1447
|
-
const agent = getMcpAgentConfig(agentType);
|
|
1448
|
-
const { path, exists, servers } = agentConfigStore.listServers(agent, options);
|
|
1449
|
-
if (!exists) continue;
|
|
1450
|
-
for (const [serverName, rawConfig] of Object.entries(servers)) {
|
|
1451
|
-
collected.push({
|
|
1452
|
-
serverName,
|
|
1453
|
-
agent: agentType,
|
|
1454
|
-
path,
|
|
1455
|
-
config: rawConfig,
|
|
1456
|
-
serverConfig: parseServerConfig(rawConfig)
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
}
|
|
1460
|
-
return collected;
|
|
1826
|
+
return {
|
|
1827
|
+
serverName: options.serverName,
|
|
1828
|
+
config: serverConfig,
|
|
1829
|
+
results,
|
|
1830
|
+
incompatible
|
|
1831
|
+
};
|
|
1461
1832
|
};
|
|
1462
1833
|
|
|
1463
1834
|
// src/remove.ts
|
|
1464
|
-
var removeMcpServerFromAgent = (serverName, agentType, options = {}) => {
|
|
1465
|
-
const agent = getMcpAgentConfig(agentType);
|
|
1466
|
-
const { target } = agentConfigStore.resolveTarget(agent, options);
|
|
1467
|
-
try {
|
|
1468
|
-
const { removed } = agentConfigStore.removeServer(agent, serverName, options);
|
|
1469
|
-
return { agent: agentType, path: target.configPath, removed };
|
|
1470
|
-
} catch (error) {
|
|
1471
|
-
return {
|
|
1472
|
-
agent: agentType,
|
|
1473
|
-
path: target.configPath,
|
|
1474
|
-
removed: false,
|
|
1475
|
-
error: toErrorMessage(error)
|
|
1476
|
-
};
|
|
1477
|
-
}
|
|
1478
|
-
};
|
|
1479
1835
|
var removeMcpServer = (options) => {
|
|
1480
1836
|
const { allAgents } = resolveTargetAgents({
|
|
1481
1837
|
requested: options.agents,
|
|
@@ -1483,24 +1839,35 @@ var removeMcpServer = (options) => {
|
|
|
1483
1839
|
global: options.global,
|
|
1484
1840
|
cwd: options.cwd
|
|
1485
1841
|
});
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
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;
|
|
1493
1861
|
}
|
|
1494
|
-
return
|
|
1862
|
+
return `${value.slice(0, 4)}***${value.slice(-3)}`;
|
|
1495
1863
|
};
|
|
1496
1864
|
|
|
1497
|
-
// src/interactive/
|
|
1498
|
-
var
|
|
1865
|
+
// src/interactive/wizard-add.ts
|
|
1866
|
+
var import_prompts7 = require("@inquirer/prompts");
|
|
1499
1867
|
var import_picocolors11 = __toESM(require("picocolors"), 1);
|
|
1500
1868
|
|
|
1501
|
-
// src/
|
|
1502
|
-
var
|
|
1503
|
-
var import_picocolors8 = __toESM(require("picocolors"), 1);
|
|
1869
|
+
// src/utils/co-hosted-feedback.ts
|
|
1870
|
+
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
1504
1871
|
|
|
1505
1872
|
// src/utils/logger.ts
|
|
1506
1873
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
@@ -1519,13 +1886,269 @@ var logger = {
|
|
|
1519
1886
|
}
|
|
1520
1887
|
};
|
|
1521
1888
|
|
|
1889
|
+
// src/utils/co-hosted-feedback.ts
|
|
1890
|
+
var formatCoHostedBadge = (kind, agents) => {
|
|
1891
|
+
if (!agents || agents.length === 0) return "";
|
|
1892
|
+
const label = kind === "configured" ? "co-configured" : "co-affected";
|
|
1893
|
+
return ` ${import_picocolors2.default.yellow(`(${label}: ${agents.join(", ")})`)}`;
|
|
1894
|
+
};
|
|
1895
|
+
var logCoHostedNotice = (kind, agents) => {
|
|
1896
|
+
if (!agents || agents.length === 0) return;
|
|
1897
|
+
const actionText = kind === "configured" ? "Also configured for" : "Also affects";
|
|
1898
|
+
logger.info(
|
|
1899
|
+
` ${import_picocolors2.default.dim("Note:")} ${actionText} co-hosted agent(s): ${import_picocolors2.default.yellow(agents.join(", "))}`
|
|
1900
|
+
);
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1522
1903
|
// src/interactive/prompts/agents.ts
|
|
1523
|
-
var
|
|
1904
|
+
var import_picocolors6 = __toESM(require("picocolors"), 1);
|
|
1905
|
+
|
|
1906
|
+
// src/interactive/utils/build-linked-agent-choices.ts
|
|
1524
1907
|
var import_picocolors3 = __toESM(require("picocolors"), 1);
|
|
1908
|
+
var buildLinkedAgentChoices = (options) => {
|
|
1909
|
+
const { agents, checkedAgents, detectedAgents = [], scopeOptions = {} } = options;
|
|
1910
|
+
const alignedCheckedSet = new Set(checkedAgents);
|
|
1911
|
+
for (const agent of checkedAgents) {
|
|
1912
|
+
const coHosted = agentConfigStore.getCoHostedAgents(agent, scopeOptions);
|
|
1913
|
+
for (const co of coHosted) {
|
|
1914
|
+
if (agents.includes(co)) {
|
|
1915
|
+
alignedCheckedSet.add(co);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
return agents.map((agent) => {
|
|
1920
|
+
const config = getMcpAgentConfig(agent);
|
|
1921
|
+
const displayName = config?.displayName ?? agent;
|
|
1922
|
+
const isDetected = detectedAgents.includes(agent);
|
|
1923
|
+
const coHosted = agentConfigStore.getCoHostedAgents(agent, scopeOptions).filter(
|
|
1924
|
+
(co) => agents.includes(co)
|
|
1925
|
+
);
|
|
1926
|
+
const detectedBadge = isDetected ? import_picocolors3.default.green(" [detected]") : "";
|
|
1927
|
+
const sharedBadge = coHosted.length > 0 ? import_picocolors3.default.dim(` [shared: ${coHosted.join(", ")}]`) : "";
|
|
1928
|
+
const label = `${displayName} ${import_picocolors3.default.dim(`(${agent})`)}${detectedBadge}${sharedBadge}`;
|
|
1929
|
+
return {
|
|
1930
|
+
name: label,
|
|
1931
|
+
value: agent,
|
|
1932
|
+
checked: alignedCheckedSet.has(agent),
|
|
1933
|
+
linkedValues: coHosted,
|
|
1934
|
+
description: coHosted.length > 0 ? `Linked with ${coHosted.map((a) => getMcpAgentConfig(a).displayName).join(", ")} (shared configuration)` : void 0
|
|
1935
|
+
};
|
|
1936
|
+
});
|
|
1937
|
+
};
|
|
1938
|
+
|
|
1939
|
+
// src/interactive/prompts/linked-checkbox.ts
|
|
1940
|
+
var import_core = require("@inquirer/core");
|
|
1941
|
+
var import_picocolors4 = __toESM(require("picocolors"), 1);
|
|
1942
|
+
var defaultTheme = {
|
|
1943
|
+
icon: {
|
|
1944
|
+
checked: import_picocolors4.default.green("[x]"),
|
|
1945
|
+
unchecked: import_picocolors4.default.dim("[ ]"),
|
|
1946
|
+
cursor: import_picocolors4.default.cyan(">"),
|
|
1947
|
+
disabledChecked: import_picocolors4.default.dim("[x]"),
|
|
1948
|
+
disabledUnchecked: import_picocolors4.default.dim("[-]")
|
|
1949
|
+
},
|
|
1950
|
+
style: {
|
|
1951
|
+
disabled: (text) => import_picocolors4.default.dim(text),
|
|
1952
|
+
renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
|
|
1953
|
+
description: (text) => import_picocolors4.default.cyan(text),
|
|
1954
|
+
keysHelpTip: (keys) => keys.map(([key, action]) => `${import_picocolors4.default.bold(key)} ${import_picocolors4.default.dim(action)}`).join(import_picocolors4.default.dim(" | ")),
|
|
1955
|
+
highlight: (text) => import_picocolors4.default.cyan(text)
|
|
1956
|
+
},
|
|
1957
|
+
i18n: {
|
|
1958
|
+
disabledError: "This option is disabled and cannot be toggled."
|
|
1959
|
+
}
|
|
1960
|
+
};
|
|
1961
|
+
function isSelectable(item) {
|
|
1962
|
+
return !import_core.Separator.isSeparator(item) && !item.disabled;
|
|
1963
|
+
}
|
|
1964
|
+
function isNavigable(item) {
|
|
1965
|
+
return !import_core.Separator.isSeparator(item);
|
|
1966
|
+
}
|
|
1967
|
+
function isChecked(item) {
|
|
1968
|
+
return !import_core.Separator.isSeparator(item) && item.checked;
|
|
1969
|
+
}
|
|
1970
|
+
function normalizeChoices(choices) {
|
|
1971
|
+
return choices.map((choice) => {
|
|
1972
|
+
if (import_core.Separator.isSeparator(choice)) {
|
|
1973
|
+
return choice;
|
|
1974
|
+
}
|
|
1975
|
+
if (typeof choice !== "object" || choice === null || !("value" in choice)) {
|
|
1976
|
+
const name2 = String(choice);
|
|
1977
|
+
return {
|
|
1978
|
+
value: choice,
|
|
1979
|
+
name: name2,
|
|
1980
|
+
short: name2,
|
|
1981
|
+
checkedName: name2,
|
|
1982
|
+
disabled: false,
|
|
1983
|
+
checked: false,
|
|
1984
|
+
linkedValues: []
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
const name = choice.name ?? String(choice.value);
|
|
1988
|
+
return {
|
|
1989
|
+
value: choice.value,
|
|
1990
|
+
name,
|
|
1991
|
+
short: choice.short ?? name,
|
|
1992
|
+
checkedName: choice.checkedName ?? name,
|
|
1993
|
+
description: choice.description,
|
|
1994
|
+
disabled: choice.disabled ?? false,
|
|
1995
|
+
checked: choice.checked ?? false,
|
|
1996
|
+
linkedValues: choice.linkedValues ?? []
|
|
1997
|
+
};
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
var linkedCheckbox = (0, import_core.createPrompt)(
|
|
2001
|
+
(config, done) => {
|
|
2002
|
+
const { pageSize = 10, loop = true, required, validate = () => true } = config;
|
|
2003
|
+
const theme = (0, import_core.makeTheme)(defaultTheme, config.theme);
|
|
2004
|
+
const [status, setStatus] = (0, import_core.useState)("idle");
|
|
2005
|
+
const prefix = (0, import_core.usePrefix)({ status, theme });
|
|
2006
|
+
const [items, setItems] = (0, import_core.useState)(() => normalizeChoices(config.choices));
|
|
2007
|
+
const bounds = (0, import_core.useMemo)(() => {
|
|
2008
|
+
const first = items.findIndex(isNavigable);
|
|
2009
|
+
let last = -1;
|
|
2010
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
2011
|
+
if (isNavigable(items[i])) {
|
|
2012
|
+
last = i;
|
|
2013
|
+
break;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (first === -1 || last === -1) {
|
|
2017
|
+
throw new import_core.ValidationError("[linkedCheckbox prompt] No selectable choices.");
|
|
2018
|
+
}
|
|
2019
|
+
return { first, last };
|
|
2020
|
+
}, [items]);
|
|
2021
|
+
const [active, setActive] = (0, import_core.useState)(bounds.first);
|
|
2022
|
+
const [errorMsg, setError] = (0, import_core.useState)();
|
|
2023
|
+
const toggleWithLinked = (targetIndex) => {
|
|
2024
|
+
const targetItem = items[targetIndex];
|
|
2025
|
+
if (!targetItem || import_core.Separator.isSeparator(targetItem) || targetItem.disabled) {
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
const nextChecked = !targetItem.checked;
|
|
2029
|
+
const targetValue = targetItem.value;
|
|
2030
|
+
const linked = new Set(targetItem.linkedValues);
|
|
2031
|
+
setItems(
|
|
2032
|
+
(prevItems) => prevItems.map((item) => {
|
|
2033
|
+
if (import_core.Separator.isSeparator(item) || item.disabled) {
|
|
2034
|
+
return item;
|
|
2035
|
+
}
|
|
2036
|
+
const isTargetOrLinked = item.value === targetValue || linked.has(item.value) || item.linkedValues.includes(targetValue);
|
|
2037
|
+
if (isTargetOrLinked) {
|
|
2038
|
+
return { ...item, checked: nextChecked };
|
|
2039
|
+
}
|
|
2040
|
+
return item;
|
|
2041
|
+
})
|
|
2042
|
+
);
|
|
2043
|
+
};
|
|
2044
|
+
(0, import_core.useKeypress)(async (key) => {
|
|
2045
|
+
if ((0, import_core.isEnterKey)(key)) {
|
|
2046
|
+
const selection = items.filter(isChecked);
|
|
2047
|
+
const isValid = await validate([...selection]);
|
|
2048
|
+
if (required && selection.length === 0) {
|
|
2049
|
+
setError("At least one choice must be selected");
|
|
2050
|
+
} else if (isValid === true) {
|
|
2051
|
+
setStatus("done");
|
|
2052
|
+
done(selection.map((choice) => choice.value));
|
|
2053
|
+
} else {
|
|
2054
|
+
setError(typeof isValid === "string" ? isValid : "You must select a valid value");
|
|
2055
|
+
}
|
|
2056
|
+
} else if ((0, import_core.isUpKey)(key) || (0, import_core.isDownKey)(key)) {
|
|
2057
|
+
if (errorMsg) setError(void 0);
|
|
2058
|
+
if (loop || (0, import_core.isUpKey)(key) && active !== bounds.first || (0, import_core.isDownKey)(key) && active !== bounds.last) {
|
|
2059
|
+
const offset = (0, import_core.isUpKey)(key) ? -1 : 1;
|
|
2060
|
+
let next = active;
|
|
2061
|
+
do {
|
|
2062
|
+
next = (next + offset + items.length) % items.length;
|
|
2063
|
+
} while (!isNavigable(items[next]));
|
|
2064
|
+
setActive(next);
|
|
2065
|
+
}
|
|
2066
|
+
} else if ((0, import_core.isSpaceKey)(key)) {
|
|
2067
|
+
const activeItem = items[active];
|
|
2068
|
+
if (activeItem && !import_core.Separator.isSeparator(activeItem)) {
|
|
2069
|
+
if (activeItem.disabled) {
|
|
2070
|
+
setError(theme.i18n.disabledError);
|
|
2071
|
+
} else {
|
|
2072
|
+
setError(void 0);
|
|
2073
|
+
toggleWithLinked(active);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
} else if (key.name === "a") {
|
|
2077
|
+
const hasUnchecked = items.some((choice) => isSelectable(choice) && !choice.checked);
|
|
2078
|
+
setItems(
|
|
2079
|
+
(prevItems) => prevItems.map((item) => isSelectable(item) ? { ...item, checked: hasUnchecked } : item)
|
|
2080
|
+
);
|
|
2081
|
+
} else if ((0, import_core.isNumberKey)(key)) {
|
|
2082
|
+
const selectedIndex = Number(key.name) - 1;
|
|
2083
|
+
let selectableIndex = -1;
|
|
2084
|
+
const position = items.findIndex((item) => {
|
|
2085
|
+
if (import_core.Separator.isSeparator(item)) return false;
|
|
2086
|
+
selectableIndex++;
|
|
2087
|
+
return selectableIndex === selectedIndex;
|
|
2088
|
+
});
|
|
2089
|
+
const selectedItem = items[position];
|
|
2090
|
+
if (selectedItem && isSelectable(selectedItem)) {
|
|
2091
|
+
setActive(position);
|
|
2092
|
+
setError(void 0);
|
|
2093
|
+
toggleWithLinked(position);
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
});
|
|
2097
|
+
const message = theme.style.message(config.message, status);
|
|
2098
|
+
let description;
|
|
2099
|
+
const page = (0, import_core.usePagination)({
|
|
2100
|
+
items,
|
|
2101
|
+
active,
|
|
2102
|
+
renderItem({ item, isActive }) {
|
|
2103
|
+
if (import_core.Separator.isSeparator(item)) {
|
|
2104
|
+
return ` ${item.separator}`;
|
|
2105
|
+
}
|
|
2106
|
+
const cursor = isActive ? theme.icon.cursor : " ";
|
|
2107
|
+
if (item.disabled) {
|
|
2108
|
+
const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
|
|
2109
|
+
const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
|
|
2110
|
+
return theme.style.disabled(`${cursor} ${checkbox2} ${item.name} ${disabledLabel}`);
|
|
2111
|
+
}
|
|
2112
|
+
if (isActive) {
|
|
2113
|
+
description = item.description;
|
|
2114
|
+
}
|
|
2115
|
+
const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
|
|
2116
|
+
const name = item.checked ? item.checkedName : item.name;
|
|
2117
|
+
const color = isActive ? theme.style.highlight : (x) => x;
|
|
2118
|
+
return color(`${cursor} ${checkbox} ${name}`);
|
|
2119
|
+
},
|
|
2120
|
+
pageSize,
|
|
2121
|
+
loop
|
|
2122
|
+
});
|
|
2123
|
+
if (status === "done") {
|
|
2124
|
+
const selection = items.filter(isChecked);
|
|
2125
|
+
const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
|
|
2126
|
+
return [prefix, message, answer].filter(Boolean).join(" ");
|
|
2127
|
+
}
|
|
2128
|
+
const helpLine = theme.style.keysHelpTip([
|
|
2129
|
+
["up/down", "navigate"],
|
|
2130
|
+
["space", "toggle"],
|
|
2131
|
+
["a", "all"],
|
|
2132
|
+
["enter", "submit"]
|
|
2133
|
+
]);
|
|
2134
|
+
const lines = [
|
|
2135
|
+
[prefix, message].filter(Boolean).join(" "),
|
|
2136
|
+
page,
|
|
2137
|
+
helpLine
|
|
2138
|
+
];
|
|
2139
|
+
if (description) {
|
|
2140
|
+
lines.push(theme.style.description(description));
|
|
2141
|
+
}
|
|
2142
|
+
if (errorMsg) {
|
|
2143
|
+
lines.push(theme.style.error(errorMsg));
|
|
2144
|
+
}
|
|
2145
|
+
return lines.join("\n");
|
|
2146
|
+
}
|
|
2147
|
+
);
|
|
1525
2148
|
|
|
1526
2149
|
// src/interactive/prompts/scope.ts
|
|
1527
2150
|
var import_prompts = require("@inquirer/prompts");
|
|
1528
|
-
var
|
|
2151
|
+
var import_picocolors5 = __toESM(require("picocolors"), 1);
|
|
1529
2152
|
var promptScope = async (options = {}) => {
|
|
1530
2153
|
const initialGlobal = options.defaultGlobal ?? options.global;
|
|
1531
2154
|
if (initialGlobal !== void 0) {
|
|
@@ -1536,11 +2159,11 @@ var promptScope = async (options = {}) => {
|
|
|
1536
2159
|
message: options.message ?? "Select MCP scope:",
|
|
1537
2160
|
choices: [
|
|
1538
2161
|
{
|
|
1539
|
-
name: `Current Project - ${
|
|
2162
|
+
name: `Current Project - ${import_picocolors5.default.dim(cwd)}`,
|
|
1540
2163
|
value: false
|
|
1541
2164
|
},
|
|
1542
2165
|
{
|
|
1543
|
-
name: `Global User Config - ${
|
|
2166
|
+
name: `Global User Config - ${import_picocolors5.default.dim("applies across all projects")}`,
|
|
1544
2167
|
value: true
|
|
1545
2168
|
}
|
|
1546
2169
|
]
|
|
@@ -1560,26 +2183,26 @@ var promptScopeAndAgents = async (options = {}) => {
|
|
|
1560
2183
|
cwd
|
|
1561
2184
|
});
|
|
1562
2185
|
const detected = resolution.detected;
|
|
1563
|
-
const
|
|
2186
|
+
const rawAvailable = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
2187
|
+
const availableAgentTypes = agentConfigStore.sortAgentsByClusters(rawAvailable, {
|
|
2188
|
+
global: isGlobal,
|
|
2189
|
+
cwd
|
|
2190
|
+
});
|
|
1564
2191
|
if (detected.length > 0) {
|
|
1565
2192
|
logger.info(
|
|
1566
|
-
`Detected configured agents: ${
|
|
2193
|
+
`Detected configured agents: ${import_picocolors6.default.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
|
|
1567
2194
|
);
|
|
1568
2195
|
} else {
|
|
1569
2196
|
logger.warn(`No active ${isGlobal ? "global" : "project"} agents detected`);
|
|
1570
2197
|
}
|
|
1571
2198
|
const defaultChecked = options.defaultAgents && options.defaultAgents.length > 0 ? options.defaultAgents : detected;
|
|
1572
|
-
const choices =
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
name: label,
|
|
1578
|
-
value: agentType,
|
|
1579
|
-
checked: defaultChecked.includes(agentType)
|
|
1580
|
-
};
|
|
2199
|
+
const choices = buildLinkedAgentChoices({
|
|
2200
|
+
agents: availableAgentTypes,
|
|
2201
|
+
checkedAgents: defaultChecked,
|
|
2202
|
+
detectedAgents: detected,
|
|
2203
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
1581
2204
|
});
|
|
1582
|
-
const selectedAgents = await (
|
|
2205
|
+
const selectedAgents = await linkedCheckbox({
|
|
1583
2206
|
message: "Select target agents (Space to select, Enter to confirm):",
|
|
1584
2207
|
choices,
|
|
1585
2208
|
validate: (chosen) => {
|
|
@@ -1596,7 +2219,7 @@ var promptScopeAndAgents = async (options = {}) => {
|
|
|
1596
2219
|
};
|
|
1597
2220
|
|
|
1598
2221
|
// src/interactive/prompts/args.ts
|
|
1599
|
-
var
|
|
2222
|
+
var import_prompts2 = require("@inquirer/prompts");
|
|
1600
2223
|
var parseArgsString = (rawText) => {
|
|
1601
2224
|
const matches = rawText.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g);
|
|
1602
2225
|
if (!matches) return [];
|
|
@@ -1611,14 +2234,14 @@ var promptArgsConfig = async (initialArgs = []) => {
|
|
|
1611
2234
|
if (initialArgs.length > 0) {
|
|
1612
2235
|
return initialArgs;
|
|
1613
2236
|
}
|
|
1614
|
-
const needArgs = await (0,
|
|
2237
|
+
const needArgs = await (0, import_prompts2.confirm)({
|
|
1615
2238
|
message: "Configure command arguments (e.g. file paths, connection strings)?",
|
|
1616
2239
|
default: false
|
|
1617
2240
|
});
|
|
1618
2241
|
if (!needArgs) {
|
|
1619
2242
|
return [];
|
|
1620
2243
|
}
|
|
1621
|
-
const raw = await (0,
|
|
2244
|
+
const raw = await (0, import_prompts2.input)({
|
|
1622
2245
|
message: "Enter command arguments (space-separated, wrap paths with spaces in quotes):",
|
|
1623
2246
|
validate: (val) => val.trim() ? true : "Arguments cannot be empty"
|
|
1624
2247
|
});
|
|
@@ -1629,7 +2252,7 @@ var formatArgsString = (args) => {
|
|
|
1629
2252
|
};
|
|
1630
2253
|
var promptEditArgs = async (currentArgs = []) => {
|
|
1631
2254
|
const defaultStr = formatArgsString(currentArgs);
|
|
1632
|
-
const raw = await (0,
|
|
2255
|
+
const raw = await (0, import_prompts2.input)({
|
|
1633
2256
|
message: "Edit command arguments (space-separated, wrap paths with spaces in quotes, leave empty to clear):",
|
|
1634
2257
|
default: defaultStr
|
|
1635
2258
|
});
|
|
@@ -1641,17 +2264,21 @@ var promptEditArgs = async (currentArgs = []) => {
|
|
|
1641
2264
|
};
|
|
1642
2265
|
|
|
1643
2266
|
// src/interactive/prompts/env.ts
|
|
1644
|
-
var
|
|
1645
|
-
var
|
|
2267
|
+
var import_prompts5 = require("@inquirer/prompts");
|
|
2268
|
+
var import_picocolors9 = __toESM(require("picocolors"), 1);
|
|
2269
|
+
|
|
2270
|
+
// src/interactive/prompts/kv.ts
|
|
2271
|
+
var import_prompts4 = require("@inquirer/prompts");
|
|
2272
|
+
var import_picocolors8 = __toESM(require("picocolors"), 1);
|
|
1646
2273
|
|
|
1647
2274
|
// src/interactive/prompts/multiline.ts
|
|
1648
2275
|
var import_node_readline = require("readline");
|
|
1649
|
-
var
|
|
1650
|
-
var
|
|
2276
|
+
var import_prompts3 = require("@inquirer/prompts");
|
|
2277
|
+
var import_picocolors7 = __toESM(require("picocolors"), 1);
|
|
1651
2278
|
var readMultilineTextFromTerminal = async (message, endHint = "When done pasting, enter END on a new line or press Enter twice to finish") => {
|
|
1652
|
-
console.log(
|
|
2279
|
+
console.log(import_picocolors7.default.cyan(`
|
|
1653
2280
|
${message}`));
|
|
1654
|
-
console.log(
|
|
2281
|
+
console.log(import_picocolors7.default.dim(` (Hint: ${endHint})
|
|
1655
2282
|
`));
|
|
1656
2283
|
return new Promise((resolve) => {
|
|
1657
2284
|
const rl = (0, import_node_readline.createInterface)({
|
|
@@ -1695,7 +2322,7 @@ ${message}`));
|
|
|
1695
2322
|
};
|
|
1696
2323
|
var promptEditorText = async (options) => {
|
|
1697
2324
|
try {
|
|
1698
|
-
return await (0,
|
|
2325
|
+
return await (0, import_prompts3.editor)({
|
|
1699
2326
|
message: options.message,
|
|
1700
2327
|
default: options.defaultText ?? "",
|
|
1701
2328
|
postfix: options.postfix
|
|
@@ -1706,24 +2333,22 @@ var promptEditorText = async (options) => {
|
|
|
1706
2333
|
};
|
|
1707
2334
|
|
|
1708
2335
|
// src/interactive/prompts/kv.ts
|
|
1709
|
-
var import_prompts5 = require("@inquirer/prompts");
|
|
1710
|
-
var import_picocolors5 = __toESM(require("picocolors"), 1);
|
|
1711
2336
|
var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
1712
2337
|
let items = { ...currentItems };
|
|
1713
2338
|
while (true) {
|
|
1714
2339
|
const keys = Object.keys(items);
|
|
1715
2340
|
console.log();
|
|
1716
2341
|
if (keys.length === 0) {
|
|
1717
|
-
console.log(
|
|
2342
|
+
console.log(import_picocolors8.default.dim(` No ${options.itemsNoun} configured.`));
|
|
1718
2343
|
} else {
|
|
1719
|
-
console.log(
|
|
2344
|
+
console.log(import_picocolors8.default.cyan(import_picocolors8.default.bold(` Configured ${options.title} (${keys.length}):`)));
|
|
1720
2345
|
for (const [k, v] of Object.entries(items)) {
|
|
1721
2346
|
const sep = options.separator === "=" ? "=" : ": ";
|
|
1722
|
-
console.log(` ${
|
|
2347
|
+
console.log(` ${import_picocolors8.default.bold(k)}${sep}${import_picocolors8.default.dim(options.maskValue(k, v))}`);
|
|
1723
2348
|
}
|
|
1724
2349
|
}
|
|
1725
2350
|
console.log();
|
|
1726
|
-
const choice = await (0,
|
|
2351
|
+
const choice = await (0, import_prompts4.select)({
|
|
1727
2352
|
message: `Manage ${options.itemsNoun}:`,
|
|
1728
2353
|
choices: [
|
|
1729
2354
|
{
|
|
@@ -1770,7 +2395,7 @@ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
|
1770
2395
|
items = parsed;
|
|
1771
2396
|
logger.success(`${options.title} updated (${Object.keys(items).length} total)`);
|
|
1772
2397
|
} else if (choice === "upsert") {
|
|
1773
|
-
const key = await (0,
|
|
2398
|
+
const key = await (0, import_prompts4.input)({
|
|
1774
2399
|
message: options.keyPromptMessage,
|
|
1775
2400
|
validate: (val) => {
|
|
1776
2401
|
const trimmed = val.trim();
|
|
@@ -1784,7 +2409,7 @@ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
|
1784
2409
|
const isSecret = options.isSecretKey(trimmedKey);
|
|
1785
2410
|
let newVal;
|
|
1786
2411
|
if (isSecret) {
|
|
1787
|
-
newVal = await (0,
|
|
2412
|
+
newVal = await (0, import_prompts4.password)({
|
|
1788
2413
|
message: existingVal !== void 0 ? `New value for (${trimmedKey}) [leave empty to keep current]:` : `${options.valuePromptMessage} for (${trimmedKey}) [sensitive content masked]:`,
|
|
1789
2414
|
mask: "*"
|
|
1790
2415
|
});
|
|
@@ -1792,15 +2417,15 @@ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
|
1792
2417
|
newVal = existingVal;
|
|
1793
2418
|
}
|
|
1794
2419
|
} else {
|
|
1795
|
-
newVal = await (0,
|
|
2420
|
+
newVal = await (0, import_prompts4.input)({
|
|
1796
2421
|
message: `${options.valuePromptMessage} for (${trimmedKey}):`,
|
|
1797
2422
|
default: existingVal
|
|
1798
2423
|
});
|
|
1799
2424
|
}
|
|
1800
2425
|
items[trimmedKey] = newVal;
|
|
1801
|
-
logger.success(`${existingVal !== void 0 ? "Updated" : "Added"}: ${
|
|
2426
|
+
logger.success(`${existingVal !== void 0 ? "Updated" : "Added"}: ${import_picocolors8.default.cyan(trimmedKey)}`);
|
|
1802
2427
|
} else if (choice === "delete") {
|
|
1803
|
-
const toDelete = await (0,
|
|
2428
|
+
const toDelete = await (0, import_prompts4.select)({
|
|
1804
2429
|
message: `Select ${options.itemNoun} to delete:`,
|
|
1805
2430
|
choices: [
|
|
1806
2431
|
...keys.map((k) => ({ name: k, value: k })),
|
|
@@ -1809,7 +2434,7 @@ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
|
1809
2434
|
});
|
|
1810
2435
|
if (toDelete !== "__cancel__") {
|
|
1811
2436
|
delete items[toDelete];
|
|
1812
|
-
logger.success(`Deleted: ${
|
|
2437
|
+
logger.success(`Deleted: ${import_picocolors8.default.cyan(toDelete)}`);
|
|
1813
2438
|
}
|
|
1814
2439
|
} else if (choice === "paste") {
|
|
1815
2440
|
const pasted = await readMultilineTextFromTerminal(options.pasteMessage);
|
|
@@ -1819,7 +2444,7 @@ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
|
1819
2444
|
logger.warn(`No valid ${options.itemsNoun} recognized`);
|
|
1820
2445
|
} else {
|
|
1821
2446
|
if (keys.length > 0) {
|
|
1822
|
-
const pasteMode = await (0,
|
|
2447
|
+
const pasteMode = await (0, import_prompts4.select)({
|
|
1823
2448
|
message: `How to apply pasted ${options.itemsNoun}?`,
|
|
1824
2449
|
choices: [
|
|
1825
2450
|
{ name: `Merge with existing ${options.itemsNoun}`, value: "merge" },
|
|
@@ -1834,10 +2459,10 @@ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
|
1834
2459
|
} else {
|
|
1835
2460
|
items = parsed;
|
|
1836
2461
|
}
|
|
1837
|
-
logger.success(`Successfully applied ${
|
|
2462
|
+
logger.success(`Successfully applied ${import_picocolors8.default.cyan(String(count))} ${options.itemsNoun}`);
|
|
1838
2463
|
}
|
|
1839
2464
|
} else if (choice === "clear") {
|
|
1840
|
-
const confirmClear = await (0,
|
|
2465
|
+
const confirmClear = await (0, import_prompts4.confirm)({
|
|
1841
2466
|
message: `Are you sure you want to clear all ${options.itemsNoun}?`,
|
|
1842
2467
|
default: false
|
|
1843
2468
|
});
|
|
@@ -1850,13 +2475,6 @@ var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
|
1850
2475
|
};
|
|
1851
2476
|
|
|
1852
2477
|
// src/interactive/prompts/env.ts
|
|
1853
|
-
var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
|
|
1854
|
-
var maskSecretValue = (key, value) => {
|
|
1855
|
-
if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
|
|
1856
|
-
return value;
|
|
1857
|
-
}
|
|
1858
|
-
return `${value.slice(0, 2)}***${value.slice(-2)}`;
|
|
1859
|
-
};
|
|
1860
2478
|
var formatEnvText = (env) => {
|
|
1861
2479
|
return Object.entries(env).map(([key, value]) => {
|
|
1862
2480
|
if (/[\s"']/.test(value)) {
|
|
@@ -1890,9 +2508,9 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1890
2508
|
const env = { ...initialEnv };
|
|
1891
2509
|
const initialCount = Object.keys(env).length;
|
|
1892
2510
|
if (initialCount > 0) {
|
|
1893
|
-
logger.info(`Includes ${
|
|
2511
|
+
logger.info(`Includes ${import_picocolors9.default.cyan(String(initialCount))} preset environment variables`);
|
|
1894
2512
|
}
|
|
1895
|
-
const mode = await (0,
|
|
2513
|
+
const mode = await (0, import_prompts5.select)({
|
|
1896
2514
|
message: "Configure environment variables?",
|
|
1897
2515
|
choices: [
|
|
1898
2516
|
{
|
|
@@ -1928,16 +2546,16 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1928
2546
|
logger.warn("No valid KEY=VALUE pairs recognized");
|
|
1929
2547
|
} else {
|
|
1930
2548
|
Object.assign(env, parsed);
|
|
1931
|
-
logger.success(`Successfully parsed ${
|
|
2549
|
+
logger.success(`Successfully parsed ${import_picocolors9.default.cyan(String(count))} environment variables:`);
|
|
1932
2550
|
for (const [k, v] of Object.entries(parsed)) {
|
|
1933
|
-
console.log(` ${
|
|
2551
|
+
console.log(` ${import_picocolors9.default.bold(k)}=${import_picocolors9.default.dim(maskSecretValue(k, v))}`);
|
|
1934
2552
|
}
|
|
1935
2553
|
}
|
|
1936
2554
|
return env;
|
|
1937
2555
|
}
|
|
1938
2556
|
logger.info("Entering environment variables (leave key empty and press enter to finish):");
|
|
1939
2557
|
while (true) {
|
|
1940
|
-
const key = await (0,
|
|
2558
|
+
const key = await (0, import_prompts5.input)({
|
|
1941
2559
|
message: "Variable name (Key, leave empty to finish):",
|
|
1942
2560
|
validate: (val2) => {
|
|
1943
2561
|
const trimmed = val2.trim();
|
|
@@ -1951,17 +2569,17 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1951
2569
|
const isSecret = SECRET_KEY_PATTERN.test(trimmedKey);
|
|
1952
2570
|
let val;
|
|
1953
2571
|
if (isSecret) {
|
|
1954
|
-
val = await (0,
|
|
2572
|
+
val = await (0, import_prompts5.password)({
|
|
1955
2573
|
message: `Value for (${trimmedKey}) [secret masked]:`,
|
|
1956
2574
|
mask: "*"
|
|
1957
2575
|
});
|
|
1958
2576
|
} else {
|
|
1959
|
-
val = await (0,
|
|
2577
|
+
val = await (0, import_prompts5.input)({
|
|
1960
2578
|
message: `Value for (${trimmedKey}):`
|
|
1961
2579
|
});
|
|
1962
2580
|
}
|
|
1963
2581
|
env[trimmedKey] = val;
|
|
1964
|
-
logger.success(`Added: ${
|
|
2582
|
+
logger.success(`Added: ${import_picocolors9.default.cyan(trimmedKey)}`);
|
|
1965
2583
|
}
|
|
1966
2584
|
return env;
|
|
1967
2585
|
};
|
|
@@ -1982,15 +2600,8 @@ var promptEditEnvConfig = async (currentEnv = {}) => promptEditKeyValueConfig(cu
|
|
|
1982
2600
|
});
|
|
1983
2601
|
|
|
1984
2602
|
// src/interactive/prompts/headers.ts
|
|
1985
|
-
var
|
|
1986
|
-
var
|
|
1987
|
-
var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
|
|
1988
|
-
var maskSecretHeader = (key, value) => {
|
|
1989
|
-
if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
|
|
1990
|
-
return value;
|
|
1991
|
-
}
|
|
1992
|
-
return `${value.slice(0, 4)}***${value.slice(-3)}`;
|
|
1993
|
-
};
|
|
2603
|
+
var import_prompts6 = require("@inquirer/prompts");
|
|
2604
|
+
var import_picocolors10 = __toESM(require("picocolors"), 1);
|
|
1994
2605
|
var formatHeadersText = (headers) => {
|
|
1995
2606
|
return Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
1996
2607
|
};
|
|
@@ -2023,7 +2634,7 @@ var parseHeadersText = (rawText) => {
|
|
|
2023
2634
|
};
|
|
2024
2635
|
var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
2025
2636
|
const headers = { ...initialHeaders };
|
|
2026
|
-
const mode = await (0,
|
|
2637
|
+
const mode = await (0, import_prompts6.select)({
|
|
2027
2638
|
message: "Select HTTP headers configuration method:",
|
|
2028
2639
|
choices: [
|
|
2029
2640
|
{
|
|
@@ -2060,16 +2671,16 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
|
2060
2671
|
logger.warn("No valid Key: Value pairs recognized");
|
|
2061
2672
|
} else {
|
|
2062
2673
|
Object.assign(headers, parsed);
|
|
2063
|
-
logger.success(`Successfully parsed ${
|
|
2674
|
+
logger.success(`Successfully parsed ${import_picocolors10.default.cyan(String(count))} headers:`);
|
|
2064
2675
|
for (const [k, v] of Object.entries(parsed)) {
|
|
2065
|
-
console.log(` ${
|
|
2676
|
+
console.log(` ${import_picocolors10.default.bold(k)}: ${import_picocolors10.default.dim(maskSecretHeader(k, v))}`);
|
|
2066
2677
|
}
|
|
2067
2678
|
}
|
|
2068
2679
|
return headers;
|
|
2069
2680
|
}
|
|
2070
2681
|
logger.info("Entering HTTP headers (leave header name empty and press enter to finish):");
|
|
2071
2682
|
while (true) {
|
|
2072
|
-
const name = await (0,
|
|
2683
|
+
const name = await (0, import_prompts6.input)({
|
|
2073
2684
|
message: "Header name (e.g. Authorization, leave empty to finish):",
|
|
2074
2685
|
validate: (val2) => {
|
|
2075
2686
|
const trimmed = val2.trim();
|
|
@@ -2083,17 +2694,17 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
|
2083
2694
|
const isSecret = SECRET_HEADER_PATTERN.test(trimmedName);
|
|
2084
2695
|
let val;
|
|
2085
2696
|
if (isSecret) {
|
|
2086
|
-
val = await (0,
|
|
2697
|
+
val = await (0, import_prompts6.password)({
|
|
2087
2698
|
message: `Header value for (${trimmedName}) [sensitive content masked]:`,
|
|
2088
2699
|
mask: "*"
|
|
2089
2700
|
});
|
|
2090
2701
|
} else {
|
|
2091
|
-
val = await (0,
|
|
2702
|
+
val = await (0, import_prompts6.input)({
|
|
2092
2703
|
message: `Header value for (${trimmedName}):`
|
|
2093
2704
|
});
|
|
2094
2705
|
}
|
|
2095
2706
|
headers[trimmedName] = val;
|
|
2096
|
-
logger.success(`Added: ${
|
|
2707
|
+
logger.success(`Added: ${import_picocolors10.default.cyan(trimmedName)}`);
|
|
2097
2708
|
}
|
|
2098
2709
|
return headers;
|
|
2099
2710
|
};
|
|
@@ -2115,10 +2726,10 @@ var promptEditHeadersConfig = async (currentHeaders = {}) => promptEditKeyValueC
|
|
|
2115
2726
|
// src/interactive/wizard-add.ts
|
|
2116
2727
|
var wizardAdd = async (initial = {}) => {
|
|
2117
2728
|
const cwd = initial.cwd ?? process.cwd();
|
|
2118
|
-
logger.info(
|
|
2729
|
+
logger.info(import_picocolors11.default.bold("Welcome to the MCP interactive add wizard"));
|
|
2119
2730
|
let source = initial.source;
|
|
2120
2731
|
if (!source) {
|
|
2121
|
-
const sourceType = await (0,
|
|
2732
|
+
const sourceType = await (0, import_prompts7.select)({
|
|
2122
2733
|
message: "Select MCP server type:",
|
|
2123
2734
|
choices: [
|
|
2124
2735
|
{
|
|
@@ -2136,12 +2747,12 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2136
2747
|
]
|
|
2137
2748
|
});
|
|
2138
2749
|
if (sourceType === "npm") {
|
|
2139
|
-
source = await (0,
|
|
2750
|
+
source = await (0, import_prompts7.input)({
|
|
2140
2751
|
message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres):",
|
|
2141
2752
|
validate: (val) => val.trim() ? true : "Package name cannot be empty"
|
|
2142
2753
|
});
|
|
2143
2754
|
} else if (sourceType === "remote") {
|
|
2144
|
-
source = await (0,
|
|
2755
|
+
source = await (0, import_prompts7.input)({
|
|
2145
2756
|
message: "Enter remote server URL (e.g. https://mcp.example.com/sse):",
|
|
2146
2757
|
validate: (val) => {
|
|
2147
2758
|
const trimmed = val.trim();
|
|
@@ -2151,7 +2762,7 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2151
2762
|
}
|
|
2152
2763
|
});
|
|
2153
2764
|
} else {
|
|
2154
|
-
source = await (0,
|
|
2765
|
+
source = await (0, import_prompts7.input)({
|
|
2155
2766
|
message: "Enter command and arguments (e.g. python -m my_mcp_server or docker run ...):",
|
|
2156
2767
|
validate: (val) => val.trim() ? true : "Command cannot be empty"
|
|
2157
2768
|
});
|
|
@@ -2161,7 +2772,7 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2161
2772
|
const parsed = parseMcpSource(source);
|
|
2162
2773
|
let serverName = initial.name;
|
|
2163
2774
|
if (!serverName) {
|
|
2164
|
-
serverName = await (0,
|
|
2775
|
+
serverName = await (0, import_prompts7.input)({
|
|
2165
2776
|
message: "MCP server name:",
|
|
2166
2777
|
default: parsed.inferredName,
|
|
2167
2778
|
validate: (val) => val.trim() ? true : "Server name cannot be empty"
|
|
@@ -2173,7 +2784,7 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2173
2784
|
if (parsed.type === "remote") {
|
|
2174
2785
|
if (!transport) {
|
|
2175
2786
|
const isSseUrl = /\/sse\b/i.test(parsed.value);
|
|
2176
|
-
transport = await (0,
|
|
2787
|
+
transport = await (0, import_prompts7.select)({
|
|
2177
2788
|
message: "Select remote transport protocol:",
|
|
2178
2789
|
choices: [
|
|
2179
2790
|
{ name: "HTTP", value: "http" },
|
|
@@ -2183,7 +2794,7 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2183
2794
|
});
|
|
2184
2795
|
}
|
|
2185
2796
|
if (Object.keys(headers).length === 0) {
|
|
2186
|
-
const needHeader = await (0,
|
|
2797
|
+
const needHeader = await (0, import_prompts7.confirm)({
|
|
2187
2798
|
message: "Configure HTTP headers (e.g. Authorization Bearer token)?",
|
|
2188
2799
|
default: false
|
|
2189
2800
|
});
|
|
@@ -2205,28 +2816,28 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2205
2816
|
if (parsed.type !== "remote") {
|
|
2206
2817
|
env = await promptEnvConfig(env);
|
|
2207
2818
|
}
|
|
2208
|
-
console.log("\n" +
|
|
2209
|
-
console.log(` ${
|
|
2210
|
-
console.log(` ${
|
|
2211
|
-
console.log(` ${
|
|
2212
|
-
console.log(` ${
|
|
2213
|
-
console.log(` ${
|
|
2819
|
+
console.log("\n" + import_picocolors11.default.cyan(import_picocolors11.default.bold("Configuration Preview:")));
|
|
2820
|
+
console.log(` ${import_picocolors11.default.bold("Server Name:")} ${import_picocolors11.default.green(serverName)}`);
|
|
2821
|
+
console.log(` ${import_picocolors11.default.bold("Server Type:")} ${import_picocolors11.default.magenta(parsed.type)}`);
|
|
2822
|
+
console.log(` ${import_picocolors11.default.bold("Source/Command:")} ${import_picocolors11.default.dim(source)}`);
|
|
2823
|
+
console.log(` ${import_picocolors11.default.bold("Scope:")} ${isGlobal ? import_picocolors11.default.yellow("Global") : import_picocolors11.default.blue("Project")}`);
|
|
2824
|
+
console.log(` ${import_picocolors11.default.bold("Target Agents:")} ${import_picocolors11.default.cyan(selectedAgents.join(", "))}`);
|
|
2214
2825
|
if (args.length > 0) {
|
|
2215
|
-
console.log(` ${
|
|
2826
|
+
console.log(` ${import_picocolors11.default.bold("Arguments:")} ${import_picocolors11.default.dim(args.join(" "))}`);
|
|
2216
2827
|
}
|
|
2217
2828
|
if (transport) {
|
|
2218
|
-
console.log(` ${
|
|
2829
|
+
console.log(` ${import_picocolors11.default.bold("Transport:")} ${import_picocolors11.default.magenta(transport)}`);
|
|
2219
2830
|
}
|
|
2220
2831
|
const envKeys = Object.keys(env);
|
|
2221
2832
|
if (envKeys.length > 0) {
|
|
2222
|
-
console.log(` ${
|
|
2833
|
+
console.log(` ${import_picocolors11.default.bold("Environment Variables:")} ${import_picocolors11.default.dim(envKeys.join(", "))} (${envKeys.length})`);
|
|
2223
2834
|
}
|
|
2224
2835
|
const headerKeys = Object.keys(headers);
|
|
2225
2836
|
if (headerKeys.length > 0) {
|
|
2226
|
-
console.log(` ${
|
|
2837
|
+
console.log(` ${import_picocolors11.default.bold("Headers:")} ${import_picocolors11.default.dim(headerKeys.join(", "))} (${headerKeys.length})`);
|
|
2227
2838
|
}
|
|
2228
2839
|
console.log();
|
|
2229
|
-
const proceed = await (0,
|
|
2840
|
+
const proceed = await (0, import_prompts7.confirm)({
|
|
2230
2841
|
message: "Confirm installation with this configuration?",
|
|
2231
2842
|
default: true
|
|
2232
2843
|
});
|
|
@@ -2246,103 +2857,281 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2246
2857
|
env
|
|
2247
2858
|
});
|
|
2248
2859
|
logger.info(
|
|
2249
|
-
`Writing ${
|
|
2860
|
+
`Writing ${import_picocolors11.default.bold(result.serverName)} to ${import_picocolors11.default.cyan(String(result.results.length))} agent config files...`
|
|
2250
2861
|
);
|
|
2251
2862
|
let allSuccess = true;
|
|
2252
2863
|
for (const record of result.results) {
|
|
2253
2864
|
if (record.success) {
|
|
2254
|
-
logger.success(
|
|
2865
|
+
logger.success(
|
|
2866
|
+
`${import_picocolors11.default.cyan(record.agent)}: Successfully written to ${import_picocolors11.default.dim(record.path)}${formatCoHostedBadge("configured", record.coConfiguredAgents)}`
|
|
2867
|
+
);
|
|
2255
2868
|
} else {
|
|
2256
2869
|
allSuccess = false;
|
|
2257
|
-
logger.error(`${
|
|
2870
|
+
logger.error(`${import_picocolors11.default.cyan(record.agent)}: Failed to write - ${record.error}`);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
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
|
+
}
|
|
2258
2979
|
}
|
|
2980
|
+
if (result.results.some((record) => !record.success)) process.exitCode = 1;
|
|
2981
|
+
} catch (error) {
|
|
2982
|
+
logger.error(toErrorMessage(error));
|
|
2983
|
+
process.exitCode = 1;
|
|
2259
2984
|
}
|
|
2260
|
-
|
|
2261
|
-
logger.success(import_picocolors8.default.bold(`MCP server "${serverName}" configured successfully!`));
|
|
2262
|
-
}
|
|
2263
|
-
return allSuccess;
|
|
2264
|
-
};
|
|
2265
|
-
|
|
2266
|
-
// src/interactive/wizard-manage.ts
|
|
2267
|
-
var import_prompts9 = require("@inquirer/prompts");
|
|
2268
|
-
var import_picocolors9 = __toESM(require("picocolors"), 1);
|
|
2985
|
+
});
|
|
2269
2986
|
|
|
2270
|
-
// src/
|
|
2271
|
-
var
|
|
2272
|
-
var
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
grouped.set(item.serverName, entry);
|
|
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;
|
|
2284
3000
|
}
|
|
2285
|
-
if (
|
|
2286
|
-
|
|
3001
|
+
if (entries.length === 0) {
|
|
3002
|
+
logger.warn("No MCP servers installed");
|
|
3003
|
+
return;
|
|
2287
3004
|
}
|
|
2288
|
-
|
|
2289
|
-
|
|
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)}`);
|
|
2290
3016
|
}
|
|
3017
|
+
} catch (error) {
|
|
3018
|
+
logger.error(toErrorMessage(error));
|
|
3019
|
+
process.exitCode = 1;
|
|
2291
3020
|
}
|
|
2292
|
-
|
|
2293
|
-
|
|
3021
|
+
});
|
|
3022
|
+
|
|
3023
|
+
// src/cli/manage.ts
|
|
3024
|
+
var import_commander3 = require("commander");
|
|
3025
|
+
var import_picocolors16 = __toESM(require("picocolors"), 1);
|
|
2294
3026
|
|
|
2295
3027
|
// src/interactive/wizard-manage.ts
|
|
3028
|
+
var import_prompts8 = require("@inquirer/prompts");
|
|
3029
|
+
var import_picocolors15 = __toESM(require("picocolors"), 1);
|
|
3030
|
+
|
|
3031
|
+
// src/utils/display-server-details.ts
|
|
3032
|
+
var import_picocolors14 = __toESM(require("picocolors"), 1);
|
|
2296
3033
|
var displayServerDetails = ({
|
|
2297
3034
|
serverName,
|
|
2298
3035
|
config,
|
|
2299
3036
|
agents,
|
|
2300
|
-
|
|
3037
|
+
hasDivergence,
|
|
3038
|
+
global: isGlobal,
|
|
2301
3039
|
titlePrefix = "MCP Server Details"
|
|
2302
3040
|
}) => {
|
|
2303
|
-
console.log("\n" +
|
|
3041
|
+
console.log("\n" + import_picocolors14.default.cyan(import_picocolors14.default.bold(`${titlePrefix}: [${serverName}]`)));
|
|
2304
3042
|
if (isGlobal !== void 0) {
|
|
2305
|
-
console.log(` ${
|
|
3043
|
+
console.log(` ${import_picocolors14.default.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
|
|
2306
3044
|
}
|
|
2307
3045
|
if (agents && agents.length > 0) {
|
|
2308
3046
|
console.log(
|
|
2309
|
-
` ${
|
|
3047
|
+
` ${import_picocolors14.default.bold("Configured Agents:")} ${import_picocolors14.default.green(agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
|
|
3048
|
+
);
|
|
3049
|
+
}
|
|
3050
|
+
if (hasDivergence) {
|
|
3051
|
+
console.log(
|
|
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.")}`
|
|
2310
3053
|
);
|
|
2311
3054
|
}
|
|
2312
3055
|
const isRemote = Boolean(config.url && config.url.length > 0);
|
|
2313
3056
|
if (isRemote) {
|
|
2314
|
-
console.log(` ${
|
|
2315
|
-
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 ?? "")}`);
|
|
2316
3059
|
const headerKeys = Object.keys(config.headers ?? {});
|
|
2317
3060
|
if (headerKeys.length > 0) {
|
|
2318
|
-
console.log(` ${
|
|
3061
|
+
console.log(` ${import_picocolors14.default.bold("Headers:")} ${import_picocolors14.default.cyan(String(headerKeys.length))}`);
|
|
2319
3062
|
for (const [k, v] of Object.entries(config.headers ?? {})) {
|
|
2320
|
-
console.log(` ${
|
|
3063
|
+
console.log(` ${import_picocolors14.default.bold(k)}: ${import_picocolors14.default.dim(maskSecretHeader(k, v))}`);
|
|
2321
3064
|
}
|
|
2322
3065
|
} else {
|
|
2323
|
-
console.log(` ${
|
|
3066
|
+
console.log(` ${import_picocolors14.default.bold("Headers:")} ${import_picocolors14.default.dim("(none)")}`);
|
|
2324
3067
|
}
|
|
2325
3068
|
} else {
|
|
2326
|
-
console.log(` ${
|
|
3069
|
+
console.log(` ${import_picocolors14.default.bold("Command:")} ${import_picocolors14.default.magenta(config.command ?? "")}`);
|
|
2327
3070
|
const argsStr = config.args && config.args.length > 0 ? config.args.join(" ") : "(none)";
|
|
2328
|
-
console.log(` ${
|
|
3071
|
+
console.log(` ${import_picocolors14.default.bold("Arguments:")} ${import_picocolors14.default.dim(argsStr)}`);
|
|
2329
3072
|
const envKeys = Object.keys(config.env ?? {});
|
|
2330
3073
|
if (envKeys.length > 0) {
|
|
2331
|
-
console.log(` ${
|
|
3074
|
+
console.log(` ${import_picocolors14.default.bold("Environment Variables:")} ${import_picocolors14.default.cyan(String(envKeys.length))}`);
|
|
2332
3075
|
for (const [k, v] of Object.entries(config.env ?? {})) {
|
|
2333
|
-
console.log(` ${
|
|
3076
|
+
console.log(` ${import_picocolors14.default.bold(k)}=${import_picocolors14.default.dim(maskSecretValue(k, v))}`);
|
|
2334
3077
|
}
|
|
2335
3078
|
} else {
|
|
2336
|
-
console.log(` ${
|
|
3079
|
+
console.log(` ${import_picocolors14.default.bold("Environment Variables:")} ${import_picocolors14.default.dim("(none)")}`);
|
|
2337
3080
|
}
|
|
2338
3081
|
}
|
|
2339
3082
|
console.log();
|
|
2340
3083
|
};
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
3084
|
+
|
|
3085
|
+
// src/interactive/wizard-manage.ts
|
|
3086
|
+
var promptSwitchServerType = async (currentConfig, serverName) => {
|
|
3087
|
+
const isRemote = Boolean(currentConfig.url && currentConfig.url.length > 0);
|
|
3088
|
+
if (isRemote) {
|
|
3089
|
+
const newCmd = await (0, import_prompts8.input)({
|
|
3090
|
+
message: "Executable command (e.g. node, npx):",
|
|
3091
|
+
validate: (val) => val.trim() ? true : "Command cannot be empty"
|
|
3092
|
+
});
|
|
3093
|
+
const newArgs = await promptEditArgs([]);
|
|
3094
|
+
const newEnv = await promptEditEnvConfig({});
|
|
3095
|
+
logger.success(`Switched [${serverName}] configuration to stdio mode`);
|
|
3096
|
+
return toStdioServerConfig({
|
|
3097
|
+
command: newCmd.trim(),
|
|
3098
|
+
args: newArgs.length > 0 ? newArgs : void 0,
|
|
3099
|
+
env: Object.keys(newEnv).length > 0 ? newEnv : void 0
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
const newUrl = await (0, import_prompts8.input)({
|
|
3103
|
+
message: "Remote server URL:",
|
|
3104
|
+
validate: (val) => {
|
|
3105
|
+
const trimmed = val.trim();
|
|
3106
|
+
if (!trimmed) return "URL cannot be empty";
|
|
3107
|
+
if (!/^https?:\/\//i.test(trimmed)) {
|
|
3108
|
+
return "Please enter a valid URL starting with http:// or https://";
|
|
3109
|
+
}
|
|
3110
|
+
return true;
|
|
3111
|
+
}
|
|
3112
|
+
});
|
|
3113
|
+
const transport = await (0, import_prompts8.select)({
|
|
3114
|
+
message: "Select remote transport protocol:",
|
|
3115
|
+
choices: [
|
|
3116
|
+
{ name: "HTTP", value: "http" },
|
|
3117
|
+
{ name: "SSE (Server-Sent Events)", value: "sse" }
|
|
3118
|
+
],
|
|
3119
|
+
default: "http"
|
|
3120
|
+
});
|
|
3121
|
+
const newHeaders = await promptEditHeadersConfig({});
|
|
3122
|
+
logger.success(`Switched [${serverName}] configuration to remote mode`);
|
|
3123
|
+
return toRemoteServerConfig(
|
|
3124
|
+
{
|
|
3125
|
+
url: newUrl.trim(),
|
|
3126
|
+
headers: Object.keys(newHeaders).length > 0 ? newHeaders : void 0
|
|
3127
|
+
},
|
|
3128
|
+
transport
|
|
3129
|
+
);
|
|
3130
|
+
};
|
|
3131
|
+
var handleEditServerConfig = async (options) => {
|
|
3132
|
+
const { targetGroup } = options;
|
|
3133
|
+
const isGlobal = options.global ?? false;
|
|
3134
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2346
3135
|
const serverName = targetGroup.serverName;
|
|
2347
3136
|
let workingConfig = {
|
|
2348
3137
|
...targetGroup.config,
|
|
@@ -2361,6 +3150,7 @@ var handleEditServerConfig = async ({
|
|
|
2361
3150
|
{ name: "Edit HTTP Headers (headers)", value: "headers" },
|
|
2362
3151
|
{ name: "Edit Remote URL (url)", value: "url" },
|
|
2363
3152
|
{ name: "Edit Transport Protocol (type)", value: "transport" },
|
|
3153
|
+
{ name: "Switch to local command (stdio)", value: "switch_type" },
|
|
2364
3154
|
{ name: "Reset changes to original", value: "reset" },
|
|
2365
3155
|
{ name: "Save and apply changes", value: "save" },
|
|
2366
3156
|
{ name: "Cancel (discard changes)", value: "cancel" }
|
|
@@ -2368,11 +3158,12 @@ var handleEditServerConfig = async ({
|
|
|
2368
3158
|
{ name: "Edit Environment Variables (env)", value: "env" },
|
|
2369
3159
|
{ name: "Edit Command Arguments (args)", value: "args" },
|
|
2370
3160
|
{ name: "Edit Executable Command (command)", value: "command" },
|
|
3161
|
+
{ name: "Switch to remote server (HTTP/SSE)", value: "switch_type" },
|
|
2371
3162
|
{ name: "Reset changes to original", value: "reset" },
|
|
2372
3163
|
{ name: "Save and apply changes", value: "save" },
|
|
2373
3164
|
{ name: "Cancel (discard changes)", value: "cancel" }
|
|
2374
3165
|
];
|
|
2375
|
-
const editAction = await (0,
|
|
3166
|
+
const editAction = await (0, import_prompts8.select)({
|
|
2376
3167
|
message: `What would you like to modify in [${serverName}]?`,
|
|
2377
3168
|
choices: editChoices
|
|
2378
3169
|
});
|
|
@@ -2390,12 +3181,16 @@ var handleEditServerConfig = async ({
|
|
|
2390
3181
|
logger.info("Configuration reset to original");
|
|
2391
3182
|
continue;
|
|
2392
3183
|
}
|
|
3184
|
+
if (editAction === "switch_type") {
|
|
3185
|
+
workingConfig = await promptSwitchServerType(workingConfig, serverName);
|
|
3186
|
+
continue;
|
|
3187
|
+
}
|
|
2393
3188
|
if (editAction === "env") {
|
|
2394
3189
|
workingConfig.env = await promptEditEnvConfig(workingConfig.env ?? {});
|
|
2395
3190
|
} else if (editAction === "args") {
|
|
2396
3191
|
workingConfig.args = await promptEditArgs(workingConfig.args ?? []);
|
|
2397
3192
|
} else if (editAction === "command") {
|
|
2398
|
-
const newCmd = await (0,
|
|
3193
|
+
const newCmd = await (0, import_prompts8.input)({
|
|
2399
3194
|
message: "Executable command:",
|
|
2400
3195
|
default: workingConfig.command,
|
|
2401
3196
|
validate: (val) => val.trim() ? true : "Command cannot be empty"
|
|
@@ -2404,7 +3199,7 @@ var handleEditServerConfig = async ({
|
|
|
2404
3199
|
} else if (editAction === "headers") {
|
|
2405
3200
|
workingConfig.headers = await promptEditHeadersConfig(workingConfig.headers ?? {});
|
|
2406
3201
|
} else if (editAction === "url") {
|
|
2407
|
-
const newUrl = await (0,
|
|
3202
|
+
const newUrl = await (0, import_prompts8.input)({
|
|
2408
3203
|
message: "Remote server URL:",
|
|
2409
3204
|
default: workingConfig.url,
|
|
2410
3205
|
validate: (val) => {
|
|
@@ -2418,7 +3213,7 @@ var handleEditServerConfig = async ({
|
|
|
2418
3213
|
});
|
|
2419
3214
|
workingConfig.url = newUrl.trim();
|
|
2420
3215
|
} else if (editAction === "transport") {
|
|
2421
|
-
workingConfig.type = await (0,
|
|
3216
|
+
workingConfig.type = await (0, import_prompts8.select)({
|
|
2422
3217
|
message: "Select remote transport protocol:",
|
|
2423
3218
|
choices: [
|
|
2424
3219
|
{ name: "HTTP", value: "http" },
|
|
@@ -2429,42 +3224,46 @@ var handleEditServerConfig = async ({
|
|
|
2429
3224
|
} else if (editAction === "save") {
|
|
2430
3225
|
let targetAgents = targetGroup.agents;
|
|
2431
3226
|
if (targetGroup.agents.length > 1) {
|
|
2432
|
-
|
|
3227
|
+
const sortedAgents = agentConfigStore.sortAgentsByClusters(targetGroup.agents, { global: isGlobal, cwd });
|
|
3228
|
+
const choices = buildLinkedAgentChoices({
|
|
3229
|
+
agents: sortedAgents,
|
|
3230
|
+
checkedAgents: sortedAgents,
|
|
3231
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
3232
|
+
});
|
|
3233
|
+
targetAgents = await linkedCheckbox({
|
|
2433
3234
|
message: "Select agents to update configuration (Space to toggle):",
|
|
2434
|
-
choices
|
|
2435
|
-
name: `${getMcpAgentConfig(a).displayName} (${a})`,
|
|
2436
|
-
value: a,
|
|
2437
|
-
checked: true
|
|
2438
|
-
})),
|
|
3235
|
+
choices,
|
|
2439
3236
|
loop: false,
|
|
2440
3237
|
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
2441
3238
|
});
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
if (isMcpTransportSupported(agentConfig, requestedTransport)) {
|
|
2449
|
-
compatibleAgents.push(agent);
|
|
2450
|
-
} else {
|
|
2451
|
-
const reason = agentConfig.unsupportedTransportMessage ?? `Agent does not support ${requestedTransport} transport`;
|
|
2452
|
-
incompatibleAgents.push({ agent, reason });
|
|
3239
|
+
if (targetAgents.length < targetGroup.agents.length) {
|
|
3240
|
+
const unselected = targetGroup.agents.filter((a) => !targetAgents.includes(a));
|
|
3241
|
+
const unselectedNames = unselected.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
3242
|
+
logger.info(
|
|
3243
|
+
`Note: Updating only a subset of agents. Server configurations will diverge from: ${unselectedNames}.`
|
|
3244
|
+
);
|
|
2453
3245
|
}
|
|
2454
3246
|
}
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
3247
|
+
const requestedTransport = workingConfig.url ? workingConfig.type ?? "http" : "stdio";
|
|
3248
|
+
const resolution = resolveTargetAgents({
|
|
3249
|
+
requested: targetAgents,
|
|
3250
|
+
global: isGlobal,
|
|
3251
|
+
cwd,
|
|
3252
|
+
transport: requestedTransport
|
|
3253
|
+
});
|
|
3254
|
+
if (resolution.incompatible.length > 0) {
|
|
3255
|
+
for (const item of resolution.incompatible) {
|
|
3256
|
+
logger.warn(`Skipping ${import_picocolors15.default.cyan(item.agent)}: ${item.reason}`);
|
|
2458
3257
|
}
|
|
2459
3258
|
}
|
|
2460
|
-
if (compatibleAgents.length === 0) {
|
|
3259
|
+
if (resolution.compatibleAgents.length === 0) {
|
|
2461
3260
|
logger.error(
|
|
2462
3261
|
`None of the selected agents support ${requestedTransport} transport. Cannot update.`
|
|
2463
3262
|
);
|
|
2464
3263
|
continue;
|
|
2465
3264
|
}
|
|
2466
|
-
const agentNames = compatibleAgents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
2467
|
-
const confirmed = await (0,
|
|
3265
|
+
const agentNames = resolution.compatibleAgents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
3266
|
+
const confirmed = await (0, import_prompts8.confirm)({
|
|
2468
3267
|
message: `Confirm updating configuration for [${serverName}] across: ${agentNames}?`,
|
|
2469
3268
|
default: true
|
|
2470
3269
|
});
|
|
@@ -2472,22 +3271,32 @@ var handleEditServerConfig = async ({
|
|
|
2472
3271
|
logger.warn("Update cancelled");
|
|
2473
3272
|
continue;
|
|
2474
3273
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
3274
|
+
const updateResult = updateMcpServer({
|
|
3275
|
+
serverName,
|
|
3276
|
+
config: workingConfig,
|
|
3277
|
+
previousConfig: targetGroup.config,
|
|
3278
|
+
agents: resolution.compatibleAgents,
|
|
3279
|
+
global: isGlobal,
|
|
3280
|
+
cwd
|
|
3281
|
+
});
|
|
3282
|
+
let updatedAny = false;
|
|
3283
|
+
const succeededAgents = [];
|
|
3284
|
+
for (const res of updateResult.results) {
|
|
2480
3285
|
if (res.success) {
|
|
3286
|
+
updatedAny = true;
|
|
3287
|
+
succeededAgents.push(res.agent);
|
|
2481
3288
|
logger.success(
|
|
2482
|
-
`${
|
|
3289
|
+
`${import_picocolors15.default.cyan(res.agent)}: Successfully updated configuration in ${import_picocolors15.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
|
|
2483
3290
|
);
|
|
2484
3291
|
} else {
|
|
2485
|
-
logger.error(`${
|
|
3292
|
+
logger.error(`${import_picocolors15.default.cyan(res.agent)}: Update failed - ${res.error}`);
|
|
2486
3293
|
}
|
|
2487
3294
|
}
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
3295
|
+
if (updatedAny) {
|
|
3296
|
+
targetGroup.config = updateResult.config;
|
|
3297
|
+
logger.success(`Configuration for [${serverName}] updated successfully!`);
|
|
3298
|
+
return;
|
|
3299
|
+
}
|
|
2491
3300
|
}
|
|
2492
3301
|
}
|
|
2493
3302
|
};
|
|
@@ -2498,13 +3307,19 @@ var wizardManage = async (options = {}) => {
|
|
|
2498
3307
|
defaultGlobal: options.global,
|
|
2499
3308
|
message: "Select MCP scope to inspect and manage:"
|
|
2500
3309
|
});
|
|
2501
|
-
const
|
|
2502
|
-
if (
|
|
3310
|
+
const grouped = queryGroupedInstalledServers({ global: isGlobal, cwd });
|
|
3311
|
+
if (grouped.size === 0) {
|
|
2503
3312
|
logger.warn(`No configured MCP servers found in ${isGlobal ? "global" : "project"} scope`);
|
|
2504
3313
|
return;
|
|
2505
3314
|
}
|
|
2506
|
-
const grouped = groupInstalledServersByName(installed);
|
|
2507
3315
|
let pendingServerName = options.serverName;
|
|
3316
|
+
const refreshGroupedServers = () => {
|
|
3317
|
+
const freshGrouped = queryGroupedInstalledServers({ global: isGlobal, cwd });
|
|
3318
|
+
grouped.clear();
|
|
3319
|
+
for (const [name, grp] of freshGrouped) {
|
|
3320
|
+
grouped.set(name, grp);
|
|
3321
|
+
}
|
|
3322
|
+
};
|
|
2508
3323
|
while (true) {
|
|
2509
3324
|
let chosenServerName;
|
|
2510
3325
|
if (pendingServerName && grouped.has(pendingServerName)) {
|
|
@@ -2515,7 +3330,7 @@ var wizardManage = async (options = {}) => {
|
|
|
2515
3330
|
const choices = Array.from(grouped.values()).map((g) => {
|
|
2516
3331
|
const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
2517
3332
|
return {
|
|
2518
|
-
name: `${
|
|
3333
|
+
name: `${import_picocolors15.default.bold(g.serverName)} ${import_picocolors15.default.dim(`(configured in: ${agentNames})`)}`,
|
|
2519
3334
|
value: g.serverName
|
|
2520
3335
|
};
|
|
2521
3336
|
});
|
|
@@ -2523,7 +3338,7 @@ var wizardManage = async (options = {}) => {
|
|
|
2523
3338
|
name: `Back`,
|
|
2524
3339
|
value: "__back__"
|
|
2525
3340
|
});
|
|
2526
|
-
chosenServerName = await (0,
|
|
3341
|
+
chosenServerName = await (0, import_prompts8.select)({
|
|
2527
3342
|
message: "Select MCP server to manage or sync:",
|
|
2528
3343
|
choices
|
|
2529
3344
|
});
|
|
@@ -2537,9 +3352,10 @@ var wizardManage = async (options = {}) => {
|
|
|
2537
3352
|
serverName: chosenServerName,
|
|
2538
3353
|
config: targetGroup.config,
|
|
2539
3354
|
agents: targetGroup.agents,
|
|
2540
|
-
isGlobal
|
|
3355
|
+
global: isGlobal,
|
|
3356
|
+
hasDivergence: targetGroup.hasDivergence
|
|
2541
3357
|
});
|
|
2542
|
-
const action = await (0,
|
|
3358
|
+
const action = await (0, import_prompts8.select)({
|
|
2543
3359
|
message: `What would you like to do with [${chosenServerName}]?`,
|
|
2544
3360
|
choices: [
|
|
2545
3361
|
{
|
|
@@ -2560,31 +3376,34 @@ var wizardManage = async (options = {}) => {
|
|
|
2560
3376
|
if (action === "edit") {
|
|
2561
3377
|
await handleEditServerConfig({
|
|
2562
3378
|
targetGroup,
|
|
2563
|
-
isGlobal,
|
|
3379
|
+
global: isGlobal,
|
|
2564
3380
|
cwd
|
|
2565
3381
|
});
|
|
3382
|
+
refreshGroupedServers();
|
|
2566
3383
|
continue;
|
|
2567
3384
|
}
|
|
2568
3385
|
if (action === "sync") {
|
|
2569
3386
|
const allAllowedAgents = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
2570
|
-
const
|
|
2571
|
-
if (
|
|
3387
|
+
const rawCandidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
|
|
3388
|
+
if (rawCandidateAgents.length === 0) {
|
|
2572
3389
|
logger.info(
|
|
2573
3390
|
"All supported agents in this scope already have this MCP server configured; no sync needed"
|
|
2574
3391
|
);
|
|
2575
3392
|
continue;
|
|
2576
3393
|
}
|
|
2577
|
-
const
|
|
3394
|
+
const candidateAgents = agentConfigStore.sortAgentsByClusters(rawCandidateAgents, { global: isGlobal, cwd });
|
|
3395
|
+
const choices = buildLinkedAgentChoices({
|
|
3396
|
+
agents: candidateAgents,
|
|
3397
|
+
checkedAgents: [],
|
|
3398
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
3399
|
+
});
|
|
3400
|
+
const selectedToSync = await linkedCheckbox({
|
|
2578
3401
|
message: "Select target agents to sync to (Space to select):",
|
|
2579
|
-
choices
|
|
2580
|
-
name: `${getMcpAgentConfig(a).displayName} (${a})`,
|
|
2581
|
-
value: a,
|
|
2582
|
-
checked: false
|
|
2583
|
-
})),
|
|
3402
|
+
choices,
|
|
2584
3403
|
loop: false,
|
|
2585
3404
|
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
2586
3405
|
});
|
|
2587
|
-
const confirmed = await (0,
|
|
3406
|
+
const confirmed = await (0, import_prompts8.confirm)({
|
|
2588
3407
|
message: `Confirm syncing configuration of [${chosenServerName}] to: ${selectedToSync.join(", ")}?`,
|
|
2589
3408
|
default: true
|
|
2590
3409
|
});
|
|
@@ -2592,188 +3411,52 @@ var wizardManage = async (options = {}) => {
|
|
|
2592
3411
|
logger.warn("Sync cancelled");
|
|
2593
3412
|
continue;
|
|
2594
3413
|
}
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
3414
|
+
const syncResult = updateMcpServer({
|
|
3415
|
+
serverName: chosenServerName,
|
|
3416
|
+
config: targetGroup.config,
|
|
3417
|
+
agents: selectedToSync,
|
|
3418
|
+
global: isGlobal,
|
|
3419
|
+
cwd
|
|
3420
|
+
});
|
|
3421
|
+
for (const item of syncResult.incompatible) {
|
|
3422
|
+
logger.warn(`Skipping ${import_picocolors15.default.cyan(item.agent)}: ${item.reason}`);
|
|
3423
|
+
}
|
|
3424
|
+
for (const res of syncResult.results) {
|
|
3425
|
+
if (syncResult.incompatible.some((i) => i.agent === res.agent)) {
|
|
3426
|
+
continue;
|
|
3427
|
+
}
|
|
2600
3428
|
if (res.success) {
|
|
2601
|
-
logger.success(
|
|
2602
|
-
|
|
3429
|
+
logger.success(
|
|
3430
|
+
`${import_picocolors15.default.cyan(res.agent)}: Successfully synced to ${import_picocolors15.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
|
|
3431
|
+
);
|
|
3432
|
+
targetGroup.agents.push(res.agent);
|
|
2603
3433
|
} else {
|
|
2604
|
-
logger.error(`${
|
|
3434
|
+
logger.error(`${import_picocolors15.default.cyan(res.agent)}: Sync failed - ${res.error}`);
|
|
2605
3435
|
}
|
|
2606
|
-
}
|
|
2607
|
-
|
|
2608
|
-
}
|
|
2609
|
-
};
|
|
2610
|
-
|
|
2611
|
-
// src/interactive/wizard-remove.ts
|
|
2612
|
-
var import_prompts10 = require("@inquirer/prompts");
|
|
2613
|
-
var import_picocolors10 = __toESM(require("picocolors"), 1);
|
|
2614
|
-
var wizardRemove = async (options = {}) => {
|
|
2615
|
-
const cwd = options.cwd ?? process.cwd();
|
|
2616
|
-
const isGlobal = await promptScope({
|
|
2617
|
-
cwd,
|
|
2618
|
-
defaultGlobal: options.global,
|
|
2619
|
-
message: "Select scope to remove MCP server from:"
|
|
2620
|
-
});
|
|
2621
|
-
const installed = listInstalledMcpServers({ global: isGlobal, cwd });
|
|
2622
|
-
if (installed.length === 0) {
|
|
2623
|
-
logger.warn(`No installed MCP servers found in ${isGlobal ? "global" : "project"} scope`);
|
|
2624
|
-
return false;
|
|
2625
|
-
}
|
|
2626
|
-
const serverMap = groupInstalledServersByName(installed);
|
|
2627
|
-
let serverName = options.name;
|
|
2628
|
-
if (!serverName) {
|
|
2629
|
-
const choices = Array.from(serverMap.values()).map((g) => ({
|
|
2630
|
-
name: `${import_picocolors10.default.bold(g.serverName)} ${import_picocolors10.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
|
|
2631
|
-
value: g.serverName
|
|
2632
|
-
}));
|
|
2633
|
-
serverName = await (0, import_prompts10.select)({
|
|
2634
|
-
message: "Select MCP server to remove:",
|
|
2635
|
-
choices
|
|
2636
|
-
});
|
|
2637
|
-
}
|
|
2638
|
-
const installedAgents = serverMap.get(serverName)?.agents || [];
|
|
2639
|
-
if (installedAgents.length === 0) {
|
|
2640
|
-
logger.warn(`No agents found with [${serverName}] installed`);
|
|
2641
|
-
return false;
|
|
2642
|
-
}
|
|
2643
|
-
let targetAgents = options.agents;
|
|
2644
|
-
if (!targetAgents || targetAgents.length === 0) {
|
|
2645
|
-
targetAgents = await (0, import_prompts10.checkbox)({
|
|
2646
|
-
message: `Select agents to remove [${serverName}] from:`,
|
|
2647
|
-
choices: installedAgents.map((agent) => ({
|
|
2648
|
-
name: `${getMcpAgentConfig(agent)?.displayName ?? agent} (${agent})`,
|
|
2649
|
-
value: agent,
|
|
2650
|
-
checked: true
|
|
2651
|
-
})),
|
|
2652
|
-
loop: false,
|
|
2653
|
-
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
2654
|
-
});
|
|
2655
|
-
} else {
|
|
2656
|
-
const validAgents = targetAgents.filter((agent) => installedAgents.includes(agent));
|
|
2657
|
-
if (validAgents.length === 0) {
|
|
2658
|
-
logger.warn(`None of the specified agents (${targetAgents.join(", ")}) have [${serverName}] installed`);
|
|
2659
|
-
return false;
|
|
2660
|
-
}
|
|
2661
|
-
targetAgents = validAgents;
|
|
2662
|
-
}
|
|
2663
|
-
const confirmed = await (0, import_prompts10.confirm)({
|
|
2664
|
-
message: `Confirm removing MCP server [${serverName}] from ${targetAgents.join(", ")}?`,
|
|
2665
|
-
default: true
|
|
2666
|
-
});
|
|
2667
|
-
if (!confirmed) {
|
|
2668
|
-
logger.warn("Operation cancelled");
|
|
2669
|
-
return false;
|
|
2670
|
-
}
|
|
2671
|
-
const results = removeMcpServer({
|
|
2672
|
-
name: serverName,
|
|
2673
|
-
agents: targetAgents,
|
|
2674
|
-
global: isGlobal,
|
|
2675
|
-
cwd
|
|
2676
|
-
});
|
|
2677
|
-
let removedCount = 0;
|
|
2678
|
-
for (const res of results) {
|
|
2679
|
-
if (res.removed) {
|
|
2680
|
-
logger.success(`${import_picocolors10.default.cyan(res.agent)}: Successfully removed from ${import_picocolors10.default.dim(res.path)}`);
|
|
2681
|
-
removedCount++;
|
|
2682
|
-
} else if (res.error) {
|
|
2683
|
-
logger.error(`${import_picocolors10.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
if (removedCount > 0) {
|
|
2687
|
-
logger.success(`Successfully removed [${serverName}] from ${removedCount} agent(s)`);
|
|
2688
|
-
return true;
|
|
2689
|
-
}
|
|
2690
|
-
logger.warn(`Failed to remove [${serverName}] from specified agents`);
|
|
2691
|
-
return false;
|
|
2692
|
-
};
|
|
2693
|
-
|
|
2694
|
-
// src/interactive/main-menu.ts
|
|
2695
|
-
var mainMenu = async () => {
|
|
2696
|
-
console.log();
|
|
2697
|
-
console.log(import_picocolors11.default.bold(import_picocolors11.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
|
|
2698
|
-
console.log(import_picocolors11.default.dim("Cross-platform MCP server configuration & synchronization tool"));
|
|
2699
|
-
console.log();
|
|
2700
|
-
while (true) {
|
|
2701
|
-
try {
|
|
2702
|
-
const action = await (0, import_prompts11.select)({
|
|
2703
|
-
message: "Select an action:",
|
|
2704
|
-
choices: [
|
|
2705
|
-
{
|
|
2706
|
-
name: "Add MCP Server",
|
|
2707
|
-
value: "add"
|
|
2708
|
-
},
|
|
2709
|
-
{
|
|
2710
|
-
name: "Manage & Sync Installed MCP Servers",
|
|
2711
|
-
value: "manage"
|
|
2712
|
-
},
|
|
2713
|
-
{
|
|
2714
|
-
name: "Remove MCP Server",
|
|
2715
|
-
value: "remove"
|
|
2716
|
-
},
|
|
2717
|
-
{
|
|
2718
|
-
name: "Exit",
|
|
2719
|
-
value: "exit"
|
|
2720
|
-
}
|
|
2721
|
-
]
|
|
2722
|
-
});
|
|
2723
|
-
if (action === "exit") {
|
|
2724
|
-
console.log(import_picocolors11.default.dim("Goodbye!"));
|
|
2725
|
-
break;
|
|
2726
|
-
}
|
|
2727
|
-
if (action === "add") {
|
|
2728
|
-
await wizardAdd();
|
|
2729
|
-
} else if (action === "manage") {
|
|
2730
|
-
await wizardManage();
|
|
2731
|
-
} else if (action === "remove") {
|
|
2732
|
-
await wizardRemove();
|
|
2733
|
-
}
|
|
2734
|
-
console.log();
|
|
2735
|
-
} catch (error) {
|
|
2736
|
-
if (error?.name === "ExitPromptError") {
|
|
2737
|
-
console.log("\n" + import_picocolors11.default.dim("Exited."));
|
|
2738
|
-
break;
|
|
2739
|
-
}
|
|
2740
|
-
throw error;
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
};
|
|
2744
|
-
|
|
2745
|
-
// src/cli/manage.ts
|
|
2746
|
-
var import_commander = require("commander");
|
|
2747
|
-
var import_picocolors12 = __toESM(require("picocolors"), 1);
|
|
2748
|
-
|
|
2749
|
-
// src/utils/parse-key-value-list.ts
|
|
2750
|
-
var parseKeyValueList = (entries, separator) => {
|
|
2751
|
-
if (!entries || entries.length === 0) return {};
|
|
2752
|
-
const result = {};
|
|
2753
|
-
for (const entry of entries) {
|
|
2754
|
-
const splitIndex = entry.indexOf(separator);
|
|
2755
|
-
if (splitIndex === -1) {
|
|
2756
|
-
throw new Error(`Invalid entry "${entry}": expected "${separator}" separator`);
|
|
3436
|
+
}
|
|
3437
|
+
refreshGroupedServers();
|
|
2757
3438
|
}
|
|
2758
|
-
const key = entry.slice(0, splitIndex).trim();
|
|
2759
|
-
const value = entry.slice(splitIndex + separator.length).trim();
|
|
2760
|
-
if (!key) throw new Error(`Invalid entry "${entry}": empty key`);
|
|
2761
|
-
result[key] = value;
|
|
2762
3439
|
}
|
|
2763
|
-
return result;
|
|
2764
3440
|
};
|
|
2765
3441
|
|
|
2766
3442
|
// src/cli/manage.ts
|
|
2767
|
-
var
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
3443
|
+
var requireTargetServerGroup = (serverName, scope) => {
|
|
3444
|
+
const grouped = queryGroupedInstalledServers(scope);
|
|
3445
|
+
const targetGroup = grouped.get(serverName);
|
|
3446
|
+
if (!targetGroup) {
|
|
3447
|
+
logger.error(
|
|
3448
|
+
`MCP server "${serverName}" is not configured in ${scope.global ? "global" : "project"} scope.`
|
|
3449
|
+
);
|
|
3450
|
+
process.exitCode = 1;
|
|
3451
|
+
return void 0;
|
|
3452
|
+
}
|
|
3453
|
+
return targetGroup;
|
|
2771
3454
|
};
|
|
2772
|
-
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) => {
|
|
2773
3456
|
try {
|
|
2774
3457
|
const cwd = process.cwd();
|
|
2775
3458
|
const isGlobal = Boolean(options.global);
|
|
2776
|
-
const hasModifications = options.command !== void 0 || options.args !== void 0 || options.env !== void 0 || options.header !== void 0 || options.url !== void 0 || options.transport !== void 0;
|
|
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;
|
|
2777
3460
|
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
2778
3461
|
if (hasModifications) {
|
|
2779
3462
|
if (!serverName) {
|
|
@@ -2781,55 +3464,64 @@ var mcpManageCommand = new import_commander.Command("manage").description("Inspe
|
|
|
2781
3464
|
process.exitCode = 1;
|
|
2782
3465
|
return;
|
|
2783
3466
|
}
|
|
2784
|
-
const
|
|
2785
|
-
const grouped = groupInstalledServersByName(installed);
|
|
2786
|
-
const targetGroup = grouped.get(serverName);
|
|
3467
|
+
const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
|
|
2787
3468
|
if (!targetGroup) {
|
|
2788
|
-
logger.error(
|
|
2789
|
-
`MCP server "${serverName}" is not configured in ${isGlobal ? "global" : "project"} scope.`
|
|
2790
|
-
);
|
|
2791
|
-
process.exitCode = 1;
|
|
2792
3469
|
return;
|
|
2793
3470
|
}
|
|
2794
|
-
const
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
const parsedEnv = parseKeyValueList(options.env, "=");
|
|
2814
|
-
updatedConfig.env = { ...updatedConfig.env ?? {}, ...parsedEnv };
|
|
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;
|
|
2815
3490
|
}
|
|
2816
|
-
if (
|
|
2817
|
-
const
|
|
2818
|
-
|
|
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
|
+
);
|
|
2819
3497
|
}
|
|
2820
|
-
const
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
const reason = agentConfig.unsupportedTransportMessage ?? `Agent does not support ${requestedTransport} transport`;
|
|
2829
|
-
logger.warn(`Skipping ${import_picocolors12.default.cyan(agent)}: ${reason}`);
|
|
3498
|
+
const incomingDelta = deltaResult.config;
|
|
3499
|
+
let targetAgents = targetGroup.agents;
|
|
3500
|
+
if (options.agent !== void 0) {
|
|
3501
|
+
const parsed = parseMcpAgentList(options.agent);
|
|
3502
|
+
if (!parsed || parsed.length === 0) {
|
|
3503
|
+
logger.error(`No valid agents recognized from: "${options.agent.join(", ")}".`);
|
|
3504
|
+
process.exitCode = 1;
|
|
3505
|
+
return;
|
|
2830
3506
|
}
|
|
3507
|
+
targetAgents = parsed;
|
|
3508
|
+
}
|
|
3509
|
+
const updateResult = updateMcpServer({
|
|
3510
|
+
serverName,
|
|
3511
|
+
config: incomingDelta,
|
|
3512
|
+
previousConfig: targetGroup.config,
|
|
3513
|
+
agents: targetAgents,
|
|
3514
|
+
global: isGlobal,
|
|
3515
|
+
cwd
|
|
3516
|
+
});
|
|
3517
|
+
for (const item of updateResult.incompatible) {
|
|
3518
|
+
logger.warn(`Skipping ${import_picocolors16.default.cyan(item.agent)}: ${item.reason}`);
|
|
2831
3519
|
}
|
|
2832
|
-
|
|
3520
|
+
const attemptedResults = updateResult.results.filter(
|
|
3521
|
+
(r) => !updateResult.incompatible.some((i) => i.agent === r.agent)
|
|
3522
|
+
);
|
|
3523
|
+
if (attemptedResults.length === 0) {
|
|
3524
|
+
const requestedTransport = updateResult.config.url ? updateResult.config.type ?? "http" : "stdio";
|
|
2833
3525
|
logger.error(
|
|
2834
3526
|
`None of the target agents support ${requestedTransport} transport. Update aborted.`
|
|
2835
3527
|
);
|
|
@@ -2837,19 +3529,16 @@ var mcpManageCommand = new import_commander.Command("manage").description("Inspe
|
|
|
2837
3529
|
return;
|
|
2838
3530
|
}
|
|
2839
3531
|
logger.info(
|
|
2840
|
-
`Updating ${
|
|
3532
|
+
`Updating ${import_picocolors16.default.bold(serverName)} across ${import_picocolors16.default.cyan(String(attemptedResults.length))} agent(s)...`
|
|
2841
3533
|
);
|
|
2842
3534
|
let allSuccess = true;
|
|
2843
|
-
for (const
|
|
2844
|
-
const res = installMcpServerForAgent(serverName, updatedConfig, agent, {
|
|
2845
|
-
global: isGlobal,
|
|
2846
|
-
cwd
|
|
2847
|
-
});
|
|
3535
|
+
for (const res of attemptedResults) {
|
|
2848
3536
|
if (res.success) {
|
|
2849
|
-
logger.success(`${
|
|
3537
|
+
logger.success(`${import_picocolors16.default.cyan(res.agent)}: Successfully updated in ${import_picocolors16.default.dim(res.path)}`);
|
|
3538
|
+
logCoHostedNotice("configured", res.coConfiguredAgents);
|
|
2850
3539
|
} else {
|
|
2851
3540
|
allSuccess = false;
|
|
2852
|
-
logger.error(`${
|
|
3541
|
+
logger.error(`${import_picocolors16.default.cyan(res.agent)}: Update failed - ${res.error}`);
|
|
2853
3542
|
}
|
|
2854
3543
|
}
|
|
2855
3544
|
if (!allSuccess) {
|
|
@@ -2865,21 +3554,16 @@ var mcpManageCommand = new import_commander.Command("manage").description("Inspe
|
|
|
2865
3554
|
process.exitCode = 1;
|
|
2866
3555
|
return;
|
|
2867
3556
|
}
|
|
2868
|
-
const
|
|
2869
|
-
const grouped = groupInstalledServersByName(installed);
|
|
2870
|
-
const targetGroup = grouped.get(serverName);
|
|
3557
|
+
const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
|
|
2871
3558
|
if (!targetGroup) {
|
|
2872
|
-
logger.error(
|
|
2873
|
-
`MCP server "${serverName}" is not configured in ${isGlobal ? "global" : "project"} scope.`
|
|
2874
|
-
);
|
|
2875
|
-
process.exitCode = 1;
|
|
2876
3559
|
return;
|
|
2877
3560
|
}
|
|
2878
3561
|
displayServerDetails({
|
|
2879
3562
|
serverName,
|
|
2880
3563
|
config: targetGroup.config,
|
|
2881
3564
|
agents: targetGroup.agents,
|
|
2882
|
-
isGlobal
|
|
3565
|
+
global: isGlobal,
|
|
3566
|
+
hasDivergence: targetGroup.hasDivergence
|
|
2883
3567
|
});
|
|
2884
3568
|
return;
|
|
2885
3569
|
}
|
|
@@ -2896,133 +3580,96 @@ var mcpManageCommand = new import_commander.Command("manage").description("Inspe
|
|
|
2896
3580
|
}
|
|
2897
3581
|
});
|
|
2898
3582
|
|
|
2899
|
-
// src/
|
|
2900
|
-
var
|
|
3583
|
+
// src/cli/remove.ts
|
|
3584
|
+
var import_commander4 = require("commander");
|
|
3585
|
+
var import_picocolors18 = __toESM(require("picocolors"), 1);
|
|
2901
3586
|
|
|
2902
|
-
// src/
|
|
2903
|
-
var
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
process.exitCode = 1;
|
|
2927
|
-
return;
|
|
2928
|
-
}
|
|
2929
|
-
const parsed = parseMcpSource(source);
|
|
2930
|
-
const cwd = process.cwd();
|
|
2931
|
-
const isGlobal = Boolean(options.global);
|
|
2932
|
-
const explicitTransport = resolveTransport2(options.transport);
|
|
2933
|
-
const transport = explicitTransport ?? (parsed.type === "remote" ? "http" : "stdio");
|
|
2934
|
-
const resolvedTargets = resolveTargetAgents({
|
|
2935
|
-
requested: options.agent,
|
|
2936
|
-
all: options.all,
|
|
2937
|
-
global: isGlobal,
|
|
2938
|
-
cwd,
|
|
2939
|
-
transport
|
|
2940
|
-
});
|
|
2941
|
-
if (resolvedTargets.agents.length === 0) {
|
|
2942
|
-
const message = resolvedTargets.diagnostic ?? `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass ${import_picocolors13.default.cyan("-a <agent>")} (e.g. ${import_picocolors13.default.cyan("-a cursor")}) or ${import_picocolors13.default.cyan("--all")} to install.`;
|
|
2943
|
-
logger.warn(message);
|
|
2944
|
-
process.exitCode = 1;
|
|
2945
|
-
return;
|
|
2946
|
-
}
|
|
2947
|
-
if (resolvedTargets.isDetected) {
|
|
2948
|
-
logger.info(
|
|
2949
|
-
`Detected ${isGlobal ? "global" : "project"} agents: ${import_picocolors13.default.cyan(formatAgentList(resolvedTargets.detected, "(none detected)"))}`
|
|
2950
|
-
);
|
|
2951
|
-
if (resolvedTargets.incompatible.length > 0) {
|
|
2952
|
-
const skippedList = resolvedTargets.incompatible.map((item) => `${item.agent} (${item.reason})`).join(", ");
|
|
2953
|
-
logger.info(
|
|
2954
|
-
`Skipping detected agents incompatible with ${transport}: ${import_picocolors13.default.yellow(skippedList)}`
|
|
2955
|
-
);
|
|
2956
|
-
}
|
|
2957
|
-
}
|
|
2958
|
-
const targetAgents = resolvedTargets.isDetected ? resolvedTargets.agents : resolvedTargets.allAgents;
|
|
2959
|
-
const result = installMcpServer({
|
|
2960
|
-
source,
|
|
2961
|
-
name: options.name,
|
|
2962
|
-
agents: targetAgents,
|
|
2963
|
-
args: options.args,
|
|
2964
|
-
global: isGlobal,
|
|
2965
|
-
cwd,
|
|
2966
|
-
transport: explicitTransport,
|
|
2967
|
-
headers: parseKeyValueList(options.header, ":"),
|
|
2968
|
-
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
|
|
2969
3611
|
});
|
|
2970
|
-
logger.info(
|
|
2971
|
-
`Installing ${import_picocolors13.default.bold(result.serverName)} (${import_picocolors13.default.cyan(parsed.type)}) to ${import_picocolors13.default.cyan(String(result.results.length))} agent(s)`
|
|
2972
|
-
);
|
|
2973
|
-
for (const record of result.results) {
|
|
2974
|
-
if (record.success) {
|
|
2975
|
-
logger.success(`${import_picocolors13.default.cyan(record.agent)} ${import_picocolors13.default.dim(record.path)}`);
|
|
2976
|
-
} else {
|
|
2977
|
-
logger.error(`${import_picocolors13.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
3612
|
}
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
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 }
|
|
2996
3625
|
});
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
const existing = grouped.get(entry.serverName) ?? [];
|
|
3008
|
-
existing.push(entry);
|
|
3009
|
-
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;
|
|
3010
3636
|
}
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
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}`);
|
|
3016
3662
|
}
|
|
3017
|
-
} catch (error) {
|
|
3018
|
-
logger.error(toErrorMessage(error));
|
|
3019
|
-
process.exitCode = 1;
|
|
3020
3663
|
}
|
|
3021
|
-
|
|
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
|
+
};
|
|
3022
3671
|
|
|
3023
3672
|
// src/cli/remove.ts
|
|
3024
|
-
var import_commander4 = require("commander");
|
|
3025
|
-
var import_picocolors15 = __toESM(require("picocolors"), 1);
|
|
3026
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) => {
|
|
3027
3674
|
try {
|
|
3028
3675
|
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
@@ -3046,16 +3693,17 @@ var mcpRemoveCommand = new import_commander4.Command("remove").alias("rm").descr
|
|
|
3046
3693
|
cwd: process.cwd()
|
|
3047
3694
|
});
|
|
3048
3695
|
if (results.length === 0) {
|
|
3049
|
-
logger.warn(`No agent config contained ${
|
|
3696
|
+
logger.warn(`No agent config contained ${import_picocolors18.default.bold(name)}`);
|
|
3050
3697
|
return;
|
|
3051
3698
|
}
|
|
3052
3699
|
for (const record of results) {
|
|
3053
3700
|
if (record.removed) {
|
|
3054
3701
|
logger.success(
|
|
3055
|
-
`${
|
|
3702
|
+
`${import_picocolors18.default.cyan(record.agent)} removed ${import_picocolors18.default.bold(name)} ${import_picocolors18.default.dim(record.path)}`
|
|
3056
3703
|
);
|
|
3704
|
+
logCoHostedNotice("affected", record.coAffectedAgents);
|
|
3057
3705
|
} else {
|
|
3058
|
-
logger.error(`${
|
|
3706
|
+
logger.error(`${import_picocolors18.default.cyan(record.agent)}: ${record.error ?? "not found"}`);
|
|
3059
3707
|
}
|
|
3060
3708
|
}
|
|
3061
3709
|
} catch (error) {
|
|
@@ -3064,8 +3712,61 @@ var mcpRemoveCommand = new import_commander4.Command("remove").alias("rm").descr
|
|
|
3064
3712
|
}
|
|
3065
3713
|
});
|
|
3066
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
|
+
|
|
3067
3768
|
// src/cli.ts
|
|
3068
|
-
var VERSION = "0.1.0
|
|
3769
|
+
var VERSION = "0.1.0";
|
|
3069
3770
|
process.on("SIGINT", () => process.exit(0));
|
|
3070
3771
|
process.on("SIGTERM", () => process.exit(0));
|
|
3071
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");
|