Revert "🚉 feat: MCP Registry Individual Server Init (#9887)"
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions

This reverts commit b8720a9b7a.
This commit is contained in:
Danny Avila 2025-09-30 09:39:19 -04:00
parent dfe236acb5
commit 4777bd22c5
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
6 changed files with 81 additions and 257 deletions

View file

@ -113,7 +113,6 @@ describe('MCPServersRegistry - Initialize Function', () => {
get: jest.fn(),
getLoaded: jest.fn(),
disconnectAll: jest.fn(),
disconnect: jest.fn().mockResolvedValue(undefined),
} as unknown as jest.Mocked<ConnectionsRepository>;
mockConnectionsRepo.get.mockImplementation((serverName: string) => {
@ -161,7 +160,6 @@ describe('MCPServersRegistry - Initialize Function', () => {
});
afterEach(() => {
delete process.env.MCP_INIT_TIMEOUT_MS;
jest.clearAllMocks();
});
@ -181,14 +179,15 @@ describe('MCPServersRegistry - Initialize Function', () => {
const registry = new MCPServersRegistry(rawConfigs);
// Verify initial state
expect(registry.oauthServers.size).toBe(0);
expect(registry.serverInstructions).toEqual({});
expect(registry.toolFunctions).toEqual({});
expect(registry.appServerConfigs).toEqual({});
expect(registry.oauthServers).toBeNull();
expect(registry.serverInstructions).toBeNull();
expect(registry.toolFunctions).toBeNull();
expect(registry.appServerConfigs).toBeNull();
await registry.initialize();
// Test oauthServers Set
expect(registry.oauthServers).toBeInstanceOf(Set);
expect(registry.oauthServers).toEqual(
new Set(['oauth_server', 'oauth_predefined', 'oauth_startup_enabled']),
);
@ -229,49 +228,18 @@ describe('MCPServersRegistry - Initialize Function', () => {
expect(registry.toolFunctions).toEqual(expectedToolFunctions);
});
it('should handle errors gracefully and continue initialization of other servers', async () => {
it('should handle errors gracefully and continue initialization', async () => {
const registry = new MCPServersRegistry(rawConfigs);
// Make one specific server throw an error during OAuth detection
mockDetectOAuthRequirement.mockImplementation((url: string) => {
if (url === 'https://api.github.com/mcp') {
return Promise.reject(new Error('OAuth detection failed'));
}
// Return normal responses for other servers
const oauthResults: Record<string, OAuthDetectionResult> = {
'https://api.disabled.com/mcp': {
requiresOAuth: false,
method: 'no-metadata-found',
metadata: null,
},
'https://api.public.com/mcp': {
requiresOAuth: false,
method: 'no-metadata-found',
metadata: null,
},
};
return Promise.resolve(
oauthResults[url] ?? {
requiresOAuth: false,
method: 'no-metadata-found',
metadata: null,
},
);
});
// Make one server throw an error
mockDetectOAuthRequirement.mockRejectedValueOnce(new Error('OAuth detection failed'));
await registry.initialize();
// Should still initialize successfully for other servers
// Should still initialize successfully
expect(registry.oauthServers).toBeInstanceOf(Set);
expect(registry.toolFunctions).toBeDefined();
// The failed server should not be in oauthServers (since it failed OAuth detection)
expect(registry.oauthServers.has('oauth_server')).toBe(false);
// But other servers should still be processed successfully
expect(registry.appServerConfigs).toHaveProperty('stdio_server');
expect(registry.appServerConfigs).toHaveProperty('non_oauth_server');
// Error should be logged as a warning at the higher level
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('[MCP][oauth_server] Failed to initialize server:'),
@ -279,15 +247,12 @@ describe('MCPServersRegistry - Initialize Function', () => {
);
});
it('should disconnect individual connections after each server initialization', async () => {
it('should disconnect all connections after initialization', async () => {
const registry = new MCPServersRegistry(rawConfigs);
await registry.initialize();
// Verify disconnect was called for each server during initialization
// All servers attempt to connect during initialization for metadata gathering
const serverNames = Object.keys(rawConfigs);
expect(mockConnectionsRepo.disconnect).toHaveBeenCalledTimes(serverNames.length);
expect(mockConnectionsRepo.disconnectAll).toHaveBeenCalledTimes(1);
});
it('should log configuration updates for each startup-enabled server', async () => {
@ -392,125 +357,5 @@ describe('MCPServersRegistry - Initialize Function', () => {
// Verify getInstructions was called for both "true" cases
expect(mockClient.getInstructions).toHaveBeenCalledTimes(2);
});
it('should use Promise.allSettled for individual server initialization', async () => {
const registry = new MCPServersRegistry(rawConfigs);
// Spy on Promise.allSettled to verify it's being used
const allSettledSpy = jest.spyOn(Promise, 'allSettled');
await registry.initialize();
// Verify Promise.allSettled was called with an array of server initialization promises
expect(allSettledSpy).toHaveBeenCalledWith(expect.arrayContaining([expect.any(Promise)]));
// Verify it was called with the correct number of server promises
const serverNames = Object.keys(rawConfigs);
expect(allSettledSpy).toHaveBeenCalledWith(
expect.arrayContaining(new Array(serverNames.length).fill(expect.any(Promise))),
);
allSettledSpy.mockRestore();
});
it('should isolate server failures and not affect other servers', async () => {
const registry = new MCPServersRegistry(rawConfigs);
// Make multiple servers fail in different ways
mockConnectionsRepo.get.mockImplementation((serverName: string) => {
if (serverName === 'stdio_server') {
// First server fails
throw new Error('Connection failed for stdio_server');
}
if (serverName === 'websocket_server') {
// Second server fails
throw new Error('Connection failed for websocket_server');
}
// Other servers succeed
const connection = mockConnections.get(serverName);
if (!connection) {
throw new Error(`Connection not found for server: ${serverName}`);
}
return Promise.resolve(connection);
});
await registry.initialize();
// Despite failures, initialization should complete
expect(registry.oauthServers).toBeInstanceOf(Set);
expect(registry.toolFunctions).toBeDefined();
// Successful servers should still be processed
expect(registry.appServerConfigs).toHaveProperty('non_oauth_server');
// Failed servers should not crash the whole initialization
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('[MCP][stdio_server] Error fetching tool functions:'),
expect.any(Error),
);
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('[MCP][websocket_server] Error fetching tool functions:'),
expect.any(Error),
);
});
it('should properly clean up connections even when some servers fail', async () => {
const registry = new MCPServersRegistry(rawConfigs);
// Make some connections fail during disconnect
mockConnectionsRepo.disconnect.mockImplementation((serverName: string) => {
if (serverName === 'stdio_server') {
return Promise.reject(new Error('Disconnect failed'));
}
return Promise.resolve();
});
await registry.initialize();
// Should still attempt to disconnect all servers during initialization
const serverNames = Object.keys(rawConfigs);
expect(mockConnectionsRepo.disconnect).toHaveBeenCalledTimes(serverNames.length);
// Failed disconnects should be logged but not crash initialization
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.stringContaining('[MCP][stdio_server] Failed to disconnect:'),
expect.any(Error),
);
});
it('should timeout individual server initialization after configured timeout', async () => {
const timeout = 2000;
// Create registry with a short timeout for testing
process.env.MCP_INIT_TIMEOUT_MS = `${timeout}`;
const registry = new MCPServersRegistry(rawConfigs);
// Make one server hang indefinitely during OAuth detection
mockDetectOAuthRequirement.mockImplementation((url: string) => {
if (url === 'https://api.github.com/mcp') {
// Slow init
return new Promise((res) => setTimeout(res, timeout * 2));
}
// Return normal responses for other servers
return Promise.resolve({
requiresOAuth: false,
method: 'no-metadata-found',
metadata: null,
});
});
const start = Date.now();
await registry.initialize();
const duration = Date.now() - start;
// Should complete within reasonable time despite one server hanging
// Allow some buffer for test execution overhead
expect(duration).toBeLessThan(timeout * 1.5);
// The timeout should prevent the hanging server from blocking initialization
// Other servers should still be processed successfully
expect(registry.appServerConfigs).toHaveProperty('stdio_server');
expect(registry.appServerConfigs).toHaveProperty('non_oauth_server');
}, 10_000); // 10 second Jest timeout
});
});