mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-23 04:21:31 -07:00
Use official ACP SDK and support HTTP/SSE based MCP servers (#13856)
This commit is contained in:
@@ -4,8 +4,6 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { ReadableStream } from 'node:stream/web';
|
||||
|
||||
import type {
|
||||
Config,
|
||||
GeminiChat,
|
||||
@@ -33,7 +31,7 @@ import {
|
||||
createWorkingStdio,
|
||||
startupProfiler,
|
||||
} from '@google/gemini-cli-core';
|
||||
import * as acp from './acp.js';
|
||||
import * as acp from '@agentclientprotocol/sdk';
|
||||
import { AcpFileSystemService } from './fileSystemService.js';
|
||||
import { Readable, Writable } from 'node:stream';
|
||||
import type { Content, Part, FunctionCall } from '@google/genai';
|
||||
@@ -53,13 +51,13 @@ export async function runZedIntegration(
|
||||
argv: CliArgs,
|
||||
) {
|
||||
const { stdout: workingStdout } = createWorkingStdio();
|
||||
const stdout = Writable.toWeb(workingStdout);
|
||||
const stdout = Writable.toWeb(workingStdout) as WritableStream;
|
||||
const stdin = Readable.toWeb(process.stdin) as ReadableStream<Uint8Array>;
|
||||
|
||||
const stream = acp.ndJsonStream(stdout, stdin);
|
||||
new acp.AgentSideConnection(
|
||||
(client: acp.Client) => new GeminiAgent(config, settings, argv, client),
|
||||
stdout,
|
||||
stdin,
|
||||
(connection) => new GeminiAgent(config, settings, argv, connection),
|
||||
stream,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,7 +69,7 @@ export class GeminiAgent {
|
||||
private config: Config,
|
||||
private settings: LoadedSettings,
|
||||
private argv: CliArgs,
|
||||
private client: acp.Client,
|
||||
private connection: acp.AgentSideConnection,
|
||||
) {}
|
||||
|
||||
async initialize(
|
||||
@@ -107,6 +105,10 @@ export class GeminiAgent {
|
||||
audio: true,
|
||||
embeddedContext: true,
|
||||
},
|
||||
mcpCapabilities: {
|
||||
http: true,
|
||||
sse: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -156,7 +158,7 @@ export class GeminiAgent {
|
||||
|
||||
if (this.clientCapabilities?.fs) {
|
||||
const acpFileSystemService = new AcpFileSystemService(
|
||||
this.client,
|
||||
this.connection,
|
||||
sessionId,
|
||||
this.clientCapabilities.fs,
|
||||
config.getFileSystemService(),
|
||||
@@ -166,7 +168,7 @@ export class GeminiAgent {
|
||||
|
||||
const geminiClient = config.getGeminiClient();
|
||||
const chat = await geminiClient.startChat();
|
||||
const session = new Session(sessionId, chat, config, this.client);
|
||||
const session = new Session(sessionId, chat, config, this.connection);
|
||||
this.sessions.set(sessionId, session);
|
||||
|
||||
return {
|
||||
@@ -181,12 +183,37 @@ export class GeminiAgent {
|
||||
): Promise<Config> {
|
||||
const mergedMcpServers = { ...this.settings.merged.mcpServers };
|
||||
|
||||
for (const { command, args, env: rawEnv, name } of mcpServers) {
|
||||
const env: Record<string, string> = {};
|
||||
for (const { name: envName, value } of rawEnv) {
|
||||
env[envName] = value;
|
||||
for (const server of mcpServers) {
|
||||
if (
|
||||
'type' in server &&
|
||||
(server.type === 'sse' || server.type === 'http')
|
||||
) {
|
||||
// HTTP or SSE MCP server
|
||||
const headers = Object.fromEntries(
|
||||
server.headers.map(({ name, value }) => [name, value]),
|
||||
);
|
||||
mergedMcpServers[server.name] = new MCPServerConfig(
|
||||
undefined, // command
|
||||
undefined, // args
|
||||
undefined, // env
|
||||
undefined, // cwd
|
||||
server.type === 'sse' ? server.url : undefined, // url (sse)
|
||||
server.type === 'http' ? server.url : undefined, // httpUrl
|
||||
headers,
|
||||
);
|
||||
} else if ('command' in server) {
|
||||
// Stdio MCP server
|
||||
const env: Record<string, string> = {};
|
||||
for (const { name: envName, value } of server.env) {
|
||||
env[envName] = value;
|
||||
}
|
||||
mergedMcpServers[server.name] = new MCPServerConfig(
|
||||
server.command,
|
||||
server.args,
|
||||
env,
|
||||
cwd,
|
||||
);
|
||||
}
|
||||
mergedMcpServers[name] = new MCPServerConfig(command, args, env, cwd);
|
||||
}
|
||||
|
||||
const settings = { ...this.settings.merged, mcpServers: mergedMcpServers };
|
||||
@@ -222,7 +249,7 @@ export class Session {
|
||||
private readonly id: string,
|
||||
private readonly chat: GeminiChat,
|
||||
private readonly config: Config,
|
||||
private readonly client: acp.Client,
|
||||
private readonly connection: acp.AgentSideConnection,
|
||||
) {}
|
||||
|
||||
async cancelPendingPrompt(): Promise<void> {
|
||||
@@ -340,13 +367,15 @@ export class Session {
|
||||
return { stopReason: 'end_turn' };
|
||||
}
|
||||
|
||||
private async sendUpdate(update: acp.SessionUpdate): Promise<void> {
|
||||
private async sendUpdate(
|
||||
update: acp.SessionNotification['update'],
|
||||
): Promise<void> {
|
||||
const params: acp.SessionNotification = {
|
||||
sessionId: this.id,
|
||||
update,
|
||||
};
|
||||
|
||||
await this.client.sessionUpdate(params);
|
||||
await this.connection.sessionUpdate(params);
|
||||
}
|
||||
|
||||
private async runTool(
|
||||
@@ -432,7 +461,7 @@ export class Session {
|
||||
},
|
||||
};
|
||||
|
||||
const output = await this.client.requestPermission(params);
|
||||
const output = await this.connection.requestPermission(params);
|
||||
const outcome =
|
||||
output.outcome.outcome === 'cancelled'
|
||||
? ToolConfirmationOutcome.Cancel
|
||||
|
||||
Reference in New Issue
Block a user