@wuyax/mcps 0.1.0-beta.1 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +554 -114
- package/dist/{chunk-XW2KL6W3.js → chunk-BUKA3NPJ.js} +1488 -203
- package/dist/cli.cjs +1425 -246
- package/dist/cli.js +9 -22
- package/dist/index.cjs +1485 -188
- package/dist/index.d.cts +248 -9
- package/dist/index.d.ts +248 -9
- package/dist/index.js +56 -1
- package/package.json +3 -1
package/dist/cli.cjs
CHANGED
|
@@ -23,11 +23,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/cli.ts
|
|
26
|
-
var
|
|
26
|
+
var import_commander5 = require("commander");
|
|
27
27
|
|
|
28
28
|
// src/cli/add.ts
|
|
29
|
-
var
|
|
30
|
-
var
|
|
29
|
+
var import_commander2 = require("commander");
|
|
30
|
+
var import_picocolors17 = __toESM(require("picocolors"), 1);
|
|
31
31
|
|
|
32
32
|
// src/agents.ts
|
|
33
33
|
var import_node_fs = require("fs");
|
|
@@ -483,9 +483,9 @@ var mcpAgentAliases = {
|
|
|
483
483
|
var getMcpAgentConfig = (agentType) => mcpAgents[agentType];
|
|
484
484
|
var getMcpAgentTypes = () => Object.values(mcpAgents).map((config) => config.name);
|
|
485
485
|
var isMcpAgentType = (value) => value in mcpAgents;
|
|
486
|
-
var resolveMcpAgentAlias = (
|
|
487
|
-
if (isMcpAgentType(
|
|
488
|
-
return mcpAgentAliases[
|
|
486
|
+
var resolveMcpAgentAlias = (input7) => {
|
|
487
|
+
if (isMcpAgentType(input7)) return input7;
|
|
488
|
+
return mcpAgentAliases[input7] ?? null;
|
|
489
489
|
};
|
|
490
490
|
var isMcpTransportSupported = (agent, transport) => agent.supportedTransports.includes(transport);
|
|
491
491
|
var detectProjectInstalledMcpAgents = (cwd) => getMcpAgentTypes().filter(
|
|
@@ -577,8 +577,6 @@ var buildMcpServerConfig = (parsed, options = {}) => {
|
|
|
577
577
|
};
|
|
578
578
|
|
|
579
579
|
// src/parse-server-config.ts
|
|
580
|
-
var isRemoteServerConfig = (config) => typeof config.url === "string" && config.url.length > 0;
|
|
581
|
-
var isStdioServerConfig = (config) => typeof config.command === "string" && config.command.length > 0;
|
|
582
580
|
var parseServerConfig = (raw) => {
|
|
583
581
|
if (!raw || typeof raw !== "object") return {};
|
|
584
582
|
const data = raw;
|
|
@@ -996,6 +994,74 @@ var agentConfigStore = new AgentConfigStore();
|
|
|
996
994
|
// src/utils/to-error-message.ts
|
|
997
995
|
var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
|
|
998
996
|
|
|
997
|
+
// src/resolve-config-clusters.ts
|
|
998
|
+
var getCandidateAgentsForScope = (options = {}) => {
|
|
999
|
+
return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
1000
|
+
};
|
|
1001
|
+
var getCoHostedAgents = (agentType, options = {}) => {
|
|
1002
|
+
const currentAgent = getMcpAgentConfig(agentType);
|
|
1003
|
+
const currentTarget = resolveMcpConfigTarget(currentAgent, options);
|
|
1004
|
+
const candidates = getCandidateAgentsForScope(options);
|
|
1005
|
+
const coHosted = [];
|
|
1006
|
+
for (const candidateType of candidates) {
|
|
1007
|
+
if (candidateType === agentType) continue;
|
|
1008
|
+
const candidateConfig = getMcpAgentConfig(candidateType);
|
|
1009
|
+
const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
|
|
1010
|
+
if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
|
|
1011
|
+
coHosted.push(candidateType);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return coHosted;
|
|
1015
|
+
};
|
|
1016
|
+
var resolveConfigClusters = (agentTypes, options = {}) => {
|
|
1017
|
+
const clustersByPath = /* @__PURE__ */ new Map();
|
|
1018
|
+
for (const agentType of agentTypes) {
|
|
1019
|
+
const agentConfig = getMcpAgentConfig(agentType);
|
|
1020
|
+
const target = resolveMcpConfigTarget(agentConfig, options);
|
|
1021
|
+
let keyMap = clustersByPath.get(target.configPath);
|
|
1022
|
+
if (!keyMap) {
|
|
1023
|
+
keyMap = /* @__PURE__ */ new Map();
|
|
1024
|
+
clustersByPath.set(target.configPath, keyMap);
|
|
1025
|
+
}
|
|
1026
|
+
let cluster = keyMap.get(target.configKey);
|
|
1027
|
+
if (!cluster) {
|
|
1028
|
+
const allCoHosted = getCoHostedAgents(agentType, options);
|
|
1029
|
+
cluster = {
|
|
1030
|
+
configPath: target.configPath,
|
|
1031
|
+
configKey: target.configKey,
|
|
1032
|
+
targetAgents: [],
|
|
1033
|
+
coHostedAgents: allCoHosted
|
|
1034
|
+
};
|
|
1035
|
+
keyMap.set(target.configKey, cluster);
|
|
1036
|
+
}
|
|
1037
|
+
if (!cluster.targetAgents.includes(agentType)) {
|
|
1038
|
+
cluster.targetAgents.push(agentType);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
const clusters = [];
|
|
1042
|
+
for (const keyMap of clustersByPath.values()) {
|
|
1043
|
+
for (const cluster of keyMap.values()) {
|
|
1044
|
+
cluster.coHostedAgents = cluster.coHostedAgents.filter(
|
|
1045
|
+
(co) => !cluster.targetAgents.includes(co)
|
|
1046
|
+
);
|
|
1047
|
+
clusters.push(cluster);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return clusters;
|
|
1051
|
+
};
|
|
1052
|
+
var sortAgentsWithClusters = (agentTypes, options = {}) => {
|
|
1053
|
+
const clusters = resolveConfigClusters(agentTypes, options);
|
|
1054
|
+
const sorted = [];
|
|
1055
|
+
for (const cluster of clusters) {
|
|
1056
|
+
for (const agent of cluster.targetAgents) {
|
|
1057
|
+
if (!sorted.includes(agent)) {
|
|
1058
|
+
sorted.push(agent);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
return sorted;
|
|
1063
|
+
};
|
|
1064
|
+
|
|
999
1065
|
// src/transforms/index.ts
|
|
1000
1066
|
var DIALECT_PRESETS = {
|
|
1001
1067
|
vscode: {
|
|
@@ -1204,32 +1270,69 @@ var transformServerConfigForAgent = (agent, serverName, config, context = { glob
|
|
|
1204
1270
|
};
|
|
1205
1271
|
|
|
1206
1272
|
// src/installer.ts
|
|
1207
|
-
var
|
|
1208
|
-
const
|
|
1273
|
+
var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => {
|
|
1274
|
+
const clusters = resolveConfigClusters(agentTypes, options);
|
|
1275
|
+
const resultsByAgent = /* @__PURE__ */ new Map();
|
|
1209
1276
|
const isGlobal = options.global ?? false;
|
|
1210
|
-
const
|
|
1211
|
-
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1277
|
+
for (const cluster of clusters) {
|
|
1278
|
+
const primaryAgentType = cluster.targetAgents[0];
|
|
1279
|
+
const primaryAgent = getMcpAgentConfig(primaryAgentType);
|
|
1280
|
+
try {
|
|
1281
|
+
const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
|
|
1282
|
+
global: isGlobal
|
|
1283
|
+
});
|
|
1284
|
+
agentConfigStore.writeServer(primaryAgent, serverName, transformed, options);
|
|
1285
|
+
for (const agentType of cluster.targetAgents) {
|
|
1286
|
+
resultsByAgent.set(agentType, {
|
|
1287
|
+
agent: agentType,
|
|
1288
|
+
success: true,
|
|
1289
|
+
path: cluster.configPath,
|
|
1290
|
+
coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
const errorMsg = toErrorMessage(error);
|
|
1295
|
+
for (const agentType of cluster.targetAgents) {
|
|
1296
|
+
resultsByAgent.set(agentType, {
|
|
1297
|
+
agent: agentType,
|
|
1298
|
+
success: false,
|
|
1299
|
+
path: cluster.configPath,
|
|
1300
|
+
error: errorMsg
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1224
1304
|
}
|
|
1305
|
+
return agentTypes.map((agentType) => resultsByAgent.get(agentType));
|
|
1306
|
+
};
|
|
1307
|
+
var installToCompatibleAgents = (serverName, serverConfig, options) => {
|
|
1308
|
+
const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
|
|
1309
|
+
const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
|
|
1310
|
+
const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
|
|
1311
|
+
const installedResults = installMcpServerForAgents(serverName, serverConfig, compatibleAgents, {
|
|
1312
|
+
global: isGlobal,
|
|
1313
|
+
cwd
|
|
1314
|
+
});
|
|
1315
|
+
const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
|
|
1316
|
+
return allAgents.map((agentType) => {
|
|
1317
|
+
const incompatibleReason = incompatibleMap.get(agentType);
|
|
1318
|
+
if (incompatibleReason) {
|
|
1319
|
+
return {
|
|
1320
|
+
agent: agentType,
|
|
1321
|
+
success: false,
|
|
1322
|
+
path: "",
|
|
1323
|
+
error: incompatibleReason
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
return installedMap.get(agentType);
|
|
1327
|
+
});
|
|
1225
1328
|
};
|
|
1226
1329
|
|
|
1227
1330
|
// src/utils/parse-mcp-agent-list.ts
|
|
1228
|
-
var parseMcpAgentList = (
|
|
1229
|
-
if (!
|
|
1230
|
-
if (
|
|
1331
|
+
var parseMcpAgentList = (input7) => {
|
|
1332
|
+
if (!input7 || input7.length === 0) return void 0;
|
|
1333
|
+
if (input7.includes("*")) return getMcpAgentTypes();
|
|
1231
1334
|
const resolved = [];
|
|
1232
|
-
for (const value of
|
|
1335
|
+
for (const value of input7) {
|
|
1233
1336
|
const agentType = resolveMcpAgentAlias(value);
|
|
1234
1337
|
if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
|
|
1235
1338
|
resolved.push(agentType);
|
|
@@ -1238,9 +1341,9 @@ var parseMcpAgentList = (input5) => {
|
|
|
1238
1341
|
};
|
|
1239
1342
|
|
|
1240
1343
|
// src/resolve-target-agents.ts
|
|
1241
|
-
var normalizeRequestedAgents = (
|
|
1242
|
-
if (!
|
|
1243
|
-
const rawList = [...
|
|
1344
|
+
var normalizeRequestedAgents = (input7) => {
|
|
1345
|
+
if (!input7 || input7.length === 0) return void 0;
|
|
1346
|
+
const rawList = [...input7];
|
|
1244
1347
|
if (rawList.every((item) => isMcpAgentType(item))) {
|
|
1245
1348
|
return rawList;
|
|
1246
1349
|
}
|
|
@@ -1300,29 +1403,29 @@ var REMOTE_URL_REGEX = /^https?:\/\//i;
|
|
|
1300
1403
|
var HAS_WHITESPACE_REGEX = /\s/;
|
|
1301
1404
|
var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
|
|
1302
1405
|
var PATH_SEPARATOR_REGEX = /[/\\]/;
|
|
1303
|
-
var stripVersionSuffix = (
|
|
1304
|
-
if (
|
|
1305
|
-
const secondAtIndex =
|
|
1306
|
-
if (secondAtIndex > 0) return
|
|
1307
|
-
return
|
|
1308
|
-
}
|
|
1309
|
-
const atIndex =
|
|
1310
|
-
if (atIndex > 0) return
|
|
1311
|
-
return
|
|
1312
|
-
};
|
|
1313
|
-
var stripScopePrefix = (
|
|
1314
|
-
if (!
|
|
1315
|
-
const parts =
|
|
1316
|
-
return parts[1] ||
|
|
1317
|
-
};
|
|
1318
|
-
var stripPathPrefix = (
|
|
1319
|
-
if (!PATH_SEPARATOR_REGEX.test(
|
|
1320
|
-
const segments =
|
|
1406
|
+
var stripVersionSuffix = (input7) => {
|
|
1407
|
+
if (input7.startsWith("@")) {
|
|
1408
|
+
const secondAtIndex = input7.indexOf("@", 1);
|
|
1409
|
+
if (secondAtIndex > 0) return input7.slice(0, secondAtIndex);
|
|
1410
|
+
return input7;
|
|
1411
|
+
}
|
|
1412
|
+
const atIndex = input7.lastIndexOf("@");
|
|
1413
|
+
if (atIndex > 0) return input7.slice(0, atIndex);
|
|
1414
|
+
return input7;
|
|
1415
|
+
};
|
|
1416
|
+
var stripScopePrefix = (input7) => {
|
|
1417
|
+
if (!input7.startsWith("@") || !input7.includes("/")) return input7;
|
|
1418
|
+
const parts = input7.split("/");
|
|
1419
|
+
return parts[1] || input7;
|
|
1420
|
+
};
|
|
1421
|
+
var stripPathPrefix = (input7) => {
|
|
1422
|
+
if (!PATH_SEPARATOR_REGEX.test(input7)) return input7;
|
|
1423
|
+
const segments = input7.split(PATH_SEPARATOR_REGEX);
|
|
1321
1424
|
const basename = segments[segments.length - 1];
|
|
1322
|
-
return basename ||
|
|
1425
|
+
return basename || input7;
|
|
1323
1426
|
};
|
|
1324
|
-
var extractPackageName = (
|
|
1325
|
-
let name = stripVersionSuffix(
|
|
1427
|
+
var extractPackageName = (input7) => {
|
|
1428
|
+
let name = stripVersionSuffix(input7);
|
|
1326
1429
|
name = stripScopePrefix(name);
|
|
1327
1430
|
name = stripPathPrefix(name);
|
|
1328
1431
|
name = name.replace(SCRIPT_EXTENSION_REGEX, "");
|
|
@@ -1340,9 +1443,9 @@ var extractPackageName = (input5) => {
|
|
|
1340
1443
|
}
|
|
1341
1444
|
return name || MCP_DEFAULT_SERVER_NAME;
|
|
1342
1445
|
};
|
|
1343
|
-
var inferNameFromUrl = (
|
|
1446
|
+
var inferNameFromUrl = (input7) => {
|
|
1344
1447
|
try {
|
|
1345
|
-
const url = new URL(
|
|
1448
|
+
const url = new URL(input7);
|
|
1346
1449
|
const host = url.hostname;
|
|
1347
1450
|
const labels = host.split(".").filter((segment) => segment.length > 0);
|
|
1348
1451
|
if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
|
|
@@ -1371,8 +1474,8 @@ var inferNameFromCommand = (command) => {
|
|
|
1371
1474
|
const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
|
|
1372
1475
|
return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
|
|
1373
1476
|
};
|
|
1374
|
-
var parseMcpSource = (
|
|
1375
|
-
const trimmed =
|
|
1477
|
+
var parseMcpSource = (input7) => {
|
|
1478
|
+
const trimmed = input7.trim();
|
|
1376
1479
|
if (trimmed.length === 0) {
|
|
1377
1480
|
throw new Error(
|
|
1378
1481
|
"Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
|
|
@@ -1425,18 +1528,11 @@ var installMcpServer = (options) => {
|
|
|
1425
1528
|
cwd,
|
|
1426
1529
|
transport: requestedTransport
|
|
1427
1530
|
});
|
|
1428
|
-
const
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
agent: agentType,
|
|
1434
|
-
success: false,
|
|
1435
|
-
path: "",
|
|
1436
|
-
error: incompatibleReason
|
|
1437
|
-
};
|
|
1438
|
-
}
|
|
1439
|
-
return installMcpServerForAgent(serverName, serverConfig, agentType, { global: isGlobal, cwd });
|
|
1531
|
+
const results = installToCompatibleAgents(serverName, serverConfig, {
|
|
1532
|
+
allAgents,
|
|
1533
|
+
incompatible,
|
|
1534
|
+
global: isGlobal,
|
|
1535
|
+
cwd
|
|
1440
1536
|
});
|
|
1441
1537
|
return { serverName, config: serverConfig, results };
|
|
1442
1538
|
};
|
|
@@ -1463,21 +1559,6 @@ var listInstalledMcpServers = (options = {}) => {
|
|
|
1463
1559
|
};
|
|
1464
1560
|
|
|
1465
1561
|
// src/remove.ts
|
|
1466
|
-
var removeMcpServerFromAgent = (serverName, agentType, options = {}) => {
|
|
1467
|
-
const agent = getMcpAgentConfig(agentType);
|
|
1468
|
-
const { target } = agentConfigStore.resolveTarget(agent, options);
|
|
1469
|
-
try {
|
|
1470
|
-
const { removed } = agentConfigStore.removeServer(agent, serverName, options);
|
|
1471
|
-
return { agent: agentType, path: target.configPath, removed };
|
|
1472
|
-
} catch (error) {
|
|
1473
|
-
return {
|
|
1474
|
-
agent: agentType,
|
|
1475
|
-
path: target.configPath,
|
|
1476
|
-
removed: false,
|
|
1477
|
-
error: toErrorMessage(error)
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
1562
|
var removeMcpServer = (options) => {
|
|
1482
1563
|
const { allAgents } = resolveTargetAgents({
|
|
1483
1564
|
requested: options.agents,
|
|
@@ -1485,24 +1566,153 @@ var removeMcpServer = (options) => {
|
|
|
1485
1566
|
global: options.global,
|
|
1486
1567
|
cwd: options.cwd
|
|
1487
1568
|
});
|
|
1569
|
+
const clusters = resolveConfigClusters(allAgents, {
|
|
1570
|
+
global: options.global,
|
|
1571
|
+
cwd: options.cwd
|
|
1572
|
+
});
|
|
1488
1573
|
const results = [];
|
|
1489
|
-
for (const
|
|
1490
|
-
const
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1574
|
+
for (const cluster of clusters) {
|
|
1575
|
+
const primaryAgentType = cluster.targetAgents[0];
|
|
1576
|
+
const primaryAgent = getMcpAgentConfig(primaryAgentType);
|
|
1577
|
+
try {
|
|
1578
|
+
const { removed } = agentConfigStore.removeServer(primaryAgent, options.name, {
|
|
1579
|
+
global: options.global,
|
|
1580
|
+
cwd: options.cwd
|
|
1581
|
+
});
|
|
1582
|
+
if (removed) {
|
|
1583
|
+
for (const agentType of cluster.targetAgents) {
|
|
1584
|
+
results.push({
|
|
1585
|
+
agent: agentType,
|
|
1586
|
+
path: cluster.configPath,
|
|
1587
|
+
removed: true,
|
|
1588
|
+
coAffectedAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
const errorMsg = toErrorMessage(error);
|
|
1594
|
+
for (const agentType of cluster.targetAgents) {
|
|
1595
|
+
results.push({
|
|
1596
|
+
agent: agentType,
|
|
1597
|
+
path: cluster.configPath,
|
|
1598
|
+
removed: false,
|
|
1599
|
+
error: errorMsg
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1495
1603
|
}
|
|
1496
1604
|
return results;
|
|
1497
1605
|
};
|
|
1498
1606
|
|
|
1607
|
+
// src/update-mcp-server.ts
|
|
1608
|
+
var toRemoteServerConfig = (config, defaultTransport = "http") => {
|
|
1609
|
+
const {
|
|
1610
|
+
command: _droppedCommand,
|
|
1611
|
+
args: _droppedArgs,
|
|
1612
|
+
env: _droppedEnv,
|
|
1613
|
+
...remoteConfig
|
|
1614
|
+
} = config;
|
|
1615
|
+
return {
|
|
1616
|
+
...remoteConfig,
|
|
1617
|
+
type: remoteConfig.type ?? defaultTransport
|
|
1618
|
+
};
|
|
1619
|
+
};
|
|
1620
|
+
var toStdioServerConfig = (config) => {
|
|
1621
|
+
const {
|
|
1622
|
+
url: _droppedUrl,
|
|
1623
|
+
type: _droppedType,
|
|
1624
|
+
headers: _droppedHeaders,
|
|
1625
|
+
...stdioConfig
|
|
1626
|
+
} = config;
|
|
1627
|
+
return stdioConfig;
|
|
1628
|
+
};
|
|
1629
|
+
var detectUpdateTransition = (incoming, previous) => {
|
|
1630
|
+
if (!previous) {
|
|
1631
|
+
return incoming.url ? "switch-to-remote" : "switch-to-stdio";
|
|
1632
|
+
}
|
|
1633
|
+
if (incoming.url && !incoming.command) {
|
|
1634
|
+
return "switch-to-remote";
|
|
1635
|
+
}
|
|
1636
|
+
if (incoming.command && !incoming.url) {
|
|
1637
|
+
return "switch-to-stdio";
|
|
1638
|
+
}
|
|
1639
|
+
if (incoming.url || !incoming.command && previous.url) {
|
|
1640
|
+
return "merge-remote";
|
|
1641
|
+
}
|
|
1642
|
+
return "merge-stdio";
|
|
1643
|
+
};
|
|
1644
|
+
var sanitizeUpdatedServerConfig = (incoming, previous) => {
|
|
1645
|
+
const transition = detectUpdateTransition(incoming, previous);
|
|
1646
|
+
const targetTransport = incoming.type ?? previous?.type ?? "http";
|
|
1647
|
+
switch (transition) {
|
|
1648
|
+
case "switch-to-remote": {
|
|
1649
|
+
const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
|
|
1650
|
+
return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
|
|
1651
|
+
}
|
|
1652
|
+
case "switch-to-stdio": {
|
|
1653
|
+
const cleanBase = previous ? toStdioServerConfig(previous) : {};
|
|
1654
|
+
return toStdioServerConfig({ ...cleanBase, ...incoming });
|
|
1655
|
+
}
|
|
1656
|
+
case "merge-remote": {
|
|
1657
|
+
return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
|
|
1658
|
+
}
|
|
1659
|
+
case "merge-stdio": {
|
|
1660
|
+
return toStdioServerConfig({ ...previous, ...incoming });
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1664
|
+
var updateMcpServer = (options) => {
|
|
1665
|
+
const isGlobal = options.global ?? false;
|
|
1666
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1667
|
+
let previousConfig = options.previousConfig;
|
|
1668
|
+
if (!previousConfig) {
|
|
1669
|
+
const existing = listInstalledMcpServers({
|
|
1670
|
+
global: isGlobal,
|
|
1671
|
+
cwd,
|
|
1672
|
+
agents: options.agents
|
|
1673
|
+
});
|
|
1674
|
+
const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
|
|
1675
|
+
if (found) {
|
|
1676
|
+
previousConfig = found.serverConfig;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
|
|
1680
|
+
let targetAgents = options.agents;
|
|
1681
|
+
if (!targetAgents || targetAgents.length === 0) {
|
|
1682
|
+
const existing = listInstalledMcpServers({ global: isGlobal, cwd });
|
|
1683
|
+
targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
|
|
1684
|
+
}
|
|
1685
|
+
const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
|
|
1686
|
+
const { allAgents, incompatible } = resolveTargetAgents({
|
|
1687
|
+
requested: targetAgents,
|
|
1688
|
+
global: isGlobal,
|
|
1689
|
+
cwd,
|
|
1690
|
+
transport: requestedTransport
|
|
1691
|
+
});
|
|
1692
|
+
const results = installToCompatibleAgents(options.serverName, serverConfig, {
|
|
1693
|
+
allAgents,
|
|
1694
|
+
incompatible,
|
|
1695
|
+
global: isGlobal,
|
|
1696
|
+
cwd
|
|
1697
|
+
});
|
|
1698
|
+
return {
|
|
1699
|
+
serverName: options.serverName,
|
|
1700
|
+
config: serverConfig,
|
|
1701
|
+
results,
|
|
1702
|
+
incompatible
|
|
1703
|
+
};
|
|
1704
|
+
};
|
|
1705
|
+
|
|
1499
1706
|
// src/interactive/main-menu.ts
|
|
1500
1707
|
var import_prompts10 = require("@inquirer/prompts");
|
|
1501
|
-
var
|
|
1708
|
+
var import_picocolors15 = __toESM(require("picocolors"), 1);
|
|
1502
1709
|
|
|
1503
1710
|
// src/interactive/wizard-add.ts
|
|
1504
1711
|
var import_prompts7 = require("@inquirer/prompts");
|
|
1505
|
-
var
|
|
1712
|
+
var import_picocolors11 = __toESM(require("picocolors"), 1);
|
|
1713
|
+
|
|
1714
|
+
// src/utils/co-hosted-feedback.ts
|
|
1715
|
+
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
1506
1716
|
|
|
1507
1717
|
// src/utils/logger.ts
|
|
1508
1718
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
@@ -1521,13 +1731,269 @@ var logger = {
|
|
|
1521
1731
|
}
|
|
1522
1732
|
};
|
|
1523
1733
|
|
|
1734
|
+
// src/utils/co-hosted-feedback.ts
|
|
1735
|
+
var formatCoHostedBadge = (kind, agents) => {
|
|
1736
|
+
if (!agents || agents.length === 0) return "";
|
|
1737
|
+
const label = kind === "configured" ? "co-configured" : "co-affected";
|
|
1738
|
+
return ` ${import_picocolors2.default.yellow(`(${label}: ${agents.join(", ")})`)}`;
|
|
1739
|
+
};
|
|
1740
|
+
var logCoHostedNotice = (kind, agents) => {
|
|
1741
|
+
if (!agents || agents.length === 0) return;
|
|
1742
|
+
const actionText = kind === "configured" ? "Also configured for" : "Also affects";
|
|
1743
|
+
logger.info(
|
|
1744
|
+
` ${import_picocolors2.default.dim("Note:")} ${actionText} co-hosted agent(s): ${import_picocolors2.default.yellow(agents.join(", "))}`
|
|
1745
|
+
);
|
|
1746
|
+
};
|
|
1747
|
+
|
|
1524
1748
|
// src/interactive/prompts/agents.ts
|
|
1525
|
-
var
|
|
1749
|
+
var import_picocolors6 = __toESM(require("picocolors"), 1);
|
|
1750
|
+
|
|
1751
|
+
// src/interactive/utils/build-linked-agent-choices.ts
|
|
1526
1752
|
var import_picocolors3 = __toESM(require("picocolors"), 1);
|
|
1753
|
+
var buildLinkedAgentChoices = (options) => {
|
|
1754
|
+
const { agents, checkedAgents, detectedAgents = [], scopeOptions = {} } = options;
|
|
1755
|
+
const alignedCheckedSet = new Set(checkedAgents);
|
|
1756
|
+
for (const agent of checkedAgents) {
|
|
1757
|
+
const coHosted = getCoHostedAgents(agent, scopeOptions);
|
|
1758
|
+
for (const co of coHosted) {
|
|
1759
|
+
if (agents.includes(co)) {
|
|
1760
|
+
alignedCheckedSet.add(co);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
return agents.map((agent) => {
|
|
1765
|
+
const config = getMcpAgentConfig(agent);
|
|
1766
|
+
const displayName = config?.displayName ?? agent;
|
|
1767
|
+
const isDetected = detectedAgents.includes(agent);
|
|
1768
|
+
const coHosted = getCoHostedAgents(agent, scopeOptions).filter(
|
|
1769
|
+
(co) => agents.includes(co)
|
|
1770
|
+
);
|
|
1771
|
+
const detectedBadge = isDetected ? import_picocolors3.default.green(" [detected]") : "";
|
|
1772
|
+
const sharedBadge = coHosted.length > 0 ? import_picocolors3.default.dim(` [shared: ${coHosted.join(", ")}]`) : "";
|
|
1773
|
+
const label = `${displayName} ${import_picocolors3.default.dim(`(${agent})`)}${detectedBadge}${sharedBadge}`;
|
|
1774
|
+
return {
|
|
1775
|
+
name: label,
|
|
1776
|
+
value: agent,
|
|
1777
|
+
checked: alignedCheckedSet.has(agent),
|
|
1778
|
+
linkedValues: coHosted,
|
|
1779
|
+
description: coHosted.length > 0 ? `Linked with ${coHosted.map((a) => getMcpAgentConfig(a).displayName).join(", ")} (shared configuration)` : void 0
|
|
1780
|
+
};
|
|
1781
|
+
});
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1784
|
+
// src/interactive/prompts/linked-checkbox.ts
|
|
1785
|
+
var import_core = require("@inquirer/core");
|
|
1786
|
+
var import_picocolors4 = __toESM(require("picocolors"), 1);
|
|
1787
|
+
var defaultTheme = {
|
|
1788
|
+
icon: {
|
|
1789
|
+
checked: import_picocolors4.default.green("[x]"),
|
|
1790
|
+
unchecked: import_picocolors4.default.dim("[ ]"),
|
|
1791
|
+
cursor: import_picocolors4.default.cyan(">"),
|
|
1792
|
+
disabledChecked: import_picocolors4.default.dim("[x]"),
|
|
1793
|
+
disabledUnchecked: import_picocolors4.default.dim("[-]")
|
|
1794
|
+
},
|
|
1795
|
+
style: {
|
|
1796
|
+
disabled: (text) => import_picocolors4.default.dim(text),
|
|
1797
|
+
renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
|
|
1798
|
+
description: (text) => import_picocolors4.default.cyan(text),
|
|
1799
|
+
keysHelpTip: (keys) => keys.map(([key, action]) => `${import_picocolors4.default.bold(key)} ${import_picocolors4.default.dim(action)}`).join(import_picocolors4.default.dim(" | ")),
|
|
1800
|
+
highlight: (text) => import_picocolors4.default.cyan(text)
|
|
1801
|
+
},
|
|
1802
|
+
i18n: {
|
|
1803
|
+
disabledError: "This option is disabled and cannot be toggled."
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
function isSelectable(item) {
|
|
1807
|
+
return !import_core.Separator.isSeparator(item) && !item.disabled;
|
|
1808
|
+
}
|
|
1809
|
+
function isNavigable(item) {
|
|
1810
|
+
return !import_core.Separator.isSeparator(item);
|
|
1811
|
+
}
|
|
1812
|
+
function isChecked(item) {
|
|
1813
|
+
return !import_core.Separator.isSeparator(item) && item.checked;
|
|
1814
|
+
}
|
|
1815
|
+
function normalizeChoices(choices) {
|
|
1816
|
+
return choices.map((choice) => {
|
|
1817
|
+
if (import_core.Separator.isSeparator(choice)) {
|
|
1818
|
+
return choice;
|
|
1819
|
+
}
|
|
1820
|
+
if (typeof choice !== "object" || choice === null || !("value" in choice)) {
|
|
1821
|
+
const name2 = String(choice);
|
|
1822
|
+
return {
|
|
1823
|
+
value: choice,
|
|
1824
|
+
name: name2,
|
|
1825
|
+
short: name2,
|
|
1826
|
+
checkedName: name2,
|
|
1827
|
+
disabled: false,
|
|
1828
|
+
checked: false,
|
|
1829
|
+
linkedValues: []
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
const name = choice.name ?? String(choice.value);
|
|
1833
|
+
return {
|
|
1834
|
+
value: choice.value,
|
|
1835
|
+
name,
|
|
1836
|
+
short: choice.short ?? name,
|
|
1837
|
+
checkedName: choice.checkedName ?? name,
|
|
1838
|
+
description: choice.description,
|
|
1839
|
+
disabled: choice.disabled ?? false,
|
|
1840
|
+
checked: choice.checked ?? false,
|
|
1841
|
+
linkedValues: choice.linkedValues ?? []
|
|
1842
|
+
};
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
var linkedCheckbox = (0, import_core.createPrompt)(
|
|
1846
|
+
(config, done) => {
|
|
1847
|
+
const { pageSize = 10, loop = true, required, validate = () => true } = config;
|
|
1848
|
+
const theme = (0, import_core.makeTheme)(defaultTheme, config.theme);
|
|
1849
|
+
const [status, setStatus] = (0, import_core.useState)("idle");
|
|
1850
|
+
const prefix = (0, import_core.usePrefix)({ status, theme });
|
|
1851
|
+
const [items, setItems] = (0, import_core.useState)(() => normalizeChoices(config.choices));
|
|
1852
|
+
const bounds = (0, import_core.useMemo)(() => {
|
|
1853
|
+
const first = items.findIndex(isNavigable);
|
|
1854
|
+
let last = -1;
|
|
1855
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
1856
|
+
if (isNavigable(items[i])) {
|
|
1857
|
+
last = i;
|
|
1858
|
+
break;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
if (first === -1 || last === -1) {
|
|
1862
|
+
throw new import_core.ValidationError("[linkedCheckbox prompt] No selectable choices.");
|
|
1863
|
+
}
|
|
1864
|
+
return { first, last };
|
|
1865
|
+
}, [items]);
|
|
1866
|
+
const [active, setActive] = (0, import_core.useState)(bounds.first);
|
|
1867
|
+
const [errorMsg, setError] = (0, import_core.useState)();
|
|
1868
|
+
const toggleWithLinked = (targetIndex) => {
|
|
1869
|
+
const targetItem = items[targetIndex];
|
|
1870
|
+
if (!targetItem || import_core.Separator.isSeparator(targetItem) || targetItem.disabled) {
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
const nextChecked = !targetItem.checked;
|
|
1874
|
+
const targetValue = targetItem.value;
|
|
1875
|
+
const linked = new Set(targetItem.linkedValues);
|
|
1876
|
+
setItems(
|
|
1877
|
+
(prevItems) => prevItems.map((item) => {
|
|
1878
|
+
if (import_core.Separator.isSeparator(item) || item.disabled) {
|
|
1879
|
+
return item;
|
|
1880
|
+
}
|
|
1881
|
+
const isTargetOrLinked = item.value === targetValue || linked.has(item.value) || item.linkedValues.includes(targetValue);
|
|
1882
|
+
if (isTargetOrLinked) {
|
|
1883
|
+
return { ...item, checked: nextChecked };
|
|
1884
|
+
}
|
|
1885
|
+
return item;
|
|
1886
|
+
})
|
|
1887
|
+
);
|
|
1888
|
+
};
|
|
1889
|
+
(0, import_core.useKeypress)(async (key) => {
|
|
1890
|
+
if ((0, import_core.isEnterKey)(key)) {
|
|
1891
|
+
const selection = items.filter(isChecked);
|
|
1892
|
+
const isValid = await validate([...selection]);
|
|
1893
|
+
if (required && selection.length === 0) {
|
|
1894
|
+
setError("At least one choice must be selected");
|
|
1895
|
+
} else if (isValid === true) {
|
|
1896
|
+
setStatus("done");
|
|
1897
|
+
done(selection.map((choice) => choice.value));
|
|
1898
|
+
} else {
|
|
1899
|
+
setError(typeof isValid === "string" ? isValid : "You must select a valid value");
|
|
1900
|
+
}
|
|
1901
|
+
} else if ((0, import_core.isUpKey)(key) || (0, import_core.isDownKey)(key)) {
|
|
1902
|
+
if (errorMsg) setError(void 0);
|
|
1903
|
+
if (loop || (0, import_core.isUpKey)(key) && active !== bounds.first || (0, import_core.isDownKey)(key) && active !== bounds.last) {
|
|
1904
|
+
const offset = (0, import_core.isUpKey)(key) ? -1 : 1;
|
|
1905
|
+
let next = active;
|
|
1906
|
+
do {
|
|
1907
|
+
next = (next + offset + items.length) % items.length;
|
|
1908
|
+
} while (!isNavigable(items[next]));
|
|
1909
|
+
setActive(next);
|
|
1910
|
+
}
|
|
1911
|
+
} else if ((0, import_core.isSpaceKey)(key)) {
|
|
1912
|
+
const activeItem = items[active];
|
|
1913
|
+
if (activeItem && !import_core.Separator.isSeparator(activeItem)) {
|
|
1914
|
+
if (activeItem.disabled) {
|
|
1915
|
+
setError(theme.i18n.disabledError);
|
|
1916
|
+
} else {
|
|
1917
|
+
setError(void 0);
|
|
1918
|
+
toggleWithLinked(active);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
} else if (key.name === "a") {
|
|
1922
|
+
const hasUnchecked = items.some((choice) => isSelectable(choice) && !choice.checked);
|
|
1923
|
+
setItems(
|
|
1924
|
+
(prevItems) => prevItems.map((item) => isSelectable(item) ? { ...item, checked: hasUnchecked } : item)
|
|
1925
|
+
);
|
|
1926
|
+
} else if ((0, import_core.isNumberKey)(key)) {
|
|
1927
|
+
const selectedIndex = Number(key.name) - 1;
|
|
1928
|
+
let selectableIndex = -1;
|
|
1929
|
+
const position = items.findIndex((item) => {
|
|
1930
|
+
if (import_core.Separator.isSeparator(item)) return false;
|
|
1931
|
+
selectableIndex++;
|
|
1932
|
+
return selectableIndex === selectedIndex;
|
|
1933
|
+
});
|
|
1934
|
+
const selectedItem = items[position];
|
|
1935
|
+
if (selectedItem && isSelectable(selectedItem)) {
|
|
1936
|
+
setActive(position);
|
|
1937
|
+
setError(void 0);
|
|
1938
|
+
toggleWithLinked(position);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
const message = theme.style.message(config.message, status);
|
|
1943
|
+
let description;
|
|
1944
|
+
const page = (0, import_core.usePagination)({
|
|
1945
|
+
items,
|
|
1946
|
+
active,
|
|
1947
|
+
renderItem({ item, isActive }) {
|
|
1948
|
+
if (import_core.Separator.isSeparator(item)) {
|
|
1949
|
+
return ` ${item.separator}`;
|
|
1950
|
+
}
|
|
1951
|
+
const cursor = isActive ? theme.icon.cursor : " ";
|
|
1952
|
+
if (item.disabled) {
|
|
1953
|
+
const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
|
|
1954
|
+
const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
|
|
1955
|
+
return theme.style.disabled(`${cursor} ${checkbox2} ${item.name} ${disabledLabel}`);
|
|
1956
|
+
}
|
|
1957
|
+
if (isActive) {
|
|
1958
|
+
description = item.description;
|
|
1959
|
+
}
|
|
1960
|
+
const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
|
|
1961
|
+
const name = item.checked ? item.checkedName : item.name;
|
|
1962
|
+
const color = isActive ? theme.style.highlight : (x) => x;
|
|
1963
|
+
return color(`${cursor} ${checkbox} ${name}`);
|
|
1964
|
+
},
|
|
1965
|
+
pageSize,
|
|
1966
|
+
loop
|
|
1967
|
+
});
|
|
1968
|
+
if (status === "done") {
|
|
1969
|
+
const selection = items.filter(isChecked);
|
|
1970
|
+
const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
|
|
1971
|
+
return [prefix, message, answer].filter(Boolean).join(" ");
|
|
1972
|
+
}
|
|
1973
|
+
const helpLine = theme.style.keysHelpTip([
|
|
1974
|
+
["up/down", "navigate"],
|
|
1975
|
+
["space", "toggle"],
|
|
1976
|
+
["a", "all"],
|
|
1977
|
+
["enter", "submit"]
|
|
1978
|
+
]);
|
|
1979
|
+
const lines = [
|
|
1980
|
+
[prefix, message].filter(Boolean).join(" "),
|
|
1981
|
+
page,
|
|
1982
|
+
helpLine
|
|
1983
|
+
];
|
|
1984
|
+
if (description) {
|
|
1985
|
+
lines.push(theme.style.description(description));
|
|
1986
|
+
}
|
|
1987
|
+
if (errorMsg) {
|
|
1988
|
+
lines.push(theme.style.error(errorMsg));
|
|
1989
|
+
}
|
|
1990
|
+
return lines.join("\n");
|
|
1991
|
+
}
|
|
1992
|
+
);
|
|
1527
1993
|
|
|
1528
1994
|
// src/interactive/prompts/scope.ts
|
|
1529
1995
|
var import_prompts = require("@inquirer/prompts");
|
|
1530
|
-
var
|
|
1996
|
+
var import_picocolors5 = __toESM(require("picocolors"), 1);
|
|
1531
1997
|
var promptScope = async (options = {}) => {
|
|
1532
1998
|
const initialGlobal = options.defaultGlobal ?? options.global;
|
|
1533
1999
|
if (initialGlobal !== void 0) {
|
|
@@ -1538,11 +2004,11 @@ var promptScope = async (options = {}) => {
|
|
|
1538
2004
|
message: options.message ?? "Select MCP scope:",
|
|
1539
2005
|
choices: [
|
|
1540
2006
|
{
|
|
1541
|
-
name: `Current Project - ${
|
|
2007
|
+
name: `Current Project - ${import_picocolors5.default.dim(cwd)}`,
|
|
1542
2008
|
value: false
|
|
1543
2009
|
},
|
|
1544
2010
|
{
|
|
1545
|
-
name: `Global User Config - ${
|
|
2011
|
+
name: `Global User Config - ${import_picocolors5.default.dim("applies across all projects")}`,
|
|
1546
2012
|
value: true
|
|
1547
2013
|
}
|
|
1548
2014
|
]
|
|
@@ -1562,26 +2028,23 @@ var promptScopeAndAgents = async (options = {}) => {
|
|
|
1562
2028
|
cwd
|
|
1563
2029
|
});
|
|
1564
2030
|
const detected = resolution.detected;
|
|
1565
|
-
const
|
|
2031
|
+
const rawAvailable = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
2032
|
+
const availableAgentTypes = sortAgentsWithClusters(rawAvailable, { global: isGlobal, cwd });
|
|
1566
2033
|
if (detected.length > 0) {
|
|
1567
2034
|
logger.info(
|
|
1568
|
-
`Detected configured agents: ${
|
|
2035
|
+
`Detected configured agents: ${import_picocolors6.default.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
|
|
1569
2036
|
);
|
|
1570
2037
|
} else {
|
|
1571
2038
|
logger.warn(`No active ${isGlobal ? "global" : "project"} agents detected`);
|
|
1572
2039
|
}
|
|
1573
2040
|
const defaultChecked = options.defaultAgents && options.defaultAgents.length > 0 ? options.defaultAgents : detected;
|
|
1574
|
-
const choices =
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
name: label,
|
|
1580
|
-
value: agentType,
|
|
1581
|
-
checked: defaultChecked.includes(agentType)
|
|
1582
|
-
};
|
|
2041
|
+
const choices = buildLinkedAgentChoices({
|
|
2042
|
+
agents: availableAgentTypes,
|
|
2043
|
+
checkedAgents: defaultChecked,
|
|
2044
|
+
detectedAgents: detected,
|
|
2045
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
1583
2046
|
});
|
|
1584
|
-
const selectedAgents = await (
|
|
2047
|
+
const selectedAgents = await linkedCheckbox({
|
|
1585
2048
|
message: "Select target agents (Space to select, Enter to confirm):",
|
|
1586
2049
|
choices,
|
|
1587
2050
|
validate: (chosen) => {
|
|
@@ -1598,7 +2061,7 @@ var promptScopeAndAgents = async (options = {}) => {
|
|
|
1598
2061
|
};
|
|
1599
2062
|
|
|
1600
2063
|
// src/interactive/prompts/args.ts
|
|
1601
|
-
var
|
|
2064
|
+
var import_prompts2 = require("@inquirer/prompts");
|
|
1602
2065
|
var parseArgsString = (rawText) => {
|
|
1603
2066
|
const matches = rawText.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g);
|
|
1604
2067
|
if (!matches) return [];
|
|
@@ -1613,32 +2076,67 @@ var promptArgsConfig = async (initialArgs = []) => {
|
|
|
1613
2076
|
if (initialArgs.length > 0) {
|
|
1614
2077
|
return initialArgs;
|
|
1615
2078
|
}
|
|
1616
|
-
const needArgs = await (0,
|
|
2079
|
+
const needArgs = await (0, import_prompts2.confirm)({
|
|
1617
2080
|
message: "Configure command arguments (e.g. file paths, connection strings)?",
|
|
1618
2081
|
default: false
|
|
1619
2082
|
});
|
|
1620
2083
|
if (!needArgs) {
|
|
1621
2084
|
return [];
|
|
1622
2085
|
}
|
|
1623
|
-
const raw = await (0,
|
|
2086
|
+
const raw = await (0, import_prompts2.input)({
|
|
1624
2087
|
message: "Enter command arguments (space-separated, wrap paths with spaces in quotes):",
|
|
1625
2088
|
validate: (val) => val.trim() ? true : "Arguments cannot be empty"
|
|
1626
2089
|
});
|
|
1627
2090
|
return parseArgsString(raw.trim());
|
|
1628
2091
|
};
|
|
2092
|
+
var formatArgsString = (args) => {
|
|
2093
|
+
return args.map((arg) => arg.includes(" ") || arg.includes('"') ? `"${arg.replace(/"/g, '\\"')}"` : arg).join(" ");
|
|
2094
|
+
};
|
|
2095
|
+
var promptEditArgs = async (currentArgs = []) => {
|
|
2096
|
+
const defaultStr = formatArgsString(currentArgs);
|
|
2097
|
+
const raw = await (0, import_prompts2.input)({
|
|
2098
|
+
message: "Edit command arguments (space-separated, wrap paths with spaces in quotes, leave empty to clear):",
|
|
2099
|
+
default: defaultStr
|
|
2100
|
+
});
|
|
2101
|
+
const trimmed = raw.trim();
|
|
2102
|
+
if (!trimmed) {
|
|
2103
|
+
return [];
|
|
2104
|
+
}
|
|
2105
|
+
return parseArgsString(trimmed);
|
|
2106
|
+
};
|
|
1629
2107
|
|
|
1630
2108
|
// src/interactive/prompts/env.ts
|
|
1631
2109
|
var import_prompts5 = require("@inquirer/prompts");
|
|
1632
|
-
var
|
|
2110
|
+
var import_picocolors9 = __toESM(require("picocolors"), 1);
|
|
2111
|
+
|
|
2112
|
+
// src/utils/mask-secret.ts
|
|
2113
|
+
var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
|
|
2114
|
+
var maskSecretValue = (key, value) => {
|
|
2115
|
+
if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
|
|
2116
|
+
return value;
|
|
2117
|
+
}
|
|
2118
|
+
return `${value.slice(0, 2)}***${value.slice(-2)}`;
|
|
2119
|
+
};
|
|
2120
|
+
var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
|
|
2121
|
+
var maskSecretHeader = (key, value) => {
|
|
2122
|
+
if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
|
|
2123
|
+
return value;
|
|
2124
|
+
}
|
|
2125
|
+
return `${value.slice(0, 4)}***${value.slice(-3)}`;
|
|
2126
|
+
};
|
|
2127
|
+
|
|
2128
|
+
// src/interactive/prompts/kv.ts
|
|
2129
|
+
var import_prompts4 = require("@inquirer/prompts");
|
|
2130
|
+
var import_picocolors8 = __toESM(require("picocolors"), 1);
|
|
1633
2131
|
|
|
1634
2132
|
// src/interactive/prompts/multiline.ts
|
|
1635
2133
|
var import_node_readline = require("readline");
|
|
1636
|
-
var
|
|
1637
|
-
var
|
|
2134
|
+
var import_prompts3 = require("@inquirer/prompts");
|
|
2135
|
+
var import_picocolors7 = __toESM(require("picocolors"), 1);
|
|
1638
2136
|
var readMultilineTextFromTerminal = async (message, endHint = "When done pasting, enter END on a new line or press Enter twice to finish") => {
|
|
1639
|
-
console.log(
|
|
2137
|
+
console.log(import_picocolors7.default.cyan(`
|
|
1640
2138
|
${message}`));
|
|
1641
|
-
console.log(
|
|
2139
|
+
console.log(import_picocolors7.default.dim(` (Hint: ${endHint})
|
|
1642
2140
|
`));
|
|
1643
2141
|
return new Promise((resolve) => {
|
|
1644
2142
|
const rl = (0, import_node_readline.createInterface)({
|
|
@@ -1682,7 +2180,7 @@ ${message}`));
|
|
|
1682
2180
|
};
|
|
1683
2181
|
var promptEditorText = async (options) => {
|
|
1684
2182
|
try {
|
|
1685
|
-
return await (0,
|
|
2183
|
+
return await (0, import_prompts3.editor)({
|
|
1686
2184
|
message: options.message,
|
|
1687
2185
|
default: options.defaultText ?? "",
|
|
1688
2186
|
postfix: options.postfix
|
|
@@ -1692,8 +2190,157 @@ var promptEditorText = async (options) => {
|
|
|
1692
2190
|
}
|
|
1693
2191
|
};
|
|
1694
2192
|
|
|
2193
|
+
// src/interactive/prompts/kv.ts
|
|
2194
|
+
var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
2195
|
+
let items = { ...currentItems };
|
|
2196
|
+
while (true) {
|
|
2197
|
+
const keys = Object.keys(items);
|
|
2198
|
+
console.log();
|
|
2199
|
+
if (keys.length === 0) {
|
|
2200
|
+
console.log(import_picocolors8.default.dim(` No ${options.itemsNoun} configured.`));
|
|
2201
|
+
} else {
|
|
2202
|
+
console.log(import_picocolors8.default.cyan(import_picocolors8.default.bold(` Configured ${options.title} (${keys.length}):`)));
|
|
2203
|
+
for (const [k, v] of Object.entries(items)) {
|
|
2204
|
+
const sep = options.separator === "=" ? "=" : ": ";
|
|
2205
|
+
console.log(` ${import_picocolors8.default.bold(k)}${sep}${import_picocolors8.default.dim(options.maskValue(k, v))}`);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
console.log();
|
|
2209
|
+
const choice = await (0, import_prompts4.select)({
|
|
2210
|
+
message: `Manage ${options.itemsNoun}:`,
|
|
2211
|
+
choices: [
|
|
2212
|
+
{
|
|
2213
|
+
name: "Open in system default editor ($EDITOR)",
|
|
2214
|
+
value: "editor"
|
|
2215
|
+
},
|
|
2216
|
+
{
|
|
2217
|
+
name: `Add or modify a ${options.itemNoun}`,
|
|
2218
|
+
value: "upsert"
|
|
2219
|
+
},
|
|
2220
|
+
...keys.length > 0 ? [
|
|
2221
|
+
{
|
|
2222
|
+
name: `Delete a ${options.itemNoun}`,
|
|
2223
|
+
value: "delete"
|
|
2224
|
+
}
|
|
2225
|
+
] : [],
|
|
2226
|
+
{
|
|
2227
|
+
name: `Paste multiline ${options.itemsNoun} into terminal`,
|
|
2228
|
+
value: "paste"
|
|
2229
|
+
},
|
|
2230
|
+
...keys.length > 0 ? [
|
|
2231
|
+
{
|
|
2232
|
+
name: `Clear all ${options.itemsNoun}`,
|
|
2233
|
+
value: "clear"
|
|
2234
|
+
}
|
|
2235
|
+
] : [],
|
|
2236
|
+
{
|
|
2237
|
+
name: `Done (finish editing ${options.itemsNoun})`,
|
|
2238
|
+
value: "done"
|
|
2239
|
+
}
|
|
2240
|
+
]
|
|
2241
|
+
});
|
|
2242
|
+
if (choice === "done") {
|
|
2243
|
+
return items;
|
|
2244
|
+
}
|
|
2245
|
+
if (choice === "editor") {
|
|
2246
|
+
const defaultText = options.formatText(items);
|
|
2247
|
+
const text = await promptEditorText({
|
|
2248
|
+
message: options.editorMessage,
|
|
2249
|
+
postfix: options.editorPostfix,
|
|
2250
|
+
defaultText
|
|
2251
|
+
});
|
|
2252
|
+
const parsed = options.parseText(text);
|
|
2253
|
+
items = parsed;
|
|
2254
|
+
logger.success(`${options.title} updated (${Object.keys(items).length} total)`);
|
|
2255
|
+
} else if (choice === "upsert") {
|
|
2256
|
+
const key = await (0, import_prompts4.input)({
|
|
2257
|
+
message: options.keyPromptMessage,
|
|
2258
|
+
validate: (val) => {
|
|
2259
|
+
const trimmed = val.trim();
|
|
2260
|
+
if (!trimmed) return `${options.itemNoun} name cannot be empty`;
|
|
2261
|
+
if (/\s/.test(trimmed)) return `${options.itemNoun} name cannot contain spaces`;
|
|
2262
|
+
return true;
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2265
|
+
const trimmedKey = key.trim();
|
|
2266
|
+
const existingVal = items[trimmedKey];
|
|
2267
|
+
const isSecret = options.isSecretKey(trimmedKey);
|
|
2268
|
+
let newVal;
|
|
2269
|
+
if (isSecret) {
|
|
2270
|
+
newVal = await (0, import_prompts4.password)({
|
|
2271
|
+
message: existingVal !== void 0 ? `New value for (${trimmedKey}) [leave empty to keep current]:` : `${options.valuePromptMessage} for (${trimmedKey}) [sensitive content masked]:`,
|
|
2272
|
+
mask: "*"
|
|
2273
|
+
});
|
|
2274
|
+
if (existingVal !== void 0 && newVal === "") {
|
|
2275
|
+
newVal = existingVal;
|
|
2276
|
+
}
|
|
2277
|
+
} else {
|
|
2278
|
+
newVal = await (0, import_prompts4.input)({
|
|
2279
|
+
message: `${options.valuePromptMessage} for (${trimmedKey}):`,
|
|
2280
|
+
default: existingVal
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
items[trimmedKey] = newVal;
|
|
2284
|
+
logger.success(`${existingVal !== void 0 ? "Updated" : "Added"}: ${import_picocolors8.default.cyan(trimmedKey)}`);
|
|
2285
|
+
} else if (choice === "delete") {
|
|
2286
|
+
const toDelete = await (0, import_prompts4.select)({
|
|
2287
|
+
message: `Select ${options.itemNoun} to delete:`,
|
|
2288
|
+
choices: [
|
|
2289
|
+
...keys.map((k) => ({ name: k, value: k })),
|
|
2290
|
+
{ name: "Cancel", value: "__cancel__" }
|
|
2291
|
+
]
|
|
2292
|
+
});
|
|
2293
|
+
if (toDelete !== "__cancel__") {
|
|
2294
|
+
delete items[toDelete];
|
|
2295
|
+
logger.success(`Deleted: ${import_picocolors8.default.cyan(toDelete)}`);
|
|
2296
|
+
}
|
|
2297
|
+
} else if (choice === "paste") {
|
|
2298
|
+
const pasted = await readMultilineTextFromTerminal(options.pasteMessage);
|
|
2299
|
+
const parsed = options.parseText(pasted);
|
|
2300
|
+
const count = Object.keys(parsed).length;
|
|
2301
|
+
if (count === 0) {
|
|
2302
|
+
logger.warn(`No valid ${options.itemsNoun} recognized`);
|
|
2303
|
+
} else {
|
|
2304
|
+
if (keys.length > 0) {
|
|
2305
|
+
const pasteMode = await (0, import_prompts4.select)({
|
|
2306
|
+
message: `How to apply pasted ${options.itemsNoun}?`,
|
|
2307
|
+
choices: [
|
|
2308
|
+
{ name: `Merge with existing ${options.itemsNoun}`, value: "merge" },
|
|
2309
|
+
{ name: `Replace all existing ${options.itemsNoun}`, value: "replace" }
|
|
2310
|
+
]
|
|
2311
|
+
});
|
|
2312
|
+
if (pasteMode === "replace") {
|
|
2313
|
+
items = parsed;
|
|
2314
|
+
} else {
|
|
2315
|
+
Object.assign(items, parsed);
|
|
2316
|
+
}
|
|
2317
|
+
} else {
|
|
2318
|
+
items = parsed;
|
|
2319
|
+
}
|
|
2320
|
+
logger.success(`Successfully applied ${import_picocolors8.default.cyan(String(count))} ${options.itemsNoun}`);
|
|
2321
|
+
}
|
|
2322
|
+
} else if (choice === "clear") {
|
|
2323
|
+
const confirmClear = await (0, import_prompts4.confirm)({
|
|
2324
|
+
message: `Are you sure you want to clear all ${options.itemsNoun}?`,
|
|
2325
|
+
default: false
|
|
2326
|
+
});
|
|
2327
|
+
if (confirmClear) {
|
|
2328
|
+
items = {};
|
|
2329
|
+
logger.success(`Cleared all ${options.itemsNoun}`);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
};
|
|
2334
|
+
|
|
1695
2335
|
// src/interactive/prompts/env.ts
|
|
1696
|
-
var
|
|
2336
|
+
var formatEnvText = (env) => {
|
|
2337
|
+
return Object.entries(env).map(([key, value]) => {
|
|
2338
|
+
if (/[\s"']/.test(value)) {
|
|
2339
|
+
return `${key}="${value.replace(/"/g, '\\"')}"`;
|
|
2340
|
+
}
|
|
2341
|
+
return `${key}=${value}`;
|
|
2342
|
+
}).join("\n");
|
|
2343
|
+
};
|
|
1697
2344
|
var parseEnvText = (rawText) => {
|
|
1698
2345
|
const result = {};
|
|
1699
2346
|
const lines = rawText.split(/\r?\n/);
|
|
@@ -1719,7 +2366,7 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1719
2366
|
const env = { ...initialEnv };
|
|
1720
2367
|
const initialCount = Object.keys(env).length;
|
|
1721
2368
|
if (initialCount > 0) {
|
|
1722
|
-
logger.info(`Includes ${
|
|
2369
|
+
logger.info(`Includes ${import_picocolors9.default.cyan(String(initialCount))} preset environment variables`);
|
|
1723
2370
|
}
|
|
1724
2371
|
const mode = await (0, import_prompts5.select)({
|
|
1725
2372
|
message: "Configure environment variables?",
|
|
@@ -1748,7 +2395,8 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1748
2395
|
if (mode === "paste" || mode === "editor") {
|
|
1749
2396
|
const pasted = mode === "editor" ? await promptEditorText({
|
|
1750
2397
|
message: "Paste or edit environment variables in editor, then save and exit:",
|
|
1751
|
-
postfix: ".env"
|
|
2398
|
+
postfix: ".env",
|
|
2399
|
+
defaultText: formatEnvText(env)
|
|
1752
2400
|
}) : await readMultilineTextFromTerminal("Paste .env formatted content (multiline supported):");
|
|
1753
2401
|
const parsed = parseEnvText(pasted);
|
|
1754
2402
|
const count = Object.keys(parsed).length;
|
|
@@ -1756,10 +2404,9 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1756
2404
|
logger.warn("No valid KEY=VALUE pairs recognized");
|
|
1757
2405
|
} else {
|
|
1758
2406
|
Object.assign(env, parsed);
|
|
1759
|
-
logger.success(`Successfully parsed ${
|
|
2407
|
+
logger.success(`Successfully parsed ${import_picocolors9.default.cyan(String(count))} environment variables:`);
|
|
1760
2408
|
for (const [k, v] of Object.entries(parsed)) {
|
|
1761
|
-
|
|
1762
|
-
console.log(` ${import_picocolors5.default.bold(k)}=${import_picocolors5.default.dim(masked)}`);
|
|
2409
|
+
console.log(` ${import_picocolors9.default.bold(k)}=${import_picocolors9.default.dim(maskSecretValue(k, v))}`);
|
|
1763
2410
|
}
|
|
1764
2411
|
}
|
|
1765
2412
|
return env;
|
|
@@ -1790,15 +2437,32 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1790
2437
|
});
|
|
1791
2438
|
}
|
|
1792
2439
|
env[trimmedKey] = val;
|
|
1793
|
-
logger.success(`Added: ${
|
|
2440
|
+
logger.success(`Added: ${import_picocolors9.default.cyan(trimmedKey)}`);
|
|
1794
2441
|
}
|
|
1795
2442
|
return env;
|
|
1796
2443
|
};
|
|
2444
|
+
var promptEditEnvConfig = async (currentEnv = {}) => promptEditKeyValueConfig(currentEnv, {
|
|
2445
|
+
title: "Environment Variables",
|
|
2446
|
+
itemNoun: "variable",
|
|
2447
|
+
itemsNoun: "environment variables",
|
|
2448
|
+
separator: "=",
|
|
2449
|
+
editorPostfix: ".env",
|
|
2450
|
+
editorMessage: "Edit environment variables in editor, then save and exit:",
|
|
2451
|
+
pasteMessage: "Paste .env formatted content (multiline supported):",
|
|
2452
|
+
keyPromptMessage: "Variable name (Key):",
|
|
2453
|
+
valuePromptMessage: "Value",
|
|
2454
|
+
isSecretKey: (k) => SECRET_KEY_PATTERN.test(k),
|
|
2455
|
+
maskValue: maskSecretValue,
|
|
2456
|
+
formatText: formatEnvText,
|
|
2457
|
+
parseText: parseEnvText
|
|
2458
|
+
});
|
|
1797
2459
|
|
|
1798
2460
|
// src/interactive/prompts/headers.ts
|
|
1799
2461
|
var import_prompts6 = require("@inquirer/prompts");
|
|
1800
|
-
var
|
|
1801
|
-
var
|
|
2462
|
+
var import_picocolors10 = __toESM(require("picocolors"), 1);
|
|
2463
|
+
var formatHeadersText = (headers) => {
|
|
2464
|
+
return Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
2465
|
+
};
|
|
1802
2466
|
var parseHeadersText = (rawText) => {
|
|
1803
2467
|
const result = {};
|
|
1804
2468
|
const lines = rawText.split(/\r?\n/);
|
|
@@ -1854,7 +2518,8 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
|
1854
2518
|
}
|
|
1855
2519
|
if (mode === "paste" || mode === "editor") {
|
|
1856
2520
|
const pasted = mode === "editor" ? await promptEditorText({
|
|
1857
|
-
message: "Paste or edit HTTP headers in editor, then save and exit:"
|
|
2521
|
+
message: "Paste or edit HTTP headers in editor, then save and exit:",
|
|
2522
|
+
defaultText: formatHeadersText(headers)
|
|
1858
2523
|
}) : await readMultilineTextFromTerminal(
|
|
1859
2524
|
"Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):"
|
|
1860
2525
|
);
|
|
@@ -1864,10 +2529,9 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
|
1864
2529
|
logger.warn("No valid Key: Value pairs recognized");
|
|
1865
2530
|
} else {
|
|
1866
2531
|
Object.assign(headers, parsed);
|
|
1867
|
-
logger.success(`Successfully parsed ${
|
|
2532
|
+
logger.success(`Successfully parsed ${import_picocolors10.default.cyan(String(count))} headers:`);
|
|
1868
2533
|
for (const [k, v] of Object.entries(parsed)) {
|
|
1869
|
-
|
|
1870
|
-
console.log(` ${import_picocolors6.default.bold(k)}: ${import_picocolors6.default.dim(masked)}`);
|
|
2534
|
+
console.log(` ${import_picocolors10.default.bold(k)}: ${import_picocolors10.default.dim(maskSecretHeader(k, v))}`);
|
|
1871
2535
|
}
|
|
1872
2536
|
}
|
|
1873
2537
|
return headers;
|
|
@@ -1898,15 +2562,29 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
|
1898
2562
|
});
|
|
1899
2563
|
}
|
|
1900
2564
|
headers[trimmedName] = val;
|
|
1901
|
-
logger.success(`Added: ${
|
|
2565
|
+
logger.success(`Added: ${import_picocolors10.default.cyan(trimmedName)}`);
|
|
1902
2566
|
}
|
|
1903
2567
|
return headers;
|
|
1904
2568
|
};
|
|
2569
|
+
var promptEditHeadersConfig = async (currentHeaders = {}) => promptEditKeyValueConfig(currentHeaders, {
|
|
2570
|
+
title: "HTTP Headers",
|
|
2571
|
+
itemNoun: "header",
|
|
2572
|
+
itemsNoun: "HTTP headers",
|
|
2573
|
+
separator: ":",
|
|
2574
|
+
editorMessage: "Edit HTTP headers in editor, then save and exit:",
|
|
2575
|
+
pasteMessage: "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):",
|
|
2576
|
+
keyPromptMessage: "Header name (e.g. Authorization):",
|
|
2577
|
+
valuePromptMessage: "Header value",
|
|
2578
|
+
isSecretKey: (k) => SECRET_HEADER_PATTERN.test(k),
|
|
2579
|
+
maskValue: maskSecretHeader,
|
|
2580
|
+
formatText: formatHeadersText,
|
|
2581
|
+
parseText: parseHeadersText
|
|
2582
|
+
});
|
|
1905
2583
|
|
|
1906
2584
|
// src/interactive/wizard-add.ts
|
|
1907
2585
|
var wizardAdd = async (initial = {}) => {
|
|
1908
2586
|
const cwd = initial.cwd ?? process.cwd();
|
|
1909
|
-
logger.info(
|
|
2587
|
+
logger.info(import_picocolors11.default.bold("Welcome to the MCP interactive add wizard"));
|
|
1910
2588
|
let source = initial.source;
|
|
1911
2589
|
if (!source) {
|
|
1912
2590
|
const sourceType = await (0, import_prompts7.select)({
|
|
@@ -1928,7 +2606,7 @@ var wizardAdd = async (initial = {}) => {
|
|
|
1928
2606
|
});
|
|
1929
2607
|
if (sourceType === "npm") {
|
|
1930
2608
|
source = await (0, import_prompts7.input)({
|
|
1931
|
-
message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres
|
|
2609
|
+
message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres):",
|
|
1932
2610
|
validate: (val) => val.trim() ? true : "Package name cannot be empty"
|
|
1933
2611
|
});
|
|
1934
2612
|
} else if (sourceType === "remote") {
|
|
@@ -1996,25 +2674,25 @@ var wizardAdd = async (initial = {}) => {
|
|
|
1996
2674
|
if (parsed.type !== "remote") {
|
|
1997
2675
|
env = await promptEnvConfig(env);
|
|
1998
2676
|
}
|
|
1999
|
-
console.log("\n" +
|
|
2000
|
-
console.log(` ${
|
|
2001
|
-
console.log(` ${
|
|
2002
|
-
console.log(` ${
|
|
2003
|
-
console.log(` ${
|
|
2004
|
-
console.log(` ${
|
|
2677
|
+
console.log("\n" + import_picocolors11.default.cyan(import_picocolors11.default.bold("Configuration Preview:")));
|
|
2678
|
+
console.log(` ${import_picocolors11.default.bold("Server Name:")} ${import_picocolors11.default.green(serverName)}`);
|
|
2679
|
+
console.log(` ${import_picocolors11.default.bold("Server Type:")} ${import_picocolors11.default.magenta(parsed.type)}`);
|
|
2680
|
+
console.log(` ${import_picocolors11.default.bold("Source/Command:")} ${import_picocolors11.default.dim(source)}`);
|
|
2681
|
+
console.log(` ${import_picocolors11.default.bold("Scope:")} ${isGlobal ? import_picocolors11.default.yellow("Global") : import_picocolors11.default.blue("Project")}`);
|
|
2682
|
+
console.log(` ${import_picocolors11.default.bold("Target Agents:")} ${import_picocolors11.default.cyan(selectedAgents.join(", "))}`);
|
|
2005
2683
|
if (args.length > 0) {
|
|
2006
|
-
console.log(` ${
|
|
2684
|
+
console.log(` ${import_picocolors11.default.bold("Arguments:")} ${import_picocolors11.default.dim(args.join(" "))}`);
|
|
2007
2685
|
}
|
|
2008
2686
|
if (transport) {
|
|
2009
|
-
console.log(` ${
|
|
2687
|
+
console.log(` ${import_picocolors11.default.bold("Transport:")} ${import_picocolors11.default.magenta(transport)}`);
|
|
2010
2688
|
}
|
|
2011
2689
|
const envKeys = Object.keys(env);
|
|
2012
2690
|
if (envKeys.length > 0) {
|
|
2013
|
-
console.log(` ${
|
|
2691
|
+
console.log(` ${import_picocolors11.default.bold("Environment Variables:")} ${import_picocolors11.default.dim(envKeys.join(", "))} (${envKeys.length})`);
|
|
2014
2692
|
}
|
|
2015
2693
|
const headerKeys = Object.keys(headers);
|
|
2016
2694
|
if (headerKeys.length > 0) {
|
|
2017
|
-
console.log(` ${
|
|
2695
|
+
console.log(` ${import_picocolors11.default.bold("Headers:")} ${import_picocolors11.default.dim(headerKeys.join(", "))} (${headerKeys.length})`);
|
|
2018
2696
|
}
|
|
2019
2697
|
console.log();
|
|
2020
2698
|
const proceed = await (0, import_prompts7.confirm)({
|
|
@@ -2037,41 +2715,103 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2037
2715
|
env
|
|
2038
2716
|
});
|
|
2039
2717
|
logger.info(
|
|
2040
|
-
`Writing ${
|
|
2718
|
+
`Writing ${import_picocolors11.default.bold(result.serverName)} to ${import_picocolors11.default.cyan(String(result.results.length))} agent config files...`
|
|
2041
2719
|
);
|
|
2042
2720
|
let allSuccess = true;
|
|
2043
2721
|
for (const record of result.results) {
|
|
2044
2722
|
if (record.success) {
|
|
2045
|
-
logger.success(
|
|
2723
|
+
logger.success(
|
|
2724
|
+
`${import_picocolors11.default.cyan(record.agent)}: Successfully written to ${import_picocolors11.default.dim(record.path)}${formatCoHostedBadge("configured", record.coConfiguredAgents)}`
|
|
2725
|
+
);
|
|
2046
2726
|
} else {
|
|
2047
2727
|
allSuccess = false;
|
|
2048
|
-
logger.error(`${
|
|
2728
|
+
logger.error(`${import_picocolors11.default.cyan(record.agent)}: Failed to write - ${record.error}`);
|
|
2049
2729
|
}
|
|
2050
2730
|
}
|
|
2051
2731
|
if (allSuccess) {
|
|
2052
|
-
logger.success(
|
|
2732
|
+
logger.success(import_picocolors11.default.bold(`MCP server "${serverName}" configured successfully!`));
|
|
2053
2733
|
}
|
|
2054
2734
|
return allSuccess;
|
|
2055
2735
|
};
|
|
2056
2736
|
|
|
2057
2737
|
// src/interactive/wizard-manage.ts
|
|
2058
2738
|
var import_prompts8 = require("@inquirer/prompts");
|
|
2059
|
-
var
|
|
2739
|
+
var import_picocolors13 = __toESM(require("picocolors"), 1);
|
|
2740
|
+
|
|
2741
|
+
// src/utils/display-server-details.ts
|
|
2742
|
+
var import_picocolors12 = __toESM(require("picocolors"), 1);
|
|
2743
|
+
var displayServerDetails = ({
|
|
2744
|
+
serverName,
|
|
2745
|
+
config,
|
|
2746
|
+
agents,
|
|
2747
|
+
hasDivergence,
|
|
2748
|
+
global: isGlobal,
|
|
2749
|
+
titlePrefix = "MCP Server Details"
|
|
2750
|
+
}) => {
|
|
2751
|
+
console.log("\n" + import_picocolors12.default.cyan(import_picocolors12.default.bold(`${titlePrefix}: [${serverName}]`)));
|
|
2752
|
+
if (isGlobal !== void 0) {
|
|
2753
|
+
console.log(` ${import_picocolors12.default.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
|
|
2754
|
+
}
|
|
2755
|
+
if (agents && agents.length > 0) {
|
|
2756
|
+
console.log(
|
|
2757
|
+
` ${import_picocolors12.default.bold("Configured Agents:")} ${import_picocolors12.default.green(agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
|
|
2758
|
+
);
|
|
2759
|
+
}
|
|
2760
|
+
if (hasDivergence) {
|
|
2761
|
+
console.log(
|
|
2762
|
+
` ${import_picocolors12.default.yellow(import_picocolors12.default.bold("Notice:"))} ${import_picocolors12.default.yellow("Configurations differ across installed agents. Showing configuration from the first agent.")}`
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
const isRemote = Boolean(config.url && config.url.length > 0);
|
|
2766
|
+
if (isRemote) {
|
|
2767
|
+
console.log(` ${import_picocolors12.default.bold("Transport:")} ${import_picocolors12.default.magenta(config.type ?? "http")}`);
|
|
2768
|
+
console.log(` ${import_picocolors12.default.bold("URL:")} ${import_picocolors12.default.dim(config.url ?? "")}`);
|
|
2769
|
+
const headerKeys = Object.keys(config.headers ?? {});
|
|
2770
|
+
if (headerKeys.length > 0) {
|
|
2771
|
+
console.log(` ${import_picocolors12.default.bold("Headers:")} ${import_picocolors12.default.cyan(String(headerKeys.length))}`);
|
|
2772
|
+
for (const [k, v] of Object.entries(config.headers ?? {})) {
|
|
2773
|
+
console.log(` ${import_picocolors12.default.bold(k)}: ${import_picocolors12.default.dim(maskSecretHeader(k, v))}`);
|
|
2774
|
+
}
|
|
2775
|
+
} else {
|
|
2776
|
+
console.log(` ${import_picocolors12.default.bold("Headers:")} ${import_picocolors12.default.dim("(none)")}`);
|
|
2777
|
+
}
|
|
2778
|
+
} else {
|
|
2779
|
+
console.log(` ${import_picocolors12.default.bold("Command:")} ${import_picocolors12.default.magenta(config.command ?? "")}`);
|
|
2780
|
+
const argsStr = config.args && config.args.length > 0 ? config.args.join(" ") : "(none)";
|
|
2781
|
+
console.log(` ${import_picocolors12.default.bold("Arguments:")} ${import_picocolors12.default.dim(argsStr)}`);
|
|
2782
|
+
const envKeys = Object.keys(config.env ?? {});
|
|
2783
|
+
if (envKeys.length > 0) {
|
|
2784
|
+
console.log(` ${import_picocolors12.default.bold("Environment Variables:")} ${import_picocolors12.default.cyan(String(envKeys.length))}`);
|
|
2785
|
+
for (const [k, v] of Object.entries(config.env ?? {})) {
|
|
2786
|
+
console.log(` ${import_picocolors12.default.bold(k)}=${import_picocolors12.default.dim(maskSecretValue(k, v))}`);
|
|
2787
|
+
}
|
|
2788
|
+
} else {
|
|
2789
|
+
console.log(` ${import_picocolors12.default.bold("Environment Variables:")} ${import_picocolors12.default.dim("(none)")}`);
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
console.log();
|
|
2793
|
+
};
|
|
2060
2794
|
|
|
2061
2795
|
// src/interactive/utils/group-installed-servers.ts
|
|
2062
2796
|
var normalizeServerConfig = parseServerConfig;
|
|
2063
2797
|
var groupInstalledServersByName = (installed) => {
|
|
2064
2798
|
const grouped = /* @__PURE__ */ new Map();
|
|
2065
2799
|
for (const item of installed) {
|
|
2800
|
+
const itemConfig = normalizeServerConfig(item.config);
|
|
2066
2801
|
let entry = grouped.get(item.serverName);
|
|
2067
2802
|
if (!entry) {
|
|
2068
2803
|
entry = {
|
|
2069
2804
|
serverName: item.serverName,
|
|
2070
2805
|
agents: [],
|
|
2071
2806
|
paths: [],
|
|
2072
|
-
config:
|
|
2807
|
+
config: itemConfig,
|
|
2808
|
+
hasDivergence: false
|
|
2073
2809
|
};
|
|
2074
2810
|
grouped.set(item.serverName, entry);
|
|
2811
|
+
} else if (!entry.hasDivergence) {
|
|
2812
|
+
if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
|
|
2813
|
+
entry.hasDivergence = true;
|
|
2814
|
+
}
|
|
2075
2815
|
}
|
|
2076
2816
|
if (!entry.agents.includes(item.agent)) {
|
|
2077
2817
|
entry.agents.push(item.agent);
|
|
@@ -2084,6 +2824,221 @@ var groupInstalledServersByName = (installed) => {
|
|
|
2084
2824
|
};
|
|
2085
2825
|
|
|
2086
2826
|
// src/interactive/wizard-manage.ts
|
|
2827
|
+
var promptSwitchServerType = async (currentConfig, serverName) => {
|
|
2828
|
+
const isRemote = Boolean(currentConfig.url && currentConfig.url.length > 0);
|
|
2829
|
+
if (isRemote) {
|
|
2830
|
+
const newCmd = await (0, import_prompts8.input)({
|
|
2831
|
+
message: "Executable command (e.g. node, npx):",
|
|
2832
|
+
validate: (val) => val.trim() ? true : "Command cannot be empty"
|
|
2833
|
+
});
|
|
2834
|
+
const newArgs = await promptEditArgs([]);
|
|
2835
|
+
const newEnv = await promptEditEnvConfig({});
|
|
2836
|
+
logger.success(`Switched [${serverName}] configuration to stdio mode`);
|
|
2837
|
+
return {
|
|
2838
|
+
command: newCmd.trim(),
|
|
2839
|
+
args: newArgs.length > 0 ? newArgs : void 0,
|
|
2840
|
+
env: Object.keys(newEnv).length > 0 ? newEnv : void 0
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
const newUrl = await (0, import_prompts8.input)({
|
|
2844
|
+
message: "Remote server URL:",
|
|
2845
|
+
validate: (val) => {
|
|
2846
|
+
const trimmed = val.trim();
|
|
2847
|
+
if (!trimmed) return "URL cannot be empty";
|
|
2848
|
+
if (!/^https?:\/\//i.test(trimmed)) {
|
|
2849
|
+
return "Please enter a valid URL starting with http:// or https://";
|
|
2850
|
+
}
|
|
2851
|
+
return true;
|
|
2852
|
+
}
|
|
2853
|
+
});
|
|
2854
|
+
const transport = await (0, import_prompts8.select)({
|
|
2855
|
+
message: "Select remote transport protocol:",
|
|
2856
|
+
choices: [
|
|
2857
|
+
{ name: "HTTP", value: "http" },
|
|
2858
|
+
{ name: "SSE (Server-Sent Events)", value: "sse" }
|
|
2859
|
+
],
|
|
2860
|
+
default: "http"
|
|
2861
|
+
});
|
|
2862
|
+
const newHeaders = await promptEditHeadersConfig({});
|
|
2863
|
+
logger.success(`Switched [${serverName}] configuration to remote mode`);
|
|
2864
|
+
return {
|
|
2865
|
+
url: newUrl.trim(),
|
|
2866
|
+
type: transport,
|
|
2867
|
+
headers: Object.keys(newHeaders).length > 0 ? newHeaders : void 0
|
|
2868
|
+
};
|
|
2869
|
+
};
|
|
2870
|
+
var handleEditServerConfig = async (options) => {
|
|
2871
|
+
const { targetGroup } = options;
|
|
2872
|
+
const isGlobal = options.global ?? false;
|
|
2873
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2874
|
+
const serverName = targetGroup.serverName;
|
|
2875
|
+
let workingConfig = {
|
|
2876
|
+
...targetGroup.config,
|
|
2877
|
+
args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
|
|
2878
|
+
env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
|
|
2879
|
+
headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
|
|
2880
|
+
};
|
|
2881
|
+
while (true) {
|
|
2882
|
+
const isRemote = Boolean(workingConfig.url && workingConfig.url.length > 0);
|
|
2883
|
+
displayServerDetails({
|
|
2884
|
+
serverName,
|
|
2885
|
+
config: workingConfig,
|
|
2886
|
+
titlePrefix: "Edit Server Configuration"
|
|
2887
|
+
});
|
|
2888
|
+
const editChoices = isRemote ? [
|
|
2889
|
+
{ name: "Edit HTTP Headers (headers)", value: "headers" },
|
|
2890
|
+
{ name: "Edit Remote URL (url)", value: "url" },
|
|
2891
|
+
{ name: "Edit Transport Protocol (type)", value: "transport" },
|
|
2892
|
+
{ name: "Switch to local command (stdio)", value: "switch_type" },
|
|
2893
|
+
{ name: "Reset changes to original", value: "reset" },
|
|
2894
|
+
{ name: "Save and apply changes", value: "save" },
|
|
2895
|
+
{ name: "Cancel (discard changes)", value: "cancel" }
|
|
2896
|
+
] : [
|
|
2897
|
+
{ name: "Edit Environment Variables (env)", value: "env" },
|
|
2898
|
+
{ name: "Edit Command Arguments (args)", value: "args" },
|
|
2899
|
+
{ name: "Edit Executable Command (command)", value: "command" },
|
|
2900
|
+
{ name: "Switch to remote server (HTTP/SSE)", value: "switch_type" },
|
|
2901
|
+
{ name: "Reset changes to original", value: "reset" },
|
|
2902
|
+
{ name: "Save and apply changes", value: "save" },
|
|
2903
|
+
{ name: "Cancel (discard changes)", value: "cancel" }
|
|
2904
|
+
];
|
|
2905
|
+
const editAction = await (0, import_prompts8.select)({
|
|
2906
|
+
message: `What would you like to modify in [${serverName}]?`,
|
|
2907
|
+
choices: editChoices
|
|
2908
|
+
});
|
|
2909
|
+
if (editAction === "cancel") {
|
|
2910
|
+
logger.info("Modification cancelled; changes discarded");
|
|
2911
|
+
return;
|
|
2912
|
+
}
|
|
2913
|
+
if (editAction === "reset") {
|
|
2914
|
+
workingConfig = {
|
|
2915
|
+
...targetGroup.config,
|
|
2916
|
+
args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
|
|
2917
|
+
env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
|
|
2918
|
+
headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
|
|
2919
|
+
};
|
|
2920
|
+
logger.info("Configuration reset to original");
|
|
2921
|
+
continue;
|
|
2922
|
+
}
|
|
2923
|
+
if (editAction === "switch_type") {
|
|
2924
|
+
workingConfig = await promptSwitchServerType(workingConfig, serverName);
|
|
2925
|
+
continue;
|
|
2926
|
+
}
|
|
2927
|
+
if (editAction === "env") {
|
|
2928
|
+
workingConfig.env = await promptEditEnvConfig(workingConfig.env ?? {});
|
|
2929
|
+
} else if (editAction === "args") {
|
|
2930
|
+
workingConfig.args = await promptEditArgs(workingConfig.args ?? []);
|
|
2931
|
+
} else if (editAction === "command") {
|
|
2932
|
+
const newCmd = await (0, import_prompts8.input)({
|
|
2933
|
+
message: "Executable command:",
|
|
2934
|
+
default: workingConfig.command,
|
|
2935
|
+
validate: (val) => val.trim() ? true : "Command cannot be empty"
|
|
2936
|
+
});
|
|
2937
|
+
workingConfig.command = newCmd.trim();
|
|
2938
|
+
} else if (editAction === "headers") {
|
|
2939
|
+
workingConfig.headers = await promptEditHeadersConfig(workingConfig.headers ?? {});
|
|
2940
|
+
} else if (editAction === "url") {
|
|
2941
|
+
const newUrl = await (0, import_prompts8.input)({
|
|
2942
|
+
message: "Remote server URL:",
|
|
2943
|
+
default: workingConfig.url,
|
|
2944
|
+
validate: (val) => {
|
|
2945
|
+
const trimmed = val.trim();
|
|
2946
|
+
if (!trimmed) return "URL cannot be empty";
|
|
2947
|
+
if (!/^https?:\/\//i.test(trimmed)) {
|
|
2948
|
+
return "Please enter a valid URL starting with http:// or https://";
|
|
2949
|
+
}
|
|
2950
|
+
return true;
|
|
2951
|
+
}
|
|
2952
|
+
});
|
|
2953
|
+
workingConfig.url = newUrl.trim();
|
|
2954
|
+
} else if (editAction === "transport") {
|
|
2955
|
+
workingConfig.type = await (0, import_prompts8.select)({
|
|
2956
|
+
message: "Select remote transport protocol:",
|
|
2957
|
+
choices: [
|
|
2958
|
+
{ name: "HTTP", value: "http" },
|
|
2959
|
+
{ name: "SSE (Server-Sent Events)", value: "sse" }
|
|
2960
|
+
],
|
|
2961
|
+
default: workingConfig.type === "sse" ? "sse" : "http"
|
|
2962
|
+
});
|
|
2963
|
+
} else if (editAction === "save") {
|
|
2964
|
+
let targetAgents = targetGroup.agents;
|
|
2965
|
+
if (targetGroup.agents.length > 1) {
|
|
2966
|
+
const sortedAgents = sortAgentsWithClusters(targetGroup.agents, { global: isGlobal, cwd });
|
|
2967
|
+
const choices = buildLinkedAgentChoices({
|
|
2968
|
+
agents: sortedAgents,
|
|
2969
|
+
checkedAgents: sortedAgents,
|
|
2970
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
2971
|
+
});
|
|
2972
|
+
targetAgents = await linkedCheckbox({
|
|
2973
|
+
message: "Select agents to update configuration (Space to toggle):",
|
|
2974
|
+
choices,
|
|
2975
|
+
loop: false,
|
|
2976
|
+
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
2977
|
+
});
|
|
2978
|
+
if (targetAgents.length < targetGroup.agents.length) {
|
|
2979
|
+
const unselected = targetGroup.agents.filter((a) => !targetAgents.includes(a));
|
|
2980
|
+
const unselectedNames = unselected.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
2981
|
+
logger.info(
|
|
2982
|
+
`Note: Updating only a subset of agents. Server configurations will diverge from: ${unselectedNames}.`
|
|
2983
|
+
);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
const requestedTransport = workingConfig.url ? workingConfig.type ?? "http" : "stdio";
|
|
2987
|
+
const resolution = resolveTargetAgents({
|
|
2988
|
+
requested: targetAgents,
|
|
2989
|
+
global: isGlobal,
|
|
2990
|
+
cwd,
|
|
2991
|
+
transport: requestedTransport
|
|
2992
|
+
});
|
|
2993
|
+
if (resolution.incompatible.length > 0) {
|
|
2994
|
+
for (const item of resolution.incompatible) {
|
|
2995
|
+
logger.warn(`Skipping ${import_picocolors13.default.cyan(item.agent)}: ${item.reason}`);
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
if (resolution.compatibleAgents.length === 0) {
|
|
2999
|
+
logger.error(
|
|
3000
|
+
`None of the selected agents support ${requestedTransport} transport. Cannot update.`
|
|
3001
|
+
);
|
|
3002
|
+
continue;
|
|
3003
|
+
}
|
|
3004
|
+
const agentNames = resolution.compatibleAgents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
3005
|
+
const confirmed = await (0, import_prompts8.confirm)({
|
|
3006
|
+
message: `Confirm updating configuration for [${serverName}] across: ${agentNames}?`,
|
|
3007
|
+
default: true
|
|
3008
|
+
});
|
|
3009
|
+
if (!confirmed) {
|
|
3010
|
+
logger.warn("Update cancelled");
|
|
3011
|
+
continue;
|
|
3012
|
+
}
|
|
3013
|
+
const updateResult = updateMcpServer({
|
|
3014
|
+
serverName,
|
|
3015
|
+
config: workingConfig,
|
|
3016
|
+
previousConfig: targetGroup.config,
|
|
3017
|
+
agents: resolution.compatibleAgents,
|
|
3018
|
+
global: isGlobal,
|
|
3019
|
+
cwd
|
|
3020
|
+
});
|
|
3021
|
+
let updatedAny = false;
|
|
3022
|
+
const succeededAgents = [];
|
|
3023
|
+
for (const res of updateResult.results) {
|
|
3024
|
+
if (res.success) {
|
|
3025
|
+
updatedAny = true;
|
|
3026
|
+
succeededAgents.push(res.agent);
|
|
3027
|
+
logger.success(
|
|
3028
|
+
`${import_picocolors13.default.cyan(res.agent)}: Successfully updated configuration in ${import_picocolors13.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
|
|
3029
|
+
);
|
|
3030
|
+
} else {
|
|
3031
|
+
logger.error(`${import_picocolors13.default.cyan(res.agent)}: Update failed - ${res.error}`);
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
if (updatedAny) {
|
|
3035
|
+
targetGroup.config = updateResult.config;
|
|
3036
|
+
logger.success(`Configuration for [${serverName}] updated successfully!`);
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
2087
3042
|
var wizardManage = async (options = {}) => {
|
|
2088
3043
|
const cwd = options.cwd ?? process.cwd();
|
|
2089
3044
|
const isGlobal = await promptScope({
|
|
@@ -2097,51 +3052,57 @@ var wizardManage = async (options = {}) => {
|
|
|
2097
3052
|
return;
|
|
2098
3053
|
}
|
|
2099
3054
|
const grouped = groupInstalledServersByName(installed);
|
|
3055
|
+
let pendingServerName = options.serverName;
|
|
3056
|
+
const refreshGroupedServers = () => {
|
|
3057
|
+
const freshInstalled = listInstalledMcpServers({ global: isGlobal, cwd });
|
|
3058
|
+
const freshGrouped = groupInstalledServersByName(freshInstalled);
|
|
3059
|
+
grouped.clear();
|
|
3060
|
+
for (const [name, grp] of freshGrouped) {
|
|
3061
|
+
grouped.set(name, grp);
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
2100
3064
|
while (true) {
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
choices
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
3065
|
+
let chosenServerName;
|
|
3066
|
+
if (pendingServerName && grouped.has(pendingServerName)) {
|
|
3067
|
+
chosenServerName = pendingServerName;
|
|
3068
|
+
pendingServerName = void 0;
|
|
3069
|
+
} else {
|
|
3070
|
+
pendingServerName = void 0;
|
|
3071
|
+
const choices = Array.from(grouped.values()).map((g) => {
|
|
3072
|
+
const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
3073
|
+
return {
|
|
3074
|
+
name: `${import_picocolors13.default.bold(g.serverName)} ${import_picocolors13.default.dim(`(configured in: ${agentNames})`)}`,
|
|
3075
|
+
value: g.serverName
|
|
3076
|
+
};
|
|
3077
|
+
});
|
|
3078
|
+
choices.push({
|
|
3079
|
+
name: `Back`,
|
|
3080
|
+
value: "__back__"
|
|
3081
|
+
});
|
|
3082
|
+
chosenServerName = await (0, import_prompts8.select)({
|
|
3083
|
+
message: "Select MCP server to manage or sync:",
|
|
3084
|
+
choices
|
|
3085
|
+
});
|
|
3086
|
+
if (chosenServerName === "__back__") {
|
|
3087
|
+
return;
|
|
3088
|
+
}
|
|
2118
3089
|
}
|
|
2119
3090
|
const targetGroup = grouped.get(chosenServerName);
|
|
2120
3091
|
if (!targetGroup) continue;
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
console.log(` ${import_picocolors8.default.bold("URL:")} ${import_picocolors8.default.dim(cfg.url)} (${cfg.type})`);
|
|
2129
|
-
if (cfg.headers && Object.keys(cfg.headers).length > 0) {
|
|
2130
|
-
console.log(` ${import_picocolors8.default.bold("Headers:")} ${Object.keys(cfg.headers).join(", ")}`);
|
|
2131
|
-
}
|
|
2132
|
-
} else if (isStdioServerConfig(cfg)) {
|
|
2133
|
-
console.log(` ${import_picocolors8.default.bold("Command:")} ${import_picocolors8.default.magenta(cfg.command)}`);
|
|
2134
|
-
if (cfg.args && cfg.args.length > 0) {
|
|
2135
|
-
console.log(` ${import_picocolors8.default.bold("Arguments:")} ${import_picocolors8.default.dim(cfg.args.join(" "))}`);
|
|
2136
|
-
}
|
|
2137
|
-
if (cfg.env && Object.keys(cfg.env).length > 0) {
|
|
2138
|
-
console.log(` ${import_picocolors8.default.bold("Environment Variables:")} ${import_picocolors8.default.dim(Object.keys(cfg.env).join(", "))}`);
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2141
|
-
console.log();
|
|
3092
|
+
displayServerDetails({
|
|
3093
|
+
serverName: chosenServerName,
|
|
3094
|
+
config: targetGroup.config,
|
|
3095
|
+
agents: targetGroup.agents,
|
|
3096
|
+
global: isGlobal,
|
|
3097
|
+
hasDivergence: targetGroup.hasDivergence
|
|
3098
|
+
});
|
|
2142
3099
|
const action = await (0, import_prompts8.select)({
|
|
2143
3100
|
message: `What would you like to do with [${chosenServerName}]?`,
|
|
2144
3101
|
choices: [
|
|
3102
|
+
{
|
|
3103
|
+
name: "Edit server configuration",
|
|
3104
|
+
value: "edit"
|
|
3105
|
+
},
|
|
2145
3106
|
{
|
|
2146
3107
|
name: "Sync / clone to other agents",
|
|
2147
3108
|
value: "sync"
|
|
@@ -2153,20 +3114,34 @@ var wizardManage = async (options = {}) => {
|
|
|
2153
3114
|
]
|
|
2154
3115
|
});
|
|
2155
3116
|
if (action === "back") continue;
|
|
3117
|
+
if (action === "edit") {
|
|
3118
|
+
await handleEditServerConfig({
|
|
3119
|
+
targetGroup,
|
|
3120
|
+
global: isGlobal,
|
|
3121
|
+
cwd
|
|
3122
|
+
});
|
|
3123
|
+
refreshGroupedServers();
|
|
3124
|
+
continue;
|
|
3125
|
+
}
|
|
2156
3126
|
if (action === "sync") {
|
|
2157
3127
|
const allAllowedAgents = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
2158
|
-
const
|
|
2159
|
-
if (
|
|
2160
|
-
logger.info(
|
|
3128
|
+
const rawCandidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
|
|
3129
|
+
if (rawCandidateAgents.length === 0) {
|
|
3130
|
+
logger.info(
|
|
3131
|
+
"All supported agents in this scope already have this MCP server configured; no sync needed"
|
|
3132
|
+
);
|
|
2161
3133
|
continue;
|
|
2162
3134
|
}
|
|
2163
|
-
const
|
|
3135
|
+
const candidateAgents = sortAgentsWithClusters(rawCandidateAgents, { global: isGlobal, cwd });
|
|
3136
|
+
const choices = buildLinkedAgentChoices({
|
|
3137
|
+
agents: candidateAgents,
|
|
3138
|
+
checkedAgents: [],
|
|
3139
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
3140
|
+
});
|
|
3141
|
+
const selectedToSync = await linkedCheckbox({
|
|
2164
3142
|
message: "Select target agents to sync to (Space to select):",
|
|
2165
|
-
choices
|
|
2166
|
-
|
|
2167
|
-
value: a,
|
|
2168
|
-
checked: false
|
|
2169
|
-
})),
|
|
3143
|
+
choices,
|
|
3144
|
+
loop: false,
|
|
2170
3145
|
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
2171
3146
|
});
|
|
2172
3147
|
const confirmed = await (0, import_prompts8.confirm)({
|
|
@@ -2177,25 +3152,37 @@ var wizardManage = async (options = {}) => {
|
|
|
2177
3152
|
logger.warn("Sync cancelled");
|
|
2178
3153
|
continue;
|
|
2179
3154
|
}
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
3155
|
+
const syncResult = updateMcpServer({
|
|
3156
|
+
serverName: chosenServerName,
|
|
3157
|
+
config: targetGroup.config,
|
|
3158
|
+
agents: selectedToSync,
|
|
3159
|
+
global: isGlobal,
|
|
3160
|
+
cwd
|
|
3161
|
+
});
|
|
3162
|
+
for (const item of syncResult.incompatible) {
|
|
3163
|
+
logger.warn(`Skipping ${import_picocolors13.default.cyan(item.agent)}: ${item.reason}`);
|
|
3164
|
+
}
|
|
3165
|
+
for (const res of syncResult.results) {
|
|
3166
|
+
if (syncResult.incompatible.some((i) => i.agent === res.agent)) {
|
|
3167
|
+
continue;
|
|
3168
|
+
}
|
|
2185
3169
|
if (res.success) {
|
|
2186
|
-
logger.success(
|
|
2187
|
-
|
|
3170
|
+
logger.success(
|
|
3171
|
+
`${import_picocolors13.default.cyan(res.agent)}: Successfully synced to ${import_picocolors13.default.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
|
|
3172
|
+
);
|
|
3173
|
+
targetGroup.agents.push(res.agent);
|
|
2188
3174
|
} else {
|
|
2189
|
-
logger.error(`${
|
|
3175
|
+
logger.error(`${import_picocolors13.default.cyan(res.agent)}: Sync failed - ${res.error}`);
|
|
2190
3176
|
}
|
|
2191
3177
|
}
|
|
3178
|
+
refreshGroupedServers();
|
|
2192
3179
|
}
|
|
2193
3180
|
}
|
|
2194
3181
|
};
|
|
2195
3182
|
|
|
2196
3183
|
// src/interactive/wizard-remove.ts
|
|
2197
3184
|
var import_prompts9 = require("@inquirer/prompts");
|
|
2198
|
-
var
|
|
3185
|
+
var import_picocolors14 = __toESM(require("picocolors"), 1);
|
|
2199
3186
|
var wizardRemove = async (options = {}) => {
|
|
2200
3187
|
const cwd = options.cwd ?? process.cwd();
|
|
2201
3188
|
const isGlobal = await promptScope({
|
|
@@ -2212,7 +3199,7 @@ var wizardRemove = async (options = {}) => {
|
|
|
2212
3199
|
let serverName = options.name;
|
|
2213
3200
|
if (!serverName) {
|
|
2214
3201
|
const choices = Array.from(serverMap.values()).map((g) => ({
|
|
2215
|
-
name: `${
|
|
3202
|
+
name: `${import_picocolors14.default.bold(g.serverName)} ${import_picocolors14.default.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
|
|
2216
3203
|
value: g.serverName
|
|
2217
3204
|
}));
|
|
2218
3205
|
serverName = await (0, import_prompts9.select)({
|
|
@@ -2220,20 +3207,22 @@ var wizardRemove = async (options = {}) => {
|
|
|
2220
3207
|
choices
|
|
2221
3208
|
});
|
|
2222
3209
|
}
|
|
2223
|
-
const
|
|
2224
|
-
if (
|
|
3210
|
+
const rawInstalledAgents = serverMap.get(serverName)?.agents || [];
|
|
3211
|
+
if (rawInstalledAgents.length === 0) {
|
|
2225
3212
|
logger.warn(`No agents found with [${serverName}] installed`);
|
|
2226
3213
|
return false;
|
|
2227
3214
|
}
|
|
3215
|
+
const installedAgents = sortAgentsWithClusters(rawInstalledAgents, { global: isGlobal, cwd });
|
|
2228
3216
|
let targetAgents = options.agents;
|
|
2229
3217
|
if (!targetAgents || targetAgents.length === 0) {
|
|
2230
|
-
|
|
3218
|
+
const choices = buildLinkedAgentChoices({
|
|
3219
|
+
agents: installedAgents,
|
|
3220
|
+
checkedAgents: installedAgents,
|
|
3221
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
3222
|
+
});
|
|
3223
|
+
targetAgents = await linkedCheckbox({
|
|
2231
3224
|
message: `Select agents to remove [${serverName}] from:`,
|
|
2232
|
-
choices
|
|
2233
|
-
name: `${getMcpAgentConfig(agent)?.displayName ?? agent} (${agent})`,
|
|
2234
|
-
value: agent,
|
|
2235
|
-
checked: true
|
|
2236
|
-
})),
|
|
3225
|
+
choices,
|
|
2237
3226
|
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
2238
3227
|
});
|
|
2239
3228
|
} else {
|
|
@@ -2261,10 +3250,12 @@ var wizardRemove = async (options = {}) => {
|
|
|
2261
3250
|
let removedCount = 0;
|
|
2262
3251
|
for (const res of results) {
|
|
2263
3252
|
if (res.removed) {
|
|
2264
|
-
logger.success(
|
|
3253
|
+
logger.success(
|
|
3254
|
+
`${import_picocolors14.default.cyan(res.agent)}: Successfully removed from ${import_picocolors14.default.dim(res.path)}${formatCoHostedBadge("affected", res.coAffectedAgents)}`
|
|
3255
|
+
);
|
|
2265
3256
|
removedCount++;
|
|
2266
3257
|
} else if (res.error) {
|
|
2267
|
-
logger.error(`${
|
|
3258
|
+
logger.error(`${import_picocolors14.default.cyan(res.agent)}: Failed to remove - ${res.error}`);
|
|
2268
3259
|
}
|
|
2269
3260
|
}
|
|
2270
3261
|
if (removedCount > 0) {
|
|
@@ -2278,8 +3269,8 @@ var wizardRemove = async (options = {}) => {
|
|
|
2278
3269
|
// src/interactive/main-menu.ts
|
|
2279
3270
|
var mainMenu = async () => {
|
|
2280
3271
|
console.log();
|
|
2281
|
-
console.log(
|
|
2282
|
-
console.log(
|
|
3272
|
+
console.log(import_picocolors15.default.bold(import_picocolors15.default.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
|
|
3273
|
+
console.log(import_picocolors15.default.dim("Cross-platform MCP server configuration & synchronization tool"));
|
|
2283
3274
|
console.log();
|
|
2284
3275
|
while (true) {
|
|
2285
3276
|
try {
|
|
@@ -2305,7 +3296,7 @@ var mainMenu = async () => {
|
|
|
2305
3296
|
]
|
|
2306
3297
|
});
|
|
2307
3298
|
if (action === "exit") {
|
|
2308
|
-
console.log(
|
|
3299
|
+
console.log(import_picocolors15.default.dim("Goodbye!"));
|
|
2309
3300
|
break;
|
|
2310
3301
|
}
|
|
2311
3302
|
if (action === "add") {
|
|
@@ -2318,7 +3309,7 @@ var mainMenu = async () => {
|
|
|
2318
3309
|
console.log();
|
|
2319
3310
|
} catch (error) {
|
|
2320
3311
|
if (error?.name === "ExitPromptError") {
|
|
2321
|
-
console.log("\n" +
|
|
3312
|
+
console.log("\n" + import_picocolors15.default.dim("Exited."));
|
|
2322
3313
|
break;
|
|
2323
3314
|
}
|
|
2324
3315
|
throw error;
|
|
@@ -2326,10 +3317,18 @@ var mainMenu = async () => {
|
|
|
2326
3317
|
}
|
|
2327
3318
|
};
|
|
2328
3319
|
|
|
2329
|
-
// src/utils/
|
|
2330
|
-
var
|
|
3320
|
+
// src/utils/resolve-transport.ts
|
|
3321
|
+
var resolveTransport = (input7) => {
|
|
3322
|
+
if (!input7) return void 0;
|
|
3323
|
+
if (input7 === "http" || input7 === "sse") return input7;
|
|
3324
|
+
throw new Error(`Unsupported transport "${input7}" (expected: http, sse)`);
|
|
3325
|
+
};
|
|
2331
3326
|
|
|
2332
|
-
// src/cli/
|
|
3327
|
+
// src/cli/manage.ts
|
|
3328
|
+
var import_commander = require("commander");
|
|
3329
|
+
var import_picocolors16 = __toESM(require("picocolors"), 1);
|
|
3330
|
+
|
|
3331
|
+
// src/utils/parse-key-value-list.ts
|
|
2333
3332
|
var parseKeyValueList = (entries, separator) => {
|
|
2334
3333
|
if (!entries || entries.length === 0) return {};
|
|
2335
3334
|
const result = {};
|
|
@@ -2345,12 +3344,189 @@ var parseKeyValueList = (entries, separator) => {
|
|
|
2345
3344
|
}
|
|
2346
3345
|
return result;
|
|
2347
3346
|
};
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
3347
|
+
|
|
3348
|
+
// src/cli/manage.ts
|
|
3349
|
+
var requireTargetServerGroup = (serverName, scope) => {
|
|
3350
|
+
const installed = listInstalledMcpServers(scope);
|
|
3351
|
+
const grouped = groupInstalledServersByName(installed);
|
|
3352
|
+
const targetGroup = grouped.get(serverName);
|
|
3353
|
+
if (!targetGroup) {
|
|
3354
|
+
logger.error(
|
|
3355
|
+
`MCP server "${serverName}" is not configured in ${scope.global ? "global" : "project"} scope.`
|
|
3356
|
+
);
|
|
3357
|
+
process.exitCode = 1;
|
|
3358
|
+
return void 0;
|
|
3359
|
+
}
|
|
3360
|
+
return targetGroup;
|
|
2352
3361
|
};
|
|
2353
|
-
var
|
|
3362
|
+
var mcpManageCommand = new import_commander.Command("manage").description("Inspect, modify, and sync installed MCP servers across coding agents").argument("[server-name]", "Optional server name to inspect or manage").option("-a, --agent <agents...>", "Target specific agents for update").option("-g, --global", "Manage global scope servers instead of project").option("-t, --transport <type>", "Transport type for remote servers (http or sse)").option("--header <header...>", "HTTP header (Header: Value), repeatable").option("--clear-headers", "Clear all HTTP headers for remote servers").option("--env <env...>", "Env var for stdio servers (KEY=VALUE), repeatable").option("--clear-env", "Clear all environment variables for stdio servers").option("--args <args...>", "CLI arguments for stdio/package servers").option("--clear-args", "Clear all arguments for stdio/package servers").option("--command <command>", "Executable command for stdio servers").option("--url <url>", "Remote endpoint URL").option("-y, --yes", "Skip interactive prompts").action(async (serverName, options) => {
|
|
3363
|
+
try {
|
|
3364
|
+
const cwd = process.cwd();
|
|
3365
|
+
const isGlobal = Boolean(options.global);
|
|
3366
|
+
const hasModifications = options.command !== void 0 || options.args !== void 0 || Boolean(options.clearArgs) || options.env !== void 0 || Boolean(options.clearEnv) || options.header !== void 0 || Boolean(options.clearHeaders) || options.url !== void 0 || options.transport !== void 0;
|
|
3367
|
+
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
3368
|
+
if (hasModifications) {
|
|
3369
|
+
if (options.url !== void 0 && options.command !== void 0) {
|
|
3370
|
+
logger.error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
|
|
3371
|
+
process.exitCode = 1;
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
if (!serverName) {
|
|
3375
|
+
logger.error('Missing required argument: "server-name" when passing modification flags.');
|
|
3376
|
+
process.exitCode = 1;
|
|
3377
|
+
return;
|
|
3378
|
+
}
|
|
3379
|
+
const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
|
|
3380
|
+
if (!targetGroup) {
|
|
3381
|
+
return;
|
|
3382
|
+
}
|
|
3383
|
+
const isCurrentRemote = Boolean(targetGroup.config.url && targetGroup.config.url.length > 0);
|
|
3384
|
+
const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
|
|
3385
|
+
if (willBeRemote) {
|
|
3386
|
+
const ignoredStdioFlags = [];
|
|
3387
|
+
if (options.env !== void 0) ignoredStdioFlags.push("--env");
|
|
3388
|
+
if (options.clearEnv) ignoredStdioFlags.push("--clear-env");
|
|
3389
|
+
if (options.args !== void 0) ignoredStdioFlags.push("--args");
|
|
3390
|
+
if (options.clearArgs) ignoredStdioFlags.push("--clear-args");
|
|
3391
|
+
if (ignoredStdioFlags.length > 0) {
|
|
3392
|
+
const hint = options.url !== void 0 ? "When configuring a remote server, stdio flags are ignored." : "Use --command to switch to stdio mode.";
|
|
3393
|
+
logger.warn(
|
|
3394
|
+
`Server "${serverName}" is a remote server. The following stdio flags will be ignored: ${ignoredStdioFlags.join(", ")}. ${hint}`
|
|
3395
|
+
);
|
|
3396
|
+
}
|
|
3397
|
+
} else {
|
|
3398
|
+
const ignoredRemoteFlags = [];
|
|
3399
|
+
if (options.header !== void 0) ignoredRemoteFlags.push("--header");
|
|
3400
|
+
if (options.clearHeaders) ignoredRemoteFlags.push("--clear-headers");
|
|
3401
|
+
if (options.transport !== void 0) ignoredRemoteFlags.push("--transport");
|
|
3402
|
+
if (ignoredRemoteFlags.length > 0) {
|
|
3403
|
+
const hint = options.command !== void 0 ? "When configuring a stdio server, remote flags are ignored." : "Use --url to switch to remote mode.";
|
|
3404
|
+
logger.warn(
|
|
3405
|
+
`Server "${serverName}" is a stdio server. The following remote flags will be ignored: ${ignoredRemoteFlags.join(", ")}. ${hint}`
|
|
3406
|
+
);
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
const incomingDelta = {};
|
|
3410
|
+
if (options.command !== void 0) {
|
|
3411
|
+
incomingDelta.command = options.command;
|
|
3412
|
+
}
|
|
3413
|
+
if (options.clearArgs) {
|
|
3414
|
+
incomingDelta.args = void 0;
|
|
3415
|
+
}
|
|
3416
|
+
if (options.args !== void 0) {
|
|
3417
|
+
incomingDelta.args = options.args;
|
|
3418
|
+
}
|
|
3419
|
+
if (options.url !== void 0) {
|
|
3420
|
+
incomingDelta.url = options.url;
|
|
3421
|
+
}
|
|
3422
|
+
if (options.transport !== void 0) {
|
|
3423
|
+
incomingDelta.type = resolveTransport(options.transport);
|
|
3424
|
+
}
|
|
3425
|
+
if (options.clearEnv) {
|
|
3426
|
+
incomingDelta.env = void 0;
|
|
3427
|
+
}
|
|
3428
|
+
if (options.env !== void 0) {
|
|
3429
|
+
const parsedEnv = parseKeyValueList(options.env, "=");
|
|
3430
|
+
const baseEnv = options.clearEnv ? {} : targetGroup.config.env ?? {};
|
|
3431
|
+
incomingDelta.env = { ...baseEnv, ...parsedEnv };
|
|
3432
|
+
}
|
|
3433
|
+
if (options.clearHeaders) {
|
|
3434
|
+
incomingDelta.headers = void 0;
|
|
3435
|
+
}
|
|
3436
|
+
if (options.header !== void 0) {
|
|
3437
|
+
const parsedHeaders = parseKeyValueList(options.header, ":");
|
|
3438
|
+
const baseHeaders = options.clearHeaders ? {} : targetGroup.config.headers ?? {};
|
|
3439
|
+
incomingDelta.headers = { ...baseHeaders, ...parsedHeaders };
|
|
3440
|
+
}
|
|
3441
|
+
let targetAgents = targetGroup.agents;
|
|
3442
|
+
if (options.agent !== void 0) {
|
|
3443
|
+
const parsed = parseMcpAgentList(options.agent);
|
|
3444
|
+
if (!parsed || parsed.length === 0) {
|
|
3445
|
+
logger.error(`No valid agents recognized from: "${options.agent.join(", ")}".`);
|
|
3446
|
+
process.exitCode = 1;
|
|
3447
|
+
return;
|
|
3448
|
+
}
|
|
3449
|
+
targetAgents = parsed;
|
|
3450
|
+
}
|
|
3451
|
+
const updateResult = updateMcpServer({
|
|
3452
|
+
serverName,
|
|
3453
|
+
config: incomingDelta,
|
|
3454
|
+
previousConfig: targetGroup.config,
|
|
3455
|
+
agents: targetAgents,
|
|
3456
|
+
global: isGlobal,
|
|
3457
|
+
cwd
|
|
3458
|
+
});
|
|
3459
|
+
for (const item of updateResult.incompatible) {
|
|
3460
|
+
logger.warn(`Skipping ${import_picocolors16.default.cyan(item.agent)}: ${item.reason}`);
|
|
3461
|
+
}
|
|
3462
|
+
const attemptedResults = updateResult.results.filter(
|
|
3463
|
+
(r) => !updateResult.incompatible.some((i) => i.agent === r.agent)
|
|
3464
|
+
);
|
|
3465
|
+
if (attemptedResults.length === 0) {
|
|
3466
|
+
const requestedTransport = updateResult.config.url ? updateResult.config.type ?? "http" : "stdio";
|
|
3467
|
+
logger.error(
|
|
3468
|
+
`None of the target agents support ${requestedTransport} transport. Update aborted.`
|
|
3469
|
+
);
|
|
3470
|
+
process.exitCode = 1;
|
|
3471
|
+
return;
|
|
3472
|
+
}
|
|
3473
|
+
logger.info(
|
|
3474
|
+
`Updating ${import_picocolors16.default.bold(serverName)} across ${import_picocolors16.default.cyan(String(attemptedResults.length))} agent(s)...`
|
|
3475
|
+
);
|
|
3476
|
+
let allSuccess = true;
|
|
3477
|
+
for (const res of attemptedResults) {
|
|
3478
|
+
if (res.success) {
|
|
3479
|
+
logger.success(`${import_picocolors16.default.cyan(res.agent)}: Successfully updated in ${import_picocolors16.default.dim(res.path)}`);
|
|
3480
|
+
logCoHostedNotice("configured", res.coConfiguredAgents);
|
|
3481
|
+
} else {
|
|
3482
|
+
allSuccess = false;
|
|
3483
|
+
logger.error(`${import_picocolors16.default.cyan(res.agent)}: Update failed - ${res.error}`);
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
if (!allSuccess) {
|
|
3487
|
+
process.exitCode = 1;
|
|
3488
|
+
}
|
|
3489
|
+
return;
|
|
3490
|
+
}
|
|
3491
|
+
if (!isInteractive) {
|
|
3492
|
+
if (!serverName) {
|
|
3493
|
+
logger.error(
|
|
3494
|
+
'Missing required argument: "server-name" for non-interactive manage command. Specify a server name or use interactive terminal.'
|
|
3495
|
+
);
|
|
3496
|
+
process.exitCode = 1;
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3499
|
+
const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
|
|
3500
|
+
if (!targetGroup) {
|
|
3501
|
+
return;
|
|
3502
|
+
}
|
|
3503
|
+
displayServerDetails({
|
|
3504
|
+
serverName,
|
|
3505
|
+
config: targetGroup.config,
|
|
3506
|
+
agents: targetGroup.agents,
|
|
3507
|
+
global: isGlobal,
|
|
3508
|
+
hasDivergence: targetGroup.hasDivergence
|
|
3509
|
+
});
|
|
3510
|
+
return;
|
|
3511
|
+
}
|
|
3512
|
+
await wizardManage({
|
|
3513
|
+
global: options.global,
|
|
3514
|
+
serverName
|
|
3515
|
+
});
|
|
3516
|
+
} catch (error) {
|
|
3517
|
+
if (error && typeof error === "object" && "name" in error && error.name === "ExitPromptError") {
|
|
3518
|
+
process.exit(0);
|
|
3519
|
+
}
|
|
3520
|
+
logger.error(toErrorMessage(error));
|
|
3521
|
+
process.exitCode = 1;
|
|
3522
|
+
}
|
|
3523
|
+
});
|
|
3524
|
+
|
|
3525
|
+
// src/utils/format-agent-list.ts
|
|
3526
|
+
var formatAgentList = (agentList, emptyLabel = "(none)") => agentList.length === 0 ? emptyLabel : agentList.join(", ");
|
|
3527
|
+
|
|
3528
|
+
// src/cli/add.ts
|
|
3529
|
+
var mcpAddCommand = new import_commander2.Command("add").description("Add an MCP server to coding agents").argument("[source]", "Remote URL, npm package, or command line").option("-a, --agent <agents...>", "Target specific agents (use '*' for all)").option("-g, --global", "Install to user-level config instead of project").option("-t, --transport <type>", "Transport type for remote servers (http or sse)").option("--header <header...>", "HTTP header (Key: Value), repeatable").option("--env <env...>", "Env var for stdio servers (KEY=VALUE), repeatable").option("--args <args...>", "CLI arguments for stdio/package servers").option("-n, --name <name>", "Server name override").option("-y, --yes", "Skip all prompts").option("--all", "Install to all supported agents").action(async (source, options) => {
|
|
2354
3530
|
try {
|
|
2355
3531
|
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
2356
3532
|
if (!source) {
|
|
@@ -2384,19 +3560,19 @@ var mcpAddCommand = new import_commander.Command("add").description("Add an MCP
|
|
|
2384
3560
|
transport
|
|
2385
3561
|
});
|
|
2386
3562
|
if (resolvedTargets.agents.length === 0) {
|
|
2387
|
-
const message = resolvedTargets.diagnostic ?? `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass ${
|
|
3563
|
+
const message = resolvedTargets.diagnostic ?? `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass ${import_picocolors17.default.cyan("-a <agent>")} (e.g. ${import_picocolors17.default.cyan("-a cursor")}) or ${import_picocolors17.default.cyan("--all")} to install.`;
|
|
2388
3564
|
logger.warn(message);
|
|
2389
3565
|
process.exitCode = 1;
|
|
2390
3566
|
return;
|
|
2391
3567
|
}
|
|
2392
3568
|
if (resolvedTargets.isDetected) {
|
|
2393
3569
|
logger.info(
|
|
2394
|
-
`Detected ${isGlobal ? "global" : "project"} agents: ${
|
|
3570
|
+
`Detected ${isGlobal ? "global" : "project"} agents: ${import_picocolors17.default.cyan(formatAgentList(resolvedTargets.detected, "(none detected)"))}`
|
|
2395
3571
|
);
|
|
2396
3572
|
if (resolvedTargets.incompatible.length > 0) {
|
|
2397
3573
|
const skippedList = resolvedTargets.incompatible.map((item) => `${item.agent} (${item.reason})`).join(", ");
|
|
2398
3574
|
logger.info(
|
|
2399
|
-
`Skipping detected agents incompatible with ${transport}: ${
|
|
3575
|
+
`Skipping detected agents incompatible with ${transport}: ${import_picocolors17.default.yellow(skippedList)}`
|
|
2400
3576
|
);
|
|
2401
3577
|
}
|
|
2402
3578
|
}
|
|
@@ -2413,13 +3589,14 @@ var mcpAddCommand = new import_commander.Command("add").description("Add an MCP
|
|
|
2413
3589
|
env: parseKeyValueList(options.env, "=")
|
|
2414
3590
|
});
|
|
2415
3591
|
logger.info(
|
|
2416
|
-
`Installing ${
|
|
3592
|
+
`Installing ${import_picocolors17.default.bold(result.serverName)} (${import_picocolors17.default.cyan(parsed.type)}) to ${import_picocolors17.default.cyan(String(result.results.length))} agent(s)`
|
|
2417
3593
|
);
|
|
2418
3594
|
for (const record of result.results) {
|
|
2419
3595
|
if (record.success) {
|
|
2420
|
-
logger.success(`${
|
|
3596
|
+
logger.success(`${import_picocolors17.default.cyan(record.agent)} ${import_picocolors17.default.dim(record.path)}`);
|
|
3597
|
+
logCoHostedNotice("configured", record.coConfiguredAgents);
|
|
2421
3598
|
} else {
|
|
2422
|
-
logger.error(`${
|
|
3599
|
+
logger.error(`${import_picocolors17.default.cyan(record.agent)}: ${record.error}`);
|
|
2423
3600
|
}
|
|
2424
3601
|
}
|
|
2425
3602
|
if (result.results.some((record) => !record.success)) process.exitCode = 1;
|
|
@@ -2430,9 +3607,9 @@ var mcpAddCommand = new import_commander.Command("add").description("Add an MCP
|
|
|
2430
3607
|
});
|
|
2431
3608
|
|
|
2432
3609
|
// src/cli/list.ts
|
|
2433
|
-
var
|
|
2434
|
-
var
|
|
2435
|
-
var mcpListCommand = new
|
|
3610
|
+
var import_commander3 = require("commander");
|
|
3611
|
+
var import_picocolors18 = __toESM(require("picocolors"), 1);
|
|
3612
|
+
var mcpListCommand = new import_commander3.Command("list").alias("ls").description("List installed MCP servers across agents").option("-g, --global", "List global configs instead of project").option("-a, --agent <agents...>", "Filter by specific agents").option("--json", "Output as JSON").action((options) => {
|
|
2436
3613
|
try {
|
|
2437
3614
|
const entries = listInstalledMcpServers({
|
|
2438
3615
|
global: Boolean(options.global),
|
|
@@ -2455,9 +3632,9 @@ var mcpListCommand = new import_commander2.Command("list").alias("ls").descripti
|
|
|
2455
3632
|
}
|
|
2456
3633
|
for (const [serverName, group] of grouped) {
|
|
2457
3634
|
const agentLabels = group.map((record) => record.agent).join(", ");
|
|
2458
|
-
console.log(` ${
|
|
3635
|
+
console.log(` ${import_picocolors18.default.bold(serverName)} ${import_picocolors18.default.dim(`[${agentLabels}]`)}`);
|
|
2459
3636
|
const firstPath = group[0]?.path;
|
|
2460
|
-
if (firstPath) console.log(` ${
|
|
3637
|
+
if (firstPath) console.log(` ${import_picocolors18.default.dim(firstPath)}`);
|
|
2461
3638
|
}
|
|
2462
3639
|
} catch (error) {
|
|
2463
3640
|
logger.error(toErrorMessage(error));
|
|
@@ -2466,9 +3643,9 @@ var mcpListCommand = new import_commander2.Command("list").alias("ls").descripti
|
|
|
2466
3643
|
});
|
|
2467
3644
|
|
|
2468
3645
|
// src/cli/remove.ts
|
|
2469
|
-
var
|
|
2470
|
-
var
|
|
2471
|
-
var mcpRemoveCommand = new
|
|
3646
|
+
var import_commander4 = require("commander");
|
|
3647
|
+
var import_picocolors19 = __toESM(require("picocolors"), 1);
|
|
3648
|
+
var mcpRemoveCommand = new import_commander4.Command("remove").alias("rm").description("Remove an MCP server from agent configs").argument("[name]", "Server name").option("-g, --global", "Remove from global scope").option("-a, --agent <agents...>", "Filter by specific agents (use '*' for all)").option("-y, --yes", "Skip confirmation prompts").action(async (name, options) => {
|
|
2472
3649
|
try {
|
|
2473
3650
|
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
2474
3651
|
if (!name) {
|
|
@@ -2491,16 +3668,17 @@ var mcpRemoveCommand = new import_commander3.Command("remove").alias("rm").descr
|
|
|
2491
3668
|
cwd: process.cwd()
|
|
2492
3669
|
});
|
|
2493
3670
|
if (results.length === 0) {
|
|
2494
|
-
logger.warn(`No agent config contained ${
|
|
3671
|
+
logger.warn(`No agent config contained ${import_picocolors19.default.bold(name)}`);
|
|
2495
3672
|
return;
|
|
2496
3673
|
}
|
|
2497
3674
|
for (const record of results) {
|
|
2498
3675
|
if (record.removed) {
|
|
2499
3676
|
logger.success(
|
|
2500
|
-
`${
|
|
3677
|
+
`${import_picocolors19.default.cyan(record.agent)} removed ${import_picocolors19.default.bold(name)} ${import_picocolors19.default.dim(record.path)}`
|
|
2501
3678
|
);
|
|
3679
|
+
logCoHostedNotice("affected", record.coAffectedAgents);
|
|
2502
3680
|
} else {
|
|
2503
|
-
logger.error(`${
|
|
3681
|
+
logger.error(`${import_picocolors19.default.cyan(record.agent)}: ${record.error ?? "not found"}`);
|
|
2504
3682
|
}
|
|
2505
3683
|
}
|
|
2506
3684
|
} catch (error) {
|
|
@@ -2510,12 +3688,13 @@ var mcpRemoveCommand = new import_commander3.Command("remove").alias("rm").descr
|
|
|
2510
3688
|
});
|
|
2511
3689
|
|
|
2512
3690
|
// src/cli.ts
|
|
2513
|
-
var VERSION = "0.1.0-beta.
|
|
3691
|
+
var VERSION = "0.1.0-beta.3";
|
|
2514
3692
|
process.on("SIGINT", () => process.exit(0));
|
|
2515
3693
|
process.on("SIGTERM", () => process.exit(0));
|
|
2516
|
-
var program = new
|
|
3694
|
+
var program = new import_commander5.Command().name("mcps").description("Install, list, and remove MCP servers across AI coding agents").version(VERSION, "-v, --version", "display the version number");
|
|
2517
3695
|
program.addCommand(mcpAddCommand);
|
|
2518
3696
|
program.addCommand(mcpListCommand);
|
|
3697
|
+
program.addCommand(mcpManageCommand);
|
|
2519
3698
|
program.addCommand(mcpRemoveCommand);
|
|
2520
3699
|
var main = async () => {
|
|
2521
3700
|
if (process.argv.length <= 2 && process.stdin.isTTY) {
|