@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
|
@@ -452,9 +452,9 @@ var mcpAgentAliases = {
|
|
|
452
452
|
var getMcpAgentConfig = (agentType) => mcpAgents[agentType];
|
|
453
453
|
var getMcpAgentTypes = () => Object.values(mcpAgents).map((config) => config.name);
|
|
454
454
|
var isMcpAgentType = (value) => value in mcpAgents;
|
|
455
|
-
var resolveMcpAgentAlias = (
|
|
456
|
-
if (isMcpAgentType(
|
|
457
|
-
return mcpAgentAliases[
|
|
455
|
+
var resolveMcpAgentAlias = (input7) => {
|
|
456
|
+
if (isMcpAgentType(input7)) return input7;
|
|
457
|
+
return mcpAgentAliases[input7] ?? null;
|
|
458
458
|
};
|
|
459
459
|
var isMcpTransportSupported = (agent, transport) => agent.supportedTransports.includes(transport);
|
|
460
460
|
var detectProjectInstalledMcpAgents = (cwd) => getMcpAgentTypes().filter(
|
|
@@ -965,6 +965,74 @@ var agentConfigStore = new AgentConfigStore();
|
|
|
965
965
|
// src/utils/to-error-message.ts
|
|
966
966
|
var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
|
|
967
967
|
|
|
968
|
+
// src/resolve-config-clusters.ts
|
|
969
|
+
var getCandidateAgentsForScope = (options = {}) => {
|
|
970
|
+
return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
971
|
+
};
|
|
972
|
+
var getCoHostedAgents = (agentType, options = {}) => {
|
|
973
|
+
const currentAgent = getMcpAgentConfig(agentType);
|
|
974
|
+
const currentTarget = resolveMcpConfigTarget(currentAgent, options);
|
|
975
|
+
const candidates = getCandidateAgentsForScope(options);
|
|
976
|
+
const coHosted = [];
|
|
977
|
+
for (const candidateType of candidates) {
|
|
978
|
+
if (candidateType === agentType) continue;
|
|
979
|
+
const candidateConfig = getMcpAgentConfig(candidateType);
|
|
980
|
+
const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
|
|
981
|
+
if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
|
|
982
|
+
coHosted.push(candidateType);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return coHosted;
|
|
986
|
+
};
|
|
987
|
+
var resolveConfigClusters = (agentTypes, options = {}) => {
|
|
988
|
+
const clustersByPath = /* @__PURE__ */ new Map();
|
|
989
|
+
for (const agentType of agentTypes) {
|
|
990
|
+
const agentConfig = getMcpAgentConfig(agentType);
|
|
991
|
+
const target = resolveMcpConfigTarget(agentConfig, options);
|
|
992
|
+
let keyMap = clustersByPath.get(target.configPath);
|
|
993
|
+
if (!keyMap) {
|
|
994
|
+
keyMap = /* @__PURE__ */ new Map();
|
|
995
|
+
clustersByPath.set(target.configPath, keyMap);
|
|
996
|
+
}
|
|
997
|
+
let cluster = keyMap.get(target.configKey);
|
|
998
|
+
if (!cluster) {
|
|
999
|
+
const allCoHosted = getCoHostedAgents(agentType, options);
|
|
1000
|
+
cluster = {
|
|
1001
|
+
configPath: target.configPath,
|
|
1002
|
+
configKey: target.configKey,
|
|
1003
|
+
targetAgents: [],
|
|
1004
|
+
coHostedAgents: allCoHosted
|
|
1005
|
+
};
|
|
1006
|
+
keyMap.set(target.configKey, cluster);
|
|
1007
|
+
}
|
|
1008
|
+
if (!cluster.targetAgents.includes(agentType)) {
|
|
1009
|
+
cluster.targetAgents.push(agentType);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
const clusters = [];
|
|
1013
|
+
for (const keyMap of clustersByPath.values()) {
|
|
1014
|
+
for (const cluster of keyMap.values()) {
|
|
1015
|
+
cluster.coHostedAgents = cluster.coHostedAgents.filter(
|
|
1016
|
+
(co) => !cluster.targetAgents.includes(co)
|
|
1017
|
+
);
|
|
1018
|
+
clusters.push(cluster);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return clusters;
|
|
1022
|
+
};
|
|
1023
|
+
var sortAgentsWithClusters = (agentTypes, options = {}) => {
|
|
1024
|
+
const clusters = resolveConfigClusters(agentTypes, options);
|
|
1025
|
+
const sorted = [];
|
|
1026
|
+
for (const cluster of clusters) {
|
|
1027
|
+
for (const agent of cluster.targetAgents) {
|
|
1028
|
+
if (!sorted.includes(agent)) {
|
|
1029
|
+
sorted.push(agent);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
return sorted;
|
|
1034
|
+
};
|
|
1035
|
+
|
|
968
1036
|
// src/transforms/index.ts
|
|
969
1037
|
var DIALECT_PRESETS = {
|
|
970
1038
|
vscode: {
|
|
@@ -1180,12 +1248,18 @@ var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {
|
|
|
1180
1248
|
const agent = getMcpAgentConfig(agentType);
|
|
1181
1249
|
const isGlobal = options.global ?? false;
|
|
1182
1250
|
const { target } = agentConfigStore.resolveTarget(agent, options);
|
|
1251
|
+
const coHosted = getCoHostedAgents(agentType, options);
|
|
1183
1252
|
try {
|
|
1184
1253
|
const transformed = transformServerConfigForAgent(agent, serverName, serverConfig, {
|
|
1185
1254
|
global: isGlobal
|
|
1186
1255
|
});
|
|
1187
1256
|
agentConfigStore.writeServer(agent, serverName, transformed, options);
|
|
1188
|
-
return {
|
|
1257
|
+
return {
|
|
1258
|
+
agent: agentType,
|
|
1259
|
+
success: true,
|
|
1260
|
+
path: target.configPath,
|
|
1261
|
+
coConfiguredAgents: coHosted.length > 0 ? coHosted : void 0
|
|
1262
|
+
};
|
|
1189
1263
|
} catch (error) {
|
|
1190
1264
|
return {
|
|
1191
1265
|
agent: agentType,
|
|
@@ -1195,16 +1269,69 @@ var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {
|
|
|
1195
1269
|
};
|
|
1196
1270
|
}
|
|
1197
1271
|
};
|
|
1198
|
-
var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) =>
|
|
1199
|
-
|
|
1200
|
-
);
|
|
1272
|
+
var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => {
|
|
1273
|
+
const clusters = resolveConfigClusters(agentTypes, options);
|
|
1274
|
+
const resultsByAgent = /* @__PURE__ */ new Map();
|
|
1275
|
+
const isGlobal = options.global ?? false;
|
|
1276
|
+
for (const cluster of clusters) {
|
|
1277
|
+
const primaryAgentType = cluster.targetAgents[0];
|
|
1278
|
+
const primaryAgent = getMcpAgentConfig(primaryAgentType);
|
|
1279
|
+
try {
|
|
1280
|
+
const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
|
|
1281
|
+
global: isGlobal
|
|
1282
|
+
});
|
|
1283
|
+
agentConfigStore.writeServer(primaryAgent, serverName, transformed, options);
|
|
1284
|
+
for (const agentType of cluster.targetAgents) {
|
|
1285
|
+
resultsByAgent.set(agentType, {
|
|
1286
|
+
agent: agentType,
|
|
1287
|
+
success: true,
|
|
1288
|
+
path: cluster.configPath,
|
|
1289
|
+
coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
const errorMsg = toErrorMessage(error);
|
|
1294
|
+
for (const agentType of cluster.targetAgents) {
|
|
1295
|
+
resultsByAgent.set(agentType, {
|
|
1296
|
+
agent: agentType,
|
|
1297
|
+
success: false,
|
|
1298
|
+
path: cluster.configPath,
|
|
1299
|
+
error: errorMsg
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return agentTypes.map((agentType) => resultsByAgent.get(agentType));
|
|
1305
|
+
};
|
|
1306
|
+
var installToCompatibleAgents = (serverName, serverConfig, options) => {
|
|
1307
|
+
const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
|
|
1308
|
+
const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
|
|
1309
|
+
const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
|
|
1310
|
+
const installedResults = installMcpServerForAgents(serverName, serverConfig, compatibleAgents, {
|
|
1311
|
+
global: isGlobal,
|
|
1312
|
+
cwd
|
|
1313
|
+
});
|
|
1314
|
+
const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
|
|
1315
|
+
return allAgents.map((agentType) => {
|
|
1316
|
+
const incompatibleReason = incompatibleMap.get(agentType);
|
|
1317
|
+
if (incompatibleReason) {
|
|
1318
|
+
return {
|
|
1319
|
+
agent: agentType,
|
|
1320
|
+
success: false,
|
|
1321
|
+
path: "",
|
|
1322
|
+
error: incompatibleReason
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
return installedMap.get(agentType);
|
|
1326
|
+
});
|
|
1327
|
+
};
|
|
1201
1328
|
|
|
1202
1329
|
// src/utils/parse-mcp-agent-list.ts
|
|
1203
|
-
var parseMcpAgentList = (
|
|
1204
|
-
if (!
|
|
1205
|
-
if (
|
|
1330
|
+
var parseMcpAgentList = (input7) => {
|
|
1331
|
+
if (!input7 || input7.length === 0) return void 0;
|
|
1332
|
+
if (input7.includes("*")) return getMcpAgentTypes();
|
|
1206
1333
|
const resolved = [];
|
|
1207
|
-
for (const value of
|
|
1334
|
+
for (const value of input7) {
|
|
1208
1335
|
const agentType = resolveMcpAgentAlias(value);
|
|
1209
1336
|
if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
|
|
1210
1337
|
resolved.push(agentType);
|
|
@@ -1213,9 +1340,9 @@ var parseMcpAgentList = (input5) => {
|
|
|
1213
1340
|
};
|
|
1214
1341
|
|
|
1215
1342
|
// src/resolve-target-agents.ts
|
|
1216
|
-
var normalizeRequestedAgents = (
|
|
1217
|
-
if (!
|
|
1218
|
-
const rawList = [...
|
|
1343
|
+
var normalizeRequestedAgents = (input7) => {
|
|
1344
|
+
if (!input7 || input7.length === 0) return void 0;
|
|
1345
|
+
const rawList = [...input7];
|
|
1219
1346
|
if (rawList.every((item) => isMcpAgentType(item))) {
|
|
1220
1347
|
return rawList;
|
|
1221
1348
|
}
|
|
@@ -1275,29 +1402,29 @@ var REMOTE_URL_REGEX = /^https?:\/\//i;
|
|
|
1275
1402
|
var HAS_WHITESPACE_REGEX = /\s/;
|
|
1276
1403
|
var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
|
|
1277
1404
|
var PATH_SEPARATOR_REGEX = /[/\\]/;
|
|
1278
|
-
var stripVersionSuffix = (
|
|
1279
|
-
if (
|
|
1280
|
-
const secondAtIndex =
|
|
1281
|
-
if (secondAtIndex > 0) return
|
|
1282
|
-
return
|
|
1283
|
-
}
|
|
1284
|
-
const atIndex =
|
|
1285
|
-
if (atIndex > 0) return
|
|
1286
|
-
return
|
|
1287
|
-
};
|
|
1288
|
-
var stripScopePrefix = (
|
|
1289
|
-
if (!
|
|
1290
|
-
const parts =
|
|
1291
|
-
return parts[1] ||
|
|
1292
|
-
};
|
|
1293
|
-
var stripPathPrefix = (
|
|
1294
|
-
if (!PATH_SEPARATOR_REGEX.test(
|
|
1295
|
-
const segments =
|
|
1405
|
+
var stripVersionSuffix = (input7) => {
|
|
1406
|
+
if (input7.startsWith("@")) {
|
|
1407
|
+
const secondAtIndex = input7.indexOf("@", 1);
|
|
1408
|
+
if (secondAtIndex > 0) return input7.slice(0, secondAtIndex);
|
|
1409
|
+
return input7;
|
|
1410
|
+
}
|
|
1411
|
+
const atIndex = input7.lastIndexOf("@");
|
|
1412
|
+
if (atIndex > 0) return input7.slice(0, atIndex);
|
|
1413
|
+
return input7;
|
|
1414
|
+
};
|
|
1415
|
+
var stripScopePrefix = (input7) => {
|
|
1416
|
+
if (!input7.startsWith("@") || !input7.includes("/")) return input7;
|
|
1417
|
+
const parts = input7.split("/");
|
|
1418
|
+
return parts[1] || input7;
|
|
1419
|
+
};
|
|
1420
|
+
var stripPathPrefix = (input7) => {
|
|
1421
|
+
if (!PATH_SEPARATOR_REGEX.test(input7)) return input7;
|
|
1422
|
+
const segments = input7.split(PATH_SEPARATOR_REGEX);
|
|
1296
1423
|
const basename = segments[segments.length - 1];
|
|
1297
|
-
return basename ||
|
|
1424
|
+
return basename || input7;
|
|
1298
1425
|
};
|
|
1299
|
-
var extractPackageName = (
|
|
1300
|
-
let name = stripVersionSuffix(
|
|
1426
|
+
var extractPackageName = (input7) => {
|
|
1427
|
+
let name = stripVersionSuffix(input7);
|
|
1301
1428
|
name = stripScopePrefix(name);
|
|
1302
1429
|
name = stripPathPrefix(name);
|
|
1303
1430
|
name = name.replace(SCRIPT_EXTENSION_REGEX, "");
|
|
@@ -1315,9 +1442,9 @@ var extractPackageName = (input5) => {
|
|
|
1315
1442
|
}
|
|
1316
1443
|
return name || MCP_DEFAULT_SERVER_NAME;
|
|
1317
1444
|
};
|
|
1318
|
-
var inferNameFromUrl = (
|
|
1445
|
+
var inferNameFromUrl = (input7) => {
|
|
1319
1446
|
try {
|
|
1320
|
-
const url = new URL(
|
|
1447
|
+
const url = new URL(input7);
|
|
1321
1448
|
const host = url.hostname;
|
|
1322
1449
|
const labels = host.split(".").filter((segment) => segment.length > 0);
|
|
1323
1450
|
if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
|
|
@@ -1346,8 +1473,8 @@ var inferNameFromCommand = (command) => {
|
|
|
1346
1473
|
const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
|
|
1347
1474
|
return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
|
|
1348
1475
|
};
|
|
1349
|
-
var parseMcpSource = (
|
|
1350
|
-
const trimmed =
|
|
1476
|
+
var parseMcpSource = (input7) => {
|
|
1477
|
+
const trimmed = input7.trim();
|
|
1351
1478
|
if (trimmed.length === 0) {
|
|
1352
1479
|
throw new Error(
|
|
1353
1480
|
"Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
|
|
@@ -1401,18 +1528,11 @@ var installMcpServer = (options) => {
|
|
|
1401
1528
|
cwd,
|
|
1402
1529
|
transport: requestedTransport
|
|
1403
1530
|
});
|
|
1404
|
-
const
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
agent: agentType,
|
|
1410
|
-
success: false,
|
|
1411
|
-
path: "",
|
|
1412
|
-
error: incompatibleReason
|
|
1413
|
-
};
|
|
1414
|
-
}
|
|
1415
|
-
return installMcpServerForAgent(serverName, serverConfig, agentType, { global: isGlobal, cwd });
|
|
1531
|
+
const results = installToCompatibleAgents(serverName, serverConfig, {
|
|
1532
|
+
allAgents,
|
|
1533
|
+
incompatible,
|
|
1534
|
+
global: isGlobal,
|
|
1535
|
+
cwd
|
|
1416
1536
|
});
|
|
1417
1537
|
return { serverName, config: serverConfig, results };
|
|
1418
1538
|
};
|
|
@@ -1442,9 +1562,15 @@ var listInstalledMcpServers = (options = {}) => {
|
|
|
1442
1562
|
var removeMcpServerFromAgent = (serverName, agentType, options = {}) => {
|
|
1443
1563
|
const agent = getMcpAgentConfig(agentType);
|
|
1444
1564
|
const { target } = agentConfigStore.resolveTarget(agent, options);
|
|
1565
|
+
const coHosted = getCoHostedAgents(agentType, options);
|
|
1445
1566
|
try {
|
|
1446
1567
|
const { removed } = agentConfigStore.removeServer(agent, serverName, options);
|
|
1447
|
-
return {
|
|
1568
|
+
return {
|
|
1569
|
+
agent: agentType,
|
|
1570
|
+
path: target.configPath,
|
|
1571
|
+
removed,
|
|
1572
|
+
coAffectedAgents: removed && coHosted.length > 0 ? coHosted : void 0
|
|
1573
|
+
};
|
|
1448
1574
|
} catch (error) {
|
|
1449
1575
|
return {
|
|
1450
1576
|
agent: agentType,
|
|
@@ -1461,24 +1587,153 @@ var removeMcpServer = (options) => {
|
|
|
1461
1587
|
global: options.global,
|
|
1462
1588
|
cwd: options.cwd
|
|
1463
1589
|
});
|
|
1590
|
+
const clusters = resolveConfigClusters(allAgents, {
|
|
1591
|
+
global: options.global,
|
|
1592
|
+
cwd: options.cwd
|
|
1593
|
+
});
|
|
1464
1594
|
const results = [];
|
|
1465
|
-
for (const
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1595
|
+
for (const cluster of clusters) {
|
|
1596
|
+
const primaryAgentType = cluster.targetAgents[0];
|
|
1597
|
+
const primaryAgent = getMcpAgentConfig(primaryAgentType);
|
|
1598
|
+
try {
|
|
1599
|
+
const { removed } = agentConfigStore.removeServer(primaryAgent, options.name, {
|
|
1600
|
+
global: options.global,
|
|
1601
|
+
cwd: options.cwd
|
|
1602
|
+
});
|
|
1603
|
+
if (removed) {
|
|
1604
|
+
for (const agentType of cluster.targetAgents) {
|
|
1605
|
+
results.push({
|
|
1606
|
+
agent: agentType,
|
|
1607
|
+
path: cluster.configPath,
|
|
1608
|
+
removed: true,
|
|
1609
|
+
coAffectedAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
const errorMsg = toErrorMessage(error);
|
|
1615
|
+
for (const agentType of cluster.targetAgents) {
|
|
1616
|
+
results.push({
|
|
1617
|
+
agent: agentType,
|
|
1618
|
+
path: cluster.configPath,
|
|
1619
|
+
removed: false,
|
|
1620
|
+
error: errorMsg
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1471
1624
|
}
|
|
1472
1625
|
return results;
|
|
1473
1626
|
};
|
|
1474
1627
|
|
|
1628
|
+
// src/update-mcp-server.ts
|
|
1629
|
+
var toRemoteServerConfig = (config, defaultTransport = "http") => {
|
|
1630
|
+
const {
|
|
1631
|
+
command: _droppedCommand,
|
|
1632
|
+
args: _droppedArgs,
|
|
1633
|
+
env: _droppedEnv,
|
|
1634
|
+
...remoteConfig
|
|
1635
|
+
} = config;
|
|
1636
|
+
return {
|
|
1637
|
+
...remoteConfig,
|
|
1638
|
+
type: remoteConfig.type ?? defaultTransport
|
|
1639
|
+
};
|
|
1640
|
+
};
|
|
1641
|
+
var toStdioServerConfig = (config) => {
|
|
1642
|
+
const {
|
|
1643
|
+
url: _droppedUrl,
|
|
1644
|
+
type: _droppedType,
|
|
1645
|
+
headers: _droppedHeaders,
|
|
1646
|
+
...stdioConfig
|
|
1647
|
+
} = config;
|
|
1648
|
+
return stdioConfig;
|
|
1649
|
+
};
|
|
1650
|
+
var detectUpdateTransition = (incoming, previous) => {
|
|
1651
|
+
if (!previous) {
|
|
1652
|
+
return incoming.url ? "switch-to-remote" : "switch-to-stdio";
|
|
1653
|
+
}
|
|
1654
|
+
if (incoming.url && !incoming.command) {
|
|
1655
|
+
return "switch-to-remote";
|
|
1656
|
+
}
|
|
1657
|
+
if (incoming.command && !incoming.url) {
|
|
1658
|
+
return "switch-to-stdio";
|
|
1659
|
+
}
|
|
1660
|
+
if (incoming.url || !incoming.command && previous.url) {
|
|
1661
|
+
return "merge-remote";
|
|
1662
|
+
}
|
|
1663
|
+
return "merge-stdio";
|
|
1664
|
+
};
|
|
1665
|
+
var sanitizeUpdatedServerConfig = (incoming, previous) => {
|
|
1666
|
+
const transition = detectUpdateTransition(incoming, previous);
|
|
1667
|
+
const targetTransport = incoming.type ?? previous?.type ?? "http";
|
|
1668
|
+
switch (transition) {
|
|
1669
|
+
case "switch-to-remote": {
|
|
1670
|
+
const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
|
|
1671
|
+
return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
|
|
1672
|
+
}
|
|
1673
|
+
case "switch-to-stdio": {
|
|
1674
|
+
const cleanBase = previous ? toStdioServerConfig(previous) : {};
|
|
1675
|
+
return toStdioServerConfig({ ...cleanBase, ...incoming });
|
|
1676
|
+
}
|
|
1677
|
+
case "merge-remote": {
|
|
1678
|
+
return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
|
|
1679
|
+
}
|
|
1680
|
+
case "merge-stdio": {
|
|
1681
|
+
return toStdioServerConfig({ ...previous, ...incoming });
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
var updateMcpServer = (options) => {
|
|
1686
|
+
const isGlobal = options.global ?? false;
|
|
1687
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1688
|
+
let previousConfig = options.previousConfig;
|
|
1689
|
+
if (!previousConfig) {
|
|
1690
|
+
const existing = listInstalledMcpServers({
|
|
1691
|
+
global: isGlobal,
|
|
1692
|
+
cwd,
|
|
1693
|
+
agents: options.agents
|
|
1694
|
+
});
|
|
1695
|
+
const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
|
|
1696
|
+
if (found) {
|
|
1697
|
+
previousConfig = found.serverConfig;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
|
|
1701
|
+
let targetAgents = options.agents;
|
|
1702
|
+
if (!targetAgents || targetAgents.length === 0) {
|
|
1703
|
+
const existing = listInstalledMcpServers({ global: isGlobal, cwd });
|
|
1704
|
+
targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
|
|
1705
|
+
}
|
|
1706
|
+
const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
|
|
1707
|
+
const { allAgents, incompatible } = resolveTargetAgents({
|
|
1708
|
+
requested: targetAgents,
|
|
1709
|
+
global: isGlobal,
|
|
1710
|
+
cwd,
|
|
1711
|
+
transport: requestedTransport
|
|
1712
|
+
});
|
|
1713
|
+
const results = installToCompatibleAgents(options.serverName, serverConfig, {
|
|
1714
|
+
allAgents,
|
|
1715
|
+
incompatible,
|
|
1716
|
+
global: isGlobal,
|
|
1717
|
+
cwd
|
|
1718
|
+
});
|
|
1719
|
+
return {
|
|
1720
|
+
serverName: options.serverName,
|
|
1721
|
+
config: serverConfig,
|
|
1722
|
+
results,
|
|
1723
|
+
incompatible
|
|
1724
|
+
};
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1475
1727
|
// src/interactive/main-menu.ts
|
|
1476
1728
|
import { select as select8 } from "@inquirer/prompts";
|
|
1477
|
-
import
|
|
1729
|
+
import pc15 from "picocolors";
|
|
1478
1730
|
|
|
1479
1731
|
// src/interactive/wizard-add.ts
|
|
1480
|
-
import { confirm as
|
|
1481
|
-
import
|
|
1732
|
+
import { confirm as confirm5, input as input5, select as select5 } from "@inquirer/prompts";
|
|
1733
|
+
import pc11 from "picocolors";
|
|
1734
|
+
|
|
1735
|
+
// src/utils/co-hosted-feedback.ts
|
|
1736
|
+
import pc2 from "picocolors";
|
|
1482
1737
|
|
|
1483
1738
|
// src/utils/logger.ts
|
|
1484
1739
|
import pc from "picocolors";
|
|
@@ -1497,13 +1752,284 @@ var logger = {
|
|
|
1497
1752
|
}
|
|
1498
1753
|
};
|
|
1499
1754
|
|
|
1755
|
+
// src/utils/co-hosted-feedback.ts
|
|
1756
|
+
var formatCoHostedBadge = (kind, agents) => {
|
|
1757
|
+
if (!agents || agents.length === 0) return "";
|
|
1758
|
+
const label = kind === "configured" ? "co-configured" : "co-affected";
|
|
1759
|
+
return ` ${pc2.yellow(`(${label}: ${agents.join(", ")})`)}`;
|
|
1760
|
+
};
|
|
1761
|
+
var logCoHostedNotice = (kind, agents) => {
|
|
1762
|
+
if (!agents || agents.length === 0) return;
|
|
1763
|
+
const actionText = kind === "configured" ? "Also configured for" : "Also affects";
|
|
1764
|
+
logger.info(
|
|
1765
|
+
` ${pc2.dim("Note:")} ${actionText} co-hosted agent(s): ${pc2.yellow(agents.join(", "))}`
|
|
1766
|
+
);
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1500
1769
|
// src/interactive/prompts/agents.ts
|
|
1501
|
-
import
|
|
1770
|
+
import pc6 from "picocolors";
|
|
1771
|
+
|
|
1772
|
+
// src/interactive/utils/build-linked-agent-choices.ts
|
|
1502
1773
|
import pc3 from "picocolors";
|
|
1774
|
+
var buildLinkedAgentChoices = (options) => {
|
|
1775
|
+
const { agents, checkedAgents, detectedAgents = [], scopeOptions = {} } = options;
|
|
1776
|
+
const alignedCheckedSet = new Set(checkedAgents);
|
|
1777
|
+
for (const agent of checkedAgents) {
|
|
1778
|
+
const coHosted = getCoHostedAgents(agent, scopeOptions);
|
|
1779
|
+
for (const co of coHosted) {
|
|
1780
|
+
if (agents.includes(co)) {
|
|
1781
|
+
alignedCheckedSet.add(co);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
return agents.map((agent) => {
|
|
1786
|
+
const config = getMcpAgentConfig(agent);
|
|
1787
|
+
const displayName = config?.displayName ?? agent;
|
|
1788
|
+
const isDetected = detectedAgents.includes(agent);
|
|
1789
|
+
const coHosted = getCoHostedAgents(agent, scopeOptions).filter(
|
|
1790
|
+
(co) => agents.includes(co)
|
|
1791
|
+
);
|
|
1792
|
+
const detectedBadge = isDetected ? pc3.green(" [detected]") : "";
|
|
1793
|
+
const sharedBadge = coHosted.length > 0 ? pc3.dim(` [shared: ${coHosted.join(", ")}]`) : "";
|
|
1794
|
+
const label = `${displayName} ${pc3.dim(`(${agent})`)}${detectedBadge}${sharedBadge}`;
|
|
1795
|
+
return {
|
|
1796
|
+
name: label,
|
|
1797
|
+
value: agent,
|
|
1798
|
+
checked: alignedCheckedSet.has(agent),
|
|
1799
|
+
linkedValues: coHosted,
|
|
1800
|
+
description: coHosted.length > 0 ? `Linked with ${coHosted.map((a) => getMcpAgentConfig(a).displayName).join(", ")} (shared configuration)` : void 0
|
|
1801
|
+
};
|
|
1802
|
+
});
|
|
1803
|
+
};
|
|
1804
|
+
|
|
1805
|
+
// src/interactive/prompts/linked-checkbox.ts
|
|
1806
|
+
import {
|
|
1807
|
+
Separator,
|
|
1808
|
+
ValidationError,
|
|
1809
|
+
createPrompt,
|
|
1810
|
+
isDownKey,
|
|
1811
|
+
isEnterKey,
|
|
1812
|
+
isNumberKey,
|
|
1813
|
+
isSpaceKey,
|
|
1814
|
+
isUpKey,
|
|
1815
|
+
makeTheme,
|
|
1816
|
+
useKeypress,
|
|
1817
|
+
useMemo,
|
|
1818
|
+
usePagination,
|
|
1819
|
+
usePrefix,
|
|
1820
|
+
useState
|
|
1821
|
+
} from "@inquirer/core";
|
|
1822
|
+
import pc4 from "picocolors";
|
|
1823
|
+
var defaultTheme = {
|
|
1824
|
+
icon: {
|
|
1825
|
+
checked: pc4.green("[x]"),
|
|
1826
|
+
unchecked: pc4.dim("[ ]"),
|
|
1827
|
+
cursor: pc4.cyan(">"),
|
|
1828
|
+
disabledChecked: pc4.dim("[x]"),
|
|
1829
|
+
disabledUnchecked: pc4.dim("[-]")
|
|
1830
|
+
},
|
|
1831
|
+
style: {
|
|
1832
|
+
disabled: (text) => pc4.dim(text),
|
|
1833
|
+
renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
|
|
1834
|
+
description: (text) => pc4.cyan(text),
|
|
1835
|
+
keysHelpTip: (keys) => keys.map(([key, action]) => `${pc4.bold(key)} ${pc4.dim(action)}`).join(pc4.dim(" | ")),
|
|
1836
|
+
highlight: (text) => pc4.cyan(text)
|
|
1837
|
+
},
|
|
1838
|
+
i18n: {
|
|
1839
|
+
disabledError: "This option is disabled and cannot be toggled."
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
function isSelectable(item) {
|
|
1843
|
+
return !Separator.isSeparator(item) && !item.disabled;
|
|
1844
|
+
}
|
|
1845
|
+
function isNavigable(item) {
|
|
1846
|
+
return !Separator.isSeparator(item);
|
|
1847
|
+
}
|
|
1848
|
+
function isChecked(item) {
|
|
1849
|
+
return !Separator.isSeparator(item) && item.checked;
|
|
1850
|
+
}
|
|
1851
|
+
function normalizeChoices(choices) {
|
|
1852
|
+
return choices.map((choice) => {
|
|
1853
|
+
if (Separator.isSeparator(choice)) {
|
|
1854
|
+
return choice;
|
|
1855
|
+
}
|
|
1856
|
+
if (typeof choice !== "object" || choice === null || !("value" in choice)) {
|
|
1857
|
+
const name2 = String(choice);
|
|
1858
|
+
return {
|
|
1859
|
+
value: choice,
|
|
1860
|
+
name: name2,
|
|
1861
|
+
short: name2,
|
|
1862
|
+
checkedName: name2,
|
|
1863
|
+
disabled: false,
|
|
1864
|
+
checked: false,
|
|
1865
|
+
linkedValues: []
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
const name = choice.name ?? String(choice.value);
|
|
1869
|
+
return {
|
|
1870
|
+
value: choice.value,
|
|
1871
|
+
name,
|
|
1872
|
+
short: choice.short ?? name,
|
|
1873
|
+
checkedName: choice.checkedName ?? name,
|
|
1874
|
+
description: choice.description,
|
|
1875
|
+
disabled: choice.disabled ?? false,
|
|
1876
|
+
checked: choice.checked ?? false,
|
|
1877
|
+
linkedValues: choice.linkedValues ?? []
|
|
1878
|
+
};
|
|
1879
|
+
});
|
|
1880
|
+
}
|
|
1881
|
+
var linkedCheckbox = createPrompt(
|
|
1882
|
+
(config, done) => {
|
|
1883
|
+
const { pageSize = 10, loop = true, required, validate = () => true } = config;
|
|
1884
|
+
const theme = makeTheme(defaultTheme, config.theme);
|
|
1885
|
+
const [status, setStatus] = useState("idle");
|
|
1886
|
+
const prefix = usePrefix({ status, theme });
|
|
1887
|
+
const [items, setItems] = useState(() => normalizeChoices(config.choices));
|
|
1888
|
+
const bounds = useMemo(() => {
|
|
1889
|
+
const first = items.findIndex(isNavigable);
|
|
1890
|
+
let last = -1;
|
|
1891
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
1892
|
+
if (isNavigable(items[i])) {
|
|
1893
|
+
last = i;
|
|
1894
|
+
break;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
if (first === -1 || last === -1) {
|
|
1898
|
+
throw new ValidationError("[linkedCheckbox prompt] No selectable choices.");
|
|
1899
|
+
}
|
|
1900
|
+
return { first, last };
|
|
1901
|
+
}, [items]);
|
|
1902
|
+
const [active, setActive] = useState(bounds.first);
|
|
1903
|
+
const [errorMsg, setError] = useState();
|
|
1904
|
+
const toggleWithLinked = (targetIndex) => {
|
|
1905
|
+
const targetItem = items[targetIndex];
|
|
1906
|
+
if (!targetItem || Separator.isSeparator(targetItem) || targetItem.disabled) {
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
const nextChecked = !targetItem.checked;
|
|
1910
|
+
const targetValue = targetItem.value;
|
|
1911
|
+
const linked = new Set(targetItem.linkedValues);
|
|
1912
|
+
setItems(
|
|
1913
|
+
(prevItems) => prevItems.map((item) => {
|
|
1914
|
+
if (Separator.isSeparator(item) || item.disabled) {
|
|
1915
|
+
return item;
|
|
1916
|
+
}
|
|
1917
|
+
const isTargetOrLinked = item.value === targetValue || linked.has(item.value) || item.linkedValues.includes(targetValue);
|
|
1918
|
+
if (isTargetOrLinked) {
|
|
1919
|
+
return { ...item, checked: nextChecked };
|
|
1920
|
+
}
|
|
1921
|
+
return item;
|
|
1922
|
+
})
|
|
1923
|
+
);
|
|
1924
|
+
};
|
|
1925
|
+
useKeypress(async (key) => {
|
|
1926
|
+
if (isEnterKey(key)) {
|
|
1927
|
+
const selection = items.filter(isChecked);
|
|
1928
|
+
const isValid = await validate([...selection]);
|
|
1929
|
+
if (required && selection.length === 0) {
|
|
1930
|
+
setError("At least one choice must be selected");
|
|
1931
|
+
} else if (isValid === true) {
|
|
1932
|
+
setStatus("done");
|
|
1933
|
+
done(selection.map((choice) => choice.value));
|
|
1934
|
+
} else {
|
|
1935
|
+
setError(typeof isValid === "string" ? isValid : "You must select a valid value");
|
|
1936
|
+
}
|
|
1937
|
+
} else if (isUpKey(key) || isDownKey(key)) {
|
|
1938
|
+
if (errorMsg) setError(void 0);
|
|
1939
|
+
if (loop || isUpKey(key) && active !== bounds.first || isDownKey(key) && active !== bounds.last) {
|
|
1940
|
+
const offset = isUpKey(key) ? -1 : 1;
|
|
1941
|
+
let next = active;
|
|
1942
|
+
do {
|
|
1943
|
+
next = (next + offset + items.length) % items.length;
|
|
1944
|
+
} while (!isNavigable(items[next]));
|
|
1945
|
+
setActive(next);
|
|
1946
|
+
}
|
|
1947
|
+
} else if (isSpaceKey(key)) {
|
|
1948
|
+
const activeItem = items[active];
|
|
1949
|
+
if (activeItem && !Separator.isSeparator(activeItem)) {
|
|
1950
|
+
if (activeItem.disabled) {
|
|
1951
|
+
setError(theme.i18n.disabledError);
|
|
1952
|
+
} else {
|
|
1953
|
+
setError(void 0);
|
|
1954
|
+
toggleWithLinked(active);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
} else if (key.name === "a") {
|
|
1958
|
+
const hasUnchecked = items.some((choice) => isSelectable(choice) && !choice.checked);
|
|
1959
|
+
setItems(
|
|
1960
|
+
(prevItems) => prevItems.map((item) => isSelectable(item) ? { ...item, checked: hasUnchecked } : item)
|
|
1961
|
+
);
|
|
1962
|
+
} else if (isNumberKey(key)) {
|
|
1963
|
+
const selectedIndex = Number(key.name) - 1;
|
|
1964
|
+
let selectableIndex = -1;
|
|
1965
|
+
const position = items.findIndex((item) => {
|
|
1966
|
+
if (Separator.isSeparator(item)) return false;
|
|
1967
|
+
selectableIndex++;
|
|
1968
|
+
return selectableIndex === selectedIndex;
|
|
1969
|
+
});
|
|
1970
|
+
const selectedItem = items[position];
|
|
1971
|
+
if (selectedItem && isSelectable(selectedItem)) {
|
|
1972
|
+
setActive(position);
|
|
1973
|
+
setError(void 0);
|
|
1974
|
+
toggleWithLinked(position);
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
});
|
|
1978
|
+
const message = theme.style.message(config.message, status);
|
|
1979
|
+
let description;
|
|
1980
|
+
const page = usePagination({
|
|
1981
|
+
items,
|
|
1982
|
+
active,
|
|
1983
|
+
renderItem({ item, isActive }) {
|
|
1984
|
+
if (Separator.isSeparator(item)) {
|
|
1985
|
+
return ` ${item.separator}`;
|
|
1986
|
+
}
|
|
1987
|
+
const cursor = isActive ? theme.icon.cursor : " ";
|
|
1988
|
+
if (item.disabled) {
|
|
1989
|
+
const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
|
|
1990
|
+
const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
|
|
1991
|
+
return theme.style.disabled(`${cursor} ${checkbox2} ${item.name} ${disabledLabel}`);
|
|
1992
|
+
}
|
|
1993
|
+
if (isActive) {
|
|
1994
|
+
description = item.description;
|
|
1995
|
+
}
|
|
1996
|
+
const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
|
|
1997
|
+
const name = item.checked ? item.checkedName : item.name;
|
|
1998
|
+
const color = isActive ? theme.style.highlight : (x) => x;
|
|
1999
|
+
return color(`${cursor} ${checkbox} ${name}`);
|
|
2000
|
+
},
|
|
2001
|
+
pageSize,
|
|
2002
|
+
loop
|
|
2003
|
+
});
|
|
2004
|
+
if (status === "done") {
|
|
2005
|
+
const selection = items.filter(isChecked);
|
|
2006
|
+
const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
|
|
2007
|
+
return [prefix, message, answer].filter(Boolean).join(" ");
|
|
2008
|
+
}
|
|
2009
|
+
const helpLine = theme.style.keysHelpTip([
|
|
2010
|
+
["up/down", "navigate"],
|
|
2011
|
+
["space", "toggle"],
|
|
2012
|
+
["a", "all"],
|
|
2013
|
+
["enter", "submit"]
|
|
2014
|
+
]);
|
|
2015
|
+
const lines = [
|
|
2016
|
+
[prefix, message].filter(Boolean).join(" "),
|
|
2017
|
+
page,
|
|
2018
|
+
helpLine
|
|
2019
|
+
];
|
|
2020
|
+
if (description) {
|
|
2021
|
+
lines.push(theme.style.description(description));
|
|
2022
|
+
}
|
|
2023
|
+
if (errorMsg) {
|
|
2024
|
+
lines.push(theme.style.error(errorMsg));
|
|
2025
|
+
}
|
|
2026
|
+
return lines.join("\n");
|
|
2027
|
+
}
|
|
2028
|
+
);
|
|
1503
2029
|
|
|
1504
2030
|
// src/interactive/prompts/scope.ts
|
|
1505
2031
|
import { select } from "@inquirer/prompts";
|
|
1506
|
-
import
|
|
2032
|
+
import pc5 from "picocolors";
|
|
1507
2033
|
var promptScope = async (options = {}) => {
|
|
1508
2034
|
const initialGlobal = options.defaultGlobal ?? options.global;
|
|
1509
2035
|
if (initialGlobal !== void 0) {
|
|
@@ -1514,11 +2040,11 @@ var promptScope = async (options = {}) => {
|
|
|
1514
2040
|
message: options.message ?? "Select MCP scope:",
|
|
1515
2041
|
choices: [
|
|
1516
2042
|
{
|
|
1517
|
-
name: `Current Project - ${
|
|
2043
|
+
name: `Current Project - ${pc5.dim(cwd)}`,
|
|
1518
2044
|
value: false
|
|
1519
2045
|
},
|
|
1520
2046
|
{
|
|
1521
|
-
name: `Global User Config - ${
|
|
2047
|
+
name: `Global User Config - ${pc5.dim("applies across all projects")}`,
|
|
1522
2048
|
value: true
|
|
1523
2049
|
}
|
|
1524
2050
|
]
|
|
@@ -1538,26 +2064,23 @@ var promptScopeAndAgents = async (options = {}) => {
|
|
|
1538
2064
|
cwd
|
|
1539
2065
|
});
|
|
1540
2066
|
const detected = resolution.detected;
|
|
1541
|
-
const
|
|
2067
|
+
const rawAvailable = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
2068
|
+
const availableAgentTypes = sortAgentsWithClusters(rawAvailable, { global: isGlobal, cwd });
|
|
1542
2069
|
if (detected.length > 0) {
|
|
1543
2070
|
logger.info(
|
|
1544
|
-
`Detected configured agents: ${
|
|
2071
|
+
`Detected configured agents: ${pc6.cyan(detected.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
|
|
1545
2072
|
);
|
|
1546
2073
|
} else {
|
|
1547
2074
|
logger.warn(`No active ${isGlobal ? "global" : "project"} agents detected`);
|
|
1548
2075
|
}
|
|
1549
2076
|
const defaultChecked = options.defaultAgents && options.defaultAgents.length > 0 ? options.defaultAgents : detected;
|
|
1550
|
-
const choices =
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
name: label,
|
|
1556
|
-
value: agentType,
|
|
1557
|
-
checked: defaultChecked.includes(agentType)
|
|
1558
|
-
};
|
|
2077
|
+
const choices = buildLinkedAgentChoices({
|
|
2078
|
+
agents: availableAgentTypes,
|
|
2079
|
+
checkedAgents: defaultChecked,
|
|
2080
|
+
detectedAgents: detected,
|
|
2081
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
1559
2082
|
});
|
|
1560
|
-
const selectedAgents = await
|
|
2083
|
+
const selectedAgents = await linkedCheckbox({
|
|
1561
2084
|
message: "Select target agents (Space to select, Enter to confirm):",
|
|
1562
2085
|
choices,
|
|
1563
2086
|
validate: (chosen) => {
|
|
@@ -1602,19 +2125,54 @@ var promptArgsConfig = async (initialArgs = []) => {
|
|
|
1602
2125
|
});
|
|
1603
2126
|
return parseArgsString(raw.trim());
|
|
1604
2127
|
};
|
|
2128
|
+
var formatArgsString = (args) => {
|
|
2129
|
+
return args.map((arg) => arg.includes(" ") || arg.includes('"') ? `"${arg.replace(/"/g, '\\"')}"` : arg).join(" ");
|
|
2130
|
+
};
|
|
2131
|
+
var promptEditArgs = async (currentArgs = []) => {
|
|
2132
|
+
const defaultStr = formatArgsString(currentArgs);
|
|
2133
|
+
const raw = await input({
|
|
2134
|
+
message: "Edit command arguments (space-separated, wrap paths with spaces in quotes, leave empty to clear):",
|
|
2135
|
+
default: defaultStr
|
|
2136
|
+
});
|
|
2137
|
+
const trimmed = raw.trim();
|
|
2138
|
+
if (!trimmed) {
|
|
2139
|
+
return [];
|
|
2140
|
+
}
|
|
2141
|
+
return parseArgsString(trimmed);
|
|
2142
|
+
};
|
|
1605
2143
|
|
|
1606
2144
|
// src/interactive/prompts/env.ts
|
|
1607
|
-
import { input as
|
|
1608
|
-
import
|
|
2145
|
+
import { input as input3, password as password2, select as select3 } from "@inquirer/prompts";
|
|
2146
|
+
import pc9 from "picocolors";
|
|
2147
|
+
|
|
2148
|
+
// src/utils/mask-secret.ts
|
|
2149
|
+
var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
|
|
2150
|
+
var maskSecretValue = (key, value) => {
|
|
2151
|
+
if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
|
|
2152
|
+
return value;
|
|
2153
|
+
}
|
|
2154
|
+
return `${value.slice(0, 2)}***${value.slice(-2)}`;
|
|
2155
|
+
};
|
|
2156
|
+
var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
|
|
2157
|
+
var maskSecretHeader = (key, value) => {
|
|
2158
|
+
if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
|
|
2159
|
+
return value;
|
|
2160
|
+
}
|
|
2161
|
+
return `${value.slice(0, 4)}***${value.slice(-3)}`;
|
|
2162
|
+
};
|
|
2163
|
+
|
|
2164
|
+
// src/interactive/prompts/kv.ts
|
|
2165
|
+
import { confirm as confirm2, input as input2, password, select as select2 } from "@inquirer/prompts";
|
|
2166
|
+
import pc8 from "picocolors";
|
|
1609
2167
|
|
|
1610
2168
|
// src/interactive/prompts/multiline.ts
|
|
1611
2169
|
import { createInterface } from "readline";
|
|
1612
2170
|
import { editor } from "@inquirer/prompts";
|
|
1613
|
-
import
|
|
2171
|
+
import pc7 from "picocolors";
|
|
1614
2172
|
var readMultilineTextFromTerminal = async (message, endHint = "When done pasting, enter END on a new line or press Enter twice to finish") => {
|
|
1615
|
-
console.log(
|
|
2173
|
+
console.log(pc7.cyan(`
|
|
1616
2174
|
${message}`));
|
|
1617
|
-
console.log(
|
|
2175
|
+
console.log(pc7.dim(` (Hint: ${endHint})
|
|
1618
2176
|
`));
|
|
1619
2177
|
return new Promise((resolve) => {
|
|
1620
2178
|
const rl = createInterface({
|
|
@@ -1668,8 +2226,157 @@ var promptEditorText = async (options) => {
|
|
|
1668
2226
|
}
|
|
1669
2227
|
};
|
|
1670
2228
|
|
|
2229
|
+
// src/interactive/prompts/kv.ts
|
|
2230
|
+
var promptEditKeyValueConfig = async (currentItems = {}, options) => {
|
|
2231
|
+
let items = { ...currentItems };
|
|
2232
|
+
while (true) {
|
|
2233
|
+
const keys = Object.keys(items);
|
|
2234
|
+
console.log();
|
|
2235
|
+
if (keys.length === 0) {
|
|
2236
|
+
console.log(pc8.dim(` No ${options.itemsNoun} configured.`));
|
|
2237
|
+
} else {
|
|
2238
|
+
console.log(pc8.cyan(pc8.bold(` Configured ${options.title} (${keys.length}):`)));
|
|
2239
|
+
for (const [k, v] of Object.entries(items)) {
|
|
2240
|
+
const sep = options.separator === "=" ? "=" : ": ";
|
|
2241
|
+
console.log(` ${pc8.bold(k)}${sep}${pc8.dim(options.maskValue(k, v))}`);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
console.log();
|
|
2245
|
+
const choice = await select2({
|
|
2246
|
+
message: `Manage ${options.itemsNoun}:`,
|
|
2247
|
+
choices: [
|
|
2248
|
+
{
|
|
2249
|
+
name: "Open in system default editor ($EDITOR)",
|
|
2250
|
+
value: "editor"
|
|
2251
|
+
},
|
|
2252
|
+
{
|
|
2253
|
+
name: `Add or modify a ${options.itemNoun}`,
|
|
2254
|
+
value: "upsert"
|
|
2255
|
+
},
|
|
2256
|
+
...keys.length > 0 ? [
|
|
2257
|
+
{
|
|
2258
|
+
name: `Delete a ${options.itemNoun}`,
|
|
2259
|
+
value: "delete"
|
|
2260
|
+
}
|
|
2261
|
+
] : [],
|
|
2262
|
+
{
|
|
2263
|
+
name: `Paste multiline ${options.itemsNoun} into terminal`,
|
|
2264
|
+
value: "paste"
|
|
2265
|
+
},
|
|
2266
|
+
...keys.length > 0 ? [
|
|
2267
|
+
{
|
|
2268
|
+
name: `Clear all ${options.itemsNoun}`,
|
|
2269
|
+
value: "clear"
|
|
2270
|
+
}
|
|
2271
|
+
] : [],
|
|
2272
|
+
{
|
|
2273
|
+
name: `Done (finish editing ${options.itemsNoun})`,
|
|
2274
|
+
value: "done"
|
|
2275
|
+
}
|
|
2276
|
+
]
|
|
2277
|
+
});
|
|
2278
|
+
if (choice === "done") {
|
|
2279
|
+
return items;
|
|
2280
|
+
}
|
|
2281
|
+
if (choice === "editor") {
|
|
2282
|
+
const defaultText = options.formatText(items);
|
|
2283
|
+
const text = await promptEditorText({
|
|
2284
|
+
message: options.editorMessage,
|
|
2285
|
+
postfix: options.editorPostfix,
|
|
2286
|
+
defaultText
|
|
2287
|
+
});
|
|
2288
|
+
const parsed = options.parseText(text);
|
|
2289
|
+
items = parsed;
|
|
2290
|
+
logger.success(`${options.title} updated (${Object.keys(items).length} total)`);
|
|
2291
|
+
} else if (choice === "upsert") {
|
|
2292
|
+
const key = await input2({
|
|
2293
|
+
message: options.keyPromptMessage,
|
|
2294
|
+
validate: (val) => {
|
|
2295
|
+
const trimmed = val.trim();
|
|
2296
|
+
if (!trimmed) return `${options.itemNoun} name cannot be empty`;
|
|
2297
|
+
if (/\s/.test(trimmed)) return `${options.itemNoun} name cannot contain spaces`;
|
|
2298
|
+
return true;
|
|
2299
|
+
}
|
|
2300
|
+
});
|
|
2301
|
+
const trimmedKey = key.trim();
|
|
2302
|
+
const existingVal = items[trimmedKey];
|
|
2303
|
+
const isSecret = options.isSecretKey(trimmedKey);
|
|
2304
|
+
let newVal;
|
|
2305
|
+
if (isSecret) {
|
|
2306
|
+
newVal = await password({
|
|
2307
|
+
message: existingVal !== void 0 ? `New value for (${trimmedKey}) [leave empty to keep current]:` : `${options.valuePromptMessage} for (${trimmedKey}) [sensitive content masked]:`,
|
|
2308
|
+
mask: "*"
|
|
2309
|
+
});
|
|
2310
|
+
if (existingVal !== void 0 && newVal === "") {
|
|
2311
|
+
newVal = existingVal;
|
|
2312
|
+
}
|
|
2313
|
+
} else {
|
|
2314
|
+
newVal = await input2({
|
|
2315
|
+
message: `${options.valuePromptMessage} for (${trimmedKey}):`,
|
|
2316
|
+
default: existingVal
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
items[trimmedKey] = newVal;
|
|
2320
|
+
logger.success(`${existingVal !== void 0 ? "Updated" : "Added"}: ${pc8.cyan(trimmedKey)}`);
|
|
2321
|
+
} else if (choice === "delete") {
|
|
2322
|
+
const toDelete = await select2({
|
|
2323
|
+
message: `Select ${options.itemNoun} to delete:`,
|
|
2324
|
+
choices: [
|
|
2325
|
+
...keys.map((k) => ({ name: k, value: k })),
|
|
2326
|
+
{ name: "Cancel", value: "__cancel__" }
|
|
2327
|
+
]
|
|
2328
|
+
});
|
|
2329
|
+
if (toDelete !== "__cancel__") {
|
|
2330
|
+
delete items[toDelete];
|
|
2331
|
+
logger.success(`Deleted: ${pc8.cyan(toDelete)}`);
|
|
2332
|
+
}
|
|
2333
|
+
} else if (choice === "paste") {
|
|
2334
|
+
const pasted = await readMultilineTextFromTerminal(options.pasteMessage);
|
|
2335
|
+
const parsed = options.parseText(pasted);
|
|
2336
|
+
const count = Object.keys(parsed).length;
|
|
2337
|
+
if (count === 0) {
|
|
2338
|
+
logger.warn(`No valid ${options.itemsNoun} recognized`);
|
|
2339
|
+
} else {
|
|
2340
|
+
if (keys.length > 0) {
|
|
2341
|
+
const pasteMode = await select2({
|
|
2342
|
+
message: `How to apply pasted ${options.itemsNoun}?`,
|
|
2343
|
+
choices: [
|
|
2344
|
+
{ name: `Merge with existing ${options.itemsNoun}`, value: "merge" },
|
|
2345
|
+
{ name: `Replace all existing ${options.itemsNoun}`, value: "replace" }
|
|
2346
|
+
]
|
|
2347
|
+
});
|
|
2348
|
+
if (pasteMode === "replace") {
|
|
2349
|
+
items = parsed;
|
|
2350
|
+
} else {
|
|
2351
|
+
Object.assign(items, parsed);
|
|
2352
|
+
}
|
|
2353
|
+
} else {
|
|
2354
|
+
items = parsed;
|
|
2355
|
+
}
|
|
2356
|
+
logger.success(`Successfully applied ${pc8.cyan(String(count))} ${options.itemsNoun}`);
|
|
2357
|
+
}
|
|
2358
|
+
} else if (choice === "clear") {
|
|
2359
|
+
const confirmClear = await confirm2({
|
|
2360
|
+
message: `Are you sure you want to clear all ${options.itemsNoun}?`,
|
|
2361
|
+
default: false
|
|
2362
|
+
});
|
|
2363
|
+
if (confirmClear) {
|
|
2364
|
+
items = {};
|
|
2365
|
+
logger.success(`Cleared all ${options.itemsNoun}`);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
};
|
|
2370
|
+
|
|
1671
2371
|
// src/interactive/prompts/env.ts
|
|
1672
|
-
var
|
|
2372
|
+
var formatEnvText = (env) => {
|
|
2373
|
+
return Object.entries(env).map(([key, value]) => {
|
|
2374
|
+
if (/[\s"']/.test(value)) {
|
|
2375
|
+
return `${key}="${value.replace(/"/g, '\\"')}"`;
|
|
2376
|
+
}
|
|
2377
|
+
return `${key}=${value}`;
|
|
2378
|
+
}).join("\n");
|
|
2379
|
+
};
|
|
1673
2380
|
var parseEnvText = (rawText) => {
|
|
1674
2381
|
const result = {};
|
|
1675
2382
|
const lines = rawText.split(/\r?\n/);
|
|
@@ -1695,7 +2402,7 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1695
2402
|
const env = { ...initialEnv };
|
|
1696
2403
|
const initialCount = Object.keys(env).length;
|
|
1697
2404
|
if (initialCount > 0) {
|
|
1698
|
-
logger.info(`Includes ${
|
|
2405
|
+
logger.info(`Includes ${pc9.cyan(String(initialCount))} preset environment variables`);
|
|
1699
2406
|
}
|
|
1700
2407
|
const mode = await select3({
|
|
1701
2408
|
message: "Configure environment variables?",
|
|
@@ -1724,7 +2431,8 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1724
2431
|
if (mode === "paste" || mode === "editor") {
|
|
1725
2432
|
const pasted = mode === "editor" ? await promptEditorText({
|
|
1726
2433
|
message: "Paste or edit environment variables in editor, then save and exit:",
|
|
1727
|
-
postfix: ".env"
|
|
2434
|
+
postfix: ".env",
|
|
2435
|
+
defaultText: formatEnvText(env)
|
|
1728
2436
|
}) : await readMultilineTextFromTerminal("Paste .env formatted content (multiline supported):");
|
|
1729
2437
|
const parsed = parseEnvText(pasted);
|
|
1730
2438
|
const count = Object.keys(parsed).length;
|
|
@@ -1732,17 +2440,16 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1732
2440
|
logger.warn("No valid KEY=VALUE pairs recognized");
|
|
1733
2441
|
} else {
|
|
1734
2442
|
Object.assign(env, parsed);
|
|
1735
|
-
logger.success(`Successfully parsed ${
|
|
2443
|
+
logger.success(`Successfully parsed ${pc9.cyan(String(count))} environment variables:`);
|
|
1736
2444
|
for (const [k, v] of Object.entries(parsed)) {
|
|
1737
|
-
|
|
1738
|
-
console.log(` ${pc5.bold(k)}=${pc5.dim(masked)}`);
|
|
2445
|
+
console.log(` ${pc9.bold(k)}=${pc9.dim(maskSecretValue(k, v))}`);
|
|
1739
2446
|
}
|
|
1740
2447
|
}
|
|
1741
2448
|
return env;
|
|
1742
2449
|
}
|
|
1743
2450
|
logger.info("Entering environment variables (leave key empty and press enter to finish):");
|
|
1744
2451
|
while (true) {
|
|
1745
|
-
const key = await
|
|
2452
|
+
const key = await input3({
|
|
1746
2453
|
message: "Variable name (Key, leave empty to finish):",
|
|
1747
2454
|
validate: (val2) => {
|
|
1748
2455
|
const trimmed = val2.trim();
|
|
@@ -1756,25 +2463,42 @@ var promptEnvConfig = async (initialEnv = {}) => {
|
|
|
1756
2463
|
const isSecret = SECRET_KEY_PATTERN.test(trimmedKey);
|
|
1757
2464
|
let val;
|
|
1758
2465
|
if (isSecret) {
|
|
1759
|
-
val = await
|
|
2466
|
+
val = await password2({
|
|
1760
2467
|
message: `Value for (${trimmedKey}) [secret masked]:`,
|
|
1761
2468
|
mask: "*"
|
|
1762
2469
|
});
|
|
1763
2470
|
} else {
|
|
1764
|
-
val = await
|
|
2471
|
+
val = await input3({
|
|
1765
2472
|
message: `Value for (${trimmedKey}):`
|
|
1766
2473
|
});
|
|
1767
2474
|
}
|
|
1768
2475
|
env[trimmedKey] = val;
|
|
1769
|
-
logger.success(`Added: ${
|
|
2476
|
+
logger.success(`Added: ${pc9.cyan(trimmedKey)}`);
|
|
1770
2477
|
}
|
|
1771
2478
|
return env;
|
|
1772
2479
|
};
|
|
2480
|
+
var promptEditEnvConfig = async (currentEnv = {}) => promptEditKeyValueConfig(currentEnv, {
|
|
2481
|
+
title: "Environment Variables",
|
|
2482
|
+
itemNoun: "variable",
|
|
2483
|
+
itemsNoun: "environment variables",
|
|
2484
|
+
separator: "=",
|
|
2485
|
+
editorPostfix: ".env",
|
|
2486
|
+
editorMessage: "Edit environment variables in editor, then save and exit:",
|
|
2487
|
+
pasteMessage: "Paste .env formatted content (multiline supported):",
|
|
2488
|
+
keyPromptMessage: "Variable name (Key):",
|
|
2489
|
+
valuePromptMessage: "Value",
|
|
2490
|
+
isSecretKey: (k) => SECRET_KEY_PATTERN.test(k),
|
|
2491
|
+
maskValue: maskSecretValue,
|
|
2492
|
+
formatText: formatEnvText,
|
|
2493
|
+
parseText: parseEnvText
|
|
2494
|
+
});
|
|
1773
2495
|
|
|
1774
2496
|
// src/interactive/prompts/headers.ts
|
|
1775
|
-
import { input as
|
|
1776
|
-
import
|
|
1777
|
-
var
|
|
2497
|
+
import { input as input4, password as password3, select as select4 } from "@inquirer/prompts";
|
|
2498
|
+
import pc10 from "picocolors";
|
|
2499
|
+
var formatHeadersText = (headers) => {
|
|
2500
|
+
return Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
2501
|
+
};
|
|
1778
2502
|
var parseHeadersText = (rawText) => {
|
|
1779
2503
|
const result = {};
|
|
1780
2504
|
const lines = rawText.split(/\r?\n/);
|
|
@@ -1830,7 +2554,8 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
|
1830
2554
|
}
|
|
1831
2555
|
if (mode === "paste" || mode === "editor") {
|
|
1832
2556
|
const pasted = mode === "editor" ? await promptEditorText({
|
|
1833
|
-
message: "Paste or edit HTTP headers in editor, then save and exit:"
|
|
2557
|
+
message: "Paste or edit HTTP headers in editor, then save and exit:",
|
|
2558
|
+
defaultText: formatHeadersText(headers)
|
|
1834
2559
|
}) : await readMultilineTextFromTerminal(
|
|
1835
2560
|
"Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):"
|
|
1836
2561
|
);
|
|
@@ -1840,17 +2565,16 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
|
1840
2565
|
logger.warn("No valid Key: Value pairs recognized");
|
|
1841
2566
|
} else {
|
|
1842
2567
|
Object.assign(headers, parsed);
|
|
1843
|
-
logger.success(`Successfully parsed ${
|
|
2568
|
+
logger.success(`Successfully parsed ${pc10.cyan(String(count))} headers:`);
|
|
1844
2569
|
for (const [k, v] of Object.entries(parsed)) {
|
|
1845
|
-
|
|
1846
|
-
console.log(` ${pc6.bold(k)}: ${pc6.dim(masked)}`);
|
|
2570
|
+
console.log(` ${pc10.bold(k)}: ${pc10.dim(maskSecretHeader(k, v))}`);
|
|
1847
2571
|
}
|
|
1848
2572
|
}
|
|
1849
2573
|
return headers;
|
|
1850
2574
|
}
|
|
1851
2575
|
logger.info("Entering HTTP headers (leave header name empty and press enter to finish):");
|
|
1852
2576
|
while (true) {
|
|
1853
|
-
const name = await
|
|
2577
|
+
const name = await input4({
|
|
1854
2578
|
message: "Header name (e.g. Authorization, leave empty to finish):",
|
|
1855
2579
|
validate: (val2) => {
|
|
1856
2580
|
const trimmed = val2.trim();
|
|
@@ -1864,25 +2588,39 @@ var promptHeadersConfig = async (initialHeaders = {}) => {
|
|
|
1864
2588
|
const isSecret = SECRET_HEADER_PATTERN.test(trimmedName);
|
|
1865
2589
|
let val;
|
|
1866
2590
|
if (isSecret) {
|
|
1867
|
-
val = await
|
|
2591
|
+
val = await password3({
|
|
1868
2592
|
message: `Header value for (${trimmedName}) [sensitive content masked]:`,
|
|
1869
2593
|
mask: "*"
|
|
1870
2594
|
});
|
|
1871
2595
|
} else {
|
|
1872
|
-
val = await
|
|
2596
|
+
val = await input4({
|
|
1873
2597
|
message: `Header value for (${trimmedName}):`
|
|
1874
2598
|
});
|
|
1875
2599
|
}
|
|
1876
2600
|
headers[trimmedName] = val;
|
|
1877
|
-
logger.success(`Added: ${
|
|
2601
|
+
logger.success(`Added: ${pc10.cyan(trimmedName)}`);
|
|
1878
2602
|
}
|
|
1879
2603
|
return headers;
|
|
1880
2604
|
};
|
|
2605
|
+
var promptEditHeadersConfig = async (currentHeaders = {}) => promptEditKeyValueConfig(currentHeaders, {
|
|
2606
|
+
title: "HTTP Headers",
|
|
2607
|
+
itemNoun: "header",
|
|
2608
|
+
itemsNoun: "HTTP headers",
|
|
2609
|
+
separator: ":",
|
|
2610
|
+
editorMessage: "Edit HTTP headers in editor, then save and exit:",
|
|
2611
|
+
pasteMessage: "Paste HTTP headers content (multiline supported, e.g. Authorization: Bearer ...):",
|
|
2612
|
+
keyPromptMessage: "Header name (e.g. Authorization):",
|
|
2613
|
+
valuePromptMessage: "Header value",
|
|
2614
|
+
isSecretKey: (k) => SECRET_HEADER_PATTERN.test(k),
|
|
2615
|
+
maskValue: maskSecretHeader,
|
|
2616
|
+
formatText: formatHeadersText,
|
|
2617
|
+
parseText: parseHeadersText
|
|
2618
|
+
});
|
|
1881
2619
|
|
|
1882
2620
|
// src/interactive/wizard-add.ts
|
|
1883
2621
|
var wizardAdd = async (initial = {}) => {
|
|
1884
2622
|
const cwd = initial.cwd ?? process.cwd();
|
|
1885
|
-
logger.info(
|
|
2623
|
+
logger.info(pc11.bold("Welcome to the MCP interactive add wizard"));
|
|
1886
2624
|
let source = initial.source;
|
|
1887
2625
|
if (!source) {
|
|
1888
2626
|
const sourceType = await select5({
|
|
@@ -1903,12 +2641,12 @@ var wizardAdd = async (initial = {}) => {
|
|
|
1903
2641
|
]
|
|
1904
2642
|
});
|
|
1905
2643
|
if (sourceType === "npm") {
|
|
1906
|
-
source = await
|
|
1907
|
-
message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres
|
|
2644
|
+
source = await input5({
|
|
2645
|
+
message: "Enter npm package name (e.g. @modelcontextprotocol/server-postgres):",
|
|
1908
2646
|
validate: (val) => val.trim() ? true : "Package name cannot be empty"
|
|
1909
2647
|
});
|
|
1910
2648
|
} else if (sourceType === "remote") {
|
|
1911
|
-
source = await
|
|
2649
|
+
source = await input5({
|
|
1912
2650
|
message: "Enter remote server URL (e.g. https://mcp.example.com/sse):",
|
|
1913
2651
|
validate: (val) => {
|
|
1914
2652
|
const trimmed = val.trim();
|
|
@@ -1918,7 +2656,7 @@ var wizardAdd = async (initial = {}) => {
|
|
|
1918
2656
|
}
|
|
1919
2657
|
});
|
|
1920
2658
|
} else {
|
|
1921
|
-
source = await
|
|
2659
|
+
source = await input5({
|
|
1922
2660
|
message: "Enter command and arguments (e.g. python -m my_mcp_server or docker run ...):",
|
|
1923
2661
|
validate: (val) => val.trim() ? true : "Command cannot be empty"
|
|
1924
2662
|
});
|
|
@@ -1928,7 +2666,7 @@ var wizardAdd = async (initial = {}) => {
|
|
|
1928
2666
|
const parsed = parseMcpSource(source);
|
|
1929
2667
|
let serverName = initial.name;
|
|
1930
2668
|
if (!serverName) {
|
|
1931
|
-
serverName = await
|
|
2669
|
+
serverName = await input5({
|
|
1932
2670
|
message: "MCP server name:",
|
|
1933
2671
|
default: parsed.inferredName,
|
|
1934
2672
|
validate: (val) => val.trim() ? true : "Server name cannot be empty"
|
|
@@ -1950,7 +2688,7 @@ var wizardAdd = async (initial = {}) => {
|
|
|
1950
2688
|
});
|
|
1951
2689
|
}
|
|
1952
2690
|
if (Object.keys(headers).length === 0) {
|
|
1953
|
-
const needHeader = await
|
|
2691
|
+
const needHeader = await confirm5({
|
|
1954
2692
|
message: "Configure HTTP headers (e.g. Authorization Bearer token)?",
|
|
1955
2693
|
default: false
|
|
1956
2694
|
});
|
|
@@ -1972,28 +2710,28 @@ var wizardAdd = async (initial = {}) => {
|
|
|
1972
2710
|
if (parsed.type !== "remote") {
|
|
1973
2711
|
env = await promptEnvConfig(env);
|
|
1974
2712
|
}
|
|
1975
|
-
console.log("\n" +
|
|
1976
|
-
console.log(` ${
|
|
1977
|
-
console.log(` ${
|
|
1978
|
-
console.log(` ${
|
|
1979
|
-
console.log(` ${
|
|
1980
|
-
console.log(` ${
|
|
2713
|
+
console.log("\n" + pc11.cyan(pc11.bold("Configuration Preview:")));
|
|
2714
|
+
console.log(` ${pc11.bold("Server Name:")} ${pc11.green(serverName)}`);
|
|
2715
|
+
console.log(` ${pc11.bold("Server Type:")} ${pc11.magenta(parsed.type)}`);
|
|
2716
|
+
console.log(` ${pc11.bold("Source/Command:")} ${pc11.dim(source)}`);
|
|
2717
|
+
console.log(` ${pc11.bold("Scope:")} ${isGlobal ? pc11.yellow("Global") : pc11.blue("Project")}`);
|
|
2718
|
+
console.log(` ${pc11.bold("Target Agents:")} ${pc11.cyan(selectedAgents.join(", "))}`);
|
|
1981
2719
|
if (args.length > 0) {
|
|
1982
|
-
console.log(` ${
|
|
2720
|
+
console.log(` ${pc11.bold("Arguments:")} ${pc11.dim(args.join(" "))}`);
|
|
1983
2721
|
}
|
|
1984
2722
|
if (transport) {
|
|
1985
|
-
console.log(` ${
|
|
2723
|
+
console.log(` ${pc11.bold("Transport:")} ${pc11.magenta(transport)}`);
|
|
1986
2724
|
}
|
|
1987
2725
|
const envKeys = Object.keys(env);
|
|
1988
2726
|
if (envKeys.length > 0) {
|
|
1989
|
-
console.log(` ${
|
|
2727
|
+
console.log(` ${pc11.bold("Environment Variables:")} ${pc11.dim(envKeys.join(", "))} (${envKeys.length})`);
|
|
1990
2728
|
}
|
|
1991
2729
|
const headerKeys = Object.keys(headers);
|
|
1992
2730
|
if (headerKeys.length > 0) {
|
|
1993
|
-
console.log(` ${
|
|
2731
|
+
console.log(` ${pc11.bold("Headers:")} ${pc11.dim(headerKeys.join(", "))} (${headerKeys.length})`);
|
|
1994
2732
|
}
|
|
1995
2733
|
console.log();
|
|
1996
|
-
const proceed = await
|
|
2734
|
+
const proceed = await confirm5({
|
|
1997
2735
|
message: "Confirm installation with this configuration?",
|
|
1998
2736
|
default: true
|
|
1999
2737
|
});
|
|
@@ -2013,41 +2751,103 @@ var wizardAdd = async (initial = {}) => {
|
|
|
2013
2751
|
env
|
|
2014
2752
|
});
|
|
2015
2753
|
logger.info(
|
|
2016
|
-
`Writing ${
|
|
2754
|
+
`Writing ${pc11.bold(result.serverName)} to ${pc11.cyan(String(result.results.length))} agent config files...`
|
|
2017
2755
|
);
|
|
2018
2756
|
let allSuccess = true;
|
|
2019
2757
|
for (const record of result.results) {
|
|
2020
2758
|
if (record.success) {
|
|
2021
|
-
logger.success(
|
|
2759
|
+
logger.success(
|
|
2760
|
+
`${pc11.cyan(record.agent)}: Successfully written to ${pc11.dim(record.path)}${formatCoHostedBadge("configured", record.coConfiguredAgents)}`
|
|
2761
|
+
);
|
|
2022
2762
|
} else {
|
|
2023
2763
|
allSuccess = false;
|
|
2024
|
-
logger.error(`${
|
|
2764
|
+
logger.error(`${pc11.cyan(record.agent)}: Failed to write - ${record.error}`);
|
|
2025
2765
|
}
|
|
2026
2766
|
}
|
|
2027
2767
|
if (allSuccess) {
|
|
2028
|
-
logger.success(
|
|
2768
|
+
logger.success(pc11.bold(`MCP server "${serverName}" configured successfully!`));
|
|
2029
2769
|
}
|
|
2030
2770
|
return allSuccess;
|
|
2031
2771
|
};
|
|
2032
2772
|
|
|
2033
2773
|
// src/interactive/wizard-manage.ts
|
|
2034
|
-
import {
|
|
2035
|
-
import
|
|
2774
|
+
import { confirm as confirm6, input as input6, select as select6 } from "@inquirer/prompts";
|
|
2775
|
+
import pc13 from "picocolors";
|
|
2776
|
+
|
|
2777
|
+
// src/utils/display-server-details.ts
|
|
2778
|
+
import pc12 from "picocolors";
|
|
2779
|
+
var displayServerDetails = ({
|
|
2780
|
+
serverName,
|
|
2781
|
+
config,
|
|
2782
|
+
agents,
|
|
2783
|
+
hasDivergence,
|
|
2784
|
+
global: isGlobal,
|
|
2785
|
+
titlePrefix = "MCP Server Details"
|
|
2786
|
+
}) => {
|
|
2787
|
+
console.log("\n" + pc12.cyan(pc12.bold(`${titlePrefix}: [${serverName}]`)));
|
|
2788
|
+
if (isGlobal !== void 0) {
|
|
2789
|
+
console.log(` ${pc12.bold("Scope:")} ${isGlobal ? "Global" : "Project"}`);
|
|
2790
|
+
}
|
|
2791
|
+
if (agents && agents.length > 0) {
|
|
2792
|
+
console.log(
|
|
2793
|
+
` ${pc12.bold("Configured Agents:")} ${pc12.green(agents.map((a) => getMcpAgentConfig(a).displayName).join(", "))}`
|
|
2794
|
+
);
|
|
2795
|
+
}
|
|
2796
|
+
if (hasDivergence) {
|
|
2797
|
+
console.log(
|
|
2798
|
+
` ${pc12.yellow(pc12.bold("Notice:"))} ${pc12.yellow("Configurations differ across installed agents. Showing configuration from the first agent.")}`
|
|
2799
|
+
);
|
|
2800
|
+
}
|
|
2801
|
+
const isRemote = Boolean(config.url && config.url.length > 0);
|
|
2802
|
+
if (isRemote) {
|
|
2803
|
+
console.log(` ${pc12.bold("Transport:")} ${pc12.magenta(config.type ?? "http")}`);
|
|
2804
|
+
console.log(` ${pc12.bold("URL:")} ${pc12.dim(config.url ?? "")}`);
|
|
2805
|
+
const headerKeys = Object.keys(config.headers ?? {});
|
|
2806
|
+
if (headerKeys.length > 0) {
|
|
2807
|
+
console.log(` ${pc12.bold("Headers:")} ${pc12.cyan(String(headerKeys.length))}`);
|
|
2808
|
+
for (const [k, v] of Object.entries(config.headers ?? {})) {
|
|
2809
|
+
console.log(` ${pc12.bold(k)}: ${pc12.dim(maskSecretHeader(k, v))}`);
|
|
2810
|
+
}
|
|
2811
|
+
} else {
|
|
2812
|
+
console.log(` ${pc12.bold("Headers:")} ${pc12.dim("(none)")}`);
|
|
2813
|
+
}
|
|
2814
|
+
} else {
|
|
2815
|
+
console.log(` ${pc12.bold("Command:")} ${pc12.magenta(config.command ?? "")}`);
|
|
2816
|
+
const argsStr = config.args && config.args.length > 0 ? config.args.join(" ") : "(none)";
|
|
2817
|
+
console.log(` ${pc12.bold("Arguments:")} ${pc12.dim(argsStr)}`);
|
|
2818
|
+
const envKeys = Object.keys(config.env ?? {});
|
|
2819
|
+
if (envKeys.length > 0) {
|
|
2820
|
+
console.log(` ${pc12.bold("Environment Variables:")} ${pc12.cyan(String(envKeys.length))}`);
|
|
2821
|
+
for (const [k, v] of Object.entries(config.env ?? {})) {
|
|
2822
|
+
console.log(` ${pc12.bold(k)}=${pc12.dim(maskSecretValue(k, v))}`);
|
|
2823
|
+
}
|
|
2824
|
+
} else {
|
|
2825
|
+
console.log(` ${pc12.bold("Environment Variables:")} ${pc12.dim("(none)")}`);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
console.log();
|
|
2829
|
+
};
|
|
2036
2830
|
|
|
2037
2831
|
// src/interactive/utils/group-installed-servers.ts
|
|
2038
2832
|
var normalizeServerConfig = parseServerConfig;
|
|
2039
2833
|
var groupInstalledServersByName = (installed) => {
|
|
2040
2834
|
const grouped = /* @__PURE__ */ new Map();
|
|
2041
2835
|
for (const item of installed) {
|
|
2836
|
+
const itemConfig = normalizeServerConfig(item.config);
|
|
2042
2837
|
let entry = grouped.get(item.serverName);
|
|
2043
2838
|
if (!entry) {
|
|
2044
2839
|
entry = {
|
|
2045
2840
|
serverName: item.serverName,
|
|
2046
2841
|
agents: [],
|
|
2047
2842
|
paths: [],
|
|
2048
|
-
config:
|
|
2843
|
+
config: itemConfig,
|
|
2844
|
+
hasDivergence: false
|
|
2049
2845
|
};
|
|
2050
2846
|
grouped.set(item.serverName, entry);
|
|
2847
|
+
} else if (!entry.hasDivergence) {
|
|
2848
|
+
if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
|
|
2849
|
+
entry.hasDivergence = true;
|
|
2850
|
+
}
|
|
2051
2851
|
}
|
|
2052
2852
|
if (!entry.agents.includes(item.agent)) {
|
|
2053
2853
|
entry.agents.push(item.agent);
|
|
@@ -2060,6 +2860,221 @@ var groupInstalledServersByName = (installed) => {
|
|
|
2060
2860
|
};
|
|
2061
2861
|
|
|
2062
2862
|
// src/interactive/wizard-manage.ts
|
|
2863
|
+
var promptSwitchServerType = async (currentConfig, serverName) => {
|
|
2864
|
+
const isRemote = Boolean(currentConfig.url && currentConfig.url.length > 0);
|
|
2865
|
+
if (isRemote) {
|
|
2866
|
+
const newCmd = await input6({
|
|
2867
|
+
message: "Executable command (e.g. node, npx):",
|
|
2868
|
+
validate: (val) => val.trim() ? true : "Command cannot be empty"
|
|
2869
|
+
});
|
|
2870
|
+
const newArgs = await promptEditArgs([]);
|
|
2871
|
+
const newEnv = await promptEditEnvConfig({});
|
|
2872
|
+
logger.success(`Switched [${serverName}] configuration to stdio mode`);
|
|
2873
|
+
return {
|
|
2874
|
+
command: newCmd.trim(),
|
|
2875
|
+
args: newArgs.length > 0 ? newArgs : void 0,
|
|
2876
|
+
env: Object.keys(newEnv).length > 0 ? newEnv : void 0
|
|
2877
|
+
};
|
|
2878
|
+
}
|
|
2879
|
+
const newUrl = await input6({
|
|
2880
|
+
message: "Remote server URL:",
|
|
2881
|
+
validate: (val) => {
|
|
2882
|
+
const trimmed = val.trim();
|
|
2883
|
+
if (!trimmed) return "URL cannot be empty";
|
|
2884
|
+
if (!/^https?:\/\//i.test(trimmed)) {
|
|
2885
|
+
return "Please enter a valid URL starting with http:// or https://";
|
|
2886
|
+
}
|
|
2887
|
+
return true;
|
|
2888
|
+
}
|
|
2889
|
+
});
|
|
2890
|
+
const transport = await select6({
|
|
2891
|
+
message: "Select remote transport protocol:",
|
|
2892
|
+
choices: [
|
|
2893
|
+
{ name: "HTTP", value: "http" },
|
|
2894
|
+
{ name: "SSE (Server-Sent Events)", value: "sse" }
|
|
2895
|
+
],
|
|
2896
|
+
default: "http"
|
|
2897
|
+
});
|
|
2898
|
+
const newHeaders = await promptEditHeadersConfig({});
|
|
2899
|
+
logger.success(`Switched [${serverName}] configuration to remote mode`);
|
|
2900
|
+
return {
|
|
2901
|
+
url: newUrl.trim(),
|
|
2902
|
+
type: transport,
|
|
2903
|
+
headers: Object.keys(newHeaders).length > 0 ? newHeaders : void 0
|
|
2904
|
+
};
|
|
2905
|
+
};
|
|
2906
|
+
var handleEditServerConfig = async (options) => {
|
|
2907
|
+
const { targetGroup } = options;
|
|
2908
|
+
const isGlobal = options.global ?? false;
|
|
2909
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2910
|
+
const serverName = targetGroup.serverName;
|
|
2911
|
+
let workingConfig = {
|
|
2912
|
+
...targetGroup.config,
|
|
2913
|
+
args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
|
|
2914
|
+
env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
|
|
2915
|
+
headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
|
|
2916
|
+
};
|
|
2917
|
+
while (true) {
|
|
2918
|
+
const isRemote = Boolean(workingConfig.url && workingConfig.url.length > 0);
|
|
2919
|
+
displayServerDetails({
|
|
2920
|
+
serverName,
|
|
2921
|
+
config: workingConfig,
|
|
2922
|
+
titlePrefix: "Edit Server Configuration"
|
|
2923
|
+
});
|
|
2924
|
+
const editChoices = isRemote ? [
|
|
2925
|
+
{ name: "Edit HTTP Headers (headers)", value: "headers" },
|
|
2926
|
+
{ name: "Edit Remote URL (url)", value: "url" },
|
|
2927
|
+
{ name: "Edit Transport Protocol (type)", value: "transport" },
|
|
2928
|
+
{ name: "Switch to local command (stdio)", value: "switch_type" },
|
|
2929
|
+
{ name: "Reset changes to original", value: "reset" },
|
|
2930
|
+
{ name: "Save and apply changes", value: "save" },
|
|
2931
|
+
{ name: "Cancel (discard changes)", value: "cancel" }
|
|
2932
|
+
] : [
|
|
2933
|
+
{ name: "Edit Environment Variables (env)", value: "env" },
|
|
2934
|
+
{ name: "Edit Command Arguments (args)", value: "args" },
|
|
2935
|
+
{ name: "Edit Executable Command (command)", value: "command" },
|
|
2936
|
+
{ name: "Switch to remote server (HTTP/SSE)", value: "switch_type" },
|
|
2937
|
+
{ name: "Reset changes to original", value: "reset" },
|
|
2938
|
+
{ name: "Save and apply changes", value: "save" },
|
|
2939
|
+
{ name: "Cancel (discard changes)", value: "cancel" }
|
|
2940
|
+
];
|
|
2941
|
+
const editAction = await select6({
|
|
2942
|
+
message: `What would you like to modify in [${serverName}]?`,
|
|
2943
|
+
choices: editChoices
|
|
2944
|
+
});
|
|
2945
|
+
if (editAction === "cancel") {
|
|
2946
|
+
logger.info("Modification cancelled; changes discarded");
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
if (editAction === "reset") {
|
|
2950
|
+
workingConfig = {
|
|
2951
|
+
...targetGroup.config,
|
|
2952
|
+
args: targetGroup.config.args ? [...targetGroup.config.args] : void 0,
|
|
2953
|
+
env: targetGroup.config.env ? { ...targetGroup.config.env } : void 0,
|
|
2954
|
+
headers: targetGroup.config.headers ? { ...targetGroup.config.headers } : void 0
|
|
2955
|
+
};
|
|
2956
|
+
logger.info("Configuration reset to original");
|
|
2957
|
+
continue;
|
|
2958
|
+
}
|
|
2959
|
+
if (editAction === "switch_type") {
|
|
2960
|
+
workingConfig = await promptSwitchServerType(workingConfig, serverName);
|
|
2961
|
+
continue;
|
|
2962
|
+
}
|
|
2963
|
+
if (editAction === "env") {
|
|
2964
|
+
workingConfig.env = await promptEditEnvConfig(workingConfig.env ?? {});
|
|
2965
|
+
} else if (editAction === "args") {
|
|
2966
|
+
workingConfig.args = await promptEditArgs(workingConfig.args ?? []);
|
|
2967
|
+
} else if (editAction === "command") {
|
|
2968
|
+
const newCmd = await input6({
|
|
2969
|
+
message: "Executable command:",
|
|
2970
|
+
default: workingConfig.command,
|
|
2971
|
+
validate: (val) => val.trim() ? true : "Command cannot be empty"
|
|
2972
|
+
});
|
|
2973
|
+
workingConfig.command = newCmd.trim();
|
|
2974
|
+
} else if (editAction === "headers") {
|
|
2975
|
+
workingConfig.headers = await promptEditHeadersConfig(workingConfig.headers ?? {});
|
|
2976
|
+
} else if (editAction === "url") {
|
|
2977
|
+
const newUrl = await input6({
|
|
2978
|
+
message: "Remote server URL:",
|
|
2979
|
+
default: workingConfig.url,
|
|
2980
|
+
validate: (val) => {
|
|
2981
|
+
const trimmed = val.trim();
|
|
2982
|
+
if (!trimmed) return "URL cannot be empty";
|
|
2983
|
+
if (!/^https?:\/\//i.test(trimmed)) {
|
|
2984
|
+
return "Please enter a valid URL starting with http:// or https://";
|
|
2985
|
+
}
|
|
2986
|
+
return true;
|
|
2987
|
+
}
|
|
2988
|
+
});
|
|
2989
|
+
workingConfig.url = newUrl.trim();
|
|
2990
|
+
} else if (editAction === "transport") {
|
|
2991
|
+
workingConfig.type = await select6({
|
|
2992
|
+
message: "Select remote transport protocol:",
|
|
2993
|
+
choices: [
|
|
2994
|
+
{ name: "HTTP", value: "http" },
|
|
2995
|
+
{ name: "SSE (Server-Sent Events)", value: "sse" }
|
|
2996
|
+
],
|
|
2997
|
+
default: workingConfig.type === "sse" ? "sse" : "http"
|
|
2998
|
+
});
|
|
2999
|
+
} else if (editAction === "save") {
|
|
3000
|
+
let targetAgents = targetGroup.agents;
|
|
3001
|
+
if (targetGroup.agents.length > 1) {
|
|
3002
|
+
const sortedAgents = sortAgentsWithClusters(targetGroup.agents, { global: isGlobal, cwd });
|
|
3003
|
+
const choices = buildLinkedAgentChoices({
|
|
3004
|
+
agents: sortedAgents,
|
|
3005
|
+
checkedAgents: sortedAgents,
|
|
3006
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
3007
|
+
});
|
|
3008
|
+
targetAgents = await linkedCheckbox({
|
|
3009
|
+
message: "Select agents to update configuration (Space to toggle):",
|
|
3010
|
+
choices,
|
|
3011
|
+
loop: false,
|
|
3012
|
+
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
3013
|
+
});
|
|
3014
|
+
if (targetAgents.length < targetGroup.agents.length) {
|
|
3015
|
+
const unselected = targetGroup.agents.filter((a) => !targetAgents.includes(a));
|
|
3016
|
+
const unselectedNames = unselected.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
3017
|
+
logger.info(
|
|
3018
|
+
`Note: Updating only a subset of agents. Server configurations will diverge from: ${unselectedNames}.`
|
|
3019
|
+
);
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
const requestedTransport = workingConfig.url ? workingConfig.type ?? "http" : "stdio";
|
|
3023
|
+
const resolution = resolveTargetAgents({
|
|
3024
|
+
requested: targetAgents,
|
|
3025
|
+
global: isGlobal,
|
|
3026
|
+
cwd,
|
|
3027
|
+
transport: requestedTransport
|
|
3028
|
+
});
|
|
3029
|
+
if (resolution.incompatible.length > 0) {
|
|
3030
|
+
for (const item of resolution.incompatible) {
|
|
3031
|
+
logger.warn(`Skipping ${pc13.cyan(item.agent)}: ${item.reason}`);
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
if (resolution.compatibleAgents.length === 0) {
|
|
3035
|
+
logger.error(
|
|
3036
|
+
`None of the selected agents support ${requestedTransport} transport. Cannot update.`
|
|
3037
|
+
);
|
|
3038
|
+
continue;
|
|
3039
|
+
}
|
|
3040
|
+
const agentNames = resolution.compatibleAgents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
3041
|
+
const confirmed = await confirm6({
|
|
3042
|
+
message: `Confirm updating configuration for [${serverName}] across: ${agentNames}?`,
|
|
3043
|
+
default: true
|
|
3044
|
+
});
|
|
3045
|
+
if (!confirmed) {
|
|
3046
|
+
logger.warn("Update cancelled");
|
|
3047
|
+
continue;
|
|
3048
|
+
}
|
|
3049
|
+
const updateResult = updateMcpServer({
|
|
3050
|
+
serverName,
|
|
3051
|
+
config: workingConfig,
|
|
3052
|
+
previousConfig: targetGroup.config,
|
|
3053
|
+
agents: resolution.compatibleAgents,
|
|
3054
|
+
global: isGlobal,
|
|
3055
|
+
cwd
|
|
3056
|
+
});
|
|
3057
|
+
let updatedAny = false;
|
|
3058
|
+
const succeededAgents = [];
|
|
3059
|
+
for (const res of updateResult.results) {
|
|
3060
|
+
if (res.success) {
|
|
3061
|
+
updatedAny = true;
|
|
3062
|
+
succeededAgents.push(res.agent);
|
|
3063
|
+
logger.success(
|
|
3064
|
+
`${pc13.cyan(res.agent)}: Successfully updated configuration in ${pc13.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
|
|
3065
|
+
);
|
|
3066
|
+
} else {
|
|
3067
|
+
logger.error(`${pc13.cyan(res.agent)}: Update failed - ${res.error}`);
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
if (updatedAny) {
|
|
3071
|
+
targetGroup.config = updateResult.config;
|
|
3072
|
+
logger.success(`Configuration for [${serverName}] updated successfully!`);
|
|
3073
|
+
return;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
};
|
|
2063
3078
|
var wizardManage = async (options = {}) => {
|
|
2064
3079
|
const cwd = options.cwd ?? process.cwd();
|
|
2065
3080
|
const isGlobal = await promptScope({
|
|
@@ -2073,51 +3088,57 @@ var wizardManage = async (options = {}) => {
|
|
|
2073
3088
|
return;
|
|
2074
3089
|
}
|
|
2075
3090
|
const grouped = groupInstalledServersByName(installed);
|
|
3091
|
+
let pendingServerName = options.serverName;
|
|
3092
|
+
const refreshGroupedServers = () => {
|
|
3093
|
+
const freshInstalled = listInstalledMcpServers({ global: isGlobal, cwd });
|
|
3094
|
+
const freshGrouped = groupInstalledServersByName(freshInstalled);
|
|
3095
|
+
grouped.clear();
|
|
3096
|
+
for (const [name, grp] of freshGrouped) {
|
|
3097
|
+
grouped.set(name, grp);
|
|
3098
|
+
}
|
|
3099
|
+
};
|
|
2076
3100
|
while (true) {
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
choices
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
3101
|
+
let chosenServerName;
|
|
3102
|
+
if (pendingServerName && grouped.has(pendingServerName)) {
|
|
3103
|
+
chosenServerName = pendingServerName;
|
|
3104
|
+
pendingServerName = void 0;
|
|
3105
|
+
} else {
|
|
3106
|
+
pendingServerName = void 0;
|
|
3107
|
+
const choices = Array.from(grouped.values()).map((g) => {
|
|
3108
|
+
const agentNames = g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ");
|
|
3109
|
+
return {
|
|
3110
|
+
name: `${pc13.bold(g.serverName)} ${pc13.dim(`(configured in: ${agentNames})`)}`,
|
|
3111
|
+
value: g.serverName
|
|
3112
|
+
};
|
|
3113
|
+
});
|
|
3114
|
+
choices.push({
|
|
3115
|
+
name: `Back`,
|
|
3116
|
+
value: "__back__"
|
|
3117
|
+
});
|
|
3118
|
+
chosenServerName = await select6({
|
|
3119
|
+
message: "Select MCP server to manage or sync:",
|
|
3120
|
+
choices
|
|
3121
|
+
});
|
|
3122
|
+
if (chosenServerName === "__back__") {
|
|
3123
|
+
return;
|
|
3124
|
+
}
|
|
2094
3125
|
}
|
|
2095
3126
|
const targetGroup = grouped.get(chosenServerName);
|
|
2096
3127
|
if (!targetGroup) continue;
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
console.log(` ${pc8.bold("URL:")} ${pc8.dim(cfg.url)} (${cfg.type})`);
|
|
2105
|
-
if (cfg.headers && Object.keys(cfg.headers).length > 0) {
|
|
2106
|
-
console.log(` ${pc8.bold("Headers:")} ${Object.keys(cfg.headers).join(", ")}`);
|
|
2107
|
-
}
|
|
2108
|
-
} else if (isStdioServerConfig(cfg)) {
|
|
2109
|
-
console.log(` ${pc8.bold("Command:")} ${pc8.magenta(cfg.command)}`);
|
|
2110
|
-
if (cfg.args && cfg.args.length > 0) {
|
|
2111
|
-
console.log(` ${pc8.bold("Arguments:")} ${pc8.dim(cfg.args.join(" "))}`);
|
|
2112
|
-
}
|
|
2113
|
-
if (cfg.env && Object.keys(cfg.env).length > 0) {
|
|
2114
|
-
console.log(` ${pc8.bold("Environment Variables:")} ${pc8.dim(Object.keys(cfg.env).join(", "))}`);
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
console.log();
|
|
3128
|
+
displayServerDetails({
|
|
3129
|
+
serverName: chosenServerName,
|
|
3130
|
+
config: targetGroup.config,
|
|
3131
|
+
agents: targetGroup.agents,
|
|
3132
|
+
global: isGlobal,
|
|
3133
|
+
hasDivergence: targetGroup.hasDivergence
|
|
3134
|
+
});
|
|
2118
3135
|
const action = await select6({
|
|
2119
3136
|
message: `What would you like to do with [${chosenServerName}]?`,
|
|
2120
3137
|
choices: [
|
|
3138
|
+
{
|
|
3139
|
+
name: "Edit server configuration",
|
|
3140
|
+
value: "edit"
|
|
3141
|
+
},
|
|
2121
3142
|
{
|
|
2122
3143
|
name: "Sync / clone to other agents",
|
|
2123
3144
|
value: "sync"
|
|
@@ -2129,23 +3150,37 @@ var wizardManage = async (options = {}) => {
|
|
|
2129
3150
|
]
|
|
2130
3151
|
});
|
|
2131
3152
|
if (action === "back") continue;
|
|
3153
|
+
if (action === "edit") {
|
|
3154
|
+
await handleEditServerConfig({
|
|
3155
|
+
targetGroup,
|
|
3156
|
+
global: isGlobal,
|
|
3157
|
+
cwd
|
|
3158
|
+
});
|
|
3159
|
+
refreshGroupedServers();
|
|
3160
|
+
continue;
|
|
3161
|
+
}
|
|
2132
3162
|
if (action === "sync") {
|
|
2133
3163
|
const allAllowedAgents = isGlobal ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
|
|
2134
|
-
const
|
|
2135
|
-
if (
|
|
2136
|
-
logger.info(
|
|
3164
|
+
const rawCandidateAgents = allAllowedAgents.filter((a) => !targetGroup.agents.includes(a));
|
|
3165
|
+
if (rawCandidateAgents.length === 0) {
|
|
3166
|
+
logger.info(
|
|
3167
|
+
"All supported agents in this scope already have this MCP server configured; no sync needed"
|
|
3168
|
+
);
|
|
2137
3169
|
continue;
|
|
2138
3170
|
}
|
|
2139
|
-
const
|
|
3171
|
+
const candidateAgents = sortAgentsWithClusters(rawCandidateAgents, { global: isGlobal, cwd });
|
|
3172
|
+
const choices = buildLinkedAgentChoices({
|
|
3173
|
+
agents: candidateAgents,
|
|
3174
|
+
checkedAgents: [],
|
|
3175
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
3176
|
+
});
|
|
3177
|
+
const selectedToSync = await linkedCheckbox({
|
|
2140
3178
|
message: "Select target agents to sync to (Space to select):",
|
|
2141
|
-
choices
|
|
2142
|
-
|
|
2143
|
-
value: a,
|
|
2144
|
-
checked: false
|
|
2145
|
-
})),
|
|
3179
|
+
choices,
|
|
3180
|
+
loop: false,
|
|
2146
3181
|
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
2147
3182
|
});
|
|
2148
|
-
const confirmed = await
|
|
3183
|
+
const confirmed = await confirm6({
|
|
2149
3184
|
message: `Confirm syncing configuration of [${chosenServerName}] to: ${selectedToSync.join(", ")}?`,
|
|
2150
3185
|
default: true
|
|
2151
3186
|
});
|
|
@@ -2153,25 +3188,37 @@ var wizardManage = async (options = {}) => {
|
|
|
2153
3188
|
logger.warn("Sync cancelled");
|
|
2154
3189
|
continue;
|
|
2155
3190
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
3191
|
+
const syncResult = updateMcpServer({
|
|
3192
|
+
serverName: chosenServerName,
|
|
3193
|
+
config: targetGroup.config,
|
|
3194
|
+
agents: selectedToSync,
|
|
3195
|
+
global: isGlobal,
|
|
3196
|
+
cwd
|
|
3197
|
+
});
|
|
3198
|
+
for (const item of syncResult.incompatible) {
|
|
3199
|
+
logger.warn(`Skipping ${pc13.cyan(item.agent)}: ${item.reason}`);
|
|
3200
|
+
}
|
|
3201
|
+
for (const res of syncResult.results) {
|
|
3202
|
+
if (syncResult.incompatible.some((i) => i.agent === res.agent)) {
|
|
3203
|
+
continue;
|
|
3204
|
+
}
|
|
2161
3205
|
if (res.success) {
|
|
2162
|
-
logger.success(
|
|
2163
|
-
|
|
3206
|
+
logger.success(
|
|
3207
|
+
`${pc13.cyan(res.agent)}: Successfully synced to ${pc13.dim(res.path)}${formatCoHostedBadge("configured", res.coConfiguredAgents)}`
|
|
3208
|
+
);
|
|
3209
|
+
targetGroup.agents.push(res.agent);
|
|
2164
3210
|
} else {
|
|
2165
|
-
logger.error(`${
|
|
3211
|
+
logger.error(`${pc13.cyan(res.agent)}: Sync failed - ${res.error}`);
|
|
2166
3212
|
}
|
|
2167
3213
|
}
|
|
3214
|
+
refreshGroupedServers();
|
|
2168
3215
|
}
|
|
2169
3216
|
}
|
|
2170
3217
|
};
|
|
2171
3218
|
|
|
2172
3219
|
// src/interactive/wizard-remove.ts
|
|
2173
|
-
import {
|
|
2174
|
-
import
|
|
3220
|
+
import { confirm as confirm7, select as select7 } from "@inquirer/prompts";
|
|
3221
|
+
import pc14 from "picocolors";
|
|
2175
3222
|
var wizardRemove = async (options = {}) => {
|
|
2176
3223
|
const cwd = options.cwd ?? process.cwd();
|
|
2177
3224
|
const isGlobal = await promptScope({
|
|
@@ -2188,7 +3235,7 @@ var wizardRemove = async (options = {}) => {
|
|
|
2188
3235
|
let serverName = options.name;
|
|
2189
3236
|
if (!serverName) {
|
|
2190
3237
|
const choices = Array.from(serverMap.values()).map((g) => ({
|
|
2191
|
-
name: `${
|
|
3238
|
+
name: `${pc14.bold(g.serverName)} ${pc14.dim(`(installed in: ${g.agents.map((a) => getMcpAgentConfig(a).displayName).join(", ")})`)}`,
|
|
2192
3239
|
value: g.serverName
|
|
2193
3240
|
}));
|
|
2194
3241
|
serverName = await select7({
|
|
@@ -2196,20 +3243,22 @@ var wizardRemove = async (options = {}) => {
|
|
|
2196
3243
|
choices
|
|
2197
3244
|
});
|
|
2198
3245
|
}
|
|
2199
|
-
const
|
|
2200
|
-
if (
|
|
3246
|
+
const rawInstalledAgents = serverMap.get(serverName)?.agents || [];
|
|
3247
|
+
if (rawInstalledAgents.length === 0) {
|
|
2201
3248
|
logger.warn(`No agents found with [${serverName}] installed`);
|
|
2202
3249
|
return false;
|
|
2203
3250
|
}
|
|
3251
|
+
const installedAgents = sortAgentsWithClusters(rawInstalledAgents, { global: isGlobal, cwd });
|
|
2204
3252
|
let targetAgents = options.agents;
|
|
2205
3253
|
if (!targetAgents || targetAgents.length === 0) {
|
|
2206
|
-
|
|
3254
|
+
const choices = buildLinkedAgentChoices({
|
|
3255
|
+
agents: installedAgents,
|
|
3256
|
+
checkedAgents: installedAgents,
|
|
3257
|
+
scopeOptions: { global: isGlobal, cwd }
|
|
3258
|
+
});
|
|
3259
|
+
targetAgents = await linkedCheckbox({
|
|
2207
3260
|
message: `Select agents to remove [${serverName}] from:`,
|
|
2208
|
-
choices
|
|
2209
|
-
name: `${getMcpAgentConfig(agent)?.displayName ?? agent} (${agent})`,
|
|
2210
|
-
value: agent,
|
|
2211
|
-
checked: true
|
|
2212
|
-
})),
|
|
3261
|
+
choices,
|
|
2213
3262
|
validate: (ans) => ans.length === 0 ? "Please select at least one agent" : true
|
|
2214
3263
|
});
|
|
2215
3264
|
} else {
|
|
@@ -2220,7 +3269,7 @@ var wizardRemove = async (options = {}) => {
|
|
|
2220
3269
|
}
|
|
2221
3270
|
targetAgents = validAgents;
|
|
2222
3271
|
}
|
|
2223
|
-
const confirmed = await
|
|
3272
|
+
const confirmed = await confirm7({
|
|
2224
3273
|
message: `Confirm removing MCP server [${serverName}] from ${targetAgents.join(", ")}?`,
|
|
2225
3274
|
default: true
|
|
2226
3275
|
});
|
|
@@ -2237,10 +3286,12 @@ var wizardRemove = async (options = {}) => {
|
|
|
2237
3286
|
let removedCount = 0;
|
|
2238
3287
|
for (const res of results) {
|
|
2239
3288
|
if (res.removed) {
|
|
2240
|
-
logger.success(
|
|
3289
|
+
logger.success(
|
|
3290
|
+
`${pc14.cyan(res.agent)}: Successfully removed from ${pc14.dim(res.path)}${formatCoHostedBadge("affected", res.coAffectedAgents)}`
|
|
3291
|
+
);
|
|
2241
3292
|
removedCount++;
|
|
2242
3293
|
} else if (res.error) {
|
|
2243
|
-
logger.error(`${
|
|
3294
|
+
logger.error(`${pc14.cyan(res.agent)}: Failed to remove - ${res.error}`);
|
|
2244
3295
|
}
|
|
2245
3296
|
}
|
|
2246
3297
|
if (removedCount > 0) {
|
|
@@ -2254,8 +3305,8 @@ var wizardRemove = async (options = {}) => {
|
|
|
2254
3305
|
// src/interactive/main-menu.ts
|
|
2255
3306
|
var mainMenu = async () => {
|
|
2256
3307
|
console.log();
|
|
2257
|
-
console.log(
|
|
2258
|
-
console.log(
|
|
3308
|
+
console.log(pc15.bold(pc15.cyan("mcps - Cross-Platform MCP Manager for AI Coding Agents")));
|
|
3309
|
+
console.log(pc15.dim("Cross-platform MCP server configuration & synchronization tool"));
|
|
2259
3310
|
console.log();
|
|
2260
3311
|
while (true) {
|
|
2261
3312
|
try {
|
|
@@ -2281,7 +3332,7 @@ var mainMenu = async () => {
|
|
|
2281
3332
|
]
|
|
2282
3333
|
});
|
|
2283
3334
|
if (action === "exit") {
|
|
2284
|
-
console.log(
|
|
3335
|
+
console.log(pc15.dim("Goodbye!"));
|
|
2285
3336
|
break;
|
|
2286
3337
|
}
|
|
2287
3338
|
if (action === "add") {
|
|
@@ -2294,7 +3345,7 @@ var mainMenu = async () => {
|
|
|
2294
3345
|
console.log();
|
|
2295
3346
|
} catch (error) {
|
|
2296
3347
|
if (error?.name === "ExitPromptError") {
|
|
2297
|
-
console.log("\n" +
|
|
3348
|
+
console.log("\n" + pc15.dim("Exited."));
|
|
2298
3349
|
break;
|
|
2299
3350
|
}
|
|
2300
3351
|
throw error;
|
|
@@ -2302,6 +3353,211 @@ var mainMenu = async () => {
|
|
|
2302
3353
|
}
|
|
2303
3354
|
};
|
|
2304
3355
|
|
|
3356
|
+
// src/utils/resolve-transport.ts
|
|
3357
|
+
var resolveTransport = (input7) => {
|
|
3358
|
+
if (!input7) return void 0;
|
|
3359
|
+
if (input7 === "http" || input7 === "sse") return input7;
|
|
3360
|
+
throw new Error(`Unsupported transport "${input7}" (expected: http, sse)`);
|
|
3361
|
+
};
|
|
3362
|
+
|
|
3363
|
+
// src/cli/manage.ts
|
|
3364
|
+
import { Command } from "commander";
|
|
3365
|
+
import pc16 from "picocolors";
|
|
3366
|
+
|
|
3367
|
+
// src/utils/parse-key-value-list.ts
|
|
3368
|
+
var parseKeyValueList = (entries, separator) => {
|
|
3369
|
+
if (!entries || entries.length === 0) return {};
|
|
3370
|
+
const result = {};
|
|
3371
|
+
for (const entry of entries) {
|
|
3372
|
+
const splitIndex = entry.indexOf(separator);
|
|
3373
|
+
if (splitIndex === -1) {
|
|
3374
|
+
throw new Error(`Invalid entry "${entry}": expected "${separator}" separator`);
|
|
3375
|
+
}
|
|
3376
|
+
const key = entry.slice(0, splitIndex).trim();
|
|
3377
|
+
const value = entry.slice(splitIndex + separator.length).trim();
|
|
3378
|
+
if (!key) throw new Error(`Invalid entry "${entry}": empty key`);
|
|
3379
|
+
result[key] = value;
|
|
3380
|
+
}
|
|
3381
|
+
return result;
|
|
3382
|
+
};
|
|
3383
|
+
|
|
3384
|
+
// src/cli/manage.ts
|
|
3385
|
+
var requireTargetServerGroup = (serverName, scope) => {
|
|
3386
|
+
const installed = listInstalledMcpServers(scope);
|
|
3387
|
+
const grouped = groupInstalledServersByName(installed);
|
|
3388
|
+
const targetGroup = grouped.get(serverName);
|
|
3389
|
+
if (!targetGroup) {
|
|
3390
|
+
logger.error(
|
|
3391
|
+
`MCP server "${serverName}" is not configured in ${scope.global ? "global" : "project"} scope.`
|
|
3392
|
+
);
|
|
3393
|
+
process.exitCode = 1;
|
|
3394
|
+
return void 0;
|
|
3395
|
+
}
|
|
3396
|
+
return targetGroup;
|
|
3397
|
+
};
|
|
3398
|
+
var mcpManageCommand = new 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) => {
|
|
3399
|
+
try {
|
|
3400
|
+
const cwd = process.cwd();
|
|
3401
|
+
const isGlobal = Boolean(options.global);
|
|
3402
|
+
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;
|
|
3403
|
+
const isInteractive = Boolean(process.stdin.isTTY && !options.yes);
|
|
3404
|
+
if (hasModifications) {
|
|
3405
|
+
if (options.url !== void 0 && options.command !== void 0) {
|
|
3406
|
+
logger.error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
|
|
3407
|
+
process.exitCode = 1;
|
|
3408
|
+
return;
|
|
3409
|
+
}
|
|
3410
|
+
if (!serverName) {
|
|
3411
|
+
logger.error('Missing required argument: "server-name" when passing modification flags.');
|
|
3412
|
+
process.exitCode = 1;
|
|
3413
|
+
return;
|
|
3414
|
+
}
|
|
3415
|
+
const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
|
|
3416
|
+
if (!targetGroup) {
|
|
3417
|
+
return;
|
|
3418
|
+
}
|
|
3419
|
+
const isCurrentRemote = Boolean(targetGroup.config.url && targetGroup.config.url.length > 0);
|
|
3420
|
+
const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
|
|
3421
|
+
if (willBeRemote) {
|
|
3422
|
+
const ignoredStdioFlags = [];
|
|
3423
|
+
if (options.env !== void 0) ignoredStdioFlags.push("--env");
|
|
3424
|
+
if (options.clearEnv) ignoredStdioFlags.push("--clear-env");
|
|
3425
|
+
if (options.args !== void 0) ignoredStdioFlags.push("--args");
|
|
3426
|
+
if (options.clearArgs) ignoredStdioFlags.push("--clear-args");
|
|
3427
|
+
if (ignoredStdioFlags.length > 0) {
|
|
3428
|
+
const hint = options.url !== void 0 ? "When configuring a remote server, stdio flags are ignored." : "Use --command to switch to stdio mode.";
|
|
3429
|
+
logger.warn(
|
|
3430
|
+
`Server "${serverName}" is a remote server. The following stdio flags will be ignored: ${ignoredStdioFlags.join(", ")}. ${hint}`
|
|
3431
|
+
);
|
|
3432
|
+
}
|
|
3433
|
+
} else {
|
|
3434
|
+
const ignoredRemoteFlags = [];
|
|
3435
|
+
if (options.header !== void 0) ignoredRemoteFlags.push("--header");
|
|
3436
|
+
if (options.clearHeaders) ignoredRemoteFlags.push("--clear-headers");
|
|
3437
|
+
if (options.transport !== void 0) ignoredRemoteFlags.push("--transport");
|
|
3438
|
+
if (ignoredRemoteFlags.length > 0) {
|
|
3439
|
+
const hint = options.command !== void 0 ? "When configuring a stdio server, remote flags are ignored." : "Use --url to switch to remote mode.";
|
|
3440
|
+
logger.warn(
|
|
3441
|
+
`Server "${serverName}" is a stdio server. The following remote flags will be ignored: ${ignoredRemoteFlags.join(", ")}. ${hint}`
|
|
3442
|
+
);
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
const incomingDelta = {};
|
|
3446
|
+
if (options.command !== void 0) {
|
|
3447
|
+
incomingDelta.command = options.command;
|
|
3448
|
+
}
|
|
3449
|
+
if (options.clearArgs) {
|
|
3450
|
+
incomingDelta.args = void 0;
|
|
3451
|
+
}
|
|
3452
|
+
if (options.args !== void 0) {
|
|
3453
|
+
incomingDelta.args = options.args;
|
|
3454
|
+
}
|
|
3455
|
+
if (options.url !== void 0) {
|
|
3456
|
+
incomingDelta.url = options.url;
|
|
3457
|
+
}
|
|
3458
|
+
if (options.transport !== void 0) {
|
|
3459
|
+
incomingDelta.type = resolveTransport(options.transport);
|
|
3460
|
+
}
|
|
3461
|
+
if (options.clearEnv) {
|
|
3462
|
+
incomingDelta.env = void 0;
|
|
3463
|
+
}
|
|
3464
|
+
if (options.env !== void 0) {
|
|
3465
|
+
const parsedEnv = parseKeyValueList(options.env, "=");
|
|
3466
|
+
const baseEnv = options.clearEnv ? {} : targetGroup.config.env ?? {};
|
|
3467
|
+
incomingDelta.env = { ...baseEnv, ...parsedEnv };
|
|
3468
|
+
}
|
|
3469
|
+
if (options.clearHeaders) {
|
|
3470
|
+
incomingDelta.headers = void 0;
|
|
3471
|
+
}
|
|
3472
|
+
if (options.header !== void 0) {
|
|
3473
|
+
const parsedHeaders = parseKeyValueList(options.header, ":");
|
|
3474
|
+
const baseHeaders = options.clearHeaders ? {} : targetGroup.config.headers ?? {};
|
|
3475
|
+
incomingDelta.headers = { ...baseHeaders, ...parsedHeaders };
|
|
3476
|
+
}
|
|
3477
|
+
let targetAgents = targetGroup.agents;
|
|
3478
|
+
if (options.agent !== void 0) {
|
|
3479
|
+
const parsed = parseMcpAgentList(options.agent);
|
|
3480
|
+
if (!parsed || parsed.length === 0) {
|
|
3481
|
+
logger.error(`No valid agents recognized from: "${options.agent.join(", ")}".`);
|
|
3482
|
+
process.exitCode = 1;
|
|
3483
|
+
return;
|
|
3484
|
+
}
|
|
3485
|
+
targetAgents = parsed;
|
|
3486
|
+
}
|
|
3487
|
+
const updateResult = updateMcpServer({
|
|
3488
|
+
serverName,
|
|
3489
|
+
config: incomingDelta,
|
|
3490
|
+
previousConfig: targetGroup.config,
|
|
3491
|
+
agents: targetAgents,
|
|
3492
|
+
global: isGlobal,
|
|
3493
|
+
cwd
|
|
3494
|
+
});
|
|
3495
|
+
for (const item of updateResult.incompatible) {
|
|
3496
|
+
logger.warn(`Skipping ${pc16.cyan(item.agent)}: ${item.reason}`);
|
|
3497
|
+
}
|
|
3498
|
+
const attemptedResults = updateResult.results.filter(
|
|
3499
|
+
(r) => !updateResult.incompatible.some((i) => i.agent === r.agent)
|
|
3500
|
+
);
|
|
3501
|
+
if (attemptedResults.length === 0) {
|
|
3502
|
+
const requestedTransport = updateResult.config.url ? updateResult.config.type ?? "http" : "stdio";
|
|
3503
|
+
logger.error(
|
|
3504
|
+
`None of the target agents support ${requestedTransport} transport. Update aborted.`
|
|
3505
|
+
);
|
|
3506
|
+
process.exitCode = 1;
|
|
3507
|
+
return;
|
|
3508
|
+
}
|
|
3509
|
+
logger.info(
|
|
3510
|
+
`Updating ${pc16.bold(serverName)} across ${pc16.cyan(String(attemptedResults.length))} agent(s)...`
|
|
3511
|
+
);
|
|
3512
|
+
let allSuccess = true;
|
|
3513
|
+
for (const res of attemptedResults) {
|
|
3514
|
+
if (res.success) {
|
|
3515
|
+
logger.success(`${pc16.cyan(res.agent)}: Successfully updated in ${pc16.dim(res.path)}`);
|
|
3516
|
+
logCoHostedNotice("configured", res.coConfiguredAgents);
|
|
3517
|
+
} else {
|
|
3518
|
+
allSuccess = false;
|
|
3519
|
+
logger.error(`${pc16.cyan(res.agent)}: Update failed - ${res.error}`);
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
if (!allSuccess) {
|
|
3523
|
+
process.exitCode = 1;
|
|
3524
|
+
}
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
if (!isInteractive) {
|
|
3528
|
+
if (!serverName) {
|
|
3529
|
+
logger.error(
|
|
3530
|
+
'Missing required argument: "server-name" for non-interactive manage command. Specify a server name or use interactive terminal.'
|
|
3531
|
+
);
|
|
3532
|
+
process.exitCode = 1;
|
|
3533
|
+
return;
|
|
3534
|
+
}
|
|
3535
|
+
const targetGroup = requireTargetServerGroup(serverName, { global: isGlobal, cwd });
|
|
3536
|
+
if (!targetGroup) {
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
displayServerDetails({
|
|
3540
|
+
serverName,
|
|
3541
|
+
config: targetGroup.config,
|
|
3542
|
+
agents: targetGroup.agents,
|
|
3543
|
+
global: isGlobal,
|
|
3544
|
+
hasDivergence: targetGroup.hasDivergence
|
|
3545
|
+
});
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
await wizardManage({
|
|
3549
|
+
global: options.global,
|
|
3550
|
+
serverName
|
|
3551
|
+
});
|
|
3552
|
+
} catch (error) {
|
|
3553
|
+
if (error && typeof error === "object" && "name" in error && error.name === "ExitPromptError") {
|
|
3554
|
+
process.exit(0);
|
|
3555
|
+
}
|
|
3556
|
+
logger.error(toErrorMessage(error));
|
|
3557
|
+
process.exitCode = 1;
|
|
3558
|
+
}
|
|
3559
|
+
});
|
|
3560
|
+
|
|
2305
3561
|
export {
|
|
2306
3562
|
mcpAgents,
|
|
2307
3563
|
mcpAgentAliases,
|
|
@@ -2328,11 +3584,16 @@ export {
|
|
|
2328
3584
|
AgentConfigStore,
|
|
2329
3585
|
agentConfigStore,
|
|
2330
3586
|
toErrorMessage,
|
|
3587
|
+
getCandidateAgentsForScope,
|
|
3588
|
+
getCoHostedAgents,
|
|
3589
|
+
resolveConfigClusters,
|
|
3590
|
+
sortAgentsWithClusters,
|
|
2331
3591
|
transformServerConfig,
|
|
2332
3592
|
createAgentTransform,
|
|
2333
3593
|
transformServerConfigForAgent,
|
|
2334
3594
|
installMcpServerForAgent,
|
|
2335
3595
|
installMcpServerForAgents,
|
|
3596
|
+
installToCompatibleAgents,
|
|
2336
3597
|
parseMcpAgentList,
|
|
2337
3598
|
resolveTargetAgents,
|
|
2338
3599
|
extractPackageName,
|
|
@@ -2342,19 +3603,43 @@ export {
|
|
|
2342
3603
|
listInstalledMcpServers,
|
|
2343
3604
|
removeMcpServerFromAgent,
|
|
2344
3605
|
removeMcpServer,
|
|
3606
|
+
toRemoteServerConfig,
|
|
3607
|
+
toStdioServerConfig,
|
|
3608
|
+
detectUpdateTransition,
|
|
3609
|
+
sanitizeUpdatedServerConfig,
|
|
3610
|
+
updateMcpServer,
|
|
2345
3611
|
logger,
|
|
3612
|
+
logCoHostedNotice,
|
|
3613
|
+
buildLinkedAgentChoices,
|
|
3614
|
+
linkedCheckbox,
|
|
2346
3615
|
promptScope,
|
|
2347
3616
|
promptScopeAndAgents,
|
|
2348
3617
|
parseArgsString,
|
|
2349
3618
|
promptArgsConfig,
|
|
3619
|
+
formatArgsString,
|
|
3620
|
+
promptEditArgs,
|
|
3621
|
+
SECRET_KEY_PATTERN,
|
|
3622
|
+
maskSecretValue,
|
|
3623
|
+
SECRET_HEADER_PATTERN,
|
|
3624
|
+
maskSecretHeader,
|
|
3625
|
+
promptEditKeyValueConfig,
|
|
3626
|
+
formatEnvText,
|
|
2350
3627
|
parseEnvText,
|
|
2351
3628
|
promptEnvConfig,
|
|
3629
|
+
promptEditEnvConfig,
|
|
3630
|
+
formatHeadersText,
|
|
2352
3631
|
parseHeadersText,
|
|
2353
3632
|
promptHeadersConfig,
|
|
3633
|
+
promptEditHeadersConfig,
|
|
2354
3634
|
wizardAdd,
|
|
3635
|
+
displayServerDetails,
|
|
2355
3636
|
normalizeServerConfig,
|
|
2356
3637
|
groupInstalledServersByName,
|
|
3638
|
+
promptSwitchServerType,
|
|
2357
3639
|
wizardManage,
|
|
2358
3640
|
wizardRemove,
|
|
2359
|
-
mainMenu
|
|
3641
|
+
mainMenu,
|
|
3642
|
+
resolveTransport,
|
|
3643
|
+
parseKeyValueList,
|
|
3644
|
+
mcpManageCommand
|
|
2360
3645
|
};
|