Create ExtensionManager class which manages all high level extension tasks (#11667)

This commit is contained in:
Jacob MacDonald
2025-10-23 11:39:36 -07:00
committed by GitHub
parent 27b7279608
commit 8c9a0c1935
31 changed files with 1450 additions and 1568 deletions
+16 -6
View File
@@ -7,19 +7,20 @@
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
import { listMcpServers } from './list.js';
import { loadSettings } from '../../config/settings.js';
import { ExtensionStorage, loadExtensions } from '../../config/extension.js';
import { createTransport, debugLogger } from '@google/gemini-cli-core';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { ExtensionStorage } from '../../config/extensions/storage.js';
import { ExtensionManager } from '../../config/extension-manager.js';
vi.mock('../../config/settings.js', () => ({
loadSettings: vi.fn(),
}));
vi.mock('../../config/extension.js', () => ({
loadExtensions: vi.fn(),
vi.mock('../../config/extensions/storage.js', () => ({
ExtensionStorage: {
getUserExtensionsDir: vi.fn(),
},
}));
vi.mock('../../config/extension-manager.js');
vi.mock('@google/gemini-cli-core', () => ({
createTransport: vi.fn(),
MCPServerStatus: {
@@ -46,9 +47,9 @@ vi.mock('@modelcontextprotocol/sdk/client/index.js');
const mockedGetUserExtensionsDir =
ExtensionStorage.getUserExtensionsDir as Mock;
const mockedLoadSettings = loadSettings as Mock;
const mockedLoadExtensions = loadExtensions as Mock;
const mockedCreateTransport = createTransport as Mock;
const MockedClient = Client as Mock;
const MockedExtensionManager = ExtensionManager as Mock;
interface MockClient {
connect: Mock;
@@ -56,12 +57,17 @@ interface MockClient {
close: Mock;
}
interface MockExtensionManager {
loadExtensions: Mock;
}
interface MockTransport {
close: Mock;
}
describe('mcp list command', () => {
let mockClient: MockClient;
let mockExtensionManager: MockExtensionManager;
let mockTransport: MockTransport;
beforeEach(() => {
@@ -73,10 +79,14 @@ describe('mcp list command', () => {
ping: vi.fn(),
close: vi.fn(),
};
mockExtensionManager = {
loadExtensions: vi.fn(),
};
MockedClient.mockImplementation(() => mockClient);
MockedExtensionManager.mockImplementation(() => mockExtensionManager);
mockedCreateTransport.mockResolvedValue(mockTransport);
mockedLoadExtensions.mockReturnValue([]);
mockExtensionManager.loadExtensions.mockReturnValue([]);
mockedGetUserExtensionsDir.mockReturnValue('/mocked/extensions/dir');
});
@@ -149,7 +159,7 @@ describe('mcp list command', () => {
},
});
mockedLoadExtensions.mockReturnValue([
mockExtensionManager.loadExtensions.mockReturnValue([
{
name: 'test-extension',
mcpServers: { 'extension-server': { command: '/ext/server' } },
+10 -3
View File
@@ -14,8 +14,9 @@ import {
debugLogger,
} from '@google/gemini-cli-core';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { loadExtensions } from '../../config/extension.js';
import { ExtensionEnablementManager } from '../../config/extensions/extensionEnablement.js';
import { ExtensionManager } from '../../config/extension-manager.js';
import { requestConsentNonInteractive } from '../../config/extensions/consent.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
const COLOR_GREEN = '\u001b[32m';
const COLOR_YELLOW = '\u001b[33m';
@@ -26,7 +27,13 @@ async function getMcpServersFromConfig(): Promise<
Record<string, MCPServerConfig>
> {
const settings = loadSettings();
const extensions = loadExtensions(new ExtensionEnablementManager());
const extensionManager = new ExtensionManager({
loadedSettings: settings,
workspaceDir: process.cwd(),
requestConsent: requestConsentNonInteractive,
requestSetting: promptForSetting,
});
const extensions = extensionManager.loadExtensions();
const mcpServers = { ...(settings.merged.mcpServers || {}) };
for (const extension of extensions) {
Object.entries(extension.mcpServers || {}).forEach(([key, server]) => {