🧬 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

@ -1,12 +1,6 @@
import { AuthType, Constants, EToolResources } from 'librechat-data-provider';
import type { TPlugin, FunctionTool } from 'librechat-data-provider';
import type { MCPManager } from '~/mcp/MCPManager';
import {
convertMCPToolsToPlugins,
filterUniquePlugins,
checkPluginAuth,
getToolkitKey,
} from './format';
import { AuthType, EToolResources } from 'librechat-data-provider';
import type { TPlugin } from 'librechat-data-provider';
import { filterUniquePlugins, checkPluginAuth, getToolkitKey } from './format';
describe('format.ts helper functions', () => {
describe('filterUniquePlugins', () => {
@ -197,212 +191,6 @@ describe('format.ts helper functions', () => {
});
});
describe('convertMCPToolsToPlugins', () => {
it('should return undefined when functionTools is undefined', () => {
const result = convertMCPToolsToPlugins({ functionTools: undefined });
expect(result).toBeUndefined();
});
it('should return undefined when functionTools is not an object', () => {
const result = convertMCPToolsToPlugins({
functionTools: 'not-an-object' as unknown as Record<string, FunctionTool>,
});
expect(result).toBeUndefined();
});
it('should return empty array when functionTools is empty object', () => {
const result = convertMCPToolsToPlugins({ functionTools: {} });
expect(result).toEqual([]);
});
it('should skip entries without function property', () => {
const functionTools: Record<string, FunctionTool> = {
tool1: { type: 'function' } as FunctionTool,
tool2: { function: { name: 'tool2', description: 'Tool 2' } } as FunctionTool,
};
const result = convertMCPToolsToPlugins({ functionTools });
expect(result).toHaveLength(0); // tool2 doesn't have mcp_delimiter in key
});
it('should skip entries without mcp_delimiter in key', () => {
const functionTools: Record<string, FunctionTool> = {
'regular-tool': {
type: 'function',
function: { name: 'regular-tool', description: 'Regular tool' },
} as FunctionTool,
};
const result = convertMCPToolsToPlugins({ functionTools });
expect(result).toHaveLength(0);
});
it('should convert MCP tools to plugins correctly', () => {
const functionTools: Record<string, FunctionTool> = {
[`tool1${Constants.mcp_delimiter}server1`]: {
type: 'function',
function: { name: 'tool1', description: 'Tool 1 description' },
} as FunctionTool,
};
const result = convertMCPToolsToPlugins({ functionTools });
expect(result).toHaveLength(1);
expect(result![0]).toEqual({
name: 'tool1',
pluginKey: `tool1${Constants.mcp_delimiter}server1`,
description: 'Tool 1 description',
authenticated: true,
icon: undefined,
authConfig: [],
});
});
it('should handle missing description', () => {
const functionTools: Record<string, FunctionTool> = {
[`tool1${Constants.mcp_delimiter}server1`]: {
type: 'function',
function: { name: 'tool1' },
} as FunctionTool,
};
const result = convertMCPToolsToPlugins({ functionTools });
expect(result).toHaveLength(1);
expect(result![0].description).toBe('');
});
it('should add icon from server config', () => {
const functionTools: Record<string, FunctionTool> = {
[`tool1${Constants.mcp_delimiter}server1`]: {
type: 'function',
function: { name: 'tool1', description: 'Tool 1' },
} as FunctionTool,
};
const mockMcpManager = {
getRawConfig: jest.fn().mockReturnValue({
command: 'test',
args: [],
iconPath: '/path/to/icon.png',
}),
} as unknown as MCPManager;
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
expect(result).toHaveLength(1);
expect(result![0].icon).toBe('/path/to/icon.png');
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
});
it('should handle customUserVars in server config', () => {
const functionTools: Record<string, FunctionTool> = {
[`tool1${Constants.mcp_delimiter}server1`]: {
type: 'function',
function: { name: 'tool1', description: 'Tool 1' },
} as FunctionTool,
};
const mockMcpManager = {
getRawConfig: jest.fn().mockReturnValue({
command: 'test',
args: [],
customUserVars: {
API_KEY: { title: 'API Key', description: 'Your API key' },
SECRET: { title: 'Secret', description: 'Your secret' },
},
}),
} as unknown as MCPManager;
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
expect(result).toHaveLength(1);
expect(result![0].authConfig).toHaveLength(2);
expect(result![0].authConfig).toEqual([
{ authField: 'API_KEY', label: 'API Key', description: 'Your API key' },
{ authField: 'SECRET', label: 'Secret', description: 'Your secret' },
]);
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
});
it('should use key as label when title is missing in customUserVars', () => {
const functionTools: Record<string, FunctionTool> = {
[`tool1${Constants.mcp_delimiter}server1`]: {
type: 'function',
function: { name: 'tool1', description: 'Tool 1' },
} as FunctionTool,
};
const mockMcpManager = {
getRawConfig: jest.fn().mockReturnValue({
command: 'test',
args: [],
customUserVars: {
API_KEY: { title: 'API Key', description: 'Your API key' },
},
}),
} as unknown as MCPManager;
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
expect(result).toHaveLength(1);
expect(result![0].authConfig).toEqual([
{ authField: 'API_KEY', label: 'API Key', description: 'Your API key' },
]);
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
});
it('should handle empty customUserVars', () => {
const functionTools: Record<string, FunctionTool> = {
[`tool1${Constants.mcp_delimiter}server1`]: {
type: 'function',
function: { name: 'tool1', description: 'Tool 1' },
} as FunctionTool,
};
const mockMcpManager = {
getRawConfig: jest.fn().mockReturnValue({
command: 'test',
args: [],
customUserVars: {},
}),
} as unknown as MCPManager;
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
expect(result).toHaveLength(1);
expect(result![0].authConfig).toEqual([]);
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
});
it('should handle missing mcpManager', () => {
const functionTools: Record<string, FunctionTool> = {
[`tool1${Constants.mcp_delimiter}server1`]: {
type: 'function',
function: { name: 'tool1', description: 'Tool 1' },
} as FunctionTool,
};
const result = convertMCPToolsToPlugins({ functionTools });
expect(result).toHaveLength(1);
expect(result![0].icon).toBeUndefined();
expect(result![0].authConfig).toEqual([]);
});
it('should handle when getRawConfig returns undefined', () => {
const functionTools: Record<string, FunctionTool> = {
[`tool1${Constants.mcp_delimiter}server1`]: {
type: 'function',
function: { name: 'tool1', description: 'Tool 1' },
} as FunctionTool,
};
const mockMcpManager = {
getRawConfig: jest.fn().mockReturnValue(undefined),
} as unknown as MCPManager;
const result = convertMCPToolsToPlugins({ functionTools, mcpManager: mockMcpManager });
expect(result).toHaveLength(1);
expect(result![0].icon).toBeUndefined();
expect(result![0].authConfig).toEqual([]);
expect(mockMcpManager.getRawConfig).toHaveBeenCalledWith('server1');
});
});
describe('getToolkitKey', () => {
it('should return undefined when toolName is undefined', () => {
const toolkits: TPlugin[] = [