From 283c9cff6f914bfb77de3a2698fc6b71e5ae99f1 Mon Sep 17 00:00:00 2001 From: Sebastien Bruel <93573440+sbruel@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:41:06 +0900 Subject: [PATCH] =?UTF-8?q?=E2=84=B9=EF=B8=8F=20fix:=20Add=20back=20Remove?= =?UTF-8?q?d=20Icons=20for=20MCP=20Servers=20in=20Tools=20Dialog=20(#8636)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bug: Fix icons for MCP servers * Add `OPENAI_API_KEY` to `jestSetup.js` to fix tests --- api/server/controllers/PluginController.js | 5 +- .../controllers/PluginController.spec.js | 89 +++++++++++++++++++ api/test/jestSetup.js | 1 + 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 api/server/controllers/PluginController.spec.js diff --git a/api/server/controllers/PluginController.js b/api/server/controllers/PluginController.js index 1dbc7633a..dfabf37f6 100644 --- a/api/server/controllers/PluginController.js +++ b/api/server/controllers/PluginController.js @@ -253,16 +253,17 @@ function convertMCPToolsToPlugins(functionTools, customConfig) { const parts = toolKey.split(Constants.mcp_delimiter); const serverName = parts[parts.length - 1]; + const serverConfig = customConfig?.mcpServers?.[serverName]; + const plugin = { name: parts[0], // Use the tool name without server suffix pluginKey: toolKey, description: functionData.description || '', authenticated: true, - icon: undefined, + icon: serverConfig?.iconPath, }; // Build authConfig for MCP tools - const serverConfig = customConfig?.mcpServers?.[serverName]; if (!serverConfig?.customUserVars) { plugin.authConfig = []; plugins.push(plugin); diff --git a/api/server/controllers/PluginController.spec.js b/api/server/controllers/PluginController.spec.js new file mode 100644 index 000000000..218d3255f --- /dev/null +++ b/api/server/controllers/PluginController.spec.js @@ -0,0 +1,89 @@ +const { Constants } = require('librechat-data-provider'); +const { getCustomConfig, getCachedTools } = require('~/server/services/Config'); +const { getLogStores } = require('~/cache'); + +// Mock the dependencies +jest.mock('@librechat/data-schemas', () => ({ + logger: { + debug: jest.fn(), + error: jest.fn(), + }, +})); + +jest.mock('~/server/services/Config', () => ({ + getCustomConfig: jest.fn(), + getCachedTools: jest.fn(), +})); + +jest.mock('~/server/services/ToolService', () => ({ + getToolkitKey: jest.fn(), +})); + +jest.mock('~/config', () => ({ + getMCPManager: jest.fn(() => ({ + loadManifestTools: jest.fn().mockResolvedValue([]), + })), + getFlowStateManager: jest.fn(), +})); + +jest.mock('~/app/clients/tools', () => ({ + availableTools: [], +})); + +jest.mock('~/cache', () => ({ + getLogStores: jest.fn(), +})); + +// Import the actual module with the function we want to test +const { getAvailableTools } = require('./PluginController'); + +describe('PluginController', () => { + describe('plugin.icon behavior', () => { + let mockReq, mockRes, mockCache; + + const callGetAvailableToolsWithMCPServer = async (mcpServers) => { + mockCache.get.mockResolvedValue(null); + getCustomConfig.mockResolvedValue({ mcpServers }); + + const functionTools = { + [`test-tool${Constants.mcp_delimiter}test-server`]: { + function: { name: 'test-tool', description: 'A test tool' }, + }, + }; + getCachedTools.mockResolvedValueOnce(functionTools); + getCachedTools.mockResolvedValueOnce({ + [`test-tool${Constants.mcp_delimiter}test-server`]: true, + }); + + await getAvailableTools(mockReq, mockRes); + const responseData = mockRes.json.mock.calls[0][0]; + return responseData.find((tool) => tool.name === 'test-tool'); + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockReq = { user: { id: 'test-user-id' } }; + mockRes = { status: jest.fn().mockReturnThis(), json: jest.fn() }; + mockCache = { get: jest.fn(), set: jest.fn() }; + getLogStores.mockReturnValue(mockCache); + }); + + it('should set plugin.icon when iconPath is defined', async () => { + const mcpServers = { + 'test-server': { + iconPath: '/path/to/icon.png', + }, + }; + const testTool = await callGetAvailableToolsWithMCPServer(mcpServers); + expect(testTool.icon).toBe('/path/to/icon.png'); + }); + + it('should set plugin.icon to undefined when iconPath is not defined', async () => { + const mcpServers = { + 'test-server': {}, + }; + const testTool = await callGetAvailableToolsWithMCPServer(mcpServers); + expect(testTool.icon).toBeUndefined(); + }); + }); +}); diff --git a/api/test/jestSetup.js b/api/test/jestSetup.js index ed92afd21..ae30db72c 100644 --- a/api/test/jestSetup.js +++ b/api/test/jestSetup.js @@ -10,3 +10,4 @@ process.env.JWT_SECRET = 'test'; process.env.JWT_REFRESH_SECRET = 'test'; process.env.CREDS_KEY = 'test'; process.env.CREDS_IV = 'test'; +process.env.OPENAI_API_KEY = 'test';