Pass whole extensions rather than just context files (#10910)

Co-authored-by: Jake Macdonald <jakemac@google.com>
This commit is contained in:
Zack Birkenbuel
2025-10-20 16:15:23 -07:00
committed by GitHub
parent 995ae717cc
commit cc7e1472f9
35 changed files with 487 additions and 1193 deletions
@@ -8,8 +8,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
import { McpClientManager } from './mcp-client-manager.js';
import { McpClient } from './mcp-client.js';
import type { ToolRegistry } from './tool-registry.js';
import type { PromptRegistry } from '../prompts/prompt-registry.js';
import type { WorkspaceContext } from '../utils/workspaceContext.js';
import type { Config } from '../config/config.js';
vi.mock('./mcp-client.js', async () => {
@@ -38,18 +36,16 @@ describe('McpClientManager', () => {
vi.mocked(McpClient).mockReturnValue(
mockedMcpClient as unknown as McpClient,
);
const manager = new McpClientManager(
{
'test-server': {},
},
'',
{} as ToolRegistry,
{} as PromptRegistry,
false,
{} as WorkspaceContext,
);
const manager = new McpClientManager({} as ToolRegistry);
await manager.discoverAllMcpTools({
isTrustedFolder: () => true,
getMcpServers: () => ({
'test-server': {},
}),
getMcpServerCommand: () => '',
getPromptRegistry: () => {},
getDebugMode: () => false,
getWorkspaceContext: () => {},
} as unknown as Config);
expect(mockedMcpClient.connect).toHaveBeenCalledOnce();
expect(mockedMcpClient.discover).toHaveBeenCalledOnce();
@@ -65,18 +61,16 @@ describe('McpClientManager', () => {
vi.mocked(McpClient).mockReturnValue(
mockedMcpClient as unknown as McpClient,
);
const manager = new McpClientManager(
{
'test-server': {},
},
'',
{} as ToolRegistry,
{} as PromptRegistry,
false,
{} as WorkspaceContext,
);
const manager = new McpClientManager({} as ToolRegistry);
await manager.discoverAllMcpTools({
isTrustedFolder: () => false,
getMcpServers: () => ({
'test-server': {},
}),
getMcpServerCommand: () => '',
getPromptRegistry: () => {},
getDebugMode: () => false,
getWorkspaceContext: () => {},
} as unknown as Config);
expect(mockedMcpClient.connect).not.toHaveBeenCalled();
expect(mockedMcpClient.discover).not.toHaveBeenCalled();
+11 -31
View File
@@ -4,9 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type { Config, MCPServerConfig } from '../config/config.js';
import type { Config } from '../config/config.js';
import type { ToolRegistry } from './tool-registry.js';
import type { PromptRegistry } from '../prompts/prompt-registry.js';
import {
McpClient,
MCPDiscoveryState,
@@ -14,7 +13,6 @@ import {
} from './mcp-client.js';
import { getErrorMessage } from '../utils/errors.js';
import type { EventEmitter } from 'node:events';
import type { WorkspaceContext } from '../utils/workspaceContext.js';
/**
* Manages the lifecycle of multiple MCP clients, including local child processes.
@@ -23,30 +21,12 @@ import type { WorkspaceContext } from '../utils/workspaceContext.js';
*/
export class McpClientManager {
private clients: Map<string, McpClient> = new Map();
private readonly mcpServers: Record<string, MCPServerConfig>;
private readonly mcpServerCommand: string | undefined;
private readonly toolRegistry: ToolRegistry;
private readonly promptRegistry: PromptRegistry;
private readonly debugMode: boolean;
private readonly workspaceContext: WorkspaceContext;
private discoveryState: MCPDiscoveryState = MCPDiscoveryState.NOT_STARTED;
private readonly eventEmitter?: EventEmitter;
constructor(
mcpServers: Record<string, MCPServerConfig>,
mcpServerCommand: string | undefined,
toolRegistry: ToolRegistry,
promptRegistry: PromptRegistry,
debugMode: boolean,
workspaceContext: WorkspaceContext,
eventEmitter?: EventEmitter,
) {
this.mcpServers = mcpServers;
this.mcpServerCommand = mcpServerCommand;
constructor(toolRegistry: ToolRegistry, eventEmitter?: EventEmitter) {
this.toolRegistry = toolRegistry;
this.promptRegistry = promptRegistry;
this.debugMode = debugMode;
this.workspaceContext = workspaceContext;
this.eventEmitter = eventEmitter;
}
@@ -62,22 +42,23 @@ export class McpClientManager {
await this.stop();
const servers = populateMcpServerCommand(
this.mcpServers,
this.mcpServerCommand,
cliConfig.getMcpServers() || {},
cliConfig.getMcpServerCommand(),
);
this.discoveryState = MCPDiscoveryState.IN_PROGRESS;
this.eventEmitter?.emit('mcp-client-update', this.clients);
const discoveryPromises = Object.entries(servers).map(
async ([name, config]) => {
const discoveryPromises = Object.entries(servers)
.filter(([_, config]) => !config.extension || config.extension.isActive)
.map(async ([name, config]) => {
const client = new McpClient(
name,
config,
this.toolRegistry,
this.promptRegistry,
this.workspaceContext,
this.debugMode,
cliConfig.getPromptRegistry(),
cliConfig.getWorkspaceContext(),
cliConfig.getDebugMode(),
);
this.clients.set(name, client);
@@ -95,8 +76,7 @@ export class McpClientManager {
)}`,
);
}
},
);
});
await Promise.all(discoveryPromises);
this.discoveryState = MCPDiscoveryState.COMPLETED;
+1 -9
View File
@@ -174,15 +174,7 @@ export class ToolRegistry {
constructor(config: Config, eventEmitter?: EventEmitter) {
this.config = config;
this.mcpClientManager = new McpClientManager(
this.config.getMcpServers() ?? {},
this.config.getMcpServerCommand(),
this,
this.config.getPromptRegistry(),
this.config.getDebugMode(),
this.config.getWorkspaceContext(),
eventEmitter,
);
this.mcpClientManager = new McpClientManager(this, eventEmitter);
}
/**