Skip to content

Conversation

@Rdmorris1994
Copy link

This PR introduces several key optimizations to improve Gemini CLI's performance and developer experience:

1. Lazy Loading for CLI Entry Point

  • Introduced a lightweight bin.ts entry point.
  • Heavy UI dependencies (React, Ink) are now dynamically imported only when an interactive session is required.
  • Significantly improves startup time for non-interactive commands (e.g., gemini -p "...").

2. MCP Connection Pooling

  • Optimized McpClientManager to maintain and reuse healthy MCP server connections.
  • Reduces latency and resource overhead for tool-heavy interactions.

These changes make the CLI faster, leaner, and more robust.

@Rdmorris1994 Rdmorris1994 requested review from a team as code owners January 25, 2026 19:31
@google-cla
Copy link

google-cla bot commented Jan 25, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Rdmorris1994, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Gemini CLI's efficiency and responsiveness. By strategically implementing lazy loading for its user interface components, the CLI now starts much faster for command-line operations that don't require a full interactive experience. Concurrently, the integration of connection pooling for MCP servers streamlines tool interactions, leading to a more robust and performant overall system.

Highlights

  • CLI Startup Optimization: Implemented lazy loading for UI dependencies (React, Ink) in the CLI, significantly improving startup performance for non-interactive commands by deferring their import until an interactive session is explicitly required.
  • MCP Connection Pooling: Refactored the McpClientManager to introduce connection pooling, allowing for the reuse of healthy MCP server connections. This reduces latency and resource overhead associated with tool interactions.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request aims to improve performance through lazy-loading UI components and connection pooling for MCP clients, and simplifies McpClientManager logic. However, the refactoring of McpClientManager has critically removed essential security enforcement, specifically trust verification for workspaces and global MCP server blocklists/allowlists, leading to a security regression where malicious project configurations can bypass user-defined policies. Beyond this, several functional regressions and API mismatches exist, including dependencies on an unincluded McpClient interface, incorrect constructor and method calls, a non-existent exit code reference, and an incorrect ToolRegistry method call, all of which will cause runtime errors and prevent correct CLI operation.

main().catch((err) => {
debugLogger.error('Fatal error in CLI:', err);
// @ts-ignore
process.exit(ExitCodes?.FATAL_INTERNAL_ERROR || 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The ExitCodes enum does not contain a FATAL_INTERNAL_ERROR member. This will be undefined at runtime, causing the process to always exit with the fallback code 1. This should be corrected to use a valid exit code or a plain number.

Suggested change
process.exit(ExitCodes?.FATAL_INTERNAL_ERROR || 1);
process.exit(1);

Comment on lines +70 to +88
// Simple pooling: reuse existing client if healthy
let client = this.clients.get(name);
if (client) {
try {
await client.connect();
await client.discover(this.cliConfig);
this.eventEmitter?.emit('mcp-client-update', this.clients);
} catch (error) {
this.eventEmitter?.emit('mcp-client-update', this.clients);
// Check if this is a 401/auth error - if so, don't show as red error
// (the info message was already shown in mcp-client.ts)
if (!isAuthenticationError(error)) {
// Log the error but don't let a single failed server stop the others
const errorMessage = getErrorMessage(error);
coreEvents.emitFeedback(
'error',
`Error during discovery for MCP server '${name}': ${errorMessage}`,
error,
);
}
await client.ping();
debugLogger.debug(`Reusing healthy MCP client for ${name}`);
return;
} catch (e) {
debugLogger.debug(`MCP client for ${name} is unhealthy, restarting...`);
await client.stop();
this.clients.delete(name);
}
} catch (error) {
const errorMessage = getErrorMessage(error);
coreEvents.emitFeedback(
'error',
`Error initializing MCP server '${name}': ${errorMessage}`,
error,
);
} finally {
resolve();
}
})().catch(reject);
});

if (this.discoveryPromise) {
// Ensure the next discovery starts regardless of the previous one's success/failure
this.discoveryPromise = this.discoveryPromise
.catch(() => {})
.then(() => currentDiscoveryPromise);
} else {
this.discoveryState = MCPDiscoveryState.IN_PROGRESS;
this.discoveryPromise = currentDiscoveryPromise;
}
this.eventEmitter?.emit('mcp-client-update', this.clients);
const currentPromise = this.discoveryPromise;
void currentPromise
.finally(() => {
// If we are the last recorded discoveryPromise, then we are done, reset
// the world.
if (currentPromise === this.discoveryPromise) {
this.discoveryPromise = undefined;
this.discoveryState = MCPDiscoveryState.COMPLETED;
this.eventEmitter?.emit('mcp-client-update', this.clients);
client = new McpClient(name, config, this.clientVersion, this.cliConfig);
this.clients.set(name, client);
await client.start();

const tools = await client.getTools();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This refactoring introduces calls to a McpClient that appears to have a different interface than the one defined in the repository.

  • The constructor call new McpClient(name, config, this.clientVersion, this.cliConfig) on line 84 does not match the expected signature from mcp-client.ts.
  • Methods like ping() (line 74), stop() (line 79), start() (line 86), and getTools() (line 88) are called on the client instance, but they are not defined on the McpClient class in packages/core/src/tools/mcp-client.ts.

This will lead to runtime errors. It seems like related changes to mcp-client.ts might be missing from this pull request.


const tools = await client.getTools();
for (const tool of tools) {
this.toolRegistry.register(tool);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The method to register a tool on ToolRegistry is registerTool, not register. This will cause a TypeError at runtime.

Suggested change
this.toolRegistry.register(tool);
this.toolRegistry.registerTool(tool);

Comment on lines +60 to 67
private async internalStart(): Promise<void> {
const servers = this.cliConfig.getMcpServers() || {};
this.allServerConfigs = new Map(Object.entries(servers));

// Check if blocked by admin settings (allowlist/excludelist)
if (this.isBlockedBySettings(name)) {
if (!this.blockedMcpServers.find((s) => s.name === name)) {
this.blockedMcpServers?.push({
name,
extensionName: config.extension?.name ?? '',
});
}
return;
}
// User-disabled servers: disconnect if running, don't start
if (await this.isDisabledByUser(name)) {
const existing = this.clients.get(name);
if (existing) {
await this.disconnectClient(name);
const promises = Object.entries(servers).map(async ([name, config]) => {
if (config.enabled === false) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The refactored internalStart method removes several critical security checks that were previously enforced before starting MCP servers. Specifically, it no longer verifies folder trust (isTrustedFolder()), nor does it enforce the global allowlist/blocklist (isBlockedBySettings) or session-based enablement callbacks (isDisabledByUser). This allows a malicious repository to bypass global security policies and execute arbitrary commands via a project-level MCP configuration. Please restore these security checks before starting any MCP client.

@gemini-cli gemini-cli bot added the status/need-issue Pull requests that need to have an associated issue. label Jan 25, 2026
@gemini-cli
Copy link
Contributor

gemini-cli bot commented Jan 27, 2026

Hi there! Thank you for your contribution to Gemini CLI.

To improve our contribution process and better track changes, we now require all pull requests to be associated with an existing issue, as announced in our recent discussion and as detailed in our CONTRIBUTING.md.

This pull request is being closed because it is not currently linked to an issue. Once you have updated the description of this PR to link an issue (e.g., by adding Fixes #123 or Related to #123), it will be automatically reopened.

How to link an issue:
Add a keyword followed by the issue number (e.g., Fixes #123) in the description of your pull request. For more details on supported keywords and how linking works, please refer to the GitHub Documentation on linking pull requests to issues.

Thank you for your understanding and for being a part of our community!

@gemini-cli gemini-cli bot closed this Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant