Skip to content

Commit ce7e6ed

Browse files
🔄 refactor: MCP Registry System with Distributed Caching (danny-avila#10191)
* refactor: Restructure MCP registry system with caching - Split MCPServersRegistry into modular components: - MCPServerInspector: handles server inspection and health checks - MCPServersInitializer: manages server initialization logic - MCPServersRegistry: simplified registry coordination - Add distributed caching layer: - ServerConfigsCacheRedis: Redis-backed configuration cache - ServerConfigsCacheInMemory: in-memory fallback cache - RegistryStatusCache: distributed leader election state - Add promise utilities (withTimeout) replacing Promise.race patterns - Add comprehensive cache integration tests for all cache implementations - Remove unused MCPManager.getAllToolFunctions method * fix: Update OAuth flow to include user-specific headers * chore: Update Jest configuration to ignore additional test files - Added patterns to ignore files ending with .helper.ts and .helper.d.ts in testPathIgnorePatterns for cleaner test runs. * fix: oauth headers in callback * chore: Update Jest testPathIgnorePatterns to exclude helper files - Modified testPathIgnorePatterns in package.json to ignore files ending with .helper.ts and .helper.d.ts for cleaner test execution. * ci: update test mocks --------- Co-authored-by: Danny Avila <danny@librechat.ai>
1 parent 961f87c commit ce7e6ed

45 files changed

Lines changed: 3116 additions & 1150 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/cache-integration-tests.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ on:
99
paths:
1010
- 'packages/api/src/cache/**'
1111
- 'packages/api/src/cluster/**'
12+
- 'packages/api/src/mcp/**'
1213
- 'redis-config/**'
1314
- '.github/workflows/cache-integration-tests.yml'
1415

@@ -77,6 +78,14 @@ jobs:
7778
REDIS_URI: redis://127.0.0.1:6379
7879
run: npm run test:cache-integration:cluster
7980

81+
- name: Run mcp integration tests
82+
working-directory: packages/api
83+
env:
84+
NODE_ENV: test
85+
USE_REDIS: true
86+
REDIS_URI: redis://127.0.0.1:6379
87+
run: npm run test:cache-integration:mcp
88+
8089
- name: Stop Redis Cluster
8190
if: always()
8291
working-directory: redis-config

api/server/controllers/UserController.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const { getMCPManager, getFlowStateManager } = require('~/config');
2828
const { getAppConfig } = require('~/server/services/Config');
2929
const { deleteToolCalls } = require('~/models/ToolCall');
3030
const { getLogStores } = require('~/cache');
31+
const { mcpServersRegistry } = require('@librechat/api');
3132

3233
const getUserController = async (req, res) => {
3334
const appConfig = await getAppConfig({ role: req.user?.role });
@@ -198,7 +199,7 @@ const updateUserPluginsController = async (req, res) => {
198199
// If auth was updated successfully, disconnect MCP sessions as they might use these credentials
199200
if (pluginKey.startsWith(Constants.mcp_prefix)) {
200201
try {
201-
const mcpManager = getMCPManager(user.id);
202+
const mcpManager = getMCPManager();
202203
if (mcpManager) {
203204
// Extract server name from pluginKey (format: "mcp_<serverName>")
204205
const serverName = pluginKey.replace(Constants.mcp_prefix, '');
@@ -295,10 +296,11 @@ const maybeUninstallOAuthMCP = async (userId, pluginKey, appConfig) => {
295296
}
296297

297298
const serverName = pluginKey.replace(Constants.mcp_prefix, '');
298-
const mcpManager = getMCPManager(userId);
299-
const serverConfig = mcpManager.getRawConfig(serverName) ?? appConfig?.mcpServers?.[serverName];
300-
301-
if (!mcpManager.getOAuthServers().has(serverName)) {
299+
const serverConfig =
300+
(await mcpServersRegistry.getServerConfig(serverName, userId)) ??
301+
appConfig?.mcpServers?.[serverName];
302+
const oauthServers = await mcpServersRegistry.getOAuthServers();
303+
if (!oauthServers.has(serverName)) {
302304
// this server does not use OAuth, so nothing to do here as well
303305
return;
304306
}

api/server/controllers/mcp.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
getAppConfig,
1111
} = require('~/server/services/Config');
1212
const { getMCPManager } = require('~/config');
13+
const { mcpServersRegistry } = require('@librechat/api');
1314

1415
/**
1516
* Get all MCP tools available to the user
@@ -65,7 +66,7 @@ const getMCPTools = async (req, res) => {
6566

6667
// Get server config once
6768
const serverConfig = appConfig.mcpConfig[serverName];
68-
const rawServerConfig = mcpManager.getRawConfig(serverName);
69+
const rawServerConfig = await mcpServersRegistry.getServerConfig(serverName, userId);
6970

7071
// Initialize server object with all server-level data
7172
const server = {

0 commit comments

Comments
 (0)