📡 refactor: MCP Runtime Config Sync with Redis Distributed Locking (#10352)

* 🔄 Refactoring: MCP Runtime Configuration Reload
 - PrivateServerConfigs own cache classes (inMemory and Redis).
 - Connections staleness detection by comparing (connection.createdAt and config.LastUpdatedAt)
 - ConnectionsRepo access Registry instead of in memory config dict and renew stale connections
 - MCPManager: adjusted init of ConnectionsRepo (app level)
 - UserConnectionManager: renew stale connections
 - skipped test, to test "should only clear keys in its own namespace"
 - MCPPrivateServerLoader: new component to manage logic of loading / editing private servers on runtime
 - PrivateServersLoadStatusCache to track private server cache status
 - New unit and integration tests.
Misc:
 - add es lint rule to enforce line between class methods

* Fix cluster mode batch update and delete workarround. Fixed unit tests for cluster mode.

* Fix Keyv redis clear cache namespace  awareness issue + Integration tests fixes

* chore: address copilot comments

* Fixing rebase issue: removed the mcp config fallback in single getServerConfig method:
- to not to interfere with the logic of the right Tier (APP/USER/Private)
- If userId is null, the getServerConfig should not return configs that are a SharedUser tier and not APP tier

* chore: add dev-staging branch to workflow triggers for backend, cache integration, and ESLint checks

---------

Co-authored-by: Atef Bellaaj <slalom.bellaaj@external.daimlertruck.com>
This commit is contained in:
Atef Bellaaj 2025-11-26 15:11:36 +01:00 committed by Danny Avila
parent 19b78ecd81
commit 36e42abce1
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
49 changed files with 5244 additions and 257 deletions

View file

@ -58,7 +58,7 @@ describe('MCPManager', () => {
appConnectionsConfig: Partial<ConnectionsRepository>,
): jest.MockedClass<typeof ConnectionsRepository> {
const mock = {
has: jest.fn().mockReturnValue(false),
has: jest.fn().mockResolvedValue(false),
get: jest.fn().mockResolvedValue({} as unknown as MCPConnection),
...appConnectionsConfig,
};
@ -303,7 +303,7 @@ describe('MCPManager', () => {
});
mockAppConnections({
has: jest.fn().mockReturnValue(true),
has: jest.fn().mockResolvedValue(true),
});
const manager = await MCPManager.createInstance(newMCPServersConfig());
@ -321,7 +321,7 @@ describe('MCPManager', () => {
(MCPServerInspector.getToolFunctions as jest.Mock) = jest.fn().mockResolvedValue({});
mockAppConnections({
has: jest.fn().mockReturnValue(false),
get: jest.fn().mockResolvedValue(null),
});
const manager = await MCPManager.createInstance(newMCPServersConfig());
@ -357,7 +357,7 @@ describe('MCPManager', () => {
.mockResolvedValue(expectedTools);
mockAppConnections({
has: jest.fn().mockReturnValue(true),
has: jest.fn().mockResolvedValue(true),
});
const manager = await MCPManager.createInstance(newMCPServersConfig());
@ -376,7 +376,7 @@ describe('MCPManager', () => {
});
mockAppConnections({
has: jest.fn().mockReturnValue(true),
has: jest.fn().mockResolvedValue(true),
});
const manager = await MCPManager.createInstance(newMCPServersConfig(specificServerName));