🧬 refactor: Optimize MCP Tool Queries with Server-Centric Architecture

🧬 refactor: Optimize MCP Tool Queries with Server-Centric Architecture

refactor: optimize mcp tool queries by removing redundancy, making server-centric structure, enabling query only when expected, minimize looping/transforming query data, eliminating unused/compute-heavy methods

ci: MCP Server Tools Mocking in Agent Tests
This commit is contained in:
Danny Avila 2025-09-21 20:19:51 -04:00
parent 5b1a31ef4d
commit f0599ad36c
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
19 changed files with 235 additions and 1104 deletions

View file

@ -4,7 +4,6 @@
*/
const { logger } = require('@librechat/data-schemas');
const { Constants } = require('librechat-data-provider');
const { convertMCPToolToPlugin } = require('@librechat/api');
const {
cacheMCPServerTools,
getMCPServerTools,
@ -14,7 +13,6 @@ const { getMCPManager } = require('~/config');
/**
* Get all MCP tools available to the user
* Returns only MCP tools, not regular LibreChat tools
*/
const getMCPTools = async (req, res) => {
try {
@ -26,77 +24,97 @@ const getMCPTools = async (req, res) => {
const appConfig = req.config ?? (await getAppConfig({ role: req.user?.role }));
if (!appConfig?.mcpConfig) {
return res.status(200).json([]);
return res.status(200).json({ servers: {} });
}
const mcpManager = getMCPManager();
const configuredServers = Object.keys(appConfig.mcpConfig);
const mcpTools = [];
const mcpServers = {};
// Fetch tools from each configured server
const cachePromises = configuredServers.map((serverName) =>
getMCPServerTools(serverName).then((tools) => ({ serverName, tools })),
);
const cacheResults = await Promise.all(cachePromises);
const serverToolsMap = new Map();
for (const { serverName, tools } of cacheResults) {
if (tools) {
serverToolsMap.set(serverName, tools);
continue;
}
const serverTools = await mcpManager.getServerToolFunctions(userId, serverName);
if (!serverTools) {
logger.debug(`[getMCPTools] No tools found for server ${serverName}`);
continue;
}
serverToolsMap.set(serverName, serverTools);
if (Object.keys(serverTools).length > 0) {
// Cache asynchronously without blocking
cacheMCPServerTools({ serverName, serverTools }).catch((err) =>
logger.error(`[getMCPTools] Failed to cache tools for ${serverName}:`, err),
);
}
}
// Process each configured server
for (const serverName of configuredServers) {
try {
// First check server-specific cache
let serverTools = await getMCPServerTools(serverName);
const serverTools = serverToolsMap.get(serverName);
if (!serverTools) {
// If not cached, fetch from MCP manager
const allTools = await mcpManager.getAllToolFunctions(userId);
serverTools = {};
// Get server config once
const serverConfig = appConfig.mcpConfig[serverName];
const rawServerConfig = mcpManager.getRawConfig(serverName);
// Filter tools for this specific server
for (const [toolKey, toolData] of Object.entries(allTools)) {
if (toolKey.endsWith(`${Constants.mcp_delimiter}${serverName}`)) {
serverTools[toolKey] = toolData;
}
}
// Initialize server object with all server-level data
const server = {
name: serverName,
icon: rawServerConfig?.iconPath || '',
authenticated: true,
authConfig: [],
tools: [],
};
// Cache server tools if found
if (Object.keys(serverTools).length > 0) {
await cacheMCPServerTools({ serverName, serverTools });
// Set authentication config once for the server
if (serverConfig?.customUserVars) {
const customVarKeys = Object.keys(serverConfig.customUserVars);
if (customVarKeys.length > 0) {
server.authConfig = Object.entries(serverConfig.customUserVars).map(([key, value]) => ({
authField: key,
label: value.title || key,
description: value.description || '',
}));
server.authenticated = false;
}
}
// Convert to plugin format
for (const [toolKey, toolData] of Object.entries(serverTools)) {
const plugin = convertMCPToolToPlugin({
toolKey,
toolData,
mcpManager,
});
if (plugin) {
// Add authentication config from server config
const serverConfig = appConfig.mcpConfig[serverName];
if (serverConfig?.customUserVars) {
const customVarKeys = Object.keys(serverConfig.customUserVars);
if (customVarKeys.length === 0) {
plugin.authConfig = [];
plugin.authenticated = true;
} else {
plugin.authConfig = Object.entries(serverConfig.customUserVars).map(
([key, value]) => ({
authField: key,
label: value.title || key,
description: value.description || '',
}),
);
plugin.authenticated = false;
}
} else {
plugin.authConfig = [];
plugin.authenticated = true;
// Process tools efficiently - no need for convertMCPToolToPlugin
if (serverTools) {
for (const [toolKey, toolData] of Object.entries(serverTools)) {
if (!toolData.function || !toolKey.includes(Constants.mcp_delimiter)) {
continue;
}
mcpTools.push(plugin);
const toolName = toolKey.split(Constants.mcp_delimiter)[0];
server.tools.push({
name: toolName,
pluginKey: toolKey,
description: toolData.function.description || '',
});
}
}
// Only add server if it has tools or is configured
if (server.tools.length > 0 || serverConfig) {
mcpServers[serverName] = server;
}
} catch (error) {
logger.error(`[getMCPTools] Error loading tools for server ${serverName}:`, error);
}
}
res.status(200).json(mcpTools);
res.status(200).json({ servers: mcpServers });
} catch (error) {
logger.error('[getMCPTools]', error);
res.status(500).json({ message: error.message });