mcpman 0.7.0 → 0.8.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/README.md +85 -0
- package/dist/chunk-AOCGFOA6.js +80 -0
- package/dist/chunk-DZ3D5RZQ.js +181 -0
- package/dist/{chunk-NS6HV723.js → chunk-XPYCEHZZ.js} +6 -62
- package/dist/{client-detector-CY7WPF3K.js → client-detector-NHXBDNMY.js} +2 -1
- package/dist/index.cjs +1798 -696
- package/dist/index.js +1527 -544
- package/dist/{lockfile-RBA7HB24.js → lockfile-5XXA26FM.js} +2 -1
- package/package.json +1 -1
- package/dist/chunk-YZNTMR6O.js +0 -88
package/dist/index.js
CHANGED
|
@@ -4,19 +4,27 @@ import {
|
|
|
4
4
|
addEntry,
|
|
5
5
|
createEmptyLockfile,
|
|
6
6
|
findLockfile,
|
|
7
|
+
listSnapshots,
|
|
7
8
|
readLockfile,
|
|
9
|
+
readSnapshot,
|
|
8
10
|
resolveLockfilePath,
|
|
11
|
+
restoreSnapshot,
|
|
9
12
|
writeLockfile
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-DZ3D5RZQ.js";
|
|
11
14
|
import {
|
|
12
15
|
computeTrustScore
|
|
13
16
|
} from "./chunk-RGKHLY5G.js";
|
|
17
|
+
import {
|
|
18
|
+
getInstalledClients
|
|
19
|
+
} from "./chunk-XPYCEHZZ.js";
|
|
14
20
|
import {
|
|
15
21
|
getConfigPath,
|
|
16
|
-
|
|
22
|
+
getEnvDir,
|
|
23
|
+
getGroupsFile,
|
|
24
|
+
getPinsFile,
|
|
17
25
|
getPluginDir,
|
|
18
26
|
getProfilesDir
|
|
19
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-AOCGFOA6.js";
|
|
20
28
|
import {
|
|
21
29
|
getMasterPassword,
|
|
22
30
|
getSecretsForServer,
|
|
@@ -29,7 +37,7 @@ import {
|
|
|
29
37
|
} from "./chunk-6X6Q6UZC.js";
|
|
30
38
|
|
|
31
39
|
// src/index.ts
|
|
32
|
-
import { defineCommand as
|
|
40
|
+
import { defineCommand as defineCommand33, runMain } from "citty";
|
|
33
41
|
|
|
34
42
|
// src/commands/audit.ts
|
|
35
43
|
import * as p from "@clack/prompts";
|
|
@@ -167,11 +175,11 @@ async function scanAllServers(servers, concurrency = 3) {
|
|
|
167
175
|
const results = [];
|
|
168
176
|
const executing = /* @__PURE__ */ new Set();
|
|
169
177
|
for (const [name, entry] of entries) {
|
|
170
|
-
const
|
|
178
|
+
const p13 = scanServer(name, entry).then((r) => {
|
|
171
179
|
results.push(r);
|
|
172
|
-
executing.delete(
|
|
180
|
+
executing.delete(p13);
|
|
173
181
|
});
|
|
174
|
-
executing.add(
|
|
182
|
+
executing.add(p13);
|
|
175
183
|
if (executing.size >= concurrency) await Promise.race(executing);
|
|
176
184
|
}
|
|
177
185
|
await Promise.all(executing);
|
|
@@ -336,8 +344,8 @@ function setConfigValue(key, value, configPath = getConfigPath()) {
|
|
|
336
344
|
// src/core/plugin-loader.ts
|
|
337
345
|
function isValidPlugin(obj) {
|
|
338
346
|
if (typeof obj !== "object" || obj === null) return false;
|
|
339
|
-
const
|
|
340
|
-
return typeof
|
|
347
|
+
const p13 = obj;
|
|
348
|
+
return typeof p13.name === "string" && typeof p13.prefix === "string" && typeof p13.resolve === "function";
|
|
341
349
|
}
|
|
342
350
|
function loadPlugin(pkg, pluginDir = getPluginDir()) {
|
|
343
351
|
try {
|
|
@@ -391,7 +399,7 @@ function removePluginPackage(name, pluginDir = getPluginDir()) {
|
|
|
391
399
|
}
|
|
392
400
|
const config = readConfig();
|
|
393
401
|
const plugins = config.plugins ?? [];
|
|
394
|
-
config.plugins = plugins.filter((
|
|
402
|
+
config.plugins = plugins.filter((p13) => p13 !== name);
|
|
395
403
|
writeConfig(config);
|
|
396
404
|
}
|
|
397
405
|
function listPluginPackages() {
|
|
@@ -440,7 +448,7 @@ async function resolveServer(input) {
|
|
|
440
448
|
if (source.type.startsWith("plugin:")) {
|
|
441
449
|
const pluginName = source.type.slice(7);
|
|
442
450
|
const plugins = loadAllPlugins();
|
|
443
|
-
const plugin = plugins.find((
|
|
451
|
+
const plugin = plugins.find((p13) => p13.name === pluginName);
|
|
444
452
|
if (plugin) {
|
|
445
453
|
const resolved = await plugin.resolve(source.input);
|
|
446
454
|
return resolved;
|
|
@@ -494,6 +502,47 @@ async function applyServerUpdate(serverName, lockEntry, clients) {
|
|
|
494
502
|
}
|
|
495
503
|
}
|
|
496
504
|
|
|
505
|
+
// src/core/pin-service.ts
|
|
506
|
+
import fs4 from "fs";
|
|
507
|
+
function readPins(file) {
|
|
508
|
+
const target = file ?? getPinsFile();
|
|
509
|
+
if (!fs4.existsSync(target)) return {};
|
|
510
|
+
try {
|
|
511
|
+
return JSON.parse(fs4.readFileSync(target, "utf-8"));
|
|
512
|
+
} catch {
|
|
513
|
+
return {};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function writePins(store, file) {
|
|
517
|
+
const target = file ?? getPinsFile();
|
|
518
|
+
const dir = target.slice(0, target.lastIndexOf("/"));
|
|
519
|
+
if (dir && !fs4.existsSync(dir)) {
|
|
520
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
521
|
+
}
|
|
522
|
+
fs4.writeFileSync(target, JSON.stringify(store, null, 2), "utf-8");
|
|
523
|
+
}
|
|
524
|
+
function pinServer(server, version, file) {
|
|
525
|
+
const store = readPins(file);
|
|
526
|
+
store[server] = version;
|
|
527
|
+
writePins(store, file);
|
|
528
|
+
}
|
|
529
|
+
function unpinServer(server, file) {
|
|
530
|
+
const store = readPins(file);
|
|
531
|
+
if (!(server in store)) return;
|
|
532
|
+
delete store[server];
|
|
533
|
+
writePins(store, file);
|
|
534
|
+
}
|
|
535
|
+
function getPinnedVersion(server, file) {
|
|
536
|
+
return readPins(file)[server] ?? null;
|
|
537
|
+
}
|
|
538
|
+
function isPinned(server, file) {
|
|
539
|
+
return server in readPins(file);
|
|
540
|
+
}
|
|
541
|
+
function listPins(file) {
|
|
542
|
+
const store = readPins(file);
|
|
543
|
+
return Object.entries(store).sort(([a], [b]) => a.localeCompare(b)).map(([server, version]) => ({ server, version }));
|
|
544
|
+
}
|
|
545
|
+
|
|
497
546
|
// src/core/version-checker.ts
|
|
498
547
|
function compareVersions(a, b) {
|
|
499
548
|
const aParts = a.replace(/^v/, "").split(".").map(Number);
|
|
@@ -595,11 +644,12 @@ async function checkAllVersions(lockfile) {
|
|
|
595
644
|
const results = [];
|
|
596
645
|
const executing = /* @__PURE__ */ new Set();
|
|
597
646
|
for (const [name, entry] of entries) {
|
|
598
|
-
|
|
647
|
+
if (isPinned(name)) continue;
|
|
648
|
+
const p13 = checkVersion(name, entry).then((r) => {
|
|
599
649
|
results.push(r);
|
|
600
|
-
executing.delete(
|
|
650
|
+
executing.delete(p13);
|
|
601
651
|
});
|
|
602
|
-
executing.add(
|
|
652
|
+
executing.add(p13);
|
|
603
653
|
if (executing.size >= 5) {
|
|
604
654
|
await Promise.race(executing);
|
|
605
655
|
}
|
|
@@ -750,7 +800,7 @@ var audit_default = defineCommand({
|
|
|
750
800
|
});
|
|
751
801
|
async function loadClients() {
|
|
752
802
|
try {
|
|
753
|
-
const mod = await import("./client-detector-
|
|
803
|
+
const mod = await import("./client-detector-NHXBDNMY.js");
|
|
754
804
|
return mod.getInstalledClients();
|
|
755
805
|
} catch {
|
|
756
806
|
return [];
|
|
@@ -853,12 +903,204 @@ async function runAuditFix(reports, servers, skipConfirm) {
|
|
|
853
903
|
`);
|
|
854
904
|
}
|
|
855
905
|
|
|
906
|
+
// src/commands/bench.ts
|
|
907
|
+
import { defineCommand as defineCommand2 } from "citty";
|
|
908
|
+
import pc2 from "picocolors";
|
|
909
|
+
|
|
910
|
+
// src/core/bench-service.ts
|
|
911
|
+
import { spawn } from "child_process";
|
|
912
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
913
|
+
function measureOneRun(command, args, env, timeoutMs) {
|
|
914
|
+
return new Promise((resolve, reject) => {
|
|
915
|
+
const start = Date.now();
|
|
916
|
+
let settled = false;
|
|
917
|
+
let stdout = "";
|
|
918
|
+
const child = spawn(command, args, {
|
|
919
|
+
env: { ...process.env, ...env },
|
|
920
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
921
|
+
});
|
|
922
|
+
const finish = (err) => {
|
|
923
|
+
if (settled) return;
|
|
924
|
+
settled = true;
|
|
925
|
+
try {
|
|
926
|
+
child.kill("SIGTERM");
|
|
927
|
+
} catch {
|
|
928
|
+
}
|
|
929
|
+
if (err) reject(err);
|
|
930
|
+
else resolve(Date.now() - start);
|
|
931
|
+
};
|
|
932
|
+
const timer = setTimeout(() => {
|
|
933
|
+
finish(new Error("Timeout waiting for initialize response"));
|
|
934
|
+
}, timeoutMs);
|
|
935
|
+
child.on("error", (err) => {
|
|
936
|
+
clearTimeout(timer);
|
|
937
|
+
finish(err);
|
|
938
|
+
});
|
|
939
|
+
child.on("exit", (code) => {
|
|
940
|
+
clearTimeout(timer);
|
|
941
|
+
if (!settled) finish(new Error(`Process exited with code ${code}`));
|
|
942
|
+
});
|
|
943
|
+
child.stdout?.on("data", (chunk) => {
|
|
944
|
+
stdout += chunk.toString();
|
|
945
|
+
for (const line of stdout.split("\n")) {
|
|
946
|
+
const trimmed = line.trim();
|
|
947
|
+
if (!trimmed) continue;
|
|
948
|
+
try {
|
|
949
|
+
const msg = JSON.parse(trimmed);
|
|
950
|
+
if (msg.jsonrpc === "2.0" && msg.id === 1) {
|
|
951
|
+
clearTimeout(timer);
|
|
952
|
+
finish();
|
|
953
|
+
}
|
|
954
|
+
} catch {
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
const initReq = JSON.stringify({
|
|
959
|
+
jsonrpc: "2.0",
|
|
960
|
+
id: 1,
|
|
961
|
+
method: "initialize",
|
|
962
|
+
params: {
|
|
963
|
+
protocolVersion: "2024-11-05",
|
|
964
|
+
capabilities: {},
|
|
965
|
+
clientInfo: { name: "mcpman-bench", version: "0.8.0" }
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
child.stdin?.write(`${initReq}
|
|
969
|
+
`);
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
function percentile(sorted, p13) {
|
|
973
|
+
if (sorted.length === 0) return 0;
|
|
974
|
+
const idx = Math.ceil(p13 / 100 * sorted.length) - 1;
|
|
975
|
+
return sorted[Math.max(0, idx)];
|
|
976
|
+
}
|
|
977
|
+
async function benchServer(command, args, env, runs = 5, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
978
|
+
const allTimes = [];
|
|
979
|
+
for (let i = 0; i < runs; i++) {
|
|
980
|
+
try {
|
|
981
|
+
const ms = await measureOneRun(command, args, env, timeoutMs);
|
|
982
|
+
allTimes.push(ms);
|
|
983
|
+
} catch (err) {
|
|
984
|
+
return {
|
|
985
|
+
runs,
|
|
986
|
+
min: 0,
|
|
987
|
+
max: 0,
|
|
988
|
+
avg: 0,
|
|
989
|
+
p50: 0,
|
|
990
|
+
p95: 0,
|
|
991
|
+
allTimes,
|
|
992
|
+
error: String(err instanceof Error ? err.message : err)
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const sorted = [...allTimes].sort((a, b) => a - b);
|
|
997
|
+
const sum = allTimes.reduce((a, b) => a + b, 0);
|
|
998
|
+
return {
|
|
999
|
+
runs,
|
|
1000
|
+
min: sorted[0] ?? 0,
|
|
1001
|
+
max: sorted[sorted.length - 1] ?? 0,
|
|
1002
|
+
avg: Math.round(sum / allTimes.length),
|
|
1003
|
+
p50: percentile(sorted, 50),
|
|
1004
|
+
p95: percentile(sorted, 95),
|
|
1005
|
+
allTimes
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/commands/bench.ts
|
|
1010
|
+
var bench_default = defineCommand2({
|
|
1011
|
+
meta: {
|
|
1012
|
+
name: "bench",
|
|
1013
|
+
description: "Benchmark MCP server latency (JSON-RPC initialize)"
|
|
1014
|
+
},
|
|
1015
|
+
args: {
|
|
1016
|
+
server: {
|
|
1017
|
+
type: "positional",
|
|
1018
|
+
description: "Server name as stored in lockfile",
|
|
1019
|
+
required: true
|
|
1020
|
+
},
|
|
1021
|
+
runs: {
|
|
1022
|
+
type: "string",
|
|
1023
|
+
description: "Number of benchmark runs (default: 5)",
|
|
1024
|
+
default: "5"
|
|
1025
|
+
},
|
|
1026
|
+
timeout: {
|
|
1027
|
+
type: "string",
|
|
1028
|
+
description: "Per-run timeout in ms (default: 10000)",
|
|
1029
|
+
default: "10000"
|
|
1030
|
+
},
|
|
1031
|
+
json: {
|
|
1032
|
+
type: "boolean",
|
|
1033
|
+
description: "Output results as JSON",
|
|
1034
|
+
default: false
|
|
1035
|
+
}
|
|
1036
|
+
},
|
|
1037
|
+
async run({ args }) {
|
|
1038
|
+
const lockfile = readLockfile();
|
|
1039
|
+
const entry = lockfile.servers[args.server];
|
|
1040
|
+
if (!entry) {
|
|
1041
|
+
console.error(`${pc2.red("\u2717")} Server "${args.server}" not found in lockfile.`);
|
|
1042
|
+
console.error(pc2.dim("Run `mcpman list` to see installed servers."));
|
|
1043
|
+
process.exit(1);
|
|
1044
|
+
}
|
|
1045
|
+
const runs = Math.max(1, Number.parseInt(args.runs, 10) || 5);
|
|
1046
|
+
const timeoutMs = Math.max(1e3, Number.parseInt(args.timeout, 10) || 1e4);
|
|
1047
|
+
if (!args.json) {
|
|
1048
|
+
console.log(`
|
|
1049
|
+
${pc2.cyan("mcpman bench")} \u2014 ${pc2.bold(args.server)}`);
|
|
1050
|
+
console.log(pc2.dim(` Command: ${entry.command} ${(entry.args ?? []).join(" ")}`));
|
|
1051
|
+
console.log(pc2.dim(` Runs: ${runs} Timeout: ${timeoutMs}ms
|
|
1052
|
+
`));
|
|
1053
|
+
process.stdout.write(pc2.dim(" Running"));
|
|
1054
|
+
}
|
|
1055
|
+
const env = {};
|
|
1056
|
+
for (const ev of entry.envVars ?? []) {
|
|
1057
|
+
const idx = ev.indexOf("=");
|
|
1058
|
+
if (idx > 0) env[ev.slice(0, idx)] = ev.slice(idx + 1);
|
|
1059
|
+
}
|
|
1060
|
+
const result = await benchServer(entry.command, entry.args ?? [], env, runs, timeoutMs);
|
|
1061
|
+
if (!args.json) process.stdout.write("\n");
|
|
1062
|
+
if (result.error) {
|
|
1063
|
+
if (args.json) {
|
|
1064
|
+
console.log(JSON.stringify({ server: args.server, error: result.error }));
|
|
1065
|
+
} else {
|
|
1066
|
+
console.error(`
|
|
1067
|
+
${pc2.red("\u2717")} Benchmark failed: ${result.error}`);
|
|
1068
|
+
}
|
|
1069
|
+
process.exit(1);
|
|
1070
|
+
}
|
|
1071
|
+
if (args.json) {
|
|
1072
|
+
console.log(JSON.stringify({ server: args.server, ...result }, null, 2));
|
|
1073
|
+
if (result.p95 > timeoutMs) process.exit(1);
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
const pad4 = (s, w) => s.padEnd(w);
|
|
1077
|
+
const ms = (n) => `${n}ms`;
|
|
1078
|
+
console.log(`
|
|
1079
|
+
${pc2.bold("Latency statistics")} for ${pc2.cyan(args.server)}`);
|
|
1080
|
+
console.log(pc2.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1081
|
+
console.log(` ${pad4("min", 8)} ${pc2.green(ms(result.min))}`);
|
|
1082
|
+
console.log(` ${pad4("avg", 8)} ${ms(result.avg)}`);
|
|
1083
|
+
console.log(` ${pad4("p50", 8)} ${ms(result.p50)}`);
|
|
1084
|
+
console.log(
|
|
1085
|
+
` ${pad4("p95", 8)} ${result.p95 > timeoutMs ? pc2.red(ms(result.p95)) : pc2.yellow(ms(result.p95))}`
|
|
1086
|
+
);
|
|
1087
|
+
console.log(` ${pad4("max", 8)} ${ms(result.max)}`);
|
|
1088
|
+
console.log(` ${pad4("runs", 8)} ${result.runs}`);
|
|
1089
|
+
console.log("");
|
|
1090
|
+
if (result.p95 > timeoutMs) {
|
|
1091
|
+
console.error(pc2.red(` \u2717 p95 (${result.p95}ms) exceeds timeout (${timeoutMs}ms)`));
|
|
1092
|
+
process.exit(1);
|
|
1093
|
+
}
|
|
1094
|
+
console.log(pc2.green(" \u2713 Benchmark complete"));
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
|
|
856
1098
|
// src/commands/completions.ts
|
|
857
|
-
import
|
|
1099
|
+
import fs5 from "fs";
|
|
858
1100
|
import os2 from "os";
|
|
859
1101
|
import path4 from "path";
|
|
860
|
-
import { defineCommand as
|
|
861
|
-
import
|
|
1102
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
1103
|
+
import pc3 from "picocolors";
|
|
862
1104
|
|
|
863
1105
|
// src/core/completion-generator.ts
|
|
864
1106
|
function getCommandList() {
|
|
@@ -888,7 +1130,13 @@ function getCommandList() {
|
|
|
888
1130
|
"watch",
|
|
889
1131
|
"registry",
|
|
890
1132
|
"completions",
|
|
891
|
-
"why"
|
|
1133
|
+
"why",
|
|
1134
|
+
"env",
|
|
1135
|
+
"bench",
|
|
1136
|
+
"diff",
|
|
1137
|
+
"group",
|
|
1138
|
+
"pin",
|
|
1139
|
+
"rollback"
|
|
892
1140
|
];
|
|
893
1141
|
}
|
|
894
1142
|
var SERVER_ARG_COMMANDS = [
|
|
@@ -1013,7 +1261,7 @@ complete -c mcpman -l runtime -s r \\
|
|
|
1013
1261
|
}
|
|
1014
1262
|
|
|
1015
1263
|
// src/commands/completions.ts
|
|
1016
|
-
var completions_default =
|
|
1264
|
+
var completions_default = defineCommand3({
|
|
1017
1265
|
meta: {
|
|
1018
1266
|
name: "completions",
|
|
1019
1267
|
description: "Generate shell completion scripts (bash, zsh, fish)"
|
|
@@ -1063,24 +1311,24 @@ var completions_default = defineCommand2({
|
|
|
1063
1311
|
await installCompletion();
|
|
1064
1312
|
break;
|
|
1065
1313
|
default:
|
|
1066
|
-
console.error(
|
|
1314
|
+
console.error(pc3.red(` Error: Unknown shell '${shell}'. Use: bash, zsh, or fish.`));
|
|
1067
1315
|
process.exit(1);
|
|
1068
1316
|
}
|
|
1069
1317
|
}
|
|
1070
1318
|
});
|
|
1071
1319
|
function printUsage() {
|
|
1072
|
-
console.log(
|
|
1320
|
+
console.log(pc3.bold("\n mcpman completions \u2014 Shell completion setup\n"));
|
|
1073
1321
|
console.log(" Usage:");
|
|
1074
|
-
console.log(` ${
|
|
1075
|
-
console.log(` ${
|
|
1076
|
-
console.log(` ${
|
|
1077
|
-
console.log(` ${
|
|
1322
|
+
console.log(` ${pc3.cyan("mcpman completions bash")} Output bash completion script`);
|
|
1323
|
+
console.log(` ${pc3.cyan("mcpman completions zsh")} Output zsh completion script`);
|
|
1324
|
+
console.log(` ${pc3.cyan("mcpman completions fish")} Output fish completion script`);
|
|
1325
|
+
console.log(` ${pc3.cyan("mcpman completions install")} Auto-detect shell and install
|
|
1078
1326
|
`);
|
|
1079
1327
|
console.log(" Quick setup:");
|
|
1080
|
-
console.log(` ${
|
|
1081
|
-
console.log(` ${
|
|
1082
|
-
console.log(` ${
|
|
1083
|
-
console.log(` ${
|
|
1328
|
+
console.log(` ${pc3.dim("# bash")}`);
|
|
1329
|
+
console.log(` ${pc3.cyan("source <(mcpman completions bash)")}`);
|
|
1330
|
+
console.log(` ${pc3.dim("# zsh")}`);
|
|
1331
|
+
console.log(` ${pc3.cyan("source <(mcpman completions zsh)")}
|
|
1084
1332
|
`);
|
|
1085
1333
|
}
|
|
1086
1334
|
async function installCompletion() {
|
|
@@ -1090,8 +1338,8 @@ async function installCompletion() {
|
|
|
1090
1338
|
else if (shellBin.includes("fish")) detectedShell = "fish";
|
|
1091
1339
|
else if (shellBin.includes("bash")) detectedShell = "bash";
|
|
1092
1340
|
if (!detectedShell) {
|
|
1093
|
-
console.error(
|
|
1094
|
-
console.error(
|
|
1341
|
+
console.error(pc3.red(" Could not detect shell from $SHELL. Run manually:"));
|
|
1342
|
+
console.error(pc3.dim(" source <(mcpman completions bash|zsh|fish)"));
|
|
1095
1343
|
process.exit(1);
|
|
1096
1344
|
}
|
|
1097
1345
|
const home = os2.homedir();
|
|
@@ -1102,10 +1350,10 @@ async function installCompletion() {
|
|
|
1102
1350
|
script = generateZshCompletion();
|
|
1103
1351
|
} else if (detectedShell === "fish") {
|
|
1104
1352
|
const fishDir = path4.join(home, ".config", "fish", "completions");
|
|
1105
|
-
|
|
1353
|
+
fs5.mkdirSync(fishDir, { recursive: true });
|
|
1106
1354
|
rcFile = path4.join(fishDir, "mcpman.fish");
|
|
1107
|
-
|
|
1108
|
-
console.log(
|
|
1355
|
+
fs5.writeFileSync(rcFile, generateFishCompletion(), "utf-8");
|
|
1356
|
+
console.log(pc3.green(` Installed fish completions to ${rcFile}`));
|
|
1109
1357
|
return;
|
|
1110
1358
|
} else {
|
|
1111
1359
|
rcFile = path4.join(home, ".bashrc");
|
|
@@ -1114,25 +1362,25 @@ async function installCompletion() {
|
|
|
1114
1362
|
const marker = "# mcpman completions";
|
|
1115
1363
|
let existing = "";
|
|
1116
1364
|
try {
|
|
1117
|
-
existing =
|
|
1365
|
+
existing = fs5.readFileSync(rcFile, "utf-8");
|
|
1118
1366
|
} catch {
|
|
1119
1367
|
}
|
|
1120
1368
|
if (existing.includes(marker)) {
|
|
1121
|
-
console.log(
|
|
1369
|
+
console.log(pc3.yellow(` Completions already installed in ${rcFile}. Skipping.`));
|
|
1122
1370
|
return;
|
|
1123
1371
|
}
|
|
1124
|
-
|
|
1372
|
+
fs5.appendFileSync(rcFile, `
|
|
1125
1373
|
${marker}
|
|
1126
1374
|
source <(mcpman completions ${detectedShell})
|
|
1127
1375
|
`);
|
|
1128
|
-
console.log(
|
|
1129
|
-
console.log(
|
|
1376
|
+
console.log(pc3.green(` Installed ${detectedShell} completions in ${rcFile}`));
|
|
1377
|
+
console.log(pc3.dim(` Restart your shell or run: source ${rcFile}`));
|
|
1130
1378
|
}
|
|
1131
1379
|
|
|
1132
1380
|
// src/commands/config.ts
|
|
1133
1381
|
import * as p2 from "@clack/prompts";
|
|
1134
|
-
import { defineCommand as
|
|
1135
|
-
import
|
|
1382
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
1383
|
+
import pc4 from "picocolors";
|
|
1136
1384
|
function coerceValue(raw) {
|
|
1137
1385
|
if (raw === "true") return true;
|
|
1138
1386
|
if (raw === "false") return false;
|
|
@@ -1140,7 +1388,7 @@ function coerceValue(raw) {
|
|
|
1140
1388
|
if (!Number.isNaN(num) && raw.trim() !== "") return num;
|
|
1141
1389
|
return raw;
|
|
1142
1390
|
}
|
|
1143
|
-
var setCommand =
|
|
1391
|
+
var setCommand = defineCommand4({
|
|
1144
1392
|
meta: { name: "set", description: "Set a config value" },
|
|
1145
1393
|
args: {
|
|
1146
1394
|
key: {
|
|
@@ -1158,14 +1406,14 @@ var setCommand = defineCommand3({
|
|
|
1158
1406
|
try {
|
|
1159
1407
|
const coerced = coerceValue(args.value);
|
|
1160
1408
|
setConfigValue(args.key, coerced);
|
|
1161
|
-
console.log(`${
|
|
1409
|
+
console.log(`${pc4.green("\u2713")} Set ${pc4.bold(args.key)} = ${pc4.cyan(String(coerced))}`);
|
|
1162
1410
|
} catch (err) {
|
|
1163
|
-
console.error(`${
|
|
1411
|
+
console.error(`${pc4.red("\u2717")} ${String(err)}`);
|
|
1164
1412
|
process.exit(1);
|
|
1165
1413
|
}
|
|
1166
1414
|
}
|
|
1167
1415
|
});
|
|
1168
|
-
var getCommand =
|
|
1416
|
+
var getCommand = defineCommand4({
|
|
1169
1417
|
meta: { name: "get", description: "Get a config value" },
|
|
1170
1418
|
args: {
|
|
1171
1419
|
key: {
|
|
@@ -1177,32 +1425,32 @@ var getCommand = defineCommand3({
|
|
|
1177
1425
|
run({ args }) {
|
|
1178
1426
|
const val = getConfigValue(args.key);
|
|
1179
1427
|
if (val === void 0) {
|
|
1180
|
-
console.log(
|
|
1428
|
+
console.log(pc4.dim(`${args.key}: (not set)`));
|
|
1181
1429
|
} else {
|
|
1182
|
-
console.log(`${
|
|
1430
|
+
console.log(`${pc4.bold(args.key)}: ${pc4.cyan(String(val))}`);
|
|
1183
1431
|
}
|
|
1184
1432
|
}
|
|
1185
1433
|
});
|
|
1186
|
-
var listCommand =
|
|
1434
|
+
var listCommand = defineCommand4({
|
|
1187
1435
|
meta: { name: "list", description: "List all config values" },
|
|
1188
1436
|
run() {
|
|
1189
1437
|
const data = readConfig();
|
|
1190
1438
|
const entries = Object.entries(data);
|
|
1191
1439
|
if (entries.length === 0) {
|
|
1192
|
-
console.log(
|
|
1440
|
+
console.log(pc4.dim("No config values set. Use `mcpman config set <key> <value>`."));
|
|
1193
1441
|
return;
|
|
1194
1442
|
}
|
|
1195
1443
|
console.log("");
|
|
1196
|
-
console.log(
|
|
1444
|
+
console.log(pc4.bold("mcpman config:"));
|
|
1197
1445
|
console.log("");
|
|
1198
1446
|
for (const [key, val] of entries) {
|
|
1199
|
-
console.log(` ${
|
|
1447
|
+
console.log(` ${pc4.green("\u25CF")} ${pc4.bold(key)} ${pc4.cyan(String(val))}`);
|
|
1200
1448
|
}
|
|
1201
1449
|
console.log("");
|
|
1202
|
-
console.log(
|
|
1450
|
+
console.log(pc4.dim(` ${entries.length} key${entries.length !== 1 ? "s" : ""} configured`));
|
|
1203
1451
|
}
|
|
1204
1452
|
});
|
|
1205
|
-
var resetCommand =
|
|
1453
|
+
var resetCommand = defineCommand4({
|
|
1206
1454
|
meta: { name: "reset", description: "Reset config to defaults (removes config file)" },
|
|
1207
1455
|
async run() {
|
|
1208
1456
|
const confirmed = await p2.confirm({
|
|
@@ -1214,10 +1462,10 @@ var resetCommand = defineCommand3({
|
|
|
1214
1462
|
return;
|
|
1215
1463
|
}
|
|
1216
1464
|
writeConfig({});
|
|
1217
|
-
console.log(`${
|
|
1465
|
+
console.log(`${pc4.green("\u2713")} Config reset to defaults.`);
|
|
1218
1466
|
}
|
|
1219
1467
|
});
|
|
1220
|
-
var config_default =
|
|
1468
|
+
var config_default = defineCommand4({
|
|
1221
1469
|
meta: {
|
|
1222
1470
|
name: "config",
|
|
1223
1471
|
description: "Manage mcpman CLI configuration"
|
|
@@ -1232,11 +1480,11 @@ var config_default = defineCommand3({
|
|
|
1232
1480
|
|
|
1233
1481
|
// src/commands/create.ts
|
|
1234
1482
|
import path6 from "path";
|
|
1235
|
-
import { defineCommand as
|
|
1236
|
-
import
|
|
1483
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
1484
|
+
import pc5 from "picocolors";
|
|
1237
1485
|
|
|
1238
1486
|
// src/core/scaffold-service.ts
|
|
1239
|
-
import
|
|
1487
|
+
import fs6 from "fs";
|
|
1240
1488
|
import path5 from "path";
|
|
1241
1489
|
function sanitizeName(name) {
|
|
1242
1490
|
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -1389,8 +1637,8 @@ if __name__ == "__main__":
|
|
|
1389
1637
|
};
|
|
1390
1638
|
}
|
|
1391
1639
|
function writeScaffold(dir, files) {
|
|
1392
|
-
if (
|
|
1393
|
-
const existing =
|
|
1640
|
+
if (fs6.existsSync(dir)) {
|
|
1641
|
+
const existing = fs6.readdirSync(dir);
|
|
1394
1642
|
if (existing.length > 0) {
|
|
1395
1643
|
throw new Error(`Directory '${dir}' already exists and is not empty.`);
|
|
1396
1644
|
}
|
|
@@ -1398,13 +1646,13 @@ function writeScaffold(dir, files) {
|
|
|
1398
1646
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
1399
1647
|
const fullPath = path5.join(dir, relativePath);
|
|
1400
1648
|
const parentDir = path5.dirname(fullPath);
|
|
1401
|
-
|
|
1402
|
-
|
|
1649
|
+
fs6.mkdirSync(parentDir, { recursive: true });
|
|
1650
|
+
fs6.writeFileSync(fullPath, content, "utf-8");
|
|
1403
1651
|
}
|
|
1404
1652
|
}
|
|
1405
1653
|
|
|
1406
1654
|
// src/commands/create.ts
|
|
1407
|
-
var create_default =
|
|
1655
|
+
var create_default = defineCommand5({
|
|
1408
1656
|
meta: {
|
|
1409
1657
|
name: "create",
|
|
1410
1658
|
description: "Scaffold a new MCP server project"
|
|
@@ -1442,7 +1690,7 @@ var create_default = defineCommand4({
|
|
|
1442
1690
|
const readline = await import("readline");
|
|
1443
1691
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1444
1692
|
projectName = await new Promise((resolve) => {
|
|
1445
|
-
rl.question(
|
|
1693
|
+
rl.question(pc5.cyan(" Project name: "), (answer) => {
|
|
1446
1694
|
rl.close();
|
|
1447
1695
|
resolve(answer.trim());
|
|
1448
1696
|
});
|
|
@@ -1452,7 +1700,7 @@ var create_default = defineCommand4({
|
|
|
1452
1700
|
const readline = await import("readline");
|
|
1453
1701
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1454
1702
|
projectDescription = await new Promise((resolve) => {
|
|
1455
|
-
rl.question(
|
|
1703
|
+
rl.question(pc5.cyan(" Description (optional): "), (answer) => {
|
|
1456
1704
|
rl.close();
|
|
1457
1705
|
resolve(answer.trim());
|
|
1458
1706
|
});
|
|
@@ -1462,7 +1710,7 @@ var create_default = defineCommand4({
|
|
|
1462
1710
|
const readline = await import("readline");
|
|
1463
1711
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1464
1712
|
const answer = await new Promise((resolve) => {
|
|
1465
|
-
rl.question(
|
|
1713
|
+
rl.question(pc5.cyan(" Runtime [node/python] (default: node): "), (a) => {
|
|
1466
1714
|
rl.close();
|
|
1467
1715
|
resolve(a.trim() || "node");
|
|
1468
1716
|
});
|
|
@@ -1471,16 +1719,16 @@ var create_default = defineCommand4({
|
|
|
1471
1719
|
}
|
|
1472
1720
|
}
|
|
1473
1721
|
if (!projectName) {
|
|
1474
|
-
console.error(
|
|
1722
|
+
console.error(pc5.red(" Error: Project name is required."));
|
|
1475
1723
|
process.exit(1);
|
|
1476
1724
|
}
|
|
1477
1725
|
const sanitized = sanitizeName(projectName);
|
|
1478
1726
|
if (!sanitized) {
|
|
1479
|
-
console.error(
|
|
1727
|
+
console.error(pc5.red(` Error: Invalid project name '${projectName}'.`));
|
|
1480
1728
|
process.exit(1);
|
|
1481
1729
|
}
|
|
1482
1730
|
if (runtime !== "node" && runtime !== "python") {
|
|
1483
|
-
console.error(
|
|
1731
|
+
console.error(pc5.red(` Error: Unknown runtime '${runtime}'. Use node or python.`));
|
|
1484
1732
|
process.exit(1);
|
|
1485
1733
|
}
|
|
1486
1734
|
const options = {
|
|
@@ -1494,44 +1742,206 @@ var create_default = defineCommand4({
|
|
|
1494
1742
|
try {
|
|
1495
1743
|
writeScaffold(targetDir, files);
|
|
1496
1744
|
} catch (err) {
|
|
1497
|
-
console.error(
|
|
1745
|
+
console.error(pc5.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
1498
1746
|
process.exit(1);
|
|
1499
1747
|
}
|
|
1500
|
-
console.log(
|
|
1501
|
-
Created ${
|
|
1748
|
+
console.log(pc5.green(`
|
|
1749
|
+
Created ${pc5.bold(sanitized)}/
|
|
1502
1750
|
`));
|
|
1503
|
-
console.log(
|
|
1751
|
+
console.log(pc5.dim(" Files generated:"));
|
|
1504
1752
|
for (const file of Object.keys(files)) {
|
|
1505
|
-
console.log(` ${
|
|
1753
|
+
console.log(` ${pc5.cyan(file)}`);
|
|
1506
1754
|
}
|
|
1507
1755
|
console.log("\n Next steps:");
|
|
1508
1756
|
if (runtime === "node") {
|
|
1509
|
-
console.log(` ${
|
|
1510
|
-
console.log(` ${
|
|
1511
|
-
console.log(` ${
|
|
1757
|
+
console.log(` ${pc5.bold(`cd ${sanitized}`)}`);
|
|
1758
|
+
console.log(` ${pc5.bold("npm install")}`);
|
|
1759
|
+
console.log(` ${pc5.bold("mcpman link .")}`);
|
|
1512
1760
|
} else {
|
|
1513
|
-
console.log(` ${
|
|
1514
|
-
console.log(` ${
|
|
1515
|
-
console.log(` ${
|
|
1761
|
+
console.log(` ${pc5.bold(`cd ${sanitized}`)}`);
|
|
1762
|
+
console.log(` ${pc5.bold("pip install -e .")}`);
|
|
1763
|
+
console.log(` ${pc5.bold("mcpman link .")}`);
|
|
1516
1764
|
}
|
|
1517
1765
|
console.log();
|
|
1518
1766
|
}
|
|
1519
1767
|
});
|
|
1520
1768
|
|
|
1769
|
+
// src/commands/diff.ts
|
|
1770
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
1771
|
+
import pc6 from "picocolors";
|
|
1772
|
+
|
|
1773
|
+
// src/core/config-differ.ts
|
|
1774
|
+
function entryDiffs(a, b) {
|
|
1775
|
+
const diffs = [];
|
|
1776
|
+
if (a.command !== b.command) {
|
|
1777
|
+
diffs.push(`command: ${a.command} \u2192 ${b.command}`);
|
|
1778
|
+
}
|
|
1779
|
+
const aArgs = JSON.stringify(a.args ?? []);
|
|
1780
|
+
const bArgs = JSON.stringify(b.args ?? []);
|
|
1781
|
+
if (aArgs !== bArgs) {
|
|
1782
|
+
diffs.push(`args: ${aArgs} \u2192 ${bArgs}`);
|
|
1783
|
+
}
|
|
1784
|
+
const aEnv = JSON.stringify(a.env ?? {});
|
|
1785
|
+
const bEnv = JSON.stringify(b.env ?? {});
|
|
1786
|
+
if (aEnv !== bEnv) {
|
|
1787
|
+
diffs.push(`env: ${aEnv} \u2192 ${bEnv}`);
|
|
1788
|
+
}
|
|
1789
|
+
return diffs;
|
|
1790
|
+
}
|
|
1791
|
+
function diffClientConfigs(configA, configB) {
|
|
1792
|
+
const results = [];
|
|
1793
|
+
const serversA = configA.servers;
|
|
1794
|
+
const serversB = configB.servers;
|
|
1795
|
+
for (const name of Object.keys(serversB)) {
|
|
1796
|
+
if (!(name in serversA)) {
|
|
1797
|
+
results.push({ server: name, change: "added" });
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
for (const name of Object.keys(serversA)) {
|
|
1801
|
+
if (!(name in serversB)) {
|
|
1802
|
+
results.push({ server: name, change: "removed" });
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
for (const name of Object.keys(serversA)) {
|
|
1806
|
+
if (name in serversB) {
|
|
1807
|
+
const details = entryDiffs(serversA[name], serversB[name]);
|
|
1808
|
+
if (details.length > 0) {
|
|
1809
|
+
results.push({ server: name, change: "changed", details });
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
const order = { removed: 0, added: 1, changed: 2 };
|
|
1814
|
+
results.sort((a, b) => {
|
|
1815
|
+
const orderDiff = order[a.change] - order[b.change];
|
|
1816
|
+
return orderDiff !== 0 ? orderDiff : a.server.localeCompare(b.server);
|
|
1817
|
+
});
|
|
1818
|
+
return results;
|
|
1819
|
+
}
|
|
1820
|
+
async function loadClientConfig(type) {
|
|
1821
|
+
try {
|
|
1822
|
+
const { getClient } = await import("./client-detector-NHXBDNMY.js");
|
|
1823
|
+
const handler = getClient(type);
|
|
1824
|
+
return await handler.readConfig();
|
|
1825
|
+
} catch {
|
|
1826
|
+
return null;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
// src/commands/diff.ts
|
|
1831
|
+
var VALID_CLIENTS = ["claude-desktop", "cursor", "vscode", "windsurf"];
|
|
1832
|
+
var CLIENT_DISPLAY = {
|
|
1833
|
+
"claude-desktop": "Claude Desktop",
|
|
1834
|
+
cursor: "Cursor",
|
|
1835
|
+
vscode: "VS Code",
|
|
1836
|
+
windsurf: "Windsurf"
|
|
1837
|
+
};
|
|
1838
|
+
var diff_default = defineCommand6({
|
|
1839
|
+
meta: {
|
|
1840
|
+
name: "diff",
|
|
1841
|
+
description: "Show config diff between two AI clients"
|
|
1842
|
+
},
|
|
1843
|
+
args: {
|
|
1844
|
+
clientA: {
|
|
1845
|
+
type: "positional",
|
|
1846
|
+
description: `Source client (${VALID_CLIENTS.join("|")})`,
|
|
1847
|
+
required: true
|
|
1848
|
+
},
|
|
1849
|
+
clientB: {
|
|
1850
|
+
type: "positional",
|
|
1851
|
+
description: `Target client (${VALID_CLIENTS.join("|")})`,
|
|
1852
|
+
required: true
|
|
1853
|
+
},
|
|
1854
|
+
json: {
|
|
1855
|
+
type: "boolean",
|
|
1856
|
+
description: "Output results as JSON",
|
|
1857
|
+
default: false
|
|
1858
|
+
}
|
|
1859
|
+
},
|
|
1860
|
+
async run({ args }) {
|
|
1861
|
+
const clientA = args.clientA;
|
|
1862
|
+
const clientB = args.clientB;
|
|
1863
|
+
if (!VALID_CLIENTS.includes(clientA)) {
|
|
1864
|
+
console.error(
|
|
1865
|
+
`${pc6.red("\u2717")} Unknown client "${clientA}". Valid: ${VALID_CLIENTS.join(", ")}`
|
|
1866
|
+
);
|
|
1867
|
+
process.exit(1);
|
|
1868
|
+
}
|
|
1869
|
+
if (!VALID_CLIENTS.includes(clientB)) {
|
|
1870
|
+
console.error(
|
|
1871
|
+
`${pc6.red("\u2717")} Unknown client "${clientB}". Valid: ${VALID_CLIENTS.join(", ")}`
|
|
1872
|
+
);
|
|
1873
|
+
process.exit(1);
|
|
1874
|
+
}
|
|
1875
|
+
if (clientA === clientB) {
|
|
1876
|
+
console.error(`${pc6.red("\u2717")} clientA and clientB must be different.`);
|
|
1877
|
+
process.exit(1);
|
|
1878
|
+
}
|
|
1879
|
+
const [configA, configB] = await Promise.all([
|
|
1880
|
+
loadClientConfig(clientA),
|
|
1881
|
+
loadClientConfig(clientB)
|
|
1882
|
+
]);
|
|
1883
|
+
if (!configA) {
|
|
1884
|
+
console.error(`${pc6.red("\u2717")} Could not read config for ${CLIENT_DISPLAY[clientA]}.`);
|
|
1885
|
+
process.exit(1);
|
|
1886
|
+
}
|
|
1887
|
+
if (!configB) {
|
|
1888
|
+
console.error(`${pc6.red("\u2717")} Could not read config for ${CLIENT_DISPLAY[clientB]}.`);
|
|
1889
|
+
process.exit(1);
|
|
1890
|
+
}
|
|
1891
|
+
const diffs = diffClientConfigs(configA, configB);
|
|
1892
|
+
if (args.json) {
|
|
1893
|
+
console.log(JSON.stringify({ clientA, clientB, diffs }, null, 2));
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
const labelA = CLIENT_DISPLAY[clientA];
|
|
1897
|
+
const labelB = CLIENT_DISPLAY[clientB];
|
|
1898
|
+
console.log(`
|
|
1899
|
+
${pc6.bold("mcpman diff")} ${pc6.cyan(labelA)} \u2192 ${pc6.cyan(labelB)}
|
|
1900
|
+
`);
|
|
1901
|
+
if (diffs.length === 0) {
|
|
1902
|
+
console.log(pc6.green(" \u2713 No differences \u2014 configs are identical."));
|
|
1903
|
+
console.log("");
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
for (const d of diffs) {
|
|
1907
|
+
if (d.change === "added") {
|
|
1908
|
+
console.log(` ${pc6.green("+")} ${pc6.bold(d.server)} ${pc6.dim(`(only in ${labelB})`)}`);
|
|
1909
|
+
} else if (d.change === "removed") {
|
|
1910
|
+
console.log(` ${pc6.red("-")} ${pc6.bold(d.server)} ${pc6.dim(`(only in ${labelA})`)}`);
|
|
1911
|
+
} else {
|
|
1912
|
+
console.log(` ${pc6.yellow("~")} ${pc6.bold(d.server)} ${pc6.dim("(changed)")}`);
|
|
1913
|
+
for (const detail of d.details ?? []) {
|
|
1914
|
+
console.log(` ${pc6.dim(detail)}`);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
const added = diffs.filter((d) => d.change === "added").length;
|
|
1919
|
+
const removed = diffs.filter((d) => d.change === "removed").length;
|
|
1920
|
+
const changed = diffs.filter((d) => d.change === "changed").length;
|
|
1921
|
+
const parts = [];
|
|
1922
|
+
if (added > 0) parts.push(pc6.green(`+${added} added`));
|
|
1923
|
+
if (removed > 0) parts.push(pc6.red(`-${removed} removed`));
|
|
1924
|
+
if (changed > 0) parts.push(pc6.yellow(`~${changed} changed`));
|
|
1925
|
+
console.log(`
|
|
1926
|
+
${parts.join(" ")}
|
|
1927
|
+
`);
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
|
|
1521
1931
|
// src/commands/doctor.ts
|
|
1522
|
-
import { defineCommand as
|
|
1523
|
-
import
|
|
1932
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
1933
|
+
import pc7 from "picocolors";
|
|
1524
1934
|
|
|
1525
1935
|
// src/core/diagnostics.ts
|
|
1526
1936
|
import { exec } from "child_process";
|
|
1527
1937
|
import { promisify } from "util";
|
|
1528
1938
|
|
|
1529
1939
|
// src/core/mcp-process-checks.ts
|
|
1530
|
-
import { spawn } from "child_process";
|
|
1940
|
+
import { spawn as spawn2 } from "child_process";
|
|
1531
1941
|
async function checkProcessSpawn(command, args, env, timeoutMs = 3e3) {
|
|
1532
1942
|
return new Promise((resolve) => {
|
|
1533
1943
|
let settled = false;
|
|
1534
|
-
const child =
|
|
1944
|
+
const child = spawn2(command, args, {
|
|
1535
1945
|
env: { ...process.env, ...env },
|
|
1536
1946
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1537
1947
|
});
|
|
@@ -1577,7 +1987,7 @@ async function checkMcpHandshake(command, args, env, timeoutMs = 5e3) {
|
|
|
1577
1987
|
let settled = false;
|
|
1578
1988
|
const start = Date.now();
|
|
1579
1989
|
let stdout = "";
|
|
1580
|
-
const child =
|
|
1990
|
+
const child = spawn2(command, args, {
|
|
1581
1991
|
env: { ...process.env, ...env },
|
|
1582
1992
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1583
1993
|
});
|
|
@@ -1865,12 +2275,12 @@ async function getInstalledServers(clientFilter) {
|
|
|
1865
2275
|
|
|
1866
2276
|
// src/commands/doctor.ts
|
|
1867
2277
|
var CHECK_ICON = {
|
|
1868
|
-
pass:
|
|
1869
|
-
fail:
|
|
1870
|
-
skip:
|
|
1871
|
-
warn:
|
|
2278
|
+
pass: pc7.green("\u2713"),
|
|
2279
|
+
fail: pc7.red("\u2717"),
|
|
2280
|
+
skip: pc7.dim("-"),
|
|
2281
|
+
warn: pc7.yellow("\u26A0")
|
|
1872
2282
|
};
|
|
1873
|
-
var doctor_default =
|
|
2283
|
+
var doctor_default = defineCommand7({
|
|
1874
2284
|
meta: {
|
|
1875
2285
|
name: "doctor",
|
|
1876
2286
|
description: "Check MCP server health and configuration"
|
|
@@ -1883,11 +2293,11 @@ var doctor_default = defineCommand5({
|
|
|
1883
2293
|
}
|
|
1884
2294
|
},
|
|
1885
2295
|
async run({ args }) {
|
|
1886
|
-
console.log(
|
|
2296
|
+
console.log(pc7.bold("\n mcpman doctor\n"));
|
|
1887
2297
|
const servers = await getInstalledServers();
|
|
1888
2298
|
if (servers.length === 0) {
|
|
1889
2299
|
console.log(
|
|
1890
|
-
|
|
2300
|
+
pc7.dim(" No MCP servers installed. Run mcpman install <server> to get started.")
|
|
1891
2301
|
);
|
|
1892
2302
|
return;
|
|
1893
2303
|
}
|
|
@@ -1904,20 +2314,20 @@ var doctor_default = defineCommand5({
|
|
|
1904
2314
|
if (pluginSummary.total > 0) {
|
|
1905
2315
|
printPluginSection(pluginSummary);
|
|
1906
2316
|
}
|
|
1907
|
-
console.log(
|
|
2317
|
+
console.log(pc7.dim(` ${"\u2500".repeat(50)}`));
|
|
1908
2318
|
const parts = [];
|
|
1909
|
-
if (passed > 0) parts.push(
|
|
1910
|
-
if (failed > 0) parts.push(
|
|
2319
|
+
if (passed > 0) parts.push(pc7.green(`${passed} healthy`));
|
|
2320
|
+
if (failed > 0) parts.push(pc7.red(`${failed} unhealthy`));
|
|
1911
2321
|
console.log(` Summary: ${parts.join(", ")}`);
|
|
1912
2322
|
if (pluginSummary.total > 0) {
|
|
1913
2323
|
const pParts = [];
|
|
1914
|
-
if (pluginSummary.healthy > 0) pParts.push(
|
|
1915
|
-
if (pluginSummary.unhealthy > 0) pParts.push(
|
|
2324
|
+
if (pluginSummary.healthy > 0) pParts.push(pc7.green(`${pluginSummary.healthy} ok`));
|
|
2325
|
+
if (pluginSummary.unhealthy > 0) pParts.push(pc7.red(`${pluginSummary.unhealthy} broken`));
|
|
1916
2326
|
console.log(` Plugins: ${pParts.join(", ")}`);
|
|
1917
2327
|
}
|
|
1918
2328
|
if (failed > 0 || pluginSummary.unhealthy > 0) {
|
|
1919
2329
|
if (!args.fix) {
|
|
1920
|
-
console.log(
|
|
2330
|
+
console.log(pc7.dim(` Run ${pc7.cyan("mcpman doctor --fix")} for fix suggestions.
|
|
1921
2331
|
`));
|
|
1922
2332
|
}
|
|
1923
2333
|
process.exit(1);
|
|
@@ -1926,26 +2336,26 @@ var doctor_default = defineCommand5({
|
|
|
1926
2336
|
}
|
|
1927
2337
|
});
|
|
1928
2338
|
function printPluginSection(summary) {
|
|
1929
|
-
console.log(
|
|
2339
|
+
console.log(pc7.bold(` Plugins (${summary.total})`));
|
|
1930
2340
|
for (const r of summary.results) {
|
|
1931
|
-
const icon = r.loadable && r.prefixUnique && r.resolvable ?
|
|
2341
|
+
const icon = r.loadable && r.prefixUnique && r.resolvable ? pc7.green("\u2713") : pc7.red("\u2717");
|
|
1932
2342
|
const label = r.pluginName ? `${r.packageName} (${r.pluginName})` : r.packageName;
|
|
1933
|
-
const prefix = r.prefix ?
|
|
2343
|
+
const prefix = r.prefix ? pc7.dim(` [${r.prefix}]`) : "";
|
|
1934
2344
|
console.log(` ${icon} ${label}${prefix}`);
|
|
1935
2345
|
if (r.error) {
|
|
1936
|
-
console.log(` ${
|
|
2346
|
+
console.log(` ${pc7.yellow("\u2192")} ${r.error}`);
|
|
1937
2347
|
}
|
|
1938
2348
|
}
|
|
1939
2349
|
console.log();
|
|
1940
2350
|
}
|
|
1941
2351
|
function printServerResult(result, showFix) {
|
|
1942
|
-
const icon = result.status === "healthy" ?
|
|
1943
|
-
console.log(` ${icon} ${
|
|
2352
|
+
const icon = result.status === "healthy" ? pc7.green("\u25CF") : pc7.red("\u25CF");
|
|
2353
|
+
console.log(` ${icon} ${pc7.bold(result.serverName)}`);
|
|
1944
2354
|
for (const check of result.checks) {
|
|
1945
2355
|
const checkIcon = check.skipped ? CHECK_ICON.skip : check.passed ? CHECK_ICON.pass : CHECK_ICON.fail;
|
|
1946
2356
|
console.log(` ${checkIcon} ${check.name}: ${check.message}`);
|
|
1947
2357
|
if (showFix && !check.passed && !check.skipped && check.fix) {
|
|
1948
|
-
console.log(` ${
|
|
2358
|
+
console.log(` ${pc7.yellow("\u2192")} Fix: ${pc7.cyan(check.fix)}`);
|
|
1949
2359
|
}
|
|
1950
2360
|
}
|
|
1951
2361
|
console.log();
|
|
@@ -1954,11 +2364,11 @@ async function runParallel(tasks, concurrency) {
|
|
|
1954
2364
|
const results = [];
|
|
1955
2365
|
const executing = /* @__PURE__ */ new Set();
|
|
1956
2366
|
for (const task of tasks) {
|
|
1957
|
-
const
|
|
2367
|
+
const p13 = task().then((r) => {
|
|
1958
2368
|
results.push(r);
|
|
1959
|
-
executing.delete(
|
|
2369
|
+
executing.delete(p13);
|
|
1960
2370
|
});
|
|
1961
|
-
executing.add(
|
|
2371
|
+
executing.add(p13);
|
|
1962
2372
|
if (executing.size >= concurrency) {
|
|
1963
2373
|
await Promise.race(executing);
|
|
1964
2374
|
}
|
|
@@ -1967,18 +2377,188 @@ async function runParallel(tasks, concurrency) {
|
|
|
1967
2377
|
return results;
|
|
1968
2378
|
}
|
|
1969
2379
|
|
|
1970
|
-
// src/commands/
|
|
2380
|
+
// src/commands/env.ts
|
|
2381
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
2382
|
+
import pc8 from "picocolors";
|
|
2383
|
+
|
|
2384
|
+
// src/core/env-manager.ts
|
|
1971
2385
|
import fs7 from "fs";
|
|
1972
2386
|
import path7 from "path";
|
|
1973
|
-
|
|
1974
|
-
|
|
2387
|
+
function envFilePath(server, dir) {
|
|
2388
|
+
const base = dir ?? getEnvDir();
|
|
2389
|
+
const safe = server.replace(/[/@]/g, "_");
|
|
2390
|
+
return path7.join(base, `${safe}.json`);
|
|
2391
|
+
}
|
|
2392
|
+
function readStore(server, dir) {
|
|
2393
|
+
const file = envFilePath(server, dir);
|
|
2394
|
+
if (!fs7.existsSync(file)) return {};
|
|
2395
|
+
try {
|
|
2396
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
2397
|
+
} catch {
|
|
2398
|
+
return {};
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
function writeStore(server, store, dir) {
|
|
2402
|
+
const base = dir ?? getEnvDir();
|
|
2403
|
+
if (!fs7.existsSync(base)) {
|
|
2404
|
+
fs7.mkdirSync(base, { recursive: true });
|
|
2405
|
+
}
|
|
2406
|
+
const file = envFilePath(server, dir);
|
|
2407
|
+
fs7.writeFileSync(file, JSON.stringify(store, null, 2), "utf-8");
|
|
2408
|
+
}
|
|
2409
|
+
function setEnv(server, key, value, dir) {
|
|
2410
|
+
const store = readStore(server, dir);
|
|
2411
|
+
store[key] = value;
|
|
2412
|
+
writeStore(server, store, dir);
|
|
2413
|
+
}
|
|
2414
|
+
function getEnv(server, key, dir) {
|
|
2415
|
+
const store = readStore(server, dir);
|
|
2416
|
+
return key in store ? store[key] : null;
|
|
2417
|
+
}
|
|
2418
|
+
function listEnv(server, dir) {
|
|
2419
|
+
return readStore(server, dir);
|
|
2420
|
+
}
|
|
2421
|
+
function deleteEnv(server, key, dir) {
|
|
2422
|
+
const store = readStore(server, dir);
|
|
2423
|
+
if (!(key in store)) return;
|
|
2424
|
+
delete store[key];
|
|
2425
|
+
writeStore(server, store, dir);
|
|
2426
|
+
}
|
|
2427
|
+
function clearEnv(server, dir) {
|
|
2428
|
+
const file = envFilePath(server, dir);
|
|
2429
|
+
if (fs7.existsSync(file)) {
|
|
2430
|
+
fs7.unlinkSync(file);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
function listEnvServers(dir) {
|
|
2434
|
+
const base = dir ?? getEnvDir();
|
|
2435
|
+
if (!fs7.existsSync(base)) return [];
|
|
2436
|
+
return fs7.readdirSync(base).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5));
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
// src/commands/env.ts
|
|
2440
|
+
var setCmd = defineCommand8({
|
|
2441
|
+
meta: { name: "set", description: "Set env var(s) for a server" },
|
|
2442
|
+
args: {
|
|
2443
|
+
server: { type: "positional", description: "Server name", required: true },
|
|
2444
|
+
pairs: { type: "positional", description: "KEY=VALUE pair(s)", required: true }
|
|
2445
|
+
},
|
|
2446
|
+
run({ args }) {
|
|
2447
|
+
const pairs = Array.isArray(args.pairs) ? args.pairs : [args.pairs];
|
|
2448
|
+
for (const pair of pairs) {
|
|
2449
|
+
const idx = pair.indexOf("=");
|
|
2450
|
+
if (idx <= 0) {
|
|
2451
|
+
console.error(`${pc8.red("\u2717")} Invalid format: "${pair}". Expected KEY=VALUE`);
|
|
2452
|
+
process.exit(1);
|
|
2453
|
+
}
|
|
2454
|
+
const key = pair.slice(0, idx);
|
|
2455
|
+
const value = pair.slice(idx + 1);
|
|
2456
|
+
setEnv(args.server, key, value);
|
|
2457
|
+
console.log(
|
|
2458
|
+
`${pc8.green("\u2713")} Set ${pc8.bold(key)}=${pc8.dim(value)} for ${pc8.cyan(args.server)}`
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
});
|
|
2463
|
+
var getCmd = defineCommand8({
|
|
2464
|
+
meta: { name: "get", description: "Get an env var for a server" },
|
|
2465
|
+
args: {
|
|
2466
|
+
server: { type: "positional", description: "Server name", required: true },
|
|
2467
|
+
key: { type: "positional", description: "Variable name", required: true }
|
|
2468
|
+
},
|
|
2469
|
+
run({ args }) {
|
|
2470
|
+
const value = getEnv(args.server, args.key);
|
|
2471
|
+
if (value === null) {
|
|
2472
|
+
console.error(`${pc8.red("\u2717")} No env var "${args.key}" for ${pc8.cyan(args.server)}`);
|
|
2473
|
+
process.exit(1);
|
|
2474
|
+
}
|
|
2475
|
+
console.log(value);
|
|
2476
|
+
}
|
|
2477
|
+
});
|
|
2478
|
+
var listCmd = defineCommand8({
|
|
2479
|
+
meta: { name: "list", description: "List env vars for a server (or all servers)" },
|
|
2480
|
+
args: {
|
|
2481
|
+
server: { type: "positional", description: "Server name (optional)", required: false }
|
|
2482
|
+
},
|
|
2483
|
+
run({ args }) {
|
|
2484
|
+
if (args.server) {
|
|
2485
|
+
const store = listEnv(args.server);
|
|
2486
|
+
const keys = Object.keys(store);
|
|
2487
|
+
if (keys.length === 0) {
|
|
2488
|
+
console.log(pc8.dim(`No env vars for ${pc8.cyan(args.server)}.`));
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
console.log(`
|
|
2492
|
+
${pc8.bold(pc8.cyan(args.server))}`);
|
|
2493
|
+
for (const [k, v] of Object.entries(store)) {
|
|
2494
|
+
console.log(` ${pc8.green("\u25CF")} ${pc8.bold(k)}=${pc8.dim(v)}`);
|
|
2495
|
+
}
|
|
2496
|
+
console.log("");
|
|
2497
|
+
} else {
|
|
2498
|
+
const servers = listEnvServers();
|
|
2499
|
+
if (servers.length === 0) {
|
|
2500
|
+
console.log(pc8.dim("No env vars stored."));
|
|
2501
|
+
return;
|
|
2502
|
+
}
|
|
2503
|
+
for (const srv of servers) {
|
|
2504
|
+
const store = listEnv(srv);
|
|
2505
|
+
console.log(`
|
|
2506
|
+
${pc8.bold(pc8.cyan(srv))}`);
|
|
2507
|
+
for (const [k, v] of Object.entries(store)) {
|
|
2508
|
+
console.log(` ${pc8.green("\u25CF")} ${pc8.bold(k)}=${pc8.dim(v)}`);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
console.log("");
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
});
|
|
2515
|
+
var delCmd = defineCommand8({
|
|
2516
|
+
meta: { name: "del", description: "Delete an env var for a server" },
|
|
2517
|
+
args: {
|
|
2518
|
+
server: { type: "positional", description: "Server name", required: true },
|
|
2519
|
+
key: { type: "positional", description: "Variable name to delete", required: true }
|
|
2520
|
+
},
|
|
2521
|
+
run({ args }) {
|
|
2522
|
+
deleteEnv(args.server, args.key);
|
|
2523
|
+
console.log(`${pc8.green("\u2713")} Deleted ${pc8.bold(args.key)} from ${pc8.cyan(args.server)}`);
|
|
2524
|
+
}
|
|
2525
|
+
});
|
|
2526
|
+
var clearCmd = defineCommand8({
|
|
2527
|
+
meta: { name: "clear", description: "Clear all env vars for a server" },
|
|
2528
|
+
args: {
|
|
2529
|
+
server: { type: "positional", description: "Server name", required: true }
|
|
2530
|
+
},
|
|
2531
|
+
run({ args }) {
|
|
2532
|
+
clearEnv(args.server);
|
|
2533
|
+
console.log(`${pc8.green("\u2713")} Cleared all env vars for ${pc8.cyan(args.server)}`);
|
|
2534
|
+
}
|
|
2535
|
+
});
|
|
2536
|
+
var env_default = defineCommand8({
|
|
2537
|
+
meta: {
|
|
2538
|
+
name: "env",
|
|
2539
|
+
description: "Manage per-server environment variables (non-sensitive)"
|
|
2540
|
+
},
|
|
2541
|
+
subCommands: {
|
|
2542
|
+
set: setCmd,
|
|
2543
|
+
get: getCmd,
|
|
2544
|
+
list: listCmd,
|
|
2545
|
+
del: delCmd,
|
|
2546
|
+
clear: clearCmd
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
|
|
2550
|
+
// src/commands/export-command.ts
|
|
2551
|
+
import fs9 from "fs";
|
|
2552
|
+
import path8 from "path";
|
|
2553
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
2554
|
+
import pc9 from "picocolors";
|
|
1975
2555
|
|
|
1976
2556
|
// src/core/export-import-service.ts
|
|
1977
|
-
import
|
|
2557
|
+
import fs8 from "fs";
|
|
1978
2558
|
|
|
1979
2559
|
// src/utils/constants.ts
|
|
1980
2560
|
var APP_NAME = "mcpman";
|
|
1981
|
-
var APP_VERSION = "0.
|
|
2561
|
+
var APP_VERSION = "0.8.0";
|
|
1982
2562
|
var APP_DESCRIPTION = "The package manager for MCP servers";
|
|
1983
2563
|
|
|
1984
2564
|
// src/core/export-import-service.ts
|
|
@@ -1994,7 +2574,7 @@ function createExportBundle(opts = {}) {
|
|
|
1994
2574
|
};
|
|
1995
2575
|
if (includeVault) {
|
|
1996
2576
|
const vaultPath = getVaultPath();
|
|
1997
|
-
if (
|
|
2577
|
+
if (fs8.existsSync(vaultPath)) {
|
|
1998
2578
|
bundle.vault = readVault();
|
|
1999
2579
|
}
|
|
2000
2580
|
}
|
|
@@ -2055,58 +2635,268 @@ function importBundle(bundle, opts = {}) {
|
|
|
2055
2635
|
}
|
|
2056
2636
|
}
|
|
2057
2637
|
}
|
|
2058
|
-
return summary;
|
|
2638
|
+
return summary;
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
// src/commands/export-command.ts
|
|
2642
|
+
var DEFAULT_OUTPUT = "mcpman-export.json";
|
|
2643
|
+
var export_command_default = defineCommand9({
|
|
2644
|
+
meta: {
|
|
2645
|
+
name: "export",
|
|
2646
|
+
description: "Export mcpman config, lockfile, vault, and plugins to a portable JSON file"
|
|
2647
|
+
},
|
|
2648
|
+
args: {
|
|
2649
|
+
output: {
|
|
2650
|
+
type: "positional",
|
|
2651
|
+
description: `Output file path (default: ${DEFAULT_OUTPUT})`,
|
|
2652
|
+
required: false
|
|
2653
|
+
},
|
|
2654
|
+
"no-vault": {
|
|
2655
|
+
type: "boolean",
|
|
2656
|
+
description: "Exclude encrypted vault from export",
|
|
2657
|
+
default: false
|
|
2658
|
+
},
|
|
2659
|
+
"no-plugins": {
|
|
2660
|
+
type: "boolean",
|
|
2661
|
+
description: "Exclude plugin list from export",
|
|
2662
|
+
default: false
|
|
2663
|
+
}
|
|
2664
|
+
},
|
|
2665
|
+
run({ args }) {
|
|
2666
|
+
const outputFile = args.output || DEFAULT_OUTPUT;
|
|
2667
|
+
const outputPath = path8.resolve(outputFile);
|
|
2668
|
+
const bundle = createExportBundle({
|
|
2669
|
+
includeVault: !args["no-vault"],
|
|
2670
|
+
includePlugins: !args["no-plugins"]
|
|
2671
|
+
});
|
|
2672
|
+
const serverCount = Object.keys(bundle.lockfile.servers).length;
|
|
2673
|
+
const configKeys = Object.keys(bundle.config).length;
|
|
2674
|
+
fs9.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
|
|
2675
|
+
console.log(`${pc9.green("\u2713")} Exported to ${pc9.bold(outputFile)}`);
|
|
2676
|
+
console.log(pc9.dim(` Config keys: ${configKeys}`));
|
|
2677
|
+
console.log(pc9.dim(` Servers: ${serverCount}`));
|
|
2678
|
+
console.log(pc9.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
|
|
2679
|
+
console.log(pc9.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
|
|
2680
|
+
}
|
|
2681
|
+
});
|
|
2682
|
+
|
|
2683
|
+
// src/commands/group.ts
|
|
2684
|
+
import { spawn as spawn3 } from "child_process";
|
|
2685
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
2686
|
+
import pc10 from "picocolors";
|
|
2687
|
+
|
|
2688
|
+
// src/core/group-manager.ts
|
|
2689
|
+
import fs10 from "fs";
|
|
2690
|
+
function readGroups(file) {
|
|
2691
|
+
const target = file ?? getGroupsFile();
|
|
2692
|
+
if (!fs10.existsSync(target)) return {};
|
|
2693
|
+
try {
|
|
2694
|
+
return JSON.parse(fs10.readFileSync(target, "utf-8"));
|
|
2695
|
+
} catch {
|
|
2696
|
+
return {};
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
function writeGroups(store, file) {
|
|
2700
|
+
const target = file ?? getGroupsFile();
|
|
2701
|
+
const dir = target.slice(0, target.lastIndexOf("/"));
|
|
2702
|
+
if (dir && !fs10.existsSync(dir)) {
|
|
2703
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
2704
|
+
}
|
|
2705
|
+
fs10.writeFileSync(target, JSON.stringify(store, null, 2), "utf-8");
|
|
2706
|
+
}
|
|
2707
|
+
function addToGroup(group, servers, file) {
|
|
2708
|
+
const store = readGroups(file);
|
|
2709
|
+
const existing = new Set(store[group] ?? []);
|
|
2710
|
+
for (const s of servers) existing.add(s);
|
|
2711
|
+
store[group] = [...existing].sort();
|
|
2712
|
+
writeGroups(store, file);
|
|
2713
|
+
}
|
|
2714
|
+
function removeFromGroup(group, servers, file) {
|
|
2715
|
+
const store = readGroups(file);
|
|
2716
|
+
if (!store[group]) return;
|
|
2717
|
+
const toRemove = new Set(servers);
|
|
2718
|
+
store[group] = store[group].filter((s) => !toRemove.has(s));
|
|
2719
|
+
if (store[group].length === 0) delete store[group];
|
|
2720
|
+
writeGroups(store, file);
|
|
2721
|
+
}
|
|
2722
|
+
function getGroup(group, file) {
|
|
2723
|
+
return readGroups(file)[group] ?? [];
|
|
2724
|
+
}
|
|
2725
|
+
function listGroups(file) {
|
|
2726
|
+
return Object.keys(readGroups(file)).sort();
|
|
2727
|
+
}
|
|
2728
|
+
function deleteGroup(group, file) {
|
|
2729
|
+
const store = readGroups(file);
|
|
2730
|
+
if (!(group in store)) return;
|
|
2731
|
+
delete store[group];
|
|
2732
|
+
writeGroups(store, file);
|
|
2733
|
+
}
|
|
2734
|
+
function groupExists(group, file) {
|
|
2735
|
+
return group in readGroups(file);
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
// src/commands/group.ts
|
|
2739
|
+
var addCmd = defineCommand10({
|
|
2740
|
+
meta: { name: "add", description: "Add servers to a group" },
|
|
2741
|
+
args: {
|
|
2742
|
+
name: { type: "positional", description: "Group name", required: true },
|
|
2743
|
+
servers: { type: "positional", description: "Server name(s)", required: true }
|
|
2744
|
+
},
|
|
2745
|
+
run({ args }) {
|
|
2746
|
+
const servers = Array.isArray(args.servers) ? args.servers : [args.servers];
|
|
2747
|
+
addToGroup(args.name, servers);
|
|
2748
|
+
console.log(`${pc10.green("\u2713")} Added ${servers.join(", ")} to group ${pc10.cyan(args.name)}`);
|
|
2749
|
+
}
|
|
2750
|
+
});
|
|
2751
|
+
var rmCmd = defineCommand10({
|
|
2752
|
+
meta: { name: "rm", description: "Remove servers from a group" },
|
|
2753
|
+
args: {
|
|
2754
|
+
name: { type: "positional", description: "Group name", required: true },
|
|
2755
|
+
servers: { type: "positional", description: "Server name(s)", required: true }
|
|
2756
|
+
},
|
|
2757
|
+
run({ args }) {
|
|
2758
|
+
const servers = Array.isArray(args.servers) ? args.servers : [args.servers];
|
|
2759
|
+
removeFromGroup(args.name, servers);
|
|
2760
|
+
console.log(`${pc10.green("\u2713")} Removed ${servers.join(", ")} from group ${pc10.cyan(args.name)}`);
|
|
2761
|
+
}
|
|
2762
|
+
});
|
|
2763
|
+
var listCmd2 = defineCommand10({
|
|
2764
|
+
meta: { name: "list", description: "List all groups (or members of a group)" },
|
|
2765
|
+
args: {
|
|
2766
|
+
name: { type: "positional", description: "Group name (optional)", required: false }
|
|
2767
|
+
},
|
|
2768
|
+
run({ args }) {
|
|
2769
|
+
if (args.name) {
|
|
2770
|
+
const members = getGroup(args.name);
|
|
2771
|
+
if (members.length === 0) {
|
|
2772
|
+
console.log(pc10.dim(`Group "${args.name}" is empty or does not exist.`));
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
console.log(`
|
|
2776
|
+
${pc10.bold(pc10.cyan(args.name))}`);
|
|
2777
|
+
for (const s of members) console.log(` ${pc10.green("\u25CF")} ${s}`);
|
|
2778
|
+
console.log("");
|
|
2779
|
+
} else {
|
|
2780
|
+
const groups = listGroups();
|
|
2781
|
+
if (groups.length === 0) {
|
|
2782
|
+
console.log(pc10.dim("No groups defined. Use `mcpman group add <name> <server>`."));
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
console.log("");
|
|
2786
|
+
for (const g of groups) {
|
|
2787
|
+
const members = getGroup(g);
|
|
2788
|
+
console.log(
|
|
2789
|
+
` ${pc10.cyan(pc10.bold(g))} ${pc10.dim(`(${members.length} server${members.length !== 1 ? "s" : ""})`)}`
|
|
2790
|
+
);
|
|
2791
|
+
for (const s of members) console.log(` ${pc10.dim("\xB7")} ${s}`);
|
|
2792
|
+
}
|
|
2793
|
+
console.log("");
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
});
|
|
2797
|
+
var deleteCmd = defineCommand10({
|
|
2798
|
+
meta: { name: "delete", description: "Delete an entire group" },
|
|
2799
|
+
args: {
|
|
2800
|
+
name: { type: "positional", description: "Group name", required: true }
|
|
2801
|
+
},
|
|
2802
|
+
run({ args }) {
|
|
2803
|
+
if (!groupExists(args.name)) {
|
|
2804
|
+
console.error(`${pc10.red("\u2717")} Group "${args.name}" does not exist.`);
|
|
2805
|
+
process.exit(1);
|
|
2806
|
+
}
|
|
2807
|
+
deleteGroup(args.name);
|
|
2808
|
+
console.log(`${pc10.green("\u2713")} Deleted group ${pc10.cyan(args.name)}`);
|
|
2809
|
+
}
|
|
2810
|
+
});
|
|
2811
|
+
var installCmd = defineCommand10({
|
|
2812
|
+
meta: { name: "install", description: "Install all servers in a group" },
|
|
2813
|
+
args: {
|
|
2814
|
+
name: { type: "positional", description: "Group name", required: true }
|
|
2815
|
+
},
|
|
2816
|
+
async run({ args }) {
|
|
2817
|
+
const members = getGroup(args.name);
|
|
2818
|
+
if (members.length === 0) {
|
|
2819
|
+
console.error(`${pc10.red("\u2717")} Group "${args.name}" is empty or does not exist.`);
|
|
2820
|
+
process.exit(1);
|
|
2821
|
+
}
|
|
2822
|
+
console.log(
|
|
2823
|
+
`${pc10.cyan("Installing")} group ${pc10.bold(args.name)} (${members.length} servers)...`
|
|
2824
|
+
);
|
|
2825
|
+
for (const server of members) {
|
|
2826
|
+
console.log(`
|
|
2827
|
+
${pc10.dim("\u2192")} Installing ${pc10.bold(server)}...`);
|
|
2828
|
+
await runInstall(server);
|
|
2829
|
+
}
|
|
2830
|
+
console.log(`
|
|
2831
|
+
${pc10.green("\u2713")} Group install complete.`);
|
|
2832
|
+
}
|
|
2833
|
+
});
|
|
2834
|
+
var runCmd = defineCommand10({
|
|
2835
|
+
meta: { name: "run", description: "Run all servers in a group concurrently" },
|
|
2836
|
+
args: {
|
|
2837
|
+
name: { type: "positional", description: "Group name", required: true }
|
|
2838
|
+
},
|
|
2839
|
+
run({ args }) {
|
|
2840
|
+
const members = getGroup(args.name);
|
|
2841
|
+
if (members.length === 0) {
|
|
2842
|
+
console.error(`${pc10.red("\u2717")} Group "${args.name}" is empty or does not exist.`);
|
|
2843
|
+
process.exit(1);
|
|
2844
|
+
}
|
|
2845
|
+
const lockfile = readLockfile();
|
|
2846
|
+
console.log(
|
|
2847
|
+
`${pc10.cyan("Spawning")} group ${pc10.bold(args.name)} (${members.length} servers)...
|
|
2848
|
+
`
|
|
2849
|
+
);
|
|
2850
|
+
for (const server of members) {
|
|
2851
|
+
const entry = lockfile.servers[server];
|
|
2852
|
+
if (!entry) {
|
|
2853
|
+
console.warn(` ${pc10.yellow("!")} ${server} not in lockfile \u2014 skipping`);
|
|
2854
|
+
continue;
|
|
2855
|
+
}
|
|
2856
|
+
const child = spawn3(entry.command, entry.args ?? [], {
|
|
2857
|
+
env: process.env,
|
|
2858
|
+
stdio: "inherit",
|
|
2859
|
+
detached: false
|
|
2860
|
+
});
|
|
2861
|
+
child.on("error", (err) => {
|
|
2862
|
+
console.error(` ${pc10.red("\u2717")} ${server}: ${err.message}`);
|
|
2863
|
+
});
|
|
2864
|
+
console.log(` ${pc10.green("\u2713")} Spawned ${pc10.bold(server)} (pid ${child.pid ?? "?"})`);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
});
|
|
2868
|
+
function runInstall(server) {
|
|
2869
|
+
return new Promise((resolve, reject) => {
|
|
2870
|
+
const child = spawn3("mcpman", ["install", server], { stdio: "inherit" });
|
|
2871
|
+
child.on("close", (code) => {
|
|
2872
|
+
if (code === 0) resolve();
|
|
2873
|
+
else reject(new Error(`install exited with code ${code}`));
|
|
2874
|
+
});
|
|
2875
|
+
child.on("error", reject);
|
|
2876
|
+
});
|
|
2059
2877
|
}
|
|
2060
|
-
|
|
2061
|
-
// src/commands/export-command.ts
|
|
2062
|
-
var DEFAULT_OUTPUT = "mcpman-export.json";
|
|
2063
|
-
var export_command_default = defineCommand6({
|
|
2878
|
+
var group_default = defineCommand10({
|
|
2064
2879
|
meta: {
|
|
2065
|
-
name: "
|
|
2066
|
-
description: "
|
|
2067
|
-
},
|
|
2068
|
-
args: {
|
|
2069
|
-
output: {
|
|
2070
|
-
type: "positional",
|
|
2071
|
-
description: `Output file path (default: ${DEFAULT_OUTPUT})`,
|
|
2072
|
-
required: false
|
|
2073
|
-
},
|
|
2074
|
-
"no-vault": {
|
|
2075
|
-
type: "boolean",
|
|
2076
|
-
description: "Exclude encrypted vault from export",
|
|
2077
|
-
default: false
|
|
2078
|
-
},
|
|
2079
|
-
"no-plugins": {
|
|
2080
|
-
type: "boolean",
|
|
2081
|
-
description: "Exclude plugin list from export",
|
|
2082
|
-
default: false
|
|
2083
|
-
}
|
|
2880
|
+
name: "group",
|
|
2881
|
+
description: "Manage named server groups"
|
|
2084
2882
|
},
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
const serverCount = Object.keys(bundle.lockfile.servers).length;
|
|
2093
|
-
const configKeys = Object.keys(bundle.config).length;
|
|
2094
|
-
fs7.writeFileSync(outputPath, JSON.stringify(bundle, null, 2), "utf-8");
|
|
2095
|
-
console.log(`${pc6.green("\u2713")} Exported to ${pc6.bold(outputFile)}`);
|
|
2096
|
-
console.log(pc6.dim(` Config keys: ${configKeys}`));
|
|
2097
|
-
console.log(pc6.dim(` Servers: ${serverCount}`));
|
|
2098
|
-
console.log(pc6.dim(` Vault: ${bundle.vault ? "included" : "excluded"}`));
|
|
2099
|
-
console.log(pc6.dim(` Plugins: ${bundle.plugins?.length ?? 0}`));
|
|
2883
|
+
subCommands: {
|
|
2884
|
+
add: addCmd,
|
|
2885
|
+
rm: rmCmd,
|
|
2886
|
+
list: listCmd2,
|
|
2887
|
+
delete: deleteCmd,
|
|
2888
|
+
install: installCmd,
|
|
2889
|
+
run: runCmd
|
|
2100
2890
|
}
|
|
2101
2891
|
});
|
|
2102
2892
|
|
|
2103
2893
|
// src/commands/import-command.ts
|
|
2104
|
-
import
|
|
2105
|
-
import
|
|
2894
|
+
import fs11 from "fs";
|
|
2895
|
+
import path9 from "path";
|
|
2106
2896
|
import * as p3 from "@clack/prompts";
|
|
2107
|
-
import { defineCommand as
|
|
2108
|
-
import
|
|
2109
|
-
var import_command_default =
|
|
2897
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
2898
|
+
import pc11 from "picocolors";
|
|
2899
|
+
var import_command_default = defineCommand11({
|
|
2110
2900
|
meta: {
|
|
2111
2901
|
name: "import",
|
|
2112
2902
|
description: "Import mcpman config, lockfile, vault, and plugins from an export file"
|
|
@@ -2129,21 +2919,21 @@ var import_command_default = defineCommand7({
|
|
|
2129
2919
|
}
|
|
2130
2920
|
},
|
|
2131
2921
|
async run({ args }) {
|
|
2132
|
-
const filePath =
|
|
2133
|
-
if (!
|
|
2134
|
-
console.error(`${
|
|
2922
|
+
const filePath = path9.resolve(args.file);
|
|
2923
|
+
if (!fs11.existsSync(filePath)) {
|
|
2924
|
+
console.error(`${pc11.red("\u2717")} File not found: ${filePath}`);
|
|
2135
2925
|
process.exit(1);
|
|
2136
2926
|
}
|
|
2137
2927
|
let raw;
|
|
2138
2928
|
try {
|
|
2139
|
-
raw = JSON.parse(
|
|
2929
|
+
raw = JSON.parse(fs11.readFileSync(filePath, "utf-8"));
|
|
2140
2930
|
} catch {
|
|
2141
|
-
console.error(`${
|
|
2931
|
+
console.error(`${pc11.red("\u2717")} Invalid JSON in ${filePath}`);
|
|
2142
2932
|
process.exit(1);
|
|
2143
2933
|
}
|
|
2144
2934
|
const error2 = validateBundle(raw);
|
|
2145
2935
|
if (error2) {
|
|
2146
|
-
console.error(`${
|
|
2936
|
+
console.error(`${pc11.red("\u2717")} Invalid export bundle: ${error2}`);
|
|
2147
2937
|
process.exit(1);
|
|
2148
2938
|
}
|
|
2149
2939
|
const bundle = raw;
|
|
@@ -2153,16 +2943,16 @@ var import_command_default = defineCommand7({
|
|
|
2153
2943
|
const hasVault = !!bundle.vault;
|
|
2154
2944
|
const isDryRun = !!args["dry-run"];
|
|
2155
2945
|
console.log("");
|
|
2156
|
-
console.log(
|
|
2157
|
-
console.log(
|
|
2158
|
-
console.log(
|
|
2159
|
-
console.log(` Config keys: ${
|
|
2160
|
-
console.log(` Servers: ${
|
|
2161
|
-
console.log(` Vault: ${hasVault ?
|
|
2162
|
-
console.log(` Plugins: ${
|
|
2946
|
+
console.log(pc11.bold("Import summary:"));
|
|
2947
|
+
console.log(pc11.dim(` Source version: mcpman ${bundle.mcpmanVersion}`));
|
|
2948
|
+
console.log(pc11.dim(` Exported at: ${bundle.exportedAt}`));
|
|
2949
|
+
console.log(` Config keys: ${pc11.cyan(String(configKeys))}`);
|
|
2950
|
+
console.log(` Servers: ${pc11.cyan(String(serverCount))}`);
|
|
2951
|
+
console.log(` Vault: ${hasVault ? pc11.green("included") : pc11.dim("not included")}`);
|
|
2952
|
+
console.log(` Plugins: ${pc11.cyan(String(pluginCount))}`);
|
|
2163
2953
|
console.log("");
|
|
2164
2954
|
if (isDryRun) {
|
|
2165
|
-
console.log(
|
|
2955
|
+
console.log(pc11.yellow(" [dry-run] No changes applied."));
|
|
2166
2956
|
return;
|
|
2167
2957
|
}
|
|
2168
2958
|
if (!args.yes) {
|
|
@@ -2176,18 +2966,18 @@ var import_command_default = defineCommand7({
|
|
|
2176
2966
|
}
|
|
2177
2967
|
}
|
|
2178
2968
|
const summary = importBundle(bundle, { dryRun: false });
|
|
2179
|
-
console.log(`${
|
|
2180
|
-
console.log(
|
|
2181
|
-
console.log(
|
|
2182
|
-
console.log(
|
|
2183
|
-
console.log(
|
|
2969
|
+
console.log(`${pc11.green("\u2713")} Import complete`);
|
|
2970
|
+
console.log(pc11.dim(` Config keys restored: ${summary.configKeys}`));
|
|
2971
|
+
console.log(pc11.dim(` Servers restored: ${summary.servers}`));
|
|
2972
|
+
console.log(pc11.dim(` Vault: ${summary.vaultImported ? "restored" : "skipped"}`));
|
|
2973
|
+
console.log(pc11.dim(` Plugins installed: ${summary.pluginsInstalled}`));
|
|
2184
2974
|
}
|
|
2185
2975
|
});
|
|
2186
2976
|
|
|
2187
2977
|
// src/commands/info.ts
|
|
2188
|
-
import { defineCommand as
|
|
2978
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
2189
2979
|
import { createSpinner as createSpinner2 } from "nanospinner";
|
|
2190
|
-
import
|
|
2980
|
+
import pc12 from "picocolors";
|
|
2191
2981
|
|
|
2192
2982
|
// src/core/package-info.ts
|
|
2193
2983
|
async function buildInfo(name, entry, source = "npm") {
|
|
@@ -2242,11 +3032,11 @@ async function getPackageInfo(serverName) {
|
|
|
2242
3032
|
// src/commands/info.ts
|
|
2243
3033
|
function colorRisk2(score, riskLevel) {
|
|
2244
3034
|
const label = score !== null ? `${score}/100 (${riskLevel})` : riskLevel;
|
|
2245
|
-
if (riskLevel === "LOW") return
|
|
2246
|
-
if (riskLevel === "MEDIUM") return
|
|
2247
|
-
if (riskLevel === "HIGH") return
|
|
2248
|
-
if (riskLevel === "CRITICAL") return
|
|
2249
|
-
return
|
|
3035
|
+
if (riskLevel === "LOW") return pc12.green(label);
|
|
3036
|
+
if (riskLevel === "MEDIUM") return pc12.yellow(label);
|
|
3037
|
+
if (riskLevel === "HIGH") return pc12.red(label);
|
|
3038
|
+
if (riskLevel === "CRITICAL") return pc12.bold(pc12.red(label));
|
|
3039
|
+
return pc12.dim(label);
|
|
2250
3040
|
}
|
|
2251
3041
|
function formatDaysAgo(isoDate) {
|
|
2252
3042
|
if (!isoDate) return "unknown";
|
|
@@ -2256,54 +3046,54 @@ function formatDaysAgo(isoDate) {
|
|
|
2256
3046
|
return `${days} days ago`;
|
|
2257
3047
|
}
|
|
2258
3048
|
function printInfo(info2) {
|
|
2259
|
-
const installedBadge = info2.isInstalled ?
|
|
3049
|
+
const installedBadge = info2.isInstalled ? pc12.green(" [installed]") : pc12.dim(" [not installed]");
|
|
2260
3050
|
console.log();
|
|
2261
|
-
console.log(
|
|
2262
|
-
console.log(
|
|
2263
|
-
console.log(` ${
|
|
2264
|
-
console.log(` ${
|
|
3051
|
+
console.log(pc12.bold(` ${info2.name}@${info2.version}`) + installedBadge);
|
|
3052
|
+
console.log(pc12.dim(` ${"\u2500".repeat(60)}`));
|
|
3053
|
+
console.log(` ${pc12.dim("Source:")} ${info2.source}`);
|
|
3054
|
+
console.log(` ${pc12.dim("Runtime:")} ${info2.runtime}`);
|
|
2265
3055
|
if (info2.description) {
|
|
2266
|
-
console.log(` ${
|
|
3056
|
+
console.log(` ${pc12.dim("Description:")} ${info2.description}`);
|
|
2267
3057
|
}
|
|
2268
3058
|
if (info2.deprecated) {
|
|
2269
|
-
console.log(` ${
|
|
3059
|
+
console.log(` ${pc12.red("[DEPRECATED]")} This package is deprecated`);
|
|
2270
3060
|
}
|
|
2271
3061
|
console.log();
|
|
2272
|
-
console.log(` ${
|
|
2273
|
-
console.log(` ${
|
|
3062
|
+
console.log(` ${pc12.bold("Trust & Security")}`);
|
|
3063
|
+
console.log(` ${pc12.dim("Trust score:")} ${colorRisk2(info2.trustScore, info2.riskLevel)}`);
|
|
2274
3064
|
if (info2.source === "npm") {
|
|
2275
3065
|
console.log(
|
|
2276
|
-
` ${
|
|
3066
|
+
` ${pc12.dim("Downloads:")} ${info2.weeklyDownloads.toLocaleString()}/week ${pc12.dim("|")} ${pc12.dim("Age:")} ${info2.packageAge}d ${pc12.dim("|")} ${pc12.dim("Maintainers:")} ${info2.maintainerCount}`
|
|
2277
3067
|
);
|
|
2278
3068
|
if (info2.lastPublish) {
|
|
2279
|
-
console.log(` ${
|
|
3069
|
+
console.log(` ${pc12.dim("Last publish:")} ${formatDaysAgo(info2.lastPublish)}`);
|
|
2280
3070
|
}
|
|
2281
3071
|
} else {
|
|
2282
|
-
console.log(
|
|
3072
|
+
console.log(pc12.dim(" (Trust data available for npm packages only)"));
|
|
2283
3073
|
}
|
|
2284
3074
|
console.log();
|
|
2285
|
-
console.log(` ${
|
|
3075
|
+
console.log(` ${pc12.bold("Environment Variables")}`);
|
|
2286
3076
|
if (info2.envVars.length > 0) {
|
|
2287
3077
|
for (const env of info2.envVars) {
|
|
2288
|
-
console.log(` ${
|
|
3078
|
+
console.log(` ${pc12.cyan("\u2022")} ${env}`);
|
|
2289
3079
|
}
|
|
2290
3080
|
} else {
|
|
2291
|
-
console.log(
|
|
3081
|
+
console.log(pc12.dim(" none required"));
|
|
2292
3082
|
}
|
|
2293
3083
|
console.log();
|
|
2294
|
-
console.log(` ${
|
|
3084
|
+
console.log(` ${pc12.bold("Installed Clients")}`);
|
|
2295
3085
|
if (info2.installedClients.length > 0) {
|
|
2296
3086
|
for (const client of info2.installedClients) {
|
|
2297
|
-
console.log(` ${
|
|
3087
|
+
console.log(` ${pc12.green("\u2713")} ${client}`);
|
|
2298
3088
|
}
|
|
2299
3089
|
} else {
|
|
2300
|
-
console.log(
|
|
3090
|
+
console.log(pc12.dim(" Not installed in any client"));
|
|
2301
3091
|
}
|
|
2302
3092
|
console.log();
|
|
2303
|
-
console.log(
|
|
3093
|
+
console.log(pc12.dim(` ${"\u2500".repeat(60)}`));
|
|
2304
3094
|
console.log();
|
|
2305
3095
|
}
|
|
2306
|
-
var info_default =
|
|
3096
|
+
var info_default = defineCommand12({
|
|
2307
3097
|
meta: {
|
|
2308
3098
|
name: "info",
|
|
2309
3099
|
description: "Show detailed metadata for an MCP server (installed or from registry)"
|
|
@@ -2327,13 +3117,13 @@ var info_default = defineCommand8({
|
|
|
2327
3117
|
info2 = await getPackageInfo(args.server);
|
|
2328
3118
|
} catch (err) {
|
|
2329
3119
|
spinner5.error({ text: "Failed to fetch package info" });
|
|
2330
|
-
console.error(
|
|
3120
|
+
console.error(pc12.red(String(err)));
|
|
2331
3121
|
process.exit(1);
|
|
2332
3122
|
}
|
|
2333
3123
|
if (!info2) {
|
|
2334
3124
|
spinner5.error({ text: `Package not found: ${args.server}` });
|
|
2335
3125
|
console.log(
|
|
2336
|
-
|
|
3126
|
+
pc12.dim(`
|
|
2337
3127
|
"${args.server}" was not found in the npm registry or your lockfile.
|
|
2338
3128
|
`)
|
|
2339
3129
|
);
|
|
@@ -2349,10 +3139,10 @@ var info_default = defineCommand8({
|
|
|
2349
3139
|
});
|
|
2350
3140
|
|
|
2351
3141
|
// src/commands/init.ts
|
|
2352
|
-
import
|
|
3142
|
+
import path10 from "path";
|
|
2353
3143
|
import * as p4 from "@clack/prompts";
|
|
2354
|
-
import { defineCommand as
|
|
2355
|
-
var init_default =
|
|
3144
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
3145
|
+
var init_default = defineCommand13({
|
|
2356
3146
|
meta: {
|
|
2357
3147
|
name: "init",
|
|
2358
3148
|
description: "Initialize mcpman.lock in the current project"
|
|
@@ -2368,7 +3158,7 @@ var init_default = defineCommand9({
|
|
|
2368
3158
|
async run({ args }) {
|
|
2369
3159
|
const nonInteractive = args.yes || !process.stdout.isTTY;
|
|
2370
3160
|
p4.intro("mcpman init");
|
|
2371
|
-
const targetPath =
|
|
3161
|
+
const targetPath = path10.join(process.cwd(), LOCKFILE_NAME);
|
|
2372
3162
|
const existing = findLockfile();
|
|
2373
3163
|
if (existing) {
|
|
2374
3164
|
if (nonInteractive) {
|
|
@@ -2384,7 +3174,7 @@ var init_default = defineCommand9({
|
|
|
2384
3174
|
}
|
|
2385
3175
|
let clients = [];
|
|
2386
3176
|
try {
|
|
2387
|
-
const mod = await import("./client-detector-
|
|
3177
|
+
const mod = await import("./client-detector-NHXBDNMY.js");
|
|
2388
3178
|
clients = await mod.getInstalledClients();
|
|
2389
3179
|
} catch {
|
|
2390
3180
|
p4.log.warn("Could not detect AI clients \u2014 creating empty lockfile.");
|
|
@@ -2452,7 +3242,7 @@ var init_default = defineCommand9({
|
|
|
2452
3242
|
|
|
2453
3243
|
// src/commands/install.ts
|
|
2454
3244
|
import * as p7 from "@clack/prompts";
|
|
2455
|
-
import { defineCommand as
|
|
3245
|
+
import { defineCommand as defineCommand14 } from "citty";
|
|
2456
3246
|
|
|
2457
3247
|
// src/core/installer.ts
|
|
2458
3248
|
import * as p6 from "@clack/prompts";
|
|
@@ -2492,7 +3282,7 @@ async function offerVaultSave(serverName, newVars, yes) {
|
|
|
2492
3282
|
// src/core/installer.ts
|
|
2493
3283
|
async function loadClients2() {
|
|
2494
3284
|
try {
|
|
2495
|
-
const mod = await import("./client-detector-
|
|
3285
|
+
const mod = await import("./client-detector-NHXBDNMY.js");
|
|
2496
3286
|
return mod.getInstalledClients();
|
|
2497
3287
|
} catch {
|
|
2498
3288
|
return [];
|
|
@@ -2604,7 +3394,7 @@ async function installServer(input, options = {}) {
|
|
|
2604
3394
|
}
|
|
2605
3395
|
|
|
2606
3396
|
// src/utils/logger.ts
|
|
2607
|
-
import
|
|
3397
|
+
import pc13 from "picocolors";
|
|
2608
3398
|
var noColor = process.env.NO_COLOR !== void 0 || process.argv.includes("--no-color");
|
|
2609
3399
|
var isVerbose = process.argv.includes("--verbose");
|
|
2610
3400
|
var isJson = process.argv.includes("--json");
|
|
@@ -2613,18 +3403,18 @@ function colorize(fn, text2) {
|
|
|
2613
3403
|
}
|
|
2614
3404
|
function info(message) {
|
|
2615
3405
|
if (isJson) return;
|
|
2616
|
-
console.log(`${colorize(
|
|
3406
|
+
console.log(`${colorize(pc13.cyan, "i")} ${message}`);
|
|
2617
3407
|
}
|
|
2618
3408
|
function error(message) {
|
|
2619
3409
|
if (isJson) return;
|
|
2620
|
-
console.error(`${colorize(
|
|
3410
|
+
console.error(`${colorize(pc13.red, "\u2717")} ${message}`);
|
|
2621
3411
|
}
|
|
2622
3412
|
function json(data) {
|
|
2623
3413
|
console.log(JSON.stringify(data, null, 2));
|
|
2624
3414
|
}
|
|
2625
3415
|
|
|
2626
3416
|
// src/commands/install.ts
|
|
2627
|
-
var install_default =
|
|
3417
|
+
var install_default = defineCommand14({
|
|
2628
3418
|
meta: {
|
|
2629
3419
|
name: "install",
|
|
2630
3420
|
description: "Install an MCP server into one or more AI clients"
|
|
@@ -2686,23 +3476,23 @@ async function restoreFromLockfile() {
|
|
|
2686
3476
|
}
|
|
2687
3477
|
|
|
2688
3478
|
// src/commands/link.ts
|
|
2689
|
-
import
|
|
2690
|
-
import { defineCommand as
|
|
2691
|
-
import
|
|
3479
|
+
import path12 from "path";
|
|
3480
|
+
import { defineCommand as defineCommand15 } from "citty";
|
|
3481
|
+
import pc14 from "picocolors";
|
|
2692
3482
|
|
|
2693
3483
|
// src/core/link-service.ts
|
|
2694
|
-
import
|
|
2695
|
-
import
|
|
3484
|
+
import fs12 from "fs";
|
|
3485
|
+
import path11 from "path";
|
|
2696
3486
|
function detectLocalServer(dir) {
|
|
2697
|
-
if (!
|
|
3487
|
+
if (!fs12.existsSync(dir)) {
|
|
2698
3488
|
throw new Error(`Directory does not exist: ${dir}`);
|
|
2699
3489
|
}
|
|
2700
|
-
const pkgPath =
|
|
2701
|
-
if (
|
|
3490
|
+
const pkgPath = path11.join(dir, "package.json");
|
|
3491
|
+
if (fs12.existsSync(pkgPath)) {
|
|
2702
3492
|
return detectNodeServer(dir, pkgPath);
|
|
2703
3493
|
}
|
|
2704
|
-
const pyprojectPath =
|
|
2705
|
-
if (
|
|
3494
|
+
const pyprojectPath = path11.join(dir, "pyproject.toml");
|
|
3495
|
+
if (fs12.existsSync(pyprojectPath)) {
|
|
2706
3496
|
return detectPythonServer(dir, pyprojectPath);
|
|
2707
3497
|
}
|
|
2708
3498
|
throw new Error(
|
|
@@ -2710,28 +3500,28 @@ function detectLocalServer(dir) {
|
|
|
2710
3500
|
);
|
|
2711
3501
|
}
|
|
2712
3502
|
function detectNodeServer(dir, pkgPath) {
|
|
2713
|
-
const raw =
|
|
3503
|
+
const raw = fs12.readFileSync(pkgPath, "utf-8");
|
|
2714
3504
|
const pkg = JSON.parse(raw);
|
|
2715
|
-
const name = String(pkg.name ??
|
|
3505
|
+
const name = String(pkg.name ?? path11.basename(dir));
|
|
2716
3506
|
const version = String(pkg.version ?? "0.0.0");
|
|
2717
3507
|
let entryPoint = null;
|
|
2718
3508
|
if (pkg.bin) {
|
|
2719
3509
|
if (typeof pkg.bin === "string") {
|
|
2720
|
-
entryPoint =
|
|
3510
|
+
entryPoint = path11.resolve(dir, pkg.bin);
|
|
2721
3511
|
} else if (typeof pkg.bin === "object" && pkg.bin !== null) {
|
|
2722
3512
|
const binObj = pkg.bin;
|
|
2723
3513
|
const firstBin = Object.values(binObj)[0];
|
|
2724
|
-
if (firstBin) entryPoint =
|
|
3514
|
+
if (firstBin) entryPoint = path11.resolve(dir, firstBin);
|
|
2725
3515
|
}
|
|
2726
3516
|
}
|
|
2727
3517
|
if (!entryPoint && pkg.main) {
|
|
2728
|
-
entryPoint =
|
|
3518
|
+
entryPoint = path11.resolve(dir, String(pkg.main));
|
|
2729
3519
|
}
|
|
2730
3520
|
if (!entryPoint) {
|
|
2731
3521
|
const candidates = ["src/index.ts", "src/index.js", "index.ts", "index.js"];
|
|
2732
3522
|
for (const c of candidates) {
|
|
2733
|
-
const candidate =
|
|
2734
|
-
if (
|
|
3523
|
+
const candidate = path11.join(dir, c);
|
|
3524
|
+
if (fs12.existsSync(candidate)) {
|
|
2735
3525
|
entryPoint = candidate;
|
|
2736
3526
|
break;
|
|
2737
3527
|
}
|
|
@@ -2756,20 +3546,20 @@ function detectNodeServer(dir, pkgPath) {
|
|
|
2756
3546
|
return { name, version, command, args, envVars, absolutePath: dir, runtime: "node" };
|
|
2757
3547
|
}
|
|
2758
3548
|
function detectPythonServer(dir, pyprojectPath) {
|
|
2759
|
-
const raw =
|
|
2760
|
-
const name = extractTomlValue(raw, "name") ??
|
|
3549
|
+
const raw = fs12.readFileSync(pyprojectPath, "utf-8");
|
|
3550
|
+
const name = extractTomlValue(raw, "name") ?? path11.basename(dir);
|
|
2761
3551
|
const version = extractTomlValue(raw, "version") ?? "0.0.0";
|
|
2762
3552
|
let pythonCmd = "python3";
|
|
2763
|
-
const venvPython =
|
|
2764
|
-
if (
|
|
3553
|
+
const venvPython = path11.join(dir, ".venv", "bin", "python");
|
|
3554
|
+
if (fs12.existsSync(venvPython)) {
|
|
2765
3555
|
pythonCmd = venvPython;
|
|
2766
3556
|
}
|
|
2767
3557
|
const entryCandidate = [
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
].find((
|
|
2772
|
-
const entryPoint = entryCandidate ??
|
|
3558
|
+
path11.join(dir, "main.py"),
|
|
3559
|
+
path11.join(dir, name.replace(/-/g, "_"), "main.py"),
|
|
3560
|
+
path11.join(dir, "__main__.py")
|
|
3561
|
+
].find((p13) => fs12.existsSync(p13));
|
|
3562
|
+
const entryPoint = entryCandidate ?? path11.join(dir, "main.py");
|
|
2773
3563
|
return {
|
|
2774
3564
|
name,
|
|
2775
3565
|
version,
|
|
@@ -2811,13 +3601,13 @@ async function registerLinkedServer(linkResult, clients, lockfilePath, nameOverr
|
|
|
2811
3601
|
};
|
|
2812
3602
|
const existing = readLockfile(lockfilePath);
|
|
2813
3603
|
existing.servers[serverName] = lockEntry;
|
|
2814
|
-
const { writeLockfile: writeLockfile2 } = await import("./lockfile-
|
|
3604
|
+
const { writeLockfile: writeLockfile2 } = await import("./lockfile-5XXA26FM.js");
|
|
2815
3605
|
writeLockfile2(existing, lockfilePath);
|
|
2816
3606
|
return registered;
|
|
2817
3607
|
}
|
|
2818
3608
|
|
|
2819
3609
|
// src/commands/link.ts
|
|
2820
|
-
var link_default =
|
|
3610
|
+
var link_default = defineCommand15({
|
|
2821
3611
|
meta: {
|
|
2822
3612
|
name: "link",
|
|
2823
3613
|
description: "Register a local MCP server directory with AI clients"
|
|
@@ -2843,42 +3633,42 @@ var link_default = defineCommand11({
|
|
|
2843
3633
|
const dirArg = args.dir ?? ".";
|
|
2844
3634
|
const clientFilter = args.client;
|
|
2845
3635
|
const nameOverride = args.name;
|
|
2846
|
-
const absoluteDir =
|
|
3636
|
+
const absoluteDir = path12.resolve(dirArg);
|
|
2847
3637
|
let linkResult;
|
|
2848
3638
|
try {
|
|
2849
3639
|
linkResult = detectLocalServer(absoluteDir);
|
|
2850
3640
|
} catch (err) {
|
|
2851
|
-
console.error(
|
|
3641
|
+
console.error(pc14.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
2852
3642
|
process.exit(1);
|
|
2853
3643
|
}
|
|
2854
3644
|
const serverName = nameOverride ?? linkResult.name;
|
|
2855
|
-
console.log(
|
|
2856
|
-
Detected: ${
|
|
2857
|
-
console.log(
|
|
2858
|
-
console.log(
|
|
3645
|
+
console.log(pc14.dim(`
|
|
3646
|
+
Detected: ${pc14.cyan(serverName)} (${linkResult.runtime})`));
|
|
3647
|
+
console.log(pc14.dim(` Path: ${absoluteDir}`));
|
|
3648
|
+
console.log(pc14.dim(` Command: ${linkResult.command} ${linkResult.args.join(" ")}`));
|
|
2859
3649
|
const allClients = await getInstalledClients();
|
|
2860
3650
|
const clients = clientFilter ? allClients.filter((c) => c.type === clientFilter) : allClients;
|
|
2861
3651
|
if (clientFilter && clients.length === 0) {
|
|
2862
|
-
console.error(
|
|
3652
|
+
console.error(pc14.red(` Error: Unknown client '${clientFilter}'.`));
|
|
2863
3653
|
process.exit(1);
|
|
2864
3654
|
}
|
|
2865
3655
|
let registered;
|
|
2866
3656
|
try {
|
|
2867
3657
|
registered = await registerLinkedServer(linkResult, clients, void 0, nameOverride);
|
|
2868
3658
|
} catch (err) {
|
|
2869
|
-
console.error(
|
|
3659
|
+
console.error(pc14.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
2870
3660
|
process.exit(1);
|
|
2871
3661
|
}
|
|
2872
3662
|
if (registered.length === 0) {
|
|
2873
|
-
console.log(
|
|
2874
|
-
console.log(
|
|
3663
|
+
console.log(pc14.yellow(" Warning: No clients registered. Are any AI clients installed?"));
|
|
3664
|
+
console.log(pc14.dim(` Server saved to lockfile with source "local".`));
|
|
2875
3665
|
} else {
|
|
2876
|
-
console.log(
|
|
2877
|
-
Linked ${
|
|
3666
|
+
console.log(pc14.green(`
|
|
3667
|
+
Linked ${pc14.bold(serverName)} to: ${registered.join(", ")}
|
|
2878
3668
|
`));
|
|
2879
|
-
console.log(
|
|
3669
|
+
console.log(pc14.dim(` Run ${pc14.cyan("mcpman list")} to verify.`));
|
|
2880
3670
|
console.log(
|
|
2881
|
-
|
|
3671
|
+
pc14.dim(` Run ${pc14.cyan(`mcpman watch ${serverName}`)} to start with auto-restart.`)
|
|
2882
3672
|
);
|
|
2883
3673
|
}
|
|
2884
3674
|
console.log();
|
|
@@ -2886,14 +3676,14 @@ var link_default = defineCommand11({
|
|
|
2886
3676
|
});
|
|
2887
3677
|
|
|
2888
3678
|
// src/commands/list.ts
|
|
2889
|
-
import { defineCommand as
|
|
2890
|
-
import
|
|
3679
|
+
import { defineCommand as defineCommand16 } from "citty";
|
|
3680
|
+
import pc15 from "picocolors";
|
|
2891
3681
|
var STATUS_ICON = {
|
|
2892
|
-
healthy:
|
|
2893
|
-
unhealthy:
|
|
2894
|
-
unknown:
|
|
3682
|
+
healthy: pc15.green("\u25CF"),
|
|
3683
|
+
unhealthy: pc15.red("\u25CF"),
|
|
3684
|
+
unknown: pc15.dim("\u25CB")
|
|
2895
3685
|
};
|
|
2896
|
-
var list_default =
|
|
3686
|
+
var list_default = defineCommand16({
|
|
2897
3687
|
meta: {
|
|
2898
3688
|
name: "list",
|
|
2899
3689
|
description: "List installed MCP servers"
|
|
@@ -2914,8 +3704,8 @@ var list_default = defineCommand12({
|
|
|
2914
3704
|
if (servers.length === 0) {
|
|
2915
3705
|
const filter = args.client ? ` for client "${args.client}"` : "";
|
|
2916
3706
|
console.log(
|
|
2917
|
-
|
|
2918
|
-
`No MCP servers installed${filter}. Run ${
|
|
3707
|
+
pc15.dim(
|
|
3708
|
+
`No MCP servers installed${filter}. Run ${pc15.cyan("mcpman install <server>")} to get started.`
|
|
2919
3709
|
)
|
|
2920
3710
|
);
|
|
2921
3711
|
return;
|
|
@@ -2941,9 +3731,9 @@ var list_default = defineCommand12({
|
|
|
2941
3731
|
const nameWidth = Math.max(4, ...withStatus.map((s) => s.name.length));
|
|
2942
3732
|
const clientsWidth = Math.max(7, ...withStatus.map((s) => formatClients(s.clients).length));
|
|
2943
3733
|
const header = ` ${pad("NAME", nameWidth)} ${pad("CLIENT(S)", clientsWidth)} ${pad("COMMAND", 20)} STATUS`;
|
|
2944
|
-
console.log(
|
|
3734
|
+
console.log(pc15.dim(header));
|
|
2945
3735
|
console.log(
|
|
2946
|
-
|
|
3736
|
+
pc15.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientsWidth)} ${"-".repeat(20)} ------`)
|
|
2947
3737
|
);
|
|
2948
3738
|
for (const s of withStatus) {
|
|
2949
3739
|
const icon = STATUS_ICON[s.status];
|
|
@@ -2958,7 +3748,7 @@ var list_default = defineCommand12({
|
|
|
2958
3748
|
}
|
|
2959
3749
|
const clientSet = new Set(withStatus.flatMap((s) => s.clients));
|
|
2960
3750
|
console.log(
|
|
2961
|
-
|
|
3751
|
+
pc15.dim(
|
|
2962
3752
|
`
|
|
2963
3753
|
${withStatus.length} server${withStatus.length !== 1 ? "s" : ""} \xB7 ${clientSet.size} client${clientSet.size !== 1 ? "s" : ""}`
|
|
2964
3754
|
)
|
|
@@ -2971,21 +3761,21 @@ function pad(s, width) {
|
|
|
2971
3761
|
function truncate(s, max) {
|
|
2972
3762
|
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
2973
3763
|
}
|
|
2974
|
-
var
|
|
3764
|
+
var CLIENT_DISPLAY2 = {
|
|
2975
3765
|
"claude-desktop": "Claude",
|
|
2976
3766
|
cursor: "Cursor",
|
|
2977
3767
|
vscode: "VS Code",
|
|
2978
3768
|
windsurf: "Windsurf"
|
|
2979
3769
|
};
|
|
2980
3770
|
function formatClients(clients) {
|
|
2981
|
-
return clients.map((c) =>
|
|
3771
|
+
return clients.map((c) => CLIENT_DISPLAY2[c] ?? c).join(", ");
|
|
2982
3772
|
}
|
|
2983
3773
|
|
|
2984
3774
|
// src/commands/logs.ts
|
|
2985
|
-
import { spawn as
|
|
2986
|
-
import { defineCommand as
|
|
2987
|
-
import
|
|
2988
|
-
var logs_default =
|
|
3775
|
+
import { spawn as spawn4 } from "child_process";
|
|
3776
|
+
import { defineCommand as defineCommand17 } from "citty";
|
|
3777
|
+
import pc16 from "picocolors";
|
|
3778
|
+
var logs_default = defineCommand17({
|
|
2989
3779
|
meta: {
|
|
2990
3780
|
name: "logs",
|
|
2991
3781
|
description: "Stream stdout/stderr from an MCP server"
|
|
@@ -3008,7 +3798,7 @@ var logs_default = defineCommand13({
|
|
|
3008
3798
|
const lockfile = readLockfile();
|
|
3009
3799
|
const entry = lockfile.servers[serverName];
|
|
3010
3800
|
if (!entry) {
|
|
3011
|
-
console.error(
|
|
3801
|
+
console.error(pc16.red(` Error: Server '${serverName}' is not installed.`));
|
|
3012
3802
|
process.exit(1);
|
|
3013
3803
|
}
|
|
3014
3804
|
const lockfileEnv = parseEnvFlags(entry.envVars);
|
|
@@ -3018,24 +3808,24 @@ var logs_default = defineCommand13({
|
|
|
3018
3808
|
...lockfileEnv,
|
|
3019
3809
|
...vaultEnv
|
|
3020
3810
|
};
|
|
3021
|
-
console.log(
|
|
3811
|
+
console.log(pc16.dim(` Streaming logs for ${pc16.cyan(serverName)}... (Ctrl+C to stop)
|
|
3022
3812
|
`));
|
|
3023
|
-
const child =
|
|
3813
|
+
const child = spawn4(entry.command, entry.args, {
|
|
3024
3814
|
env: finalEnv,
|
|
3025
3815
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3026
3816
|
});
|
|
3027
3817
|
child.stdout?.on("data", (chunk) => {
|
|
3028
|
-
process.stdout.write(
|
|
3818
|
+
process.stdout.write(pc16.dim("[stdout] ") + chunk.toString());
|
|
3029
3819
|
});
|
|
3030
3820
|
child.stderr?.on("data", (chunk) => {
|
|
3031
|
-
process.stderr.write(
|
|
3821
|
+
process.stderr.write(pc16.yellow("[stderr] ") + chunk.toString());
|
|
3032
3822
|
});
|
|
3033
3823
|
child.on("error", (err) => {
|
|
3034
|
-
console.error(
|
|
3824
|
+
console.error(pc16.red(` Failed to start '${serverName}': ${err.message}`));
|
|
3035
3825
|
process.exit(1);
|
|
3036
3826
|
});
|
|
3037
3827
|
child.on("close", (code) => {
|
|
3038
|
-
console.log(
|
|
3828
|
+
console.log(pc16.dim(`
|
|
3039
3829
|
Process exited with code ${code ?? 0}`));
|
|
3040
3830
|
process.exit(code ?? 0);
|
|
3041
3831
|
});
|
|
@@ -3059,11 +3849,98 @@ async function loadVaultSecrets(serverName) {
|
|
|
3059
3849
|
}
|
|
3060
3850
|
}
|
|
3061
3851
|
|
|
3852
|
+
// src/commands/pin.ts
|
|
3853
|
+
import { defineCommand as defineCommand18 } from "citty";
|
|
3854
|
+
import pc17 from "picocolors";
|
|
3855
|
+
var pin_default = defineCommand18({
|
|
3856
|
+
meta: {
|
|
3857
|
+
name: "pin",
|
|
3858
|
+
description: "Pin a server to a specific version"
|
|
3859
|
+
},
|
|
3860
|
+
args: {
|
|
3861
|
+
server: {
|
|
3862
|
+
type: "positional",
|
|
3863
|
+
description: "Server name to pin/unpin",
|
|
3864
|
+
required: false
|
|
3865
|
+
},
|
|
3866
|
+
version: {
|
|
3867
|
+
type: "positional",
|
|
3868
|
+
description: "Version to pin to (defaults to currently installed version)",
|
|
3869
|
+
required: false
|
|
3870
|
+
},
|
|
3871
|
+
unpin: {
|
|
3872
|
+
type: "boolean",
|
|
3873
|
+
description: "Remove the pin for a server",
|
|
3874
|
+
default: false
|
|
3875
|
+
},
|
|
3876
|
+
list: {
|
|
3877
|
+
type: "boolean",
|
|
3878
|
+
description: "List all pinned servers",
|
|
3879
|
+
default: false
|
|
3880
|
+
}
|
|
3881
|
+
},
|
|
3882
|
+
run({ args }) {
|
|
3883
|
+
if (args.list) {
|
|
3884
|
+
const pins = listPins();
|
|
3885
|
+
if (pins.length === 0) {
|
|
3886
|
+
console.log(pc17.dim("No servers are pinned."));
|
|
3887
|
+
return;
|
|
3888
|
+
}
|
|
3889
|
+
console.log(`
|
|
3890
|
+
${pc17.bold("Pinned servers")}
|
|
3891
|
+
`);
|
|
3892
|
+
for (const { server, version: version2 } of pins) {
|
|
3893
|
+
console.log(` ${pc17.cyan(pc17.bold(server))} ${pc17.dim("@")}${pc17.green(version2)}`);
|
|
3894
|
+
}
|
|
3895
|
+
console.log("");
|
|
3896
|
+
return;
|
|
3897
|
+
}
|
|
3898
|
+
if (args.unpin) {
|
|
3899
|
+
if (!args.server) {
|
|
3900
|
+
console.error(`${pc17.red("\u2717")} Specify a server name to unpin.`);
|
|
3901
|
+
process.exit(1);
|
|
3902
|
+
}
|
|
3903
|
+
if (!isPinned(args.server)) {
|
|
3904
|
+
console.log(pc17.dim(`"${args.server}" is not pinned.`));
|
|
3905
|
+
return;
|
|
3906
|
+
}
|
|
3907
|
+
unpinServer(args.server);
|
|
3908
|
+
console.log(`${pc17.green("\u2713")} Unpinned ${pc17.cyan(args.server)}`);
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3911
|
+
if (!args.server) {
|
|
3912
|
+
console.error(`${pc17.red("\u2717")} Specify a server name to pin. Use --list to see pins.`);
|
|
3913
|
+
process.exit(1);
|
|
3914
|
+
}
|
|
3915
|
+
let version = args.version;
|
|
3916
|
+
if (!version) {
|
|
3917
|
+
const lockfile = readLockfile();
|
|
3918
|
+
version = lockfile.servers[args.server]?.version;
|
|
3919
|
+
if (!version) {
|
|
3920
|
+
console.error(
|
|
3921
|
+
`${pc17.red("\u2717")} "${args.server}" not found in lockfile. Specify a version explicitly.`
|
|
3922
|
+
);
|
|
3923
|
+
process.exit(1);
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
pinServer(args.server, version);
|
|
3927
|
+
const prev = getPinnedVersion(args.server);
|
|
3928
|
+
if (prev && prev !== version) {
|
|
3929
|
+
console.log(
|
|
3930
|
+
`${pc17.green("\u2713")} Re-pinned ${pc17.cyan(args.server)} ${pc17.dim(prev)} \u2192 ${pc17.green(version)}`
|
|
3931
|
+
);
|
|
3932
|
+
} else {
|
|
3933
|
+
console.log(`${pc17.green("\u2713")} Pinned ${pc17.cyan(args.server)} @ ${pc17.green(version)}`);
|
|
3934
|
+
}
|
|
3935
|
+
console.log(pc17.dim(" Update notifications will be suppressed for this server."));
|
|
3936
|
+
}
|
|
3937
|
+
});
|
|
3938
|
+
|
|
3062
3939
|
// src/commands/plugin.ts
|
|
3063
|
-
import { defineCommand as
|
|
3940
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
3064
3941
|
import { createSpinner as createSpinner3 } from "nanospinner";
|
|
3065
|
-
import
|
|
3066
|
-
var addCommand =
|
|
3942
|
+
import pc18 from "picocolors";
|
|
3943
|
+
var addCommand = defineCommand19({
|
|
3067
3944
|
meta: { name: "add", description: "Install a plugin package" },
|
|
3068
3945
|
args: {
|
|
3069
3946
|
package: {
|
|
@@ -3081,23 +3958,23 @@ var addCommand = defineCommand14({
|
|
|
3081
3958
|
spinner5.stop();
|
|
3082
3959
|
if (loaded) {
|
|
3083
3960
|
console.log(
|
|
3084
|
-
`${
|
|
3961
|
+
`${pc18.green("\u2713")} Plugin ${pc18.bold(loaded.name)} installed (prefix: ${pc18.cyan(loaded.prefix)})`
|
|
3085
3962
|
);
|
|
3086
3963
|
} else {
|
|
3087
3964
|
console.log(
|
|
3088
|
-
`${
|
|
3965
|
+
`${pc18.yellow("\u26A0")} Package ${pc18.bold(pkg)} installed but does not export a valid mcpman plugin.`
|
|
3089
3966
|
);
|
|
3090
3967
|
}
|
|
3091
3968
|
} catch (err) {
|
|
3092
3969
|
spinner5.stop();
|
|
3093
3970
|
console.error(
|
|
3094
|
-
`${
|
|
3971
|
+
`${pc18.red("\u2717")} Failed to install plugin: ${err instanceof Error ? err.message : String(err)}`
|
|
3095
3972
|
);
|
|
3096
3973
|
process.exit(1);
|
|
3097
3974
|
}
|
|
3098
3975
|
}
|
|
3099
3976
|
});
|
|
3100
|
-
var removeCommand =
|
|
3977
|
+
var removeCommand = defineCommand19({
|
|
3101
3978
|
meta: { name: "remove", description: "Uninstall a plugin package" },
|
|
3102
3979
|
args: {
|
|
3103
3980
|
package: {
|
|
@@ -3110,46 +3987,46 @@ var removeCommand = defineCommand14({
|
|
|
3110
3987
|
const pkg = args.package;
|
|
3111
3988
|
const installed = listPluginPackages();
|
|
3112
3989
|
if (!installed.includes(pkg)) {
|
|
3113
|
-
console.log(
|
|
3990
|
+
console.log(pc18.dim(`Plugin "${pkg}" is not installed.`));
|
|
3114
3991
|
return;
|
|
3115
3992
|
}
|
|
3116
3993
|
try {
|
|
3117
3994
|
removePluginPackage(pkg);
|
|
3118
|
-
console.log(`${
|
|
3995
|
+
console.log(`${pc18.green("\u2713")} Plugin ${pc18.bold(pkg)} removed.`);
|
|
3119
3996
|
} catch (err) {
|
|
3120
3997
|
console.error(
|
|
3121
|
-
`${
|
|
3998
|
+
`${pc18.red("\u2717")} Failed to remove plugin: ${err instanceof Error ? err.message : String(err)}`
|
|
3122
3999
|
);
|
|
3123
4000
|
process.exit(1);
|
|
3124
4001
|
}
|
|
3125
4002
|
}
|
|
3126
4003
|
});
|
|
3127
|
-
var listCommand2 =
|
|
4004
|
+
var listCommand2 = defineCommand19({
|
|
3128
4005
|
meta: { name: "list", description: "List installed plugins" },
|
|
3129
4006
|
run() {
|
|
3130
4007
|
const packages = listPluginPackages();
|
|
3131
4008
|
if (packages.length === 0) {
|
|
3132
|
-
console.log(
|
|
4009
|
+
console.log(pc18.dim("No plugins installed. Use `mcpman plugin add <package>`."));
|
|
3133
4010
|
return;
|
|
3134
4011
|
}
|
|
3135
4012
|
console.log("");
|
|
3136
|
-
console.log(
|
|
4013
|
+
console.log(pc18.bold("Installed plugins:"));
|
|
3137
4014
|
console.log("");
|
|
3138
4015
|
for (const pkg of packages) {
|
|
3139
4016
|
const loaded = loadPlugin(pkg);
|
|
3140
4017
|
if (loaded) {
|
|
3141
4018
|
console.log(
|
|
3142
|
-
` ${
|
|
4019
|
+
` ${pc18.green("\u25CF")} ${pc18.bold(loaded.name)} prefix: ${pc18.cyan(loaded.prefix)} pkg: ${pc18.dim(pkg)}`
|
|
3143
4020
|
);
|
|
3144
4021
|
} else {
|
|
3145
|
-
console.log(` ${
|
|
4022
|
+
console.log(` ${pc18.yellow("\u25CF")} ${pc18.dim(pkg)} ${pc18.yellow("(failed to load)")}`);
|
|
3146
4023
|
}
|
|
3147
4024
|
}
|
|
3148
4025
|
console.log("");
|
|
3149
|
-
console.log(
|
|
4026
|
+
console.log(pc18.dim(` ${packages.length} plugin${packages.length !== 1 ? "s" : ""} installed`));
|
|
3150
4027
|
}
|
|
3151
4028
|
});
|
|
3152
|
-
var plugin_default =
|
|
4029
|
+
var plugin_default = defineCommand19({
|
|
3153
4030
|
meta: {
|
|
3154
4031
|
name: "plugin",
|
|
3155
4032
|
description: "Manage mcpman plugins for custom registries"
|
|
@@ -3162,22 +4039,22 @@ var plugin_default = defineCommand14({
|
|
|
3162
4039
|
});
|
|
3163
4040
|
|
|
3164
4041
|
// src/commands/profiles.ts
|
|
3165
|
-
import { defineCommand as
|
|
3166
|
-
import
|
|
4042
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
4043
|
+
import pc19 from "picocolors";
|
|
3167
4044
|
|
|
3168
4045
|
// src/core/profile-service.ts
|
|
3169
|
-
import
|
|
3170
|
-
import
|
|
4046
|
+
import fs13 from "fs";
|
|
4047
|
+
import path13 from "path";
|
|
3171
4048
|
function ensureDir(dir = getProfilesDir()) {
|
|
3172
|
-
|
|
4049
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
3173
4050
|
}
|
|
3174
4051
|
function profilePath(name, dir = getProfilesDir()) {
|
|
3175
|
-
return
|
|
4052
|
+
return path13.join(dir, `${name}.json`);
|
|
3176
4053
|
}
|
|
3177
4054
|
function createProfile(name, description = "", dir = getProfilesDir()) {
|
|
3178
4055
|
ensureDir(dir);
|
|
3179
4056
|
const filePath = profilePath(name, dir);
|
|
3180
|
-
if (
|
|
4057
|
+
if (fs13.existsSync(filePath)) {
|
|
3181
4058
|
throw new Error(`Profile '${name}' already exists. Delete it first or use a different name.`);
|
|
3182
4059
|
}
|
|
3183
4060
|
const lockfile = readLockfile();
|
|
@@ -3187,16 +4064,16 @@ function createProfile(name, description = "", dir = getProfilesDir()) {
|
|
|
3187
4064
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3188
4065
|
servers: lockfile.servers
|
|
3189
4066
|
};
|
|
3190
|
-
|
|
4067
|
+
fs13.writeFileSync(filePath, JSON.stringify(profile, null, 2), "utf-8");
|
|
3191
4068
|
return profile;
|
|
3192
4069
|
}
|
|
3193
4070
|
function listProfiles(dir = getProfilesDir()) {
|
|
3194
4071
|
ensureDir(dir);
|
|
3195
|
-
const files =
|
|
4072
|
+
const files = fs13.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
3196
4073
|
const profiles = [];
|
|
3197
4074
|
for (const file of files) {
|
|
3198
4075
|
try {
|
|
3199
|
-
const raw =
|
|
4076
|
+
const raw = fs13.readFileSync(path13.join(dir, file), "utf-8");
|
|
3200
4077
|
const data = JSON.parse(raw);
|
|
3201
4078
|
profiles.push(data);
|
|
3202
4079
|
} catch {
|
|
@@ -3206,9 +4083,9 @@ function listProfiles(dir = getProfilesDir()) {
|
|
|
3206
4083
|
}
|
|
3207
4084
|
function loadProfile(name, dir = getProfilesDir()) {
|
|
3208
4085
|
const filePath = profilePath(name, dir);
|
|
3209
|
-
if (!
|
|
4086
|
+
if (!fs13.existsSync(filePath)) return null;
|
|
3210
4087
|
try {
|
|
3211
|
-
const raw =
|
|
4088
|
+
const raw = fs13.readFileSync(filePath, "utf-8");
|
|
3212
4089
|
return JSON.parse(raw);
|
|
3213
4090
|
} catch {
|
|
3214
4091
|
return null;
|
|
@@ -3216,13 +4093,13 @@ function loadProfile(name, dir = getProfilesDir()) {
|
|
|
3216
4093
|
}
|
|
3217
4094
|
function deleteProfile(name, dir = getProfilesDir()) {
|
|
3218
4095
|
const filePath = profilePath(name, dir);
|
|
3219
|
-
if (!
|
|
3220
|
-
|
|
4096
|
+
if (!fs13.existsSync(filePath)) return false;
|
|
4097
|
+
fs13.unlinkSync(filePath);
|
|
3221
4098
|
return true;
|
|
3222
4099
|
}
|
|
3223
4100
|
|
|
3224
4101
|
// src/commands/profiles.ts
|
|
3225
|
-
var profiles_default =
|
|
4102
|
+
var profiles_default = defineCommand20({
|
|
3226
4103
|
meta: {
|
|
3227
4104
|
name: "profiles",
|
|
3228
4105
|
description: "Manage named server configuration profiles"
|
|
@@ -3251,16 +4128,16 @@ var profiles_default = defineCommand15({
|
|
|
3251
4128
|
case "create": {
|
|
3252
4129
|
if (!name) {
|
|
3253
4130
|
console.error(
|
|
3254
|
-
|
|
4131
|
+
pc19.red(" Error: Profile name required. Usage: mcpman profiles create <name>")
|
|
3255
4132
|
);
|
|
3256
4133
|
process.exit(1);
|
|
3257
4134
|
}
|
|
3258
4135
|
try {
|
|
3259
4136
|
const profile = createProfile(name, args.description ?? "");
|
|
3260
4137
|
const count = Object.keys(profile.servers).length;
|
|
3261
|
-
console.log(
|
|
4138
|
+
console.log(pc19.green(` \u2713 Profile '${name}' created with ${count} server(s).`));
|
|
3262
4139
|
} catch (err) {
|
|
3263
|
-
console.error(
|
|
4140
|
+
console.error(pc19.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
3264
4141
|
process.exit(1);
|
|
3265
4142
|
}
|
|
3266
4143
|
break;
|
|
@@ -3268,38 +4145,38 @@ var profiles_default = defineCommand15({
|
|
|
3268
4145
|
case "switch": {
|
|
3269
4146
|
if (!name) {
|
|
3270
4147
|
console.error(
|
|
3271
|
-
|
|
4148
|
+
pc19.red(" Error: Profile name required. Usage: mcpman profiles switch <name>")
|
|
3272
4149
|
);
|
|
3273
4150
|
process.exit(1);
|
|
3274
4151
|
}
|
|
3275
4152
|
const profile = loadProfile(name);
|
|
3276
4153
|
if (!profile) {
|
|
3277
|
-
console.error(
|
|
4154
|
+
console.error(pc19.red(` Error: Profile '${name}' not found.`));
|
|
3278
4155
|
process.exit(1);
|
|
3279
4156
|
}
|
|
3280
4157
|
const lockData = { lockfileVersion: 1, servers: profile.servers };
|
|
3281
4158
|
writeLockfile(lockData);
|
|
3282
4159
|
const count = Object.keys(profile.servers).length;
|
|
3283
|
-
console.log(
|
|
3284
|
-
console.log(
|
|
4160
|
+
console.log(pc19.green(` \u2713 Switched to profile '${name}' (${count} servers).`));
|
|
4161
|
+
console.log(pc19.dim(" Run mcpman sync to apply to all clients."));
|
|
3285
4162
|
break;
|
|
3286
4163
|
}
|
|
3287
4164
|
case "list": {
|
|
3288
4165
|
const profiles = listProfiles();
|
|
3289
4166
|
if (profiles.length === 0) {
|
|
3290
4167
|
console.log(
|
|
3291
|
-
|
|
4168
|
+
pc19.dim(" No profiles saved. Create one with: mcpman profiles create <name>")
|
|
3292
4169
|
);
|
|
3293
4170
|
return;
|
|
3294
4171
|
}
|
|
3295
|
-
console.log(
|
|
4172
|
+
console.log(pc19.bold(`
|
|
3296
4173
|
Profiles (${profiles.length})
|
|
3297
4174
|
`));
|
|
3298
|
-
for (const
|
|
3299
|
-
const count = Object.keys(
|
|
3300
|
-
const desc =
|
|
4175
|
+
for (const p13 of profiles) {
|
|
4176
|
+
const count = Object.keys(p13.servers).length;
|
|
4177
|
+
const desc = p13.description ? pc19.dim(` \u2014 ${p13.description}`) : "";
|
|
3301
4178
|
console.log(
|
|
3302
|
-
` ${
|
|
4179
|
+
` ${pc19.cyan("\u25CF")} ${pc19.bold(p13.name)} ${pc19.dim(`${count} server(s)`)}${desc}`
|
|
3303
4180
|
);
|
|
3304
4181
|
}
|
|
3305
4182
|
console.log();
|
|
@@ -3308,22 +4185,22 @@ var profiles_default = defineCommand15({
|
|
|
3308
4185
|
case "delete": {
|
|
3309
4186
|
if (!name) {
|
|
3310
4187
|
console.error(
|
|
3311
|
-
|
|
4188
|
+
pc19.red(" Error: Profile name required. Usage: mcpman profiles delete <name>")
|
|
3312
4189
|
);
|
|
3313
4190
|
process.exit(1);
|
|
3314
4191
|
}
|
|
3315
4192
|
const deleted = deleteProfile(name);
|
|
3316
4193
|
if (deleted) {
|
|
3317
|
-
console.log(
|
|
4194
|
+
console.log(pc19.green(` \u2713 Profile '${name}' deleted.`));
|
|
3318
4195
|
} else {
|
|
3319
|
-
console.error(
|
|
4196
|
+
console.error(pc19.red(` Error: Profile '${name}' not found.`));
|
|
3320
4197
|
process.exit(1);
|
|
3321
4198
|
}
|
|
3322
4199
|
break;
|
|
3323
4200
|
}
|
|
3324
4201
|
default:
|
|
3325
4202
|
console.error(
|
|
3326
|
-
|
|
4203
|
+
pc19.red(` Unknown action '${action}'. Use: create, switch, list, or delete.`)
|
|
3327
4204
|
);
|
|
3328
4205
|
process.exit(1);
|
|
3329
4206
|
}
|
|
@@ -3331,8 +4208,8 @@ var profiles_default = defineCommand15({
|
|
|
3331
4208
|
});
|
|
3332
4209
|
|
|
3333
4210
|
// src/commands/registry.ts
|
|
3334
|
-
import { defineCommand as
|
|
3335
|
-
import
|
|
4211
|
+
import { defineCommand as defineCommand21 } from "citty";
|
|
4212
|
+
import pc20 from "picocolors";
|
|
3336
4213
|
|
|
3337
4214
|
// src/core/registry-manager.ts
|
|
3338
4215
|
var BUILTIN_REGISTRIES = [
|
|
@@ -3402,7 +4279,7 @@ function getDefaultRegistry(configPath) {
|
|
|
3402
4279
|
}
|
|
3403
4280
|
|
|
3404
4281
|
// src/commands/registry.ts
|
|
3405
|
-
var registry_default =
|
|
4282
|
+
var registry_default = defineCommand21({
|
|
3406
4283
|
meta: {
|
|
3407
4284
|
name: "registry",
|
|
3408
4285
|
description: "Manage custom registry URLs"
|
|
@@ -3432,67 +4309,67 @@ var registry_default = defineCommand16({
|
|
|
3432
4309
|
case "list": {
|
|
3433
4310
|
const registries = getRegistries();
|
|
3434
4311
|
const defaultName = getDefaultRegistry();
|
|
3435
|
-
console.log(
|
|
4312
|
+
console.log(pc20.bold("\n Registries\n"));
|
|
3436
4313
|
for (const r of registries) {
|
|
3437
4314
|
const isDefault = r.name === defaultName;
|
|
3438
|
-
const defaultTag = isDefault ?
|
|
3439
|
-
const builtinTag = r.builtin ?
|
|
4315
|
+
const defaultTag = isDefault ? pc20.green(" (default)") : "";
|
|
4316
|
+
const builtinTag = r.builtin ? pc20.dim(" [builtin]") : "";
|
|
3440
4317
|
console.log(
|
|
3441
|
-
` ${isDefault ?
|
|
4318
|
+
` ${isDefault ? pc20.green("\u25CF") : pc20.dim("\u25CB")} ${pc20.bold(r.name)}${defaultTag}${builtinTag}`
|
|
3442
4319
|
);
|
|
3443
|
-
console.log(` ${
|
|
4320
|
+
console.log(` ${pc20.dim(r.url)}`);
|
|
3444
4321
|
}
|
|
3445
4322
|
console.log();
|
|
3446
4323
|
break;
|
|
3447
4324
|
}
|
|
3448
4325
|
case "add": {
|
|
3449
4326
|
if (!name) {
|
|
3450
|
-
console.error(
|
|
4327
|
+
console.error(pc20.red(" Error: Usage: mcpman registry add <name> <url>"));
|
|
3451
4328
|
process.exit(1);
|
|
3452
4329
|
}
|
|
3453
4330
|
if (!url) {
|
|
3454
|
-
console.error(
|
|
4331
|
+
console.error(pc20.red(" Error: Usage: mcpman registry add <name> <url>"));
|
|
3455
4332
|
process.exit(1);
|
|
3456
4333
|
}
|
|
3457
4334
|
try {
|
|
3458
4335
|
addRegistry(name, url);
|
|
3459
|
-
console.log(
|
|
4336
|
+
console.log(pc20.green(` Added registry '${name}' \u2192 ${url}`));
|
|
3460
4337
|
} catch (err) {
|
|
3461
|
-
console.error(
|
|
4338
|
+
console.error(pc20.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
3462
4339
|
process.exit(1);
|
|
3463
4340
|
}
|
|
3464
4341
|
break;
|
|
3465
4342
|
}
|
|
3466
4343
|
case "remove": {
|
|
3467
4344
|
if (!name) {
|
|
3468
|
-
console.error(
|
|
4345
|
+
console.error(pc20.red(" Error: Usage: mcpman registry remove <name>"));
|
|
3469
4346
|
process.exit(1);
|
|
3470
4347
|
}
|
|
3471
4348
|
try {
|
|
3472
4349
|
removeRegistry(name);
|
|
3473
|
-
console.log(
|
|
4350
|
+
console.log(pc20.green(` Removed registry '${name}'.`));
|
|
3474
4351
|
} catch (err) {
|
|
3475
|
-
console.error(
|
|
4352
|
+
console.error(pc20.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
3476
4353
|
process.exit(1);
|
|
3477
4354
|
}
|
|
3478
4355
|
break;
|
|
3479
4356
|
}
|
|
3480
4357
|
case "set-default": {
|
|
3481
4358
|
if (!name) {
|
|
3482
|
-
console.error(
|
|
4359
|
+
console.error(pc20.red(" Error: Usage: mcpman registry set-default <name>"));
|
|
3483
4360
|
process.exit(1);
|
|
3484
4361
|
}
|
|
3485
4362
|
try {
|
|
3486
4363
|
setDefaultRegistry(name);
|
|
3487
|
-
console.log(
|
|
4364
|
+
console.log(pc20.green(` Default registry set to '${name}'.`));
|
|
3488
4365
|
} catch (err) {
|
|
3489
|
-
console.error(
|
|
4366
|
+
console.error(pc20.red(` Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
3490
4367
|
process.exit(1);
|
|
3491
4368
|
}
|
|
3492
4369
|
break;
|
|
3493
4370
|
}
|
|
3494
4371
|
default:
|
|
3495
|
-
console.error(
|
|
4372
|
+
console.error(pc20.red(` Unknown action '${action}'. Use: list, add, remove, set-default.`));
|
|
3496
4373
|
process.exit(1);
|
|
3497
4374
|
}
|
|
3498
4375
|
}
|
|
@@ -3500,18 +4377,18 @@ var registry_default = defineCommand16({
|
|
|
3500
4377
|
|
|
3501
4378
|
// src/commands/remove.ts
|
|
3502
4379
|
import * as p8 from "@clack/prompts";
|
|
3503
|
-
import { defineCommand as
|
|
3504
|
-
import
|
|
3505
|
-
var
|
|
4380
|
+
import { defineCommand as defineCommand22 } from "citty";
|
|
4381
|
+
import pc21 from "picocolors";
|
|
4382
|
+
var CLIENT_DISPLAY3 = {
|
|
3506
4383
|
"claude-desktop": "Claude",
|
|
3507
4384
|
cursor: "Cursor",
|
|
3508
4385
|
vscode: "VS Code",
|
|
3509
4386
|
windsurf: "Windsurf"
|
|
3510
4387
|
};
|
|
3511
4388
|
function clientDisplayName(type) {
|
|
3512
|
-
return
|
|
4389
|
+
return CLIENT_DISPLAY3[type] ?? type;
|
|
3513
4390
|
}
|
|
3514
|
-
var remove_default =
|
|
4391
|
+
var remove_default = defineCommand22({
|
|
3515
4392
|
meta: {
|
|
3516
4393
|
name: "remove",
|
|
3517
4394
|
description: "Remove an MCP server from one or more AI clients"
|
|
@@ -3538,7 +4415,7 @@ var remove_default = defineCommand17({
|
|
|
3538
4415
|
}
|
|
3539
4416
|
},
|
|
3540
4417
|
async run({ args }) {
|
|
3541
|
-
p8.intro(
|
|
4418
|
+
p8.intro(pc21.bold("mcpman remove"));
|
|
3542
4419
|
const serverName = args.server;
|
|
3543
4420
|
const servers = await getInstalledServers();
|
|
3544
4421
|
const match = servers.find((s) => s.name === serverName);
|
|
@@ -3548,7 +4425,7 @@ var remove_default = defineCommand17({
|
|
|
3548
4425
|
(s) => s.name.includes(serverName) || serverName.includes(s.name)
|
|
3549
4426
|
);
|
|
3550
4427
|
if (similar.length > 0) {
|
|
3551
|
-
p8.log.info(`Did you mean: ${similar.map((s) =>
|
|
4428
|
+
p8.log.info(`Did you mean: ${similar.map((s) => pc21.cyan(s.name)).join(", ")}?`);
|
|
3552
4429
|
}
|
|
3553
4430
|
p8.outro("Nothing to remove.");
|
|
3554
4431
|
return;
|
|
@@ -3583,7 +4460,7 @@ var remove_default = defineCommand17({
|
|
|
3583
4460
|
if (!args.yes) {
|
|
3584
4461
|
const clientNames = targetClients.map(clientDisplayName).join(", ");
|
|
3585
4462
|
const confirmed = await p8.confirm({
|
|
3586
|
-
message: `Remove ${
|
|
4463
|
+
message: `Remove ${pc21.cyan(serverName)} from ${pc21.yellow(clientNames)}?`
|
|
3587
4464
|
});
|
|
3588
4465
|
if (p8.isCancel(confirmed) || !confirmed) {
|
|
3589
4466
|
p8.outro("Cancelled.");
|
|
@@ -3608,18 +4485,118 @@ var remove_default = defineCommand17({
|
|
|
3608
4485
|
}
|
|
3609
4486
|
if (errors.length > 0) {
|
|
3610
4487
|
for (const e of errors) p8.log.error(e);
|
|
3611
|
-
p8.outro(
|
|
4488
|
+
p8.outro(pc21.red("Completed with errors."));
|
|
4489
|
+
process.exit(1);
|
|
4490
|
+
}
|
|
4491
|
+
p8.outro(pc21.green(`Removed "${serverName}" successfully.`));
|
|
4492
|
+
}
|
|
4493
|
+
});
|
|
4494
|
+
|
|
4495
|
+
// src/commands/rollback.ts
|
|
4496
|
+
import * as p9 from "@clack/prompts";
|
|
4497
|
+
import { defineCommand as defineCommand23 } from "citty";
|
|
4498
|
+
import pc22 from "picocolors";
|
|
4499
|
+
var rollback_default = defineCommand23({
|
|
4500
|
+
meta: {
|
|
4501
|
+
name: "rollback",
|
|
4502
|
+
description: "Restore a previous lockfile snapshot"
|
|
4503
|
+
},
|
|
4504
|
+
args: {
|
|
4505
|
+
index: {
|
|
4506
|
+
type: "positional",
|
|
4507
|
+
description: "Snapshot index to restore (0 = most recent). Omit to use --list.",
|
|
4508
|
+
required: false
|
|
4509
|
+
},
|
|
4510
|
+
list: {
|
|
4511
|
+
type: "boolean",
|
|
4512
|
+
description: "List available snapshots",
|
|
4513
|
+
default: false
|
|
4514
|
+
},
|
|
4515
|
+
yes: {
|
|
4516
|
+
type: "boolean",
|
|
4517
|
+
description: "Skip confirmation prompt",
|
|
4518
|
+
default: false
|
|
4519
|
+
}
|
|
4520
|
+
},
|
|
4521
|
+
async run({ args }) {
|
|
4522
|
+
const snapshots = listSnapshots();
|
|
4523
|
+
if (args.list || args.index === void 0) {
|
|
4524
|
+
if (snapshots.length === 0) {
|
|
4525
|
+
console.log(
|
|
4526
|
+
pc22.dim("No snapshots available. Snapshots are created on each lockfile write.")
|
|
4527
|
+
);
|
|
4528
|
+
return;
|
|
4529
|
+
}
|
|
4530
|
+
console.log(
|
|
4531
|
+
`
|
|
4532
|
+
${pc22.bold("Lockfile snapshots")} ${pc22.dim(`(${snapshots.length} available, 0 = most recent)`)}
|
|
4533
|
+
`
|
|
4534
|
+
);
|
|
4535
|
+
for (const snap2 of snapshots) {
|
|
4536
|
+
const size = `${Math.ceil(snap2.sizeBytes / 1024)}KB`;
|
|
4537
|
+
const date2 = snap2.createdAt ? new Date(snap2.createdAt).toLocaleString() : "unknown";
|
|
4538
|
+
console.log(` ${pc22.cyan(`[${snap2.index}]`)} ${pc22.dim(date2)} ${pc22.dim(size)}`);
|
|
4539
|
+
}
|
|
4540
|
+
console.log("");
|
|
4541
|
+
if (args.index === void 0) return;
|
|
4542
|
+
}
|
|
4543
|
+
const idx = Number.parseInt(String(args.index), 10);
|
|
4544
|
+
if (Number.isNaN(idx) || idx < 0) {
|
|
4545
|
+
console.error(
|
|
4546
|
+
`${pc22.red("\u2717")} Invalid index "${args.index}". Must be a non-negative integer.`
|
|
4547
|
+
);
|
|
4548
|
+
process.exit(1);
|
|
4549
|
+
}
|
|
4550
|
+
const snap = snapshots[idx];
|
|
4551
|
+
if (!snap) {
|
|
4552
|
+
console.error(
|
|
4553
|
+
`${pc22.red("\u2717")} Snapshot [${idx}] does not exist. Use --list to see available snapshots.`
|
|
4554
|
+
);
|
|
4555
|
+
process.exit(1);
|
|
4556
|
+
}
|
|
4557
|
+
const content = readSnapshot(idx);
|
|
4558
|
+
if (!content) {
|
|
4559
|
+
console.error(`${pc22.red("\u2717")} Could not read snapshot [${idx}].`);
|
|
3612
4560
|
process.exit(1);
|
|
3613
4561
|
}
|
|
3614
|
-
|
|
4562
|
+
const date = snap.createdAt ? new Date(snap.createdAt).toLocaleString() : "unknown";
|
|
4563
|
+
console.log(`
|
|
4564
|
+
${pc22.bold("Restoring snapshot")} ${pc22.cyan(`[${idx}]`)} ${pc22.dim(date)}
|
|
4565
|
+
`);
|
|
4566
|
+
try {
|
|
4567
|
+
const parsed = JSON.parse(content);
|
|
4568
|
+
const count = Object.keys(parsed.servers ?? {}).length;
|
|
4569
|
+
console.log(` ${pc22.dim(`Preview: ${count} server(s) in snapshot`)}
|
|
4570
|
+
`);
|
|
4571
|
+
} catch {
|
|
4572
|
+
}
|
|
4573
|
+
const lockfilePath = resolveLockfilePath();
|
|
4574
|
+
if (!args.yes) {
|
|
4575
|
+
const confirmed = await p9.confirm({
|
|
4576
|
+
message: `Restore snapshot [${idx}] to ${lockfilePath}?`,
|
|
4577
|
+
initialValue: false
|
|
4578
|
+
});
|
|
4579
|
+
if (p9.isCancel(confirmed) || !confirmed) {
|
|
4580
|
+
p9.cancel("Cancelled.");
|
|
4581
|
+
return;
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
const restored = restoreSnapshot(idx, lockfilePath);
|
|
4585
|
+
if (!restored) {
|
|
4586
|
+
console.error(`${pc22.red("\u2717")} Restore failed.`);
|
|
4587
|
+
process.exit(1);
|
|
4588
|
+
}
|
|
4589
|
+
console.log(`
|
|
4590
|
+
${pc22.green("\u2713")} Lockfile restored from snapshot [${idx}].`);
|
|
4591
|
+
console.log(pc22.dim(` Written to: ${lockfilePath}`));
|
|
3615
4592
|
}
|
|
3616
4593
|
});
|
|
3617
4594
|
|
|
3618
4595
|
// src/commands/run.ts
|
|
3619
|
-
import { spawn as
|
|
3620
|
-
import { defineCommand as
|
|
3621
|
-
import
|
|
3622
|
-
var run_default =
|
|
4596
|
+
import { spawn as spawn5 } from "child_process";
|
|
4597
|
+
import { defineCommand as defineCommand24 } from "citty";
|
|
4598
|
+
import pc23 from "picocolors";
|
|
4599
|
+
var run_default = defineCommand24({
|
|
3623
4600
|
meta: {
|
|
3624
4601
|
name: "run",
|
|
3625
4602
|
description: "Run an installed MCP server with vault secrets injected"
|
|
@@ -3641,8 +4618,8 @@ var run_default = defineCommand18({
|
|
|
3641
4618
|
const lockfile = readLockfile();
|
|
3642
4619
|
const entry = lockfile.servers[serverName];
|
|
3643
4620
|
if (!entry) {
|
|
3644
|
-
console.error(
|
|
3645
|
-
console.error(
|
|
4621
|
+
console.error(pc23.red(` Error: Server '${serverName}' is not installed.`));
|
|
4622
|
+
console.error(pc23.dim(` Run ${pc23.cyan("mcpman install <server>")} to install it first.`));
|
|
3646
4623
|
process.exit(1);
|
|
3647
4624
|
}
|
|
3648
4625
|
const lockfileEnv = parseEnvFlags(entry.envVars);
|
|
@@ -3654,8 +4631,8 @@ var run_default = defineCommand18({
|
|
|
3654
4631
|
...vaultEnv,
|
|
3655
4632
|
...cliEnv
|
|
3656
4633
|
};
|
|
3657
|
-
console.log(
|
|
3658
|
-
const child =
|
|
4634
|
+
console.log(pc23.dim(` Running ${pc23.cyan(serverName)}...`));
|
|
4635
|
+
const child = spawn5(entry.command, entry.args, {
|
|
3659
4636
|
env: finalEnv,
|
|
3660
4637
|
stdio: "inherit"
|
|
3661
4638
|
});
|
|
@@ -3672,7 +4649,7 @@ var run_default = defineCommand18({
|
|
|
3672
4649
|
resolve();
|
|
3673
4650
|
});
|
|
3674
4651
|
child.on("error", (err) => {
|
|
3675
|
-
console.error(
|
|
4652
|
+
console.error(pc23.red(` Failed to start '${serverName}': ${err.message}`));
|
|
3676
4653
|
process.exit(1);
|
|
3677
4654
|
resolve();
|
|
3678
4655
|
});
|
|
@@ -3688,15 +4665,15 @@ async function loadVaultSecrets2(serverName) {
|
|
|
3688
4665
|
const password = await getMasterPassword();
|
|
3689
4666
|
return getSecretsForServer(serverName, password);
|
|
3690
4667
|
} catch {
|
|
3691
|
-
console.warn(
|
|
4668
|
+
console.warn(pc23.yellow(" Warning: Could not load vault secrets, continuing without them."));
|
|
3692
4669
|
return {};
|
|
3693
4670
|
}
|
|
3694
4671
|
}
|
|
3695
4672
|
|
|
3696
4673
|
// src/commands/search.ts
|
|
3697
|
-
import { defineCommand as
|
|
4674
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
3698
4675
|
import { createSpinner as createSpinner4 } from "nanospinner";
|
|
3699
|
-
import
|
|
4676
|
+
import pc24 from "picocolors";
|
|
3700
4677
|
|
|
3701
4678
|
// src/core/registry-search.ts
|
|
3702
4679
|
var SEARCH_TIMEOUT_MS = 1e4;
|
|
@@ -3769,10 +4746,10 @@ function pad2(s, width) {
|
|
|
3769
4746
|
function highlightMatch(name, query) {
|
|
3770
4747
|
const idx = name.toLowerCase().indexOf(query.toLowerCase());
|
|
3771
4748
|
if (idx === -1) return name;
|
|
3772
|
-
return name.slice(0, idx) +
|
|
4749
|
+
return name.slice(0, idx) + pc24.yellow(name.slice(idx, idx + query.length)) + name.slice(idx + query.length);
|
|
3773
4750
|
}
|
|
3774
4751
|
function formatDownloads(n) {
|
|
3775
|
-
if (!n) return
|
|
4752
|
+
if (!n) return pc24.dim("\u2014");
|
|
3776
4753
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
3777
4754
|
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
3778
4755
|
return String(n);
|
|
@@ -3783,9 +4760,9 @@ function printNpmResults(results, query) {
|
|
|
3783
4760
|
const dlWidth = 9;
|
|
3784
4761
|
const descMax = 50;
|
|
3785
4762
|
const header = ` ${pad2("NAME", nameWidth)} ${pad2("VERSION", verWidth)} ${pad2("DOWNLOADS", dlWidth)} DESCRIPTION`;
|
|
3786
|
-
console.log(
|
|
4763
|
+
console.log(pc24.dim(header));
|
|
3787
4764
|
console.log(
|
|
3788
|
-
|
|
4765
|
+
pc24.dim(
|
|
3789
4766
|
` ${"-".repeat(nameWidth)} ${"-".repeat(verWidth)} ${"-".repeat(dlWidth)} ${"-".repeat(descMax)}`
|
|
3790
4767
|
)
|
|
3791
4768
|
);
|
|
@@ -3793,8 +4770,8 @@ function printNpmResults(results, query) {
|
|
|
3793
4770
|
const name = highlightMatch(pad2(r.name, nameWidth), query);
|
|
3794
4771
|
const ver = pad2(r.version, verWidth);
|
|
3795
4772
|
const dl = pad2(formatDownloads(r.downloads), dlWidth);
|
|
3796
|
-
const desc = truncate2(r.description ||
|
|
3797
|
-
console.log(` ${name} ${
|
|
4773
|
+
const desc = truncate2(r.description || pc24.dim("(no description)"), descMax);
|
|
4774
|
+
console.log(` ${name} ${pc24.dim(ver)} ${dl} ${desc}`);
|
|
3798
4775
|
}
|
|
3799
4776
|
}
|
|
3800
4777
|
function printSmitheryResults(results, query) {
|
|
@@ -3802,19 +4779,19 @@ function printSmitheryResults(results, query) {
|
|
|
3802
4779
|
const usesWidth = 8;
|
|
3803
4780
|
const descMax = 50;
|
|
3804
4781
|
const header = ` ${pad2("NAME", nameWidth)} ${pad2("USES", usesWidth)} DESCRIPTION`;
|
|
3805
|
-
console.log(
|
|
4782
|
+
console.log(pc24.dim(header));
|
|
3806
4783
|
console.log(
|
|
3807
|
-
|
|
4784
|
+
pc24.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(usesWidth)} ${"-".repeat(descMax)}`)
|
|
3808
4785
|
);
|
|
3809
4786
|
for (const r of results) {
|
|
3810
4787
|
const name = highlightMatch(pad2(r.name, nameWidth), query);
|
|
3811
4788
|
const uses = pad2(formatDownloads(r.useCount), usesWidth);
|
|
3812
|
-
const badge = r.verified ?
|
|
3813
|
-
const desc = truncate2(r.description ||
|
|
4789
|
+
const badge = r.verified ? pc24.green(" \u2713") : "";
|
|
4790
|
+
const desc = truncate2(r.description || pc24.dim("(no description)"), descMax);
|
|
3814
4791
|
console.log(` ${name}${badge} ${uses} ${desc}`);
|
|
3815
4792
|
}
|
|
3816
4793
|
}
|
|
3817
|
-
var search_default =
|
|
4794
|
+
var search_default = defineCommand25({
|
|
3818
4795
|
meta: {
|
|
3819
4796
|
name: "search",
|
|
3820
4797
|
description: "Search for MCP servers on npm or Smithery registry"
|
|
@@ -3846,7 +4823,7 @@ var search_default = defineCommand19({
|
|
|
3846
4823
|
const registry = args.registry.toLowerCase();
|
|
3847
4824
|
const limit = Math.min(Math.max(1, Number.parseInt(args.limit, 10) || 20), 100);
|
|
3848
4825
|
if (registry !== "npm" && registry !== "smithery") {
|
|
3849
|
-
console.error(
|
|
4826
|
+
console.error(pc24.red(` Unknown registry "${registry}". Use "npm" or "smithery".`));
|
|
3850
4827
|
process.exit(1);
|
|
3851
4828
|
}
|
|
3852
4829
|
const spinner5 = createSpinner4(`Searching ${registry} for "${query}"...`).start();
|
|
@@ -3854,20 +4831,20 @@ var search_default = defineCommand19({
|
|
|
3854
4831
|
const results2 = await searchNpm(query, limit);
|
|
3855
4832
|
spinner5.stop();
|
|
3856
4833
|
if (results2.length === 0) {
|
|
3857
|
-
console.log(
|
|
4834
|
+
console.log(pc24.dim(`
|
|
3858
4835
|
No results found for "${query}" on npm.
|
|
3859
4836
|
`));
|
|
3860
4837
|
return;
|
|
3861
4838
|
}
|
|
3862
4839
|
console.log(
|
|
3863
|
-
|
|
4840
|
+
pc24.bold(
|
|
3864
4841
|
`
|
|
3865
4842
|
mcpman search \u2014 npm (${results2.length} result${results2.length !== 1 ? "s" : ""})
|
|
3866
4843
|
`
|
|
3867
4844
|
)
|
|
3868
4845
|
);
|
|
3869
4846
|
printNpmResults(results2, query);
|
|
3870
|
-
console.log(
|
|
4847
|
+
console.log(pc24.dim("\n Install with: mcpman install <name>\n"));
|
|
3871
4848
|
if (args.all) {
|
|
3872
4849
|
await printPluginResults(query, limit);
|
|
3873
4850
|
}
|
|
@@ -3876,20 +4853,20 @@ var search_default = defineCommand19({
|
|
|
3876
4853
|
const results = await searchSmithery(query, limit);
|
|
3877
4854
|
spinner5.stop();
|
|
3878
4855
|
if (results.length === 0) {
|
|
3879
|
-
console.log(
|
|
4856
|
+
console.log(pc24.dim(`
|
|
3880
4857
|
No results found for "${query}" on Smithery.
|
|
3881
4858
|
`));
|
|
3882
4859
|
return;
|
|
3883
4860
|
}
|
|
3884
4861
|
console.log(
|
|
3885
|
-
|
|
4862
|
+
pc24.bold(
|
|
3886
4863
|
`
|
|
3887
4864
|
mcpman search \u2014 Smithery (${results.length} result${results.length !== 1 ? "s" : ""})
|
|
3888
4865
|
`
|
|
3889
4866
|
)
|
|
3890
4867
|
);
|
|
3891
4868
|
printSmitheryResults(results, query);
|
|
3892
|
-
console.log(
|
|
4869
|
+
console.log(pc24.dim("\n Install with: mcpman install <name>\n"));
|
|
3893
4870
|
if (args.all) {
|
|
3894
4871
|
await printPluginResults(query, limit);
|
|
3895
4872
|
}
|
|
@@ -3899,7 +4876,7 @@ async function printPluginResults(query, limit) {
|
|
|
3899
4876
|
const pluginResults = await searchPlugins(query, limit);
|
|
3900
4877
|
if (pluginResults.length === 0) return;
|
|
3901
4878
|
console.log(
|
|
3902
|
-
|
|
4879
|
+
pc24.bold(
|
|
3903
4880
|
`
|
|
3904
4881
|
Plugins (${pluginResults.length} result${pluginResults.length !== 1 ? "s" : ""})
|
|
3905
4882
|
`
|
|
@@ -3909,22 +4886,22 @@ async function printPluginResults(query, limit) {
|
|
|
3909
4886
|
const srcWidth = Math.max(6, ...pluginResults.map((r) => r.source.length));
|
|
3910
4887
|
const descMax = 50;
|
|
3911
4888
|
const header = ` ${pad2("NAME", nameWidth)} ${pad2("SOURCE", srcWidth)} DESCRIPTION`;
|
|
3912
|
-
console.log(
|
|
4889
|
+
console.log(pc24.dim(header));
|
|
3913
4890
|
console.log(
|
|
3914
|
-
|
|
4891
|
+
pc24.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(srcWidth)} ${"-".repeat(descMax)}`)
|
|
3915
4892
|
);
|
|
3916
4893
|
for (const r of pluginResults) {
|
|
3917
4894
|
const name = highlightMatch(pad2(r.name, nameWidth), query);
|
|
3918
4895
|
const src = pad2(r.source, srcWidth);
|
|
3919
|
-
const desc = truncate2(r.description ||
|
|
3920
|
-
console.log(` ${name} ${
|
|
4896
|
+
const desc = truncate2(r.description || pc24.dim("(no description)"), descMax);
|
|
4897
|
+
console.log(` ${name} ${pc24.dim(src)} ${desc}`);
|
|
3921
4898
|
}
|
|
3922
4899
|
}
|
|
3923
4900
|
|
|
3924
4901
|
// src/commands/secrets.ts
|
|
3925
|
-
import * as
|
|
3926
|
-
import { defineCommand as
|
|
3927
|
-
import
|
|
4902
|
+
import * as p10 from "@clack/prompts";
|
|
4903
|
+
import { defineCommand as defineCommand26 } from "citty";
|
|
4904
|
+
import pc25 from "picocolors";
|
|
3928
4905
|
function maskValue(value) {
|
|
3929
4906
|
if (value.length <= 8) return "***";
|
|
3930
4907
|
return `${value.slice(0, 4)}***${value.slice(-3)}`;
|
|
@@ -3934,7 +4911,7 @@ function parseKeyValue(input) {
|
|
|
3934
4911
|
if (idx <= 0) return null;
|
|
3935
4912
|
return { key: input.slice(0, idx), value: input.slice(idx + 1) };
|
|
3936
4913
|
}
|
|
3937
|
-
var setCommand2 =
|
|
4914
|
+
var setCommand2 = defineCommand26({
|
|
3938
4915
|
meta: { name: "set", description: "Store an encrypted secret for a server" },
|
|
3939
4916
|
args: {
|
|
3940
4917
|
server: {
|
|
@@ -3951,28 +4928,28 @@ var setCommand2 = defineCommand20({
|
|
|
3951
4928
|
async run({ args }) {
|
|
3952
4929
|
const parsed = parseKeyValue(args.keyvalue);
|
|
3953
4930
|
if (!parsed) {
|
|
3954
|
-
console.error(`${
|
|
4931
|
+
console.error(`${pc25.red("\u2717")} Invalid format. Expected KEY=VALUE`);
|
|
3955
4932
|
process.exit(1);
|
|
3956
4933
|
}
|
|
3957
|
-
|
|
4934
|
+
p10.intro(pc25.cyan("mcpman secrets set"));
|
|
3958
4935
|
const isNew = listSecrets(args.server).length === 0 || !listSecrets(args.server)[0]?.keys.includes(parsed.key);
|
|
3959
4936
|
const vaultPath = (await import("./vault-service-UTZAV6N6.js")).getVaultPath();
|
|
3960
4937
|
const vaultExists = (await import("fs")).existsSync(vaultPath);
|
|
3961
4938
|
const password = await getMasterPassword(!vaultExists && isNew);
|
|
3962
|
-
const spin =
|
|
4939
|
+
const spin = p10.spinner();
|
|
3963
4940
|
spin.start("Encrypting secret...");
|
|
3964
4941
|
try {
|
|
3965
4942
|
setSecret(args.server, parsed.key, parsed.value, password);
|
|
3966
|
-
spin.stop(`${
|
|
4943
|
+
spin.stop(`${pc25.green("\u2713")} Stored ${pc25.bold(parsed.key)} for ${pc25.cyan(args.server)}`);
|
|
3967
4944
|
} catch (err) {
|
|
3968
|
-
spin.stop(`${
|
|
3969
|
-
console.error(
|
|
4945
|
+
spin.stop(`${pc25.red("\u2717")} Failed to store secret`);
|
|
4946
|
+
console.error(pc25.dim(String(err)));
|
|
3970
4947
|
process.exit(1);
|
|
3971
4948
|
}
|
|
3972
|
-
|
|
4949
|
+
p10.outro(pc25.dim("Secret encrypted and saved to vault."));
|
|
3973
4950
|
}
|
|
3974
4951
|
});
|
|
3975
|
-
var listCommand3 =
|
|
4952
|
+
var listCommand3 = defineCommand26({
|
|
3976
4953
|
meta: { name: "list", description: "List secret keys stored in the vault" },
|
|
3977
4954
|
args: {
|
|
3978
4955
|
server: {
|
|
@@ -3984,27 +4961,27 @@ var listCommand3 = defineCommand20({
|
|
|
3984
4961
|
async run({ args }) {
|
|
3985
4962
|
const results = listSecrets(args.server || void 0);
|
|
3986
4963
|
if (results.length === 0) {
|
|
3987
|
-
const filter = args.server ? ` for ${
|
|
3988
|
-
console.log(
|
|
4964
|
+
const filter = args.server ? ` for ${pc25.cyan(args.server)}` : "";
|
|
4965
|
+
console.log(pc25.dim(`No secrets stored${filter}.`));
|
|
3989
4966
|
return;
|
|
3990
4967
|
}
|
|
3991
4968
|
console.log("");
|
|
3992
4969
|
for (const { server, keys } of results) {
|
|
3993
|
-
console.log(
|
|
4970
|
+
console.log(pc25.bold(pc25.cyan(server)));
|
|
3994
4971
|
for (const key of keys) {
|
|
3995
|
-
console.log(` ${
|
|
4972
|
+
console.log(` ${pc25.green("\u25CF")} ${pc25.bold(key)} ${pc25.dim(maskValue("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"))}`);
|
|
3996
4973
|
}
|
|
3997
4974
|
console.log("");
|
|
3998
4975
|
}
|
|
3999
4976
|
const total = results.reduce((n, r) => n + r.keys.length, 0);
|
|
4000
4977
|
console.log(
|
|
4001
|
-
|
|
4978
|
+
pc25.dim(
|
|
4002
4979
|
` ${total} secret${total !== 1 ? "s" : ""} in ${results.length} server${results.length !== 1 ? "s" : ""}`
|
|
4003
4980
|
)
|
|
4004
4981
|
);
|
|
4005
4982
|
}
|
|
4006
4983
|
});
|
|
4007
|
-
var removeCommand2 =
|
|
4984
|
+
var removeCommand2 = defineCommand26({
|
|
4008
4985
|
meta: { name: "remove", description: "Delete a secret from the vault" },
|
|
4009
4986
|
args: {
|
|
4010
4987
|
server: {
|
|
@@ -4019,25 +4996,25 @@ var removeCommand2 = defineCommand20({
|
|
|
4019
4996
|
}
|
|
4020
4997
|
},
|
|
4021
4998
|
async run({ args }) {
|
|
4022
|
-
const confirmed = await
|
|
4023
|
-
message: `Remove ${
|
|
4999
|
+
const confirmed = await p10.confirm({
|
|
5000
|
+
message: `Remove ${pc25.bold(args.key)} from ${pc25.cyan(args.server)}?`,
|
|
4024
5001
|
initialValue: false
|
|
4025
5002
|
});
|
|
4026
|
-
if (
|
|
4027
|
-
|
|
5003
|
+
if (p10.isCancel(confirmed) || !confirmed) {
|
|
5004
|
+
p10.cancel("Cancelled.");
|
|
4028
5005
|
return;
|
|
4029
5006
|
}
|
|
4030
5007
|
try {
|
|
4031
5008
|
removeSecret(args.server, args.key);
|
|
4032
|
-
console.log(`${
|
|
5009
|
+
console.log(`${pc25.green("\u2713")} Removed ${pc25.bold(args.key)} from ${pc25.cyan(args.server)}`);
|
|
4033
5010
|
} catch (err) {
|
|
4034
|
-
console.error(`${
|
|
4035
|
-
console.error(
|
|
5011
|
+
console.error(`${pc25.red("\u2717")} Failed to remove secret`);
|
|
5012
|
+
console.error(pc25.dim(String(err)));
|
|
4036
5013
|
process.exit(1);
|
|
4037
5014
|
}
|
|
4038
5015
|
}
|
|
4039
5016
|
});
|
|
4040
|
-
var secrets_default =
|
|
5017
|
+
var secrets_default = defineCommand26({
|
|
4041
5018
|
meta: {
|
|
4042
5019
|
name: "secrets",
|
|
4043
5020
|
description: "Manage encrypted secrets for MCP servers"
|
|
@@ -4050,9 +5027,9 @@ var secrets_default = defineCommand20({
|
|
|
4050
5027
|
});
|
|
4051
5028
|
|
|
4052
5029
|
// src/commands/sync.ts
|
|
4053
|
-
import * as
|
|
4054
|
-
import { defineCommand as
|
|
4055
|
-
import
|
|
5030
|
+
import * as p11 from "@clack/prompts";
|
|
5031
|
+
import { defineCommand as defineCommand27 } from "citty";
|
|
5032
|
+
import pc26 from "picocolors";
|
|
4056
5033
|
|
|
4057
5034
|
// src/core/config-diff.ts
|
|
4058
5035
|
function reconstructServerEntry(lockEntry) {
|
|
@@ -4197,14 +5174,14 @@ async function getClientConfigs() {
|
|
|
4197
5174
|
}
|
|
4198
5175
|
|
|
4199
5176
|
// src/commands/sync.ts
|
|
4200
|
-
var
|
|
4201
|
-
var
|
|
5177
|
+
var VALID_CLIENTS2 = ["claude-desktop", "cursor", "vscode", "windsurf"];
|
|
5178
|
+
var CLIENT_DISPLAY4 = {
|
|
4202
5179
|
"claude-desktop": "Claude Desktop",
|
|
4203
5180
|
cursor: "Cursor",
|
|
4204
5181
|
vscode: "VS Code",
|
|
4205
5182
|
windsurf: "Windsurf"
|
|
4206
5183
|
};
|
|
4207
|
-
var sync_default =
|
|
5184
|
+
var sync_default = defineCommand27({
|
|
4208
5185
|
meta: {
|
|
4209
5186
|
name: "sync",
|
|
4210
5187
|
description: "Sync MCP server configs across all detected AI clients"
|
|
@@ -4231,20 +5208,20 @@ var sync_default = defineCommand21({
|
|
|
4231
5208
|
}
|
|
4232
5209
|
},
|
|
4233
5210
|
async run({ args }) {
|
|
4234
|
-
|
|
5211
|
+
p11.intro(`${pc26.cyan("mcpman sync")}`);
|
|
4235
5212
|
const sourceClient = args.source;
|
|
4236
|
-
if (sourceClient && !
|
|
4237
|
-
|
|
4238
|
-
`Invalid --source "${sourceClient}". Must be one of: ${
|
|
5213
|
+
if (sourceClient && !VALID_CLIENTS2.includes(sourceClient)) {
|
|
5214
|
+
p11.log.error(
|
|
5215
|
+
`Invalid --source "${sourceClient}". Must be one of: ${VALID_CLIENTS2.join(", ")}`
|
|
4239
5216
|
);
|
|
4240
5217
|
process.exit(1);
|
|
4241
5218
|
}
|
|
4242
|
-
const spinner5 =
|
|
5219
|
+
const spinner5 = p11.spinner();
|
|
4243
5220
|
spinner5.start("Detecting clients and reading configs...");
|
|
4244
5221
|
const { configs, handlers } = await getClientConfigs();
|
|
4245
5222
|
spinner5.stop(`Found ${configs.size} client(s)`);
|
|
4246
5223
|
if (configs.size === 0) {
|
|
4247
|
-
|
|
5224
|
+
p11.log.warn(
|
|
4248
5225
|
"No AI clients detected. Install Claude Desktop, Cursor, VS Code, or Windsurf first."
|
|
4249
5226
|
);
|
|
4250
5227
|
process.exit(0);
|
|
@@ -4253,10 +5230,10 @@ var sync_default = defineCommand21({
|
|
|
4253
5230
|
let actions;
|
|
4254
5231
|
if (sourceClient) {
|
|
4255
5232
|
if (!configs.has(sourceClient)) {
|
|
4256
|
-
|
|
5233
|
+
p11.log.error(`Source client "${sourceClient}" is not detected or its config is unreadable.`);
|
|
4257
5234
|
process.exit(1);
|
|
4258
5235
|
}
|
|
4259
|
-
|
|
5236
|
+
p11.log.info(`Using ${CLIENT_DISPLAY4[sourceClient]} as source of truth`);
|
|
4260
5237
|
actions = computeDiffFromClient(sourceClient, configs, diffOptions);
|
|
4261
5238
|
} else {
|
|
4262
5239
|
const lockfile = readLockfile();
|
|
@@ -4267,32 +5244,32 @@ var sync_default = defineCommand21({
|
|
|
4267
5244
|
const extraCount = actions.filter((a) => a.action === "extra").length;
|
|
4268
5245
|
const removeCount = actions.filter((a) => a.action === "remove").length;
|
|
4269
5246
|
if (addCount === 0 && removeCount === 0 && extraCount === 0) {
|
|
4270
|
-
|
|
5247
|
+
p11.outro(pc26.green("All clients are in sync."));
|
|
4271
5248
|
process.exit(0);
|
|
4272
5249
|
}
|
|
4273
5250
|
const parts = [];
|
|
4274
|
-
if (addCount > 0) parts.push(
|
|
4275
|
-
if (removeCount > 0) parts.push(
|
|
4276
|
-
if (extraCount > 0) parts.push(
|
|
4277
|
-
|
|
5251
|
+
if (addCount > 0) parts.push(pc26.green(`${addCount} to add`));
|
|
5252
|
+
if (removeCount > 0) parts.push(pc26.red(`${removeCount} to remove`));
|
|
5253
|
+
if (extraCount > 0) parts.push(pc26.yellow(`${extraCount} extra (informational)`));
|
|
5254
|
+
p11.log.info(parts.join(" \xB7 "));
|
|
4278
5255
|
if (args["dry-run"]) {
|
|
4279
|
-
|
|
5256
|
+
p11.outro(pc26.dim("Dry run \u2014 no changes applied."));
|
|
4280
5257
|
process.exit(1);
|
|
4281
5258
|
}
|
|
4282
5259
|
if (addCount === 0 && removeCount === 0) {
|
|
4283
|
-
|
|
5260
|
+
p11.outro(pc26.dim("No additions needed. Extra servers left untouched."));
|
|
4284
5261
|
process.exit(1);
|
|
4285
5262
|
}
|
|
4286
5263
|
if (!args.yes) {
|
|
4287
5264
|
const actionParts = [];
|
|
4288
5265
|
if (addCount > 0) actionParts.push(`${addCount} addition(s)`);
|
|
4289
5266
|
if (removeCount > 0) actionParts.push(`${removeCount} removal(s)`);
|
|
4290
|
-
const confirmed = await
|
|
5267
|
+
const confirmed = await p11.confirm({
|
|
4291
5268
|
message: `Apply ${actionParts.join(" and ")} to client configs?`,
|
|
4292
5269
|
initialValue: true
|
|
4293
5270
|
});
|
|
4294
|
-
if (
|
|
4295
|
-
|
|
5271
|
+
if (p11.isCancel(confirmed) || !confirmed) {
|
|
5272
|
+
p11.outro(pc26.dim("Cancelled \u2014 no changes applied."));
|
|
4296
5273
|
process.exit(0);
|
|
4297
5274
|
}
|
|
4298
5275
|
}
|
|
@@ -4300,37 +5277,37 @@ var sync_default = defineCommand21({
|
|
|
4300
5277
|
const result = await applySyncActions(actions, handlers);
|
|
4301
5278
|
spinner5.stop("Done");
|
|
4302
5279
|
if (result.applied > 0) {
|
|
4303
|
-
|
|
5280
|
+
p11.log.success(`Added ${result.applied} server(s) to client configs.`);
|
|
4304
5281
|
}
|
|
4305
5282
|
if (result.removed > 0) {
|
|
4306
|
-
|
|
5283
|
+
p11.log.success(`Removed ${result.removed} server(s) from client configs.`);
|
|
4307
5284
|
}
|
|
4308
5285
|
if (result.failed > 0) {
|
|
4309
5286
|
for (const e of result.errors) {
|
|
4310
|
-
|
|
5287
|
+
p11.log.error(`Failed to sync "${e.server}" on ${e.client}: ${e.error}`);
|
|
4311
5288
|
}
|
|
4312
5289
|
}
|
|
4313
|
-
|
|
4314
|
-
result.failed === 0 ?
|
|
5290
|
+
p11.outro(
|
|
5291
|
+
result.failed === 0 ? pc26.green("Sync complete.") : pc26.yellow("Sync complete with errors.")
|
|
4315
5292
|
);
|
|
4316
5293
|
process.exit(result.failed > 0 ? 1 : 0);
|
|
4317
5294
|
}
|
|
4318
5295
|
});
|
|
4319
5296
|
function printDiffTable(actions) {
|
|
4320
5297
|
if (actions.length === 0) {
|
|
4321
|
-
|
|
5298
|
+
p11.log.info("No actions to display.");
|
|
4322
5299
|
return;
|
|
4323
5300
|
}
|
|
4324
5301
|
const nameWidth = Math.max(6, ...actions.map((a) => a.server.length));
|
|
4325
5302
|
const clientWidth = Math.max(
|
|
4326
5303
|
6,
|
|
4327
|
-
...actions.map((a) =>
|
|
5304
|
+
...actions.map((a) => CLIENT_DISPLAY4[a.client]?.length ?? a.client.length)
|
|
4328
5305
|
);
|
|
4329
5306
|
const header = ` ${pad3("SERVER", nameWidth)} ${pad3("CLIENT", clientWidth)} STATUS`;
|
|
4330
|
-
console.log(
|
|
4331
|
-
console.log(
|
|
5307
|
+
console.log(pc26.dim(header));
|
|
5308
|
+
console.log(pc26.dim(` ${"-".repeat(nameWidth)} ${"-".repeat(clientWidth)} ------`));
|
|
4332
5309
|
for (const action of actions) {
|
|
4333
|
-
const clientDisplay =
|
|
5310
|
+
const clientDisplay = CLIENT_DISPLAY4[action.client] ?? action.client;
|
|
4334
5311
|
const [icon, statusText] = formatAction(action.action);
|
|
4335
5312
|
console.log(
|
|
4336
5313
|
` ${pad3(action.server, nameWidth)} ${pad3(clientDisplay, clientWidth)} ${icon} ${statusText}`
|
|
@@ -4341,13 +5318,13 @@ function printDiffTable(actions) {
|
|
|
4341
5318
|
function formatAction(action) {
|
|
4342
5319
|
switch (action) {
|
|
4343
5320
|
case "add":
|
|
4344
|
-
return [
|
|
5321
|
+
return [pc26.green("+"), pc26.green("missing \u2014 will add")];
|
|
4345
5322
|
case "extra":
|
|
4346
|
-
return [
|
|
5323
|
+
return [pc26.yellow("?"), pc26.yellow("extra (not in lockfile)")];
|
|
4347
5324
|
case "remove":
|
|
4348
|
-
return [
|
|
5325
|
+
return [pc26.red("\u2013"), pc26.red("extra \u2014 will remove")];
|
|
4349
5326
|
case "ok":
|
|
4350
|
-
return [
|
|
5327
|
+
return [pc26.dim("\xB7"), pc26.dim("in sync")];
|
|
4351
5328
|
}
|
|
4352
5329
|
}
|
|
4353
5330
|
function pad3(s, width) {
|
|
@@ -4355,11 +5332,11 @@ function pad3(s, width) {
|
|
|
4355
5332
|
}
|
|
4356
5333
|
|
|
4357
5334
|
// src/commands/test-command.ts
|
|
4358
|
-
import { defineCommand as
|
|
4359
|
-
import
|
|
5335
|
+
import { defineCommand as defineCommand28 } from "citty";
|
|
5336
|
+
import pc27 from "picocolors";
|
|
4360
5337
|
|
|
4361
5338
|
// src/core/mcp-tester.ts
|
|
4362
|
-
import { spawn as
|
|
5339
|
+
import { spawn as spawn6 } from "child_process";
|
|
4363
5340
|
var TIMEOUT_MS = 1e4;
|
|
4364
5341
|
async function testMcpServer(serverName, command, args, env) {
|
|
4365
5342
|
const start = Date.now();
|
|
@@ -4369,7 +5346,7 @@ async function testMcpServer(serverName, command, args, env) {
|
|
|
4369
5346
|
let initOk = false;
|
|
4370
5347
|
let toolsOk = false;
|
|
4371
5348
|
let tools = [];
|
|
4372
|
-
const child =
|
|
5349
|
+
const child = spawn6(command, args, {
|
|
4373
5350
|
env: { ...process.env, ...env },
|
|
4374
5351
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4375
5352
|
});
|
|
@@ -4454,7 +5431,7 @@ async function testMcpServer(serverName, command, args, env) {
|
|
|
4454
5431
|
}
|
|
4455
5432
|
|
|
4456
5433
|
// src/commands/test-command.ts
|
|
4457
|
-
var test_command_default =
|
|
5434
|
+
var test_command_default = defineCommand28({
|
|
4458
5435
|
meta: {
|
|
4459
5436
|
name: "test",
|
|
4460
5437
|
description: "Test MCP server connectivity and capabilities"
|
|
@@ -4475,10 +5452,10 @@ var test_command_default = defineCommand22({
|
|
|
4475
5452
|
const lockfile = readLockfile();
|
|
4476
5453
|
const serverNames = args.all ? Object.keys(lockfile.servers) : args.server ? [args.server] : [];
|
|
4477
5454
|
if (serverNames.length === 0) {
|
|
4478
|
-
console.error(
|
|
5455
|
+
console.error(pc27.red(" Error: Specify a server name or use --all."));
|
|
4479
5456
|
process.exit(1);
|
|
4480
5457
|
}
|
|
4481
|
-
console.log(
|
|
5458
|
+
console.log(pc27.bold(`
|
|
4482
5459
|
mcpman test \u2014 ${serverNames.length} server(s)
|
|
4483
5460
|
`));
|
|
4484
5461
|
let passed = 0;
|
|
@@ -4486,7 +5463,7 @@ var test_command_default = defineCommand22({
|
|
|
4486
5463
|
for (const name of serverNames) {
|
|
4487
5464
|
const entry = lockfile.servers[name];
|
|
4488
5465
|
if (!entry) {
|
|
4489
|
-
console.log(` ${
|
|
5466
|
+
console.log(` ${pc27.red("\u2717")} ${pc27.bold(name)} \u2014 not installed`);
|
|
4490
5467
|
failed++;
|
|
4491
5468
|
continue;
|
|
4492
5469
|
}
|
|
@@ -4497,27 +5474,27 @@ var test_command_default = defineCommand22({
|
|
|
4497
5474
|
if (result.passed) {
|
|
4498
5475
|
passed++;
|
|
4499
5476
|
console.log(
|
|
4500
|
-
` ${
|
|
5477
|
+
` ${pc27.green("\u2713")} ${pc27.bold(name)} ${pc27.dim(`(${result.responseTimeMs}ms)`)}`
|
|
4501
5478
|
);
|
|
4502
5479
|
if (result.tools.length > 0) {
|
|
4503
|
-
console.log(
|
|
5480
|
+
console.log(pc27.dim(` Tools: ${result.tools.join(", ")}`));
|
|
4504
5481
|
}
|
|
4505
5482
|
} else {
|
|
4506
5483
|
failed++;
|
|
4507
|
-
console.log(` ${
|
|
5484
|
+
console.log(` ${pc27.red("\u2717")} ${pc27.bold(name)} ${pc27.dim(`(${result.responseTimeMs}ms)`)}`);
|
|
4508
5485
|
if (result.error) {
|
|
4509
|
-
console.log(` ${
|
|
5486
|
+
console.log(` ${pc27.red(result.error)}`);
|
|
4510
5487
|
}
|
|
4511
5488
|
console.log(
|
|
4512
|
-
` ${
|
|
5489
|
+
` ${pc27.dim("initialize:")} ${result.initializeOk ? pc27.green("ok") : pc27.red("fail")} ${pc27.dim("tools/list:")} ${result.toolsListOk ? pc27.green("ok") : pc27.red("fail")}`
|
|
4513
5490
|
);
|
|
4514
5491
|
}
|
|
4515
5492
|
}
|
|
4516
|
-
console.log(
|
|
5493
|
+
console.log(pc27.dim(`
|
|
4517
5494
|
${"\u2500".repeat(40)}`));
|
|
4518
5495
|
const parts = [];
|
|
4519
|
-
if (passed > 0) parts.push(
|
|
4520
|
-
if (failed > 0) parts.push(
|
|
5496
|
+
if (passed > 0) parts.push(pc27.green(`${passed} passed`));
|
|
5497
|
+
if (failed > 0) parts.push(pc27.red(`${failed} failed`));
|
|
4521
5498
|
console.log(` ${parts.join(", ")}
|
|
4522
5499
|
`);
|
|
4523
5500
|
if (failed > 0) process.exit(1);
|
|
@@ -4535,24 +5512,24 @@ async function loadVaultSecrets3(serverName) {
|
|
|
4535
5512
|
}
|
|
4536
5513
|
|
|
4537
5514
|
// src/commands/update.ts
|
|
4538
|
-
import * as
|
|
4539
|
-
import { defineCommand as
|
|
4540
|
-
import
|
|
5515
|
+
import * as p12 from "@clack/prompts";
|
|
5516
|
+
import { defineCommand as defineCommand29 } from "citty";
|
|
5517
|
+
import pc29 from "picocolors";
|
|
4541
5518
|
|
|
4542
5519
|
// src/core/update-notifier.ts
|
|
4543
|
-
import
|
|
5520
|
+
import fs14 from "fs";
|
|
4544
5521
|
import os3 from "os";
|
|
4545
|
-
import
|
|
4546
|
-
import
|
|
4547
|
-
var CACHE_FILE =
|
|
5522
|
+
import path14 from "path";
|
|
5523
|
+
import pc28 from "picocolors";
|
|
5524
|
+
var CACHE_FILE = path14.join(os3.homedir(), ".mcpman", ".update-check");
|
|
4548
5525
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4549
5526
|
function writeUpdateCache(data) {
|
|
4550
5527
|
try {
|
|
4551
|
-
const dir =
|
|
4552
|
-
if (!
|
|
5528
|
+
const dir = path14.dirname(CACHE_FILE);
|
|
5529
|
+
if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
|
|
4553
5530
|
const tmp = `${CACHE_FILE}.tmp`;
|
|
4554
|
-
|
|
4555
|
-
|
|
5531
|
+
fs14.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
5532
|
+
fs14.renameSync(tmp, CACHE_FILE);
|
|
4556
5533
|
} catch {
|
|
4557
5534
|
}
|
|
4558
5535
|
}
|
|
@@ -4560,7 +5537,7 @@ function writeUpdateCache(data) {
|
|
|
4560
5537
|
// src/commands/update.ts
|
|
4561
5538
|
async function loadClients3() {
|
|
4562
5539
|
try {
|
|
4563
|
-
const mod = await import("./client-detector-
|
|
5540
|
+
const mod = await import("./client-detector-NHXBDNMY.js");
|
|
4564
5541
|
return mod.getInstalledClients();
|
|
4565
5542
|
} catch {
|
|
4566
5543
|
return [];
|
|
@@ -4575,19 +5552,19 @@ function printTable(updates) {
|
|
|
4575
5552
|
"LATEST".padEnd(VER_W),
|
|
4576
5553
|
"STATUS"
|
|
4577
5554
|
].join(" ");
|
|
4578
|
-
console.log(
|
|
5555
|
+
console.log(pc29.bold(`
|
|
4579
5556
|
${header}`));
|
|
4580
|
-
console.log(
|
|
5557
|
+
console.log(pc29.dim(` ${"\u2500".repeat(NAME_W + VER_W * 2 + 20)}`));
|
|
4581
5558
|
for (const u of updates) {
|
|
4582
5559
|
const nameCol = u.server.slice(0, NAME_W).padEnd(NAME_W);
|
|
4583
5560
|
const curCol = u.currentVersion.padEnd(VER_W);
|
|
4584
5561
|
const latCol = u.latestVersion.padEnd(VER_W);
|
|
4585
|
-
const statusCol = u.hasUpdate ?
|
|
5562
|
+
const statusCol = u.hasUpdate ? pc29.yellow(`Update available${u.updateType ? ` [${u.updateType}]` : ""}`) : pc29.green("Up to date");
|
|
4586
5563
|
console.log(` ${nameCol} ${curCol} ${latCol} ${statusCol}`);
|
|
4587
5564
|
}
|
|
4588
5565
|
console.log();
|
|
4589
5566
|
}
|
|
4590
|
-
var update_default =
|
|
5567
|
+
var update_default = defineCommand29({
|
|
4591
5568
|
meta: {
|
|
4592
5569
|
name: "update",
|
|
4593
5570
|
description: "Check for and apply updates to installed MCP servers"
|
|
@@ -4626,7 +5603,7 @@ var update_default = defineCommand23({
|
|
|
4626
5603
|
}
|
|
4627
5604
|
process.exit(1);
|
|
4628
5605
|
}
|
|
4629
|
-
const spinner5 =
|
|
5606
|
+
const spinner5 = p12.spinner();
|
|
4630
5607
|
spinner5.start("Checking versions...");
|
|
4631
5608
|
let updates;
|
|
4632
5609
|
try {
|
|
@@ -4648,50 +5625,50 @@ var update_default = defineCommand23({
|
|
|
4648
5625
|
printTable(updates);
|
|
4649
5626
|
const outdated = updates.filter((u) => u.hasUpdate);
|
|
4650
5627
|
if (outdated.length === 0) {
|
|
4651
|
-
console.log(
|
|
5628
|
+
console.log(pc29.green(" All servers are up to date."));
|
|
4652
5629
|
return;
|
|
4653
5630
|
}
|
|
4654
5631
|
if (args.check) {
|
|
4655
5632
|
console.log(
|
|
4656
|
-
|
|
5633
|
+
pc29.yellow(` ${outdated.length} update(s) available. Run mcpman update to apply.`)
|
|
4657
5634
|
);
|
|
4658
5635
|
return;
|
|
4659
5636
|
}
|
|
4660
5637
|
if (!args.yes) {
|
|
4661
|
-
const confirmed = await
|
|
5638
|
+
const confirmed = await p12.confirm({
|
|
4662
5639
|
message: `Apply ${outdated.length} update(s)?`,
|
|
4663
5640
|
initialValue: true
|
|
4664
5641
|
});
|
|
4665
|
-
if (
|
|
4666
|
-
|
|
5642
|
+
if (p12.isCancel(confirmed) || !confirmed) {
|
|
5643
|
+
p12.outro("Cancelled.");
|
|
4667
5644
|
return;
|
|
4668
5645
|
}
|
|
4669
5646
|
}
|
|
4670
5647
|
const clients = await loadClients3();
|
|
4671
5648
|
let successCount = 0;
|
|
4672
5649
|
for (const update of outdated) {
|
|
4673
|
-
const s =
|
|
5650
|
+
const s = p12.spinner();
|
|
4674
5651
|
s.start(`Updating ${update.server}...`);
|
|
4675
5652
|
const result = await applyServerUpdate(update.server, servers[update.server], clients);
|
|
4676
5653
|
if (result.success) {
|
|
4677
|
-
s.stop(`${
|
|
5654
|
+
s.stop(`${pc29.green("\u2713")} ${update.server}: ${result.fromVersion} \u2192 ${result.toVersion}`);
|
|
4678
5655
|
successCount++;
|
|
4679
5656
|
} else {
|
|
4680
|
-
s.stop(`${
|
|
5657
|
+
s.stop(`${pc29.red("\u2717")} ${update.server}: ${result.error}`);
|
|
4681
5658
|
}
|
|
4682
5659
|
}
|
|
4683
5660
|
const freshLockfile = readLockfile(resolveLockfilePath());
|
|
4684
5661
|
const freshUpdates = await checkAllVersions(freshLockfile);
|
|
4685
5662
|
writeUpdateCache({ lastCheck: (/* @__PURE__ */ new Date()).toISOString(), updates: freshUpdates });
|
|
4686
|
-
|
|
5663
|
+
p12.outro(`${successCount} of ${outdated.length} server(s) updated.`);
|
|
4687
5664
|
}
|
|
4688
5665
|
});
|
|
4689
5666
|
|
|
4690
5667
|
// src/commands/upgrade.ts
|
|
4691
5668
|
import { execSync as execSync2 } from "child_process";
|
|
4692
|
-
import { defineCommand as
|
|
4693
|
-
import
|
|
4694
|
-
var upgrade_default =
|
|
5669
|
+
import { defineCommand as defineCommand30 } from "citty";
|
|
5670
|
+
import pc30 from "picocolors";
|
|
5671
|
+
var upgrade_default = defineCommand30({
|
|
4695
5672
|
meta: {
|
|
4696
5673
|
name: "upgrade",
|
|
4697
5674
|
description: "Upgrade mcpman to the latest version"
|
|
@@ -4704,42 +5681,42 @@ var upgrade_default = defineCommand24({
|
|
|
4704
5681
|
}
|
|
4705
5682
|
},
|
|
4706
5683
|
async run({ args }) {
|
|
4707
|
-
console.log(
|
|
5684
|
+
console.log(pc30.dim(` Current version: ${APP_VERSION}`));
|
|
4708
5685
|
let latest;
|
|
4709
5686
|
try {
|
|
4710
5687
|
latest = execSync2("npm view mcpman version", { encoding: "utf-8", timeout: 15e3 }).trim();
|
|
4711
5688
|
} catch {
|
|
4712
|
-
console.error(
|
|
5689
|
+
console.error(pc30.red(" Error: Could not check latest version from npm."));
|
|
4713
5690
|
process.exit(1);
|
|
4714
5691
|
}
|
|
4715
5692
|
if (latest === APP_VERSION) {
|
|
4716
|
-
console.log(
|
|
5693
|
+
console.log(pc30.green(" \u2713 Already on the latest version."));
|
|
4717
5694
|
return;
|
|
4718
5695
|
}
|
|
4719
|
-
console.log(
|
|
5696
|
+
console.log(pc30.yellow(` Update available: ${APP_VERSION} \u2192 ${latest}`));
|
|
4720
5697
|
if (args.check) {
|
|
4721
|
-
console.log(
|
|
5698
|
+
console.log(pc30.dim(" Run mcpman upgrade to install."));
|
|
4722
5699
|
return;
|
|
4723
5700
|
}
|
|
4724
|
-
console.log(
|
|
5701
|
+
console.log(pc30.dim(" Installing..."));
|
|
4725
5702
|
try {
|
|
4726
5703
|
execSync2(`npm install -g mcpman@${latest}`, { stdio: "inherit", timeout: 6e4 });
|
|
4727
|
-
console.log(
|
|
5704
|
+
console.log(pc30.green(`
|
|
4728
5705
|
\u2713 Upgraded to mcpman@${latest}`));
|
|
4729
5706
|
} catch {
|
|
4730
|
-
console.error(
|
|
5707
|
+
console.error(pc30.red(" Error: Upgrade failed. Try manually: npm install -g mcpman@latest"));
|
|
4731
5708
|
process.exit(1);
|
|
4732
5709
|
}
|
|
4733
5710
|
}
|
|
4734
5711
|
});
|
|
4735
5712
|
|
|
4736
5713
|
// src/commands/watch.ts
|
|
4737
|
-
import { defineCommand as
|
|
4738
|
-
import
|
|
5714
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
5715
|
+
import pc31 from "picocolors";
|
|
4739
5716
|
|
|
4740
5717
|
// src/core/file-watcher-service.ts
|
|
4741
|
-
import { spawn as
|
|
4742
|
-
import
|
|
5718
|
+
import { spawn as spawn7 } from "child_process";
|
|
5719
|
+
import fs15 from "fs";
|
|
4743
5720
|
var IGNORE_PATTERNS = [
|
|
4744
5721
|
"node_modules",
|
|
4745
5722
|
".git",
|
|
@@ -4751,7 +5728,7 @@ var IGNORE_PATTERNS = [
|
|
|
4751
5728
|
".tox"
|
|
4752
5729
|
];
|
|
4753
5730
|
function shouldIgnore(filename) {
|
|
4754
|
-
return IGNORE_PATTERNS.some((
|
|
5731
|
+
return IGNORE_PATTERNS.some((p13) => filename.includes(p13));
|
|
4755
5732
|
}
|
|
4756
5733
|
function hasWatchedExtension(filename, extensions) {
|
|
4757
5734
|
return extensions.some((ext) => filename.endsWith(`.${ext}`));
|
|
@@ -4777,7 +5754,7 @@ var ServerWatcher = class {
|
|
|
4777
5754
|
`);
|
|
4778
5755
|
this.spawnChild();
|
|
4779
5756
|
try {
|
|
4780
|
-
this.watcher =
|
|
5757
|
+
this.watcher = fs15.watch(options.watchDir, { recursive: true }, (_event, filename) => {
|
|
4781
5758
|
if (!filename) return;
|
|
4782
5759
|
this.onFileChange(filename);
|
|
4783
5760
|
});
|
|
@@ -4812,7 +5789,7 @@ var ServerWatcher = class {
|
|
|
4812
5789
|
process.stdout.write("\x1Bc");
|
|
4813
5790
|
}
|
|
4814
5791
|
console.log(` [${timestamp()}] Starting ${serverName}...`);
|
|
4815
|
-
this.child =
|
|
5792
|
+
this.child = spawn7(command, args, { env, stdio: ["pipe", "pipe", "pipe"] });
|
|
4816
5793
|
this.child.stdout?.on("data", (data) => {
|
|
4817
5794
|
process.stdout.write(` [stdout] ${data.toString().trimEnd()}
|
|
4818
5795
|
`);
|
|
@@ -4876,7 +5853,7 @@ var ServerWatcher = class {
|
|
|
4876
5853
|
// src/commands/watch.ts
|
|
4877
5854
|
var DEFAULT_EXTENSIONS = ["ts", "js", "json", "py", "mjs", "cjs"];
|
|
4878
5855
|
var DEFAULT_DEBOUNCE_MS = 300;
|
|
4879
|
-
var watch_default =
|
|
5856
|
+
var watch_default = defineCommand31({
|
|
4880
5857
|
meta: {
|
|
4881
5858
|
name: "watch",
|
|
4882
5859
|
description: "Watch a local MCP server for file changes and auto-restart"
|
|
@@ -4915,8 +5892,8 @@ var watch_default = defineCommand25({
|
|
|
4915
5892
|
const lockfile = readLockfile();
|
|
4916
5893
|
const entry = lockfile.servers[serverName];
|
|
4917
5894
|
if (!entry) {
|
|
4918
|
-
console.error(
|
|
4919
|
-
console.error(
|
|
5895
|
+
console.error(pc31.red(` Error: Server '${serverName}' not found in lockfile.`));
|
|
5896
|
+
console.error(pc31.dim(` Run ${pc31.cyan("mcpman link .")} to register a local server.`));
|
|
4920
5897
|
process.exit(1);
|
|
4921
5898
|
}
|
|
4922
5899
|
let watchDir = args.dir;
|
|
@@ -4924,8 +5901,8 @@ var watch_default = defineCommand25({
|
|
|
4924
5901
|
if (entry.source === "local" && entry.resolved) {
|
|
4925
5902
|
watchDir = entry.resolved;
|
|
4926
5903
|
} else {
|
|
4927
|
-
console.error(
|
|
4928
|
-
console.error(
|
|
5904
|
+
console.error(pc31.red(` Error: Cannot determine watch directory for '${serverName}'.`));
|
|
5905
|
+
console.error(pc31.dim(" Use --dir to specify the directory to watch."));
|
|
4929
5906
|
process.exit(1);
|
|
4930
5907
|
}
|
|
4931
5908
|
}
|
|
@@ -4971,12 +5948,12 @@ async function loadVaultSecrets4(serverName) {
|
|
|
4971
5948
|
}
|
|
4972
5949
|
|
|
4973
5950
|
// src/commands/why.ts
|
|
4974
|
-
import { defineCommand as
|
|
4975
|
-
import
|
|
5951
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
5952
|
+
import pc32 from "picocolors";
|
|
4976
5953
|
|
|
4977
5954
|
// src/core/why-service.ts
|
|
4978
|
-
import
|
|
4979
|
-
import
|
|
5955
|
+
import fs16 from "fs";
|
|
5956
|
+
import path15 from "path";
|
|
4980
5957
|
var ALL_CLIENT_TYPES = ["claude-desktop", "cursor", "vscode", "windsurf"];
|
|
4981
5958
|
async function getServerProvenance(serverName, lockfilePath, profilesDir) {
|
|
4982
5959
|
const lockfile = readLockfile(lockfilePath);
|
|
@@ -5020,7 +5997,7 @@ async function buildClientStatuses(serverName, lockfileClients) {
|
|
|
5020
5997
|
}));
|
|
5021
5998
|
}
|
|
5022
5999
|
async function findOrphanedClients(serverName) {
|
|
5023
|
-
const { getInstalledClients: getInstalledClients2 } = await import("./client-detector-
|
|
6000
|
+
const { getInstalledClients: getInstalledClients2 } = await import("./client-detector-NHXBDNMY.js");
|
|
5024
6001
|
const handlers = await getInstalledClients2();
|
|
5025
6002
|
const results = [];
|
|
5026
6003
|
for (const handler of handlers) {
|
|
@@ -5036,16 +6013,16 @@ async function findOrphanedClients(serverName) {
|
|
|
5036
6013
|
}
|
|
5037
6014
|
function scanProfiles(serverName, profilesDir) {
|
|
5038
6015
|
const found = [];
|
|
5039
|
-
if (!
|
|
6016
|
+
if (!fs16.existsSync(profilesDir)) return found;
|
|
5040
6017
|
let files;
|
|
5041
6018
|
try {
|
|
5042
|
-
files =
|
|
6019
|
+
files = fs16.readdirSync(profilesDir).filter((f) => f.endsWith(".json"));
|
|
5043
6020
|
} catch {
|
|
5044
6021
|
return found;
|
|
5045
6022
|
}
|
|
5046
6023
|
for (const file of files) {
|
|
5047
6024
|
try {
|
|
5048
|
-
const raw =
|
|
6025
|
+
const raw = fs16.readFileSync(path15.join(profilesDir, file), "utf-8");
|
|
5049
6026
|
const profile = JSON.parse(raw);
|
|
5050
6027
|
if (serverName in (profile.servers ?? {})) {
|
|
5051
6028
|
found.push(profile.name ?? file.replace(".json", ""));
|
|
@@ -5074,8 +6051,8 @@ function formatWhyOutput(result) {
|
|
|
5074
6051
|
if (result.profiles.length > 0) {
|
|
5075
6052
|
lines.push("");
|
|
5076
6053
|
lines.push(" Profiles:");
|
|
5077
|
-
for (const
|
|
5078
|
-
lines.push(` ${
|
|
6054
|
+
for (const p13 of result.profiles) {
|
|
6055
|
+
lines.push(` ${p13}`);
|
|
5079
6056
|
}
|
|
5080
6057
|
}
|
|
5081
6058
|
if (result.envVars.length > 0) {
|
|
@@ -5089,7 +6066,7 @@ function formatWhyOutput(result) {
|
|
|
5089
6066
|
}
|
|
5090
6067
|
|
|
5091
6068
|
// src/commands/why.ts
|
|
5092
|
-
var why_default =
|
|
6069
|
+
var why_default = defineCommand32({
|
|
5093
6070
|
meta: {
|
|
5094
6071
|
name: "why",
|
|
5095
6072
|
description: "Show why a server is installed (provenance, clients, profiles)"
|
|
@@ -5111,15 +6088,15 @@ var why_default = defineCommand26({
|
|
|
5111
6088
|
const asJson = args.json;
|
|
5112
6089
|
const result = await getServerProvenance(serverName);
|
|
5113
6090
|
if (!result) {
|
|
5114
|
-
console.error(
|
|
5115
|
-
console.error(
|
|
6091
|
+
console.error(pc32.red(` Server '${serverName}' not found in lockfile or any client config.`));
|
|
6092
|
+
console.error(pc32.dim(` Run ${pc32.cyan("mcpman list")} to see installed servers.`));
|
|
5116
6093
|
process.exit(1);
|
|
5117
6094
|
}
|
|
5118
6095
|
if (result.orphaned) {
|
|
5119
|
-
console.log(
|
|
6096
|
+
console.log(pc32.yellow(`
|
|
5120
6097
|
Server '${serverName}' is orphaned:`));
|
|
5121
|
-
console.log(
|
|
5122
|
-
console.log(
|
|
6098
|
+
console.log(pc32.dim(" Found in client config(s) but not in lockfile."));
|
|
6099
|
+
console.log(pc32.dim(` Run ${pc32.cyan("mcpman sync --remove")} to clean up.
|
|
5123
6100
|
`));
|
|
5124
6101
|
const registeredClients = result.clients.filter((c) => c.registered).map((c) => c.type);
|
|
5125
6102
|
if (registeredClients.length > 0) {
|
|
@@ -5142,7 +6119,7 @@ process.on("SIGINT", () => {
|
|
|
5142
6119
|
console.log("\nAborted.");
|
|
5143
6120
|
process.exit(130);
|
|
5144
6121
|
});
|
|
5145
|
-
var main =
|
|
6122
|
+
var main = defineCommand33({
|
|
5146
6123
|
meta: {
|
|
5147
6124
|
name: APP_NAME,
|
|
5148
6125
|
version: APP_VERSION,
|
|
@@ -5174,7 +6151,13 @@ var main = defineCommand27({
|
|
|
5174
6151
|
watch: watch_default,
|
|
5175
6152
|
registry: registry_default,
|
|
5176
6153
|
completions: completions_default,
|
|
5177
|
-
why: why_default
|
|
6154
|
+
why: why_default,
|
|
6155
|
+
env: env_default,
|
|
6156
|
+
bench: bench_default,
|
|
6157
|
+
diff: diff_default,
|
|
6158
|
+
group: group_default,
|
|
6159
|
+
pin: pin_default,
|
|
6160
|
+
rollback: rollback_default
|
|
5178
6161
|
}
|
|
5179
6162
|
});
|
|
5180
6163
|
runMain(main);
|