mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 08:12:00 +02:00
🧰 fix: Available Tools Retrieval with correct MCP Caching (#9181)
* fix: available tools retrieval with correct mcp caching and conversion * test: Enhance PluginController tests with MCP tool mocking and conversion * refactor: Simplify PluginController tests by removing unused mocks and enhancing test clarity
This commit is contained in:
parent
a49b2b2833
commit
aba0a93d1d
5 changed files with 268 additions and 195 deletions
|
@ -58,6 +58,21 @@ export class MCPManager extends UserConnectionManager {
|
|||
public getAppToolFunctions(): t.LCAvailableTools | null {
|
||||
return this.serversRegistry.toolFunctions!;
|
||||
}
|
||||
/** Returns all available tool functions from all connections available to user */
|
||||
public async getAllToolFunctions(userId: string): Promise<t.LCAvailableTools | null> {
|
||||
const allToolFunctions: t.LCAvailableTools = this.getAppToolFunctions() ?? {};
|
||||
const userConnections = this.getUserConnections(userId);
|
||||
if (!userConnections || userConnections.size === 0) {
|
||||
return allToolFunctions;
|
||||
}
|
||||
|
||||
for (const [serverName, connection] of userConnections.entries()) {
|
||||
const toolFunctions = await this.serversRegistry.getToolFunctions(serverName, connection);
|
||||
Object.assign(allToolFunctions, toolFunctions);
|
||||
}
|
||||
|
||||
return allToolFunctions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instructions for MCP servers
|
||||
|
|
|
@ -118,7 +118,7 @@ export class MCPServersRegistry {
|
|||
}
|
||||
|
||||
/** Converts server tools to LibreChat-compatible tool functions format */
|
||||
private async getToolFunctions(
|
||||
public async getToolFunctions(
|
||||
serverName: string,
|
||||
conn: MCPConnection,
|
||||
): Promise<t.LCAvailableTools> {
|
||||
|
|
|
@ -46,6 +46,62 @@ export const checkPluginAuth = (plugin?: TPlugin): boolean => {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts MCP function format tool to plugin format
|
||||
* @param params
|
||||
* @param params.toolKey
|
||||
* @param params.toolData
|
||||
* @param params.customConfig
|
||||
* @returns
|
||||
*/
|
||||
export function convertMCPToolToPlugin({
|
||||
toolKey,
|
||||
toolData,
|
||||
customConfig,
|
||||
}: {
|
||||
toolKey: string;
|
||||
toolData: FunctionTool;
|
||||
customConfig?: Partial<TCustomConfig> | null;
|
||||
}): TPlugin | undefined {
|
||||
if (!toolData.function || !toolKey.includes(Constants.mcp_delimiter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const functionData = toolData.function;
|
||||
const parts = toolKey.split(Constants.mcp_delimiter);
|
||||
const serverName = parts[parts.length - 1];
|
||||
|
||||
const serverConfig = customConfig?.mcpServers?.[serverName];
|
||||
|
||||
const plugin: TPlugin = {
|
||||
/** Tool name without server suffix */
|
||||
name: parts[0],
|
||||
pluginKey: toolKey,
|
||||
description: functionData.description || '',
|
||||
authenticated: true,
|
||||
icon: serverConfig?.iconPath,
|
||||
};
|
||||
|
||||
if (!serverConfig?.customUserVars) {
|
||||
/** `authConfig` for MCP tools */
|
||||
plugin.authConfig = [];
|
||||
return plugin;
|
||||
}
|
||||
|
||||
const customVarKeys = Object.keys(serverConfig.customUserVars);
|
||||
if (customVarKeys.length === 0) {
|
||||
plugin.authConfig = [];
|
||||
} else {
|
||||
plugin.authConfig = Object.entries(serverConfig.customUserVars).map(([key, value]) => ({
|
||||
authField: key,
|
||||
label: value.title || key,
|
||||
description: value.description || '',
|
||||
}));
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts MCP function format tools to plugin format
|
||||
* @param functionTools - Object with function format tools
|
||||
|
@ -65,44 +121,10 @@ export function convertMCPToolsToPlugins({
|
|||
|
||||
const plugins: TPlugin[] = [];
|
||||
for (const [toolKey, toolData] of Object.entries(functionTools)) {
|
||||
if (!toolData.function || !toolKey.includes(Constants.mcp_delimiter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const functionData = toolData.function;
|
||||
const parts = toolKey.split(Constants.mcp_delimiter);
|
||||
const serverName = parts[parts.length - 1];
|
||||
|
||||
const serverConfig = customConfig?.mcpServers?.[serverName];
|
||||
|
||||
const plugin: TPlugin = {
|
||||
/** Tool name without server suffix */
|
||||
name: parts[0],
|
||||
pluginKey: toolKey,
|
||||
description: functionData.description || '',
|
||||
authenticated: true,
|
||||
icon: serverConfig?.iconPath,
|
||||
};
|
||||
|
||||
if (!serverConfig?.customUserVars) {
|
||||
/** `authConfig` for MCP tools */
|
||||
plugin.authConfig = [];
|
||||
const plugin = convertMCPToolToPlugin({ toolKey, toolData, customConfig });
|
||||
if (plugin) {
|
||||
plugins.push(plugin);
|
||||
continue;
|
||||
}
|
||||
|
||||
const customVarKeys = Object.keys(serverConfig.customUserVars);
|
||||
if (customVarKeys.length === 0) {
|
||||
plugin.authConfig = [];
|
||||
} else {
|
||||
plugin.authConfig = Object.entries(serverConfig.customUserVars).map(([key, value]) => ({
|
||||
authField: key,
|
||||
label: value.title || key,
|
||||
description: value.description || '',
|
||||
}));
|
||||
}
|
||||
|
||||
plugins.push(plugin);
|
||||
}
|
||||
|
||||
return plugins;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue