Skip to content

Commit 6d99113

Browse files
authored
fix(core): disconnect extension-backed MCP clients in stopExtension (#26136)
1 parent fbd8aaa commit 6d99113

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

packages/core/src/tools/mcp-client-manager.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { McpClientManager } from './mcp-client-manager.js';
1717
import { McpClient, MCPDiscoveryState, MCPServerStatus } from './mcp-client.js';
1818
import type { ToolRegistry } from './tool-registry.js';
1919
import type { Config, GeminiCLIExtension } from '../config/config.js';
20+
import { MCPServerConfig } from '../config/config.js';
2021
import type { PromptRegistry } from '../prompts/prompt-registry.js';
2122
import type { ResourceRegistry } from '../resources/resource-registry.js';
2223

@@ -726,6 +727,40 @@ describe('McpClientManager', () => {
726727
extensionName: 'test-extension',
727728
});
728729
});
730+
731+
it('should disconnect extension-backed MCP clients when stopping extension (#24050)', async () => {
732+
const manager = setupManager(new McpClientManager('0.0.1', mockConfig));
733+
const extension: GeminiCLIExtension = {
734+
id: 'test-ext-id',
735+
name: 'test-extension',
736+
isActive: true,
737+
version: '1.0.0',
738+
path: '/fake/path',
739+
contextFiles: [],
740+
mcpServers: {
741+
'test-server': new MCPServerConfig('node', ['script.js']),
742+
},
743+
};
744+
745+
await manager.startExtension(extension);
746+
747+
// Wait for discovery to complete
748+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
749+
while ((manager as any).discoveryPromise) {
750+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
751+
await (manager as any).discoveryPromise;
752+
}
753+
754+
// Verify it was connected
755+
expect(mockedMcpClient.connect).toHaveBeenCalled();
756+
757+
// Stop the extension
758+
await manager.stopExtension(extension);
759+
760+
// Verify disconnect was called on the client
761+
expect(mockedMcpClient.disconnect).toHaveBeenCalled();
762+
expect(manager.getClient('test-server')).toBeUndefined();
763+
});
729764
});
730765

731766
describe('diagnostic reporting', () => {

packages/core/src/tools/mcp-client-manager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ export class McpClientManager {
215215
Object.keys(extension.mcpServers ?? {}).map((name) => {
216216
const config = this.allServerConfigs.get(name);
217217
if (config?.extension?.id === extension.id) {
218+
const clientKey = this.getClientKey(name, config);
218219
this.allServerConfigs.delete(name);
219220
// Also remove from blocked servers if present
220221
const index = this.blockedMcpServers.findIndex(
@@ -223,7 +224,7 @@ export class McpClientManager {
223224
if (index !== -1) {
224225
this.blockedMcpServers.splice(index, 1);
225226
}
226-
return this.disconnectClient(name, true);
227+
return this.disconnectClient(clientKey, true);
227228
}
228229
return Promise.resolve();
229230
}),

0 commit comments

Comments
 (0)