mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 18:11:02 -07:00
fix(cli): send gemini-cli version as mcp client version (#13407)
Co-authored-by: Taylor Mullen <ntaylormullen@google.com>
This commit is contained in:
committed by
GitHub
parent
645e2ec041
commit
b288f124b2
@@ -451,6 +451,7 @@ export async function loadCliConfig(
|
||||
workspaceDir: cwd,
|
||||
enabledExtensionOverrides: argv.extensions,
|
||||
eventEmitter: appEvents as EventEmitter<ExtensionEvents>,
|
||||
clientVersion: await getVersion(),
|
||||
});
|
||||
await extensionManager.loadExtensions();
|
||||
|
||||
@@ -653,6 +654,7 @@ export async function loadCliConfig(
|
||||
|
||||
return new Config({
|
||||
sessionId,
|
||||
clientVersion: await getVersion(),
|
||||
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
|
||||
sandbox: sandboxConfig,
|
||||
targetDir: cwd,
|
||||
|
||||
@@ -76,6 +76,7 @@ interface ExtensionManagerParams {
|
||||
requestSetting: ((setting: ExtensionSetting) => Promise<string>) | null;
|
||||
workspaceDir: string;
|
||||
eventEmitter?: EventEmitter<ExtensionEvents>;
|
||||
clientVersion?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,6 +106,7 @@ export class ExtensionManager extends ExtensionLoader {
|
||||
telemetry: options.settings.telemetry,
|
||||
interactive: false,
|
||||
sessionId: randomUUID(),
|
||||
clientVersion: options.clientVersion ?? 'unknown',
|
||||
targetDir: options.workspaceDir,
|
||||
cwd: options.workspaceDir,
|
||||
model: '',
|
||||
|
||||
@@ -290,6 +290,7 @@ export interface SandboxConfig {
|
||||
|
||||
export interface ConfigParameters {
|
||||
sessionId: string;
|
||||
clientVersion?: string;
|
||||
embeddingModel?: string;
|
||||
sandbox?: SandboxConfig;
|
||||
targetDir: string;
|
||||
@@ -415,6 +416,7 @@ export class Config {
|
||||
private agentRegistry!: AgentRegistry;
|
||||
private skillManager!: SkillManager;
|
||||
private sessionId: string;
|
||||
private clientVersion: string;
|
||||
private fileSystemService: FileSystemService;
|
||||
private contentGeneratorConfig!: ContentGeneratorConfig;
|
||||
private contentGenerator!: ContentGenerator;
|
||||
@@ -553,6 +555,7 @@ export class Config {
|
||||
|
||||
constructor(params: ConfigParameters) {
|
||||
this.sessionId = params.sessionId;
|
||||
this.clientVersion = params.clientVersion ?? 'unknown';
|
||||
this.embeddingModel =
|
||||
params.embeddingModel ?? DEFAULT_GEMINI_EMBEDDING_MODEL;
|
||||
this.fileSystemService = new StandardFileSystemService();
|
||||
@@ -810,6 +813,7 @@ export class Config {
|
||||
this.toolRegistry = await this.createToolRegistry();
|
||||
discoverToolsHandle?.end();
|
||||
this.mcpClientManager = new McpClientManager(
|
||||
this.clientVersion,
|
||||
this.toolRegistry,
|
||||
this,
|
||||
this.eventEmitter,
|
||||
|
||||
@@ -66,7 +66,7 @@ describe('McpClientManager', () => {
|
||||
mockConfig.getMcpServers.mockReturnValue({
|
||||
'test-server': {},
|
||||
});
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startConfiguredMcpServers();
|
||||
expect(mockedMcpClient.connect).toHaveBeenCalledOnce();
|
||||
expect(mockedMcpClient.discover).toHaveBeenCalledOnce();
|
||||
@@ -79,7 +79,7 @@ describe('McpClientManager', () => {
|
||||
'server-2': {},
|
||||
'server-3': {},
|
||||
});
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startConfiguredMcpServers();
|
||||
|
||||
// Each client should be connected/discovered
|
||||
@@ -94,7 +94,7 @@ describe('McpClientManager', () => {
|
||||
mockConfig.getMcpServers.mockReturnValue({
|
||||
'test-server': {},
|
||||
});
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
expect(manager.getDiscoveryState()).toBe(MCPDiscoveryState.NOT_STARTED);
|
||||
const promise = manager.startConfiguredMcpServers();
|
||||
expect(manager.getDiscoveryState()).toBe(MCPDiscoveryState.IN_PROGRESS);
|
||||
@@ -107,7 +107,7 @@ describe('McpClientManager', () => {
|
||||
'test-server': {},
|
||||
});
|
||||
mockConfig.isTrustedFolder.mockReturnValue(false);
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startConfiguredMcpServers();
|
||||
expect(mockedMcpClient.connect).not.toHaveBeenCalled();
|
||||
expect(mockedMcpClient.discover).not.toHaveBeenCalled();
|
||||
@@ -118,7 +118,7 @@ describe('McpClientManager', () => {
|
||||
'test-server': {},
|
||||
});
|
||||
mockConfig.getBlockedMcpServers.mockReturnValue(['test-server']);
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startConfiguredMcpServers();
|
||||
expect(mockedMcpClient.connect).not.toHaveBeenCalled();
|
||||
expect(mockedMcpClient.discover).not.toHaveBeenCalled();
|
||||
@@ -130,14 +130,14 @@ describe('McpClientManager', () => {
|
||||
'another-server': {},
|
||||
});
|
||||
mockConfig.getAllowedMcpServers.mockReturnValue(['another-server']);
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startConfiguredMcpServers();
|
||||
expect(mockedMcpClient.connect).toHaveBeenCalledOnce();
|
||||
expect(mockedMcpClient.discover).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should start servers from extensions', async () => {
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startExtension({
|
||||
name: 'test-extension',
|
||||
mcpServers: {
|
||||
@@ -154,7 +154,7 @@ describe('McpClientManager', () => {
|
||||
});
|
||||
|
||||
it('should not start servers from disabled extensions', async () => {
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startExtension({
|
||||
name: 'test-extension',
|
||||
mcpServers: {
|
||||
@@ -175,7 +175,7 @@ describe('McpClientManager', () => {
|
||||
'test-server': {},
|
||||
});
|
||||
mockConfig.getBlockedMcpServers.mockReturnValue(['test-server']);
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startConfiguredMcpServers();
|
||||
expect(manager.getBlockedMcpServers()).toEqual([
|
||||
{ name: 'test-server', extensionName: '' },
|
||||
@@ -188,7 +188,7 @@ describe('McpClientManager', () => {
|
||||
'test-server': {},
|
||||
});
|
||||
mockedMcpClient.getServerConfig.mockReturnValue({});
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startConfiguredMcpServers();
|
||||
|
||||
expect(mockedMcpClient.connect).toHaveBeenCalledTimes(1);
|
||||
@@ -207,7 +207,7 @@ describe('McpClientManager', () => {
|
||||
'test-server': {},
|
||||
});
|
||||
mockedMcpClient.getServerConfig.mockReturnValue({});
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await manager.startConfiguredMcpServers();
|
||||
|
||||
expect(mockedMcpClient.connect).toHaveBeenCalledTimes(1);
|
||||
@@ -221,7 +221,7 @@ describe('McpClientManager', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if the server does not exist', async () => {
|
||||
const manager = new McpClientManager(toolRegistry, mockConfig);
|
||||
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
|
||||
await expect(manager.restartServer('non-existent')).rejects.toThrow(
|
||||
'No MCP server registered with the name "non-existent"',
|
||||
);
|
||||
@@ -247,7 +247,11 @@ describe('McpClientManager', () => {
|
||||
}) as unknown as McpClient,
|
||||
);
|
||||
|
||||
const manager = new McpClientManager({} as ToolRegistry, mockConfig);
|
||||
const manager = new McpClientManager(
|
||||
'0.0.1',
|
||||
{} as ToolRegistry,
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
mockConfig.getMcpServers.mockReturnValue({
|
||||
'server-with-instructions': {},
|
||||
@@ -282,7 +286,11 @@ describe('McpClientManager', () => {
|
||||
'test-server': {},
|
||||
});
|
||||
|
||||
const manager = new McpClientManager({} as ToolRegistry, mockConfig);
|
||||
const manager = new McpClientManager(
|
||||
'0.0.1',
|
||||
{} as ToolRegistry,
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
await expect(manager.startConfiguredMcpServers()).resolves.not.toThrow();
|
||||
});
|
||||
@@ -301,7 +309,11 @@ describe('McpClientManager', () => {
|
||||
'test-server': {},
|
||||
});
|
||||
|
||||
const manager = new McpClientManager({} as ToolRegistry, mockConfig);
|
||||
const manager = new McpClientManager(
|
||||
'0.0.1',
|
||||
{} as ToolRegistry,
|
||||
mockConfig,
|
||||
);
|
||||
await manager.startConfiguredMcpServers();
|
||||
|
||||
await expect(manager.restartServer('test-server')).resolves.not.toThrow();
|
||||
|
||||
@@ -27,6 +27,7 @@ import { debugLogger } from '../utils/debugLogger.js';
|
||||
*/
|
||||
export class McpClientManager {
|
||||
private clients: Map<string, McpClient> = new Map();
|
||||
private readonly clientVersion: string;
|
||||
private readonly toolRegistry: ToolRegistry;
|
||||
private readonly cliConfig: Config;
|
||||
// If we have ongoing MCP client discovery, this completes once that is done.
|
||||
@@ -40,10 +41,12 @@ export class McpClientManager {
|
||||
}> = [];
|
||||
|
||||
constructor(
|
||||
clientVersion: string,
|
||||
toolRegistry: ToolRegistry,
|
||||
cliConfig: Config,
|
||||
eventEmitter?: EventEmitter,
|
||||
) {
|
||||
this.clientVersion = clientVersion;
|
||||
this.toolRegistry = toolRegistry;
|
||||
this.cliConfig = cliConfig;
|
||||
this.eventEmitter = eventEmitter;
|
||||
@@ -183,6 +186,7 @@ export class McpClientManager {
|
||||
this.cliConfig.getWorkspaceContext(),
|
||||
this.cliConfig,
|
||||
this.cliConfig.getDebugMode(),
|
||||
this.clientVersion,
|
||||
async () => {
|
||||
debugLogger.log('Tools changed, updating Gemini context...');
|
||||
await this.scheduleMcpContextRefresh();
|
||||
|
||||
@@ -133,6 +133,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await client.discover({} as Config);
|
||||
@@ -213,6 +214,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await client.discover({} as Config);
|
||||
@@ -264,6 +266,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await expect(client.discover({} as Config)).rejects.toThrow(
|
||||
@@ -319,6 +322,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await expect(client.discover({} as Config)).rejects.toThrow(
|
||||
@@ -378,6 +382,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await client.discover({} as Config);
|
||||
@@ -451,6 +456,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await client.discover({} as Config);
|
||||
@@ -527,6 +533,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await client.discover({} as Config);
|
||||
@@ -610,6 +617,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await client.discover({} as Config);
|
||||
@@ -690,6 +698,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
await client.connect();
|
||||
await client.discover({} as Config);
|
||||
@@ -739,6 +748,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
|
||||
await client.connect();
|
||||
@@ -775,6 +785,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
|
||||
await client.connect();
|
||||
@@ -830,6 +841,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
onToolsUpdatedSpy,
|
||||
);
|
||||
|
||||
@@ -900,6 +912,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
|
||||
await client.connect();
|
||||
@@ -970,6 +983,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
onToolsUpdatedSpy,
|
||||
);
|
||||
|
||||
@@ -982,6 +996,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
onToolsUpdatedSpy,
|
||||
);
|
||||
|
||||
@@ -1064,6 +1079,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
);
|
||||
|
||||
await client.connect();
|
||||
@@ -1128,6 +1144,7 @@ describe('mcp-client', () => {
|
||||
workspaceContext,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
'0.0.1',
|
||||
onToolsUpdatedSpy,
|
||||
);
|
||||
|
||||
@@ -1675,6 +1692,7 @@ describe('connectToMcpServer with OAuth', () => {
|
||||
);
|
||||
|
||||
const client = await connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ httpUrl: serverUrl, oauth: { enabled: true } },
|
||||
false,
|
||||
@@ -1720,6 +1738,7 @@ describe('connectToMcpServer with OAuth', () => {
|
||||
);
|
||||
|
||||
const client = await connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ httpUrl: serverUrl, oauth: { enabled: true } },
|
||||
false,
|
||||
@@ -1775,6 +1794,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
|
||||
await expect(
|
||||
connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ url: 'http://test-server', type: 'http' },
|
||||
false,
|
||||
@@ -1794,6 +1814,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
|
||||
await expect(
|
||||
connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ url: 'http://test-server', type: 'sse' },
|
||||
false,
|
||||
@@ -1812,6 +1833,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
.mockResolvedValueOnce(undefined);
|
||||
|
||||
const client = await connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ url: 'http://test-server' },
|
||||
false,
|
||||
@@ -1834,6 +1856,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
|
||||
await expect(
|
||||
connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ url: 'http://test-server' },
|
||||
false,
|
||||
@@ -1851,6 +1874,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
.mockResolvedValueOnce(undefined);
|
||||
|
||||
const client = await connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ url: 'http://test-server' },
|
||||
false,
|
||||
@@ -1921,6 +1945,7 @@ describe('connectToMcpServer - OAuth with transport fallback', () => {
|
||||
.mockResolvedValueOnce(undefined);
|
||||
|
||||
const client = await connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ url: 'http://test-server', oauth: { enabled: true } },
|
||||
false,
|
||||
|
||||
@@ -122,6 +122,7 @@ export class McpClient {
|
||||
private readonly workspaceContext: WorkspaceContext,
|
||||
private readonly cliConfig: Config,
|
||||
private readonly debugMode: boolean,
|
||||
private readonly clientVersion: string,
|
||||
private readonly onToolsUpdated?: (signal?: AbortSignal) => Promise<void>,
|
||||
) {}
|
||||
|
||||
@@ -137,6 +138,7 @@ export class McpClient {
|
||||
this.updateStatus(MCPServerStatus.CONNECTING);
|
||||
try {
|
||||
this.client = await connectToMcpServer(
|
||||
this.clientVersion,
|
||||
this.serverName,
|
||||
this.serverConfig,
|
||||
this.debugMode,
|
||||
@@ -715,6 +717,7 @@ async function createTransportWithOAuth(
|
||||
*/
|
||||
|
||||
export async function discoverMcpTools(
|
||||
clientVersion: string,
|
||||
mcpServers: Record<string, MCPServerConfig>,
|
||||
mcpServerCommand: string | undefined,
|
||||
toolRegistry: ToolRegistry,
|
||||
@@ -730,6 +733,7 @@ export async function discoverMcpTools(
|
||||
const discoveryPromises = Object.entries(mcpServers).map(
|
||||
([mcpServerName, mcpServerConfig]) =>
|
||||
connectAndDiscover(
|
||||
clientVersion,
|
||||
mcpServerName,
|
||||
mcpServerConfig,
|
||||
toolRegistry,
|
||||
@@ -808,6 +812,7 @@ export function populateMcpServerCommand(
|
||||
* @returns Promise that resolves when discovery is complete
|
||||
*/
|
||||
export async function connectAndDiscover(
|
||||
clientVersion: string,
|
||||
mcpServerName: string,
|
||||
mcpServerConfig: MCPServerConfig,
|
||||
toolRegistry: ToolRegistry,
|
||||
@@ -821,6 +826,7 @@ export async function connectAndDiscover(
|
||||
let mcpClient: Client | undefined;
|
||||
try {
|
||||
mcpClient = await connectToMcpServer(
|
||||
clientVersion,
|
||||
mcpServerName,
|
||||
mcpServerConfig,
|
||||
debugMode,
|
||||
@@ -1331,6 +1337,7 @@ async function retryWithOAuth(
|
||||
* @throws An error if the connection fails or the configuration is invalid.
|
||||
*/
|
||||
export async function connectToMcpServer(
|
||||
clientVersion: string,
|
||||
mcpServerName: string,
|
||||
mcpServerConfig: MCPServerConfig,
|
||||
debugMode: boolean,
|
||||
@@ -1340,7 +1347,7 @@ export async function connectToMcpServer(
|
||||
const mcpClient = new Client(
|
||||
{
|
||||
name: 'gemini-cli-mcp-client',
|
||||
version: '0.0.1',
|
||||
version: clientVersion,
|
||||
},
|
||||
{
|
||||
// Use a tolerant validator so bad output schemas don't block discovery.
|
||||
|
||||
Reference in New Issue
Block a user