diff options
Diffstat (limited to 'packages/cli/src/ui')
| -rw-r--r-- | packages/cli/src/ui/App.test.tsx | 8 | ||||
| -rw-r--r-- | packages/cli/src/ui/App.tsx | 1 | ||||
| -rw-r--r-- | packages/cli/src/ui/commands/extensionsCommand.test.ts | 9 | ||||
| -rw-r--r-- | packages/cli/src/ui/commands/extensionsCommand.ts | 4 | ||||
| -rw-r--r-- | packages/cli/src/ui/commands/mcpCommand.test.ts | 57 | ||||
| -rw-r--r-- | packages/cli/src/ui/commands/mcpCommand.ts | 17 | ||||
| -rw-r--r-- | packages/cli/src/ui/components/ContextSummaryDisplay.tsx | 34 |
7 files changed, 118 insertions, 12 deletions
diff --git a/packages/cli/src/ui/App.test.tsx b/packages/cli/src/ui/App.test.tsx index 0c18b042..ed4418e9 100644 --- a/packages/cli/src/ui/App.test.tsx +++ b/packages/cli/src/ui/App.test.tsx @@ -58,6 +58,12 @@ interface MockServerConfig { getToolCallCommand: Mock<() => string | undefined>; getMcpServerCommand: Mock<() => string | undefined>; getMcpServers: Mock<() => Record<string, MCPServerConfig> | undefined>; + getExtensions: Mock< + () => Array<{ name: string; version: string; isActive: boolean }> + >; + getBlockedMcpServers: Mock< + () => Array<{ name: string; extensionName: string }> + >; getUserAgent: Mock<() => string>; getUserMemory: Mock<() => string>; setUserMemory: Mock<(newUserMemory: string) => void>; @@ -118,6 +124,8 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { getToolCallCommand: vi.fn(() => opts.toolCallCommand), getMcpServerCommand: vi.fn(() => opts.mcpServerCommand), getMcpServers: vi.fn(() => opts.mcpServers), + getExtensions: vi.fn(() => []), + getBlockedMcpServers: vi.fn(() => []), getUserAgent: vi.fn(() => opts.userAgent || 'test-agent'), getUserMemory: vi.fn(() => opts.userMemory || ''), setUserMemory: vi.fn(), diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 5d8ab39d..782e2ff8 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -886,6 +886,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { geminiMdFileCount={geminiMdFileCount} contextFileNames={contextFileNames} mcpServers={config.getMcpServers()} + blockedMcpServers={config.getBlockedMcpServers()} showToolDescriptions={showToolDescriptions} /> )} diff --git a/packages/cli/src/ui/commands/extensionsCommand.test.ts b/packages/cli/src/ui/commands/extensionsCommand.test.ts index a989d9b0..0a69e01c 100644 --- a/packages/cli/src/ui/commands/extensionsCommand.test.ts +++ b/packages/cli/src/ui/commands/extensionsCommand.test.ts @@ -17,7 +17,7 @@ describe('extensionsCommand', () => { mockContext = createMockCommandContext({ services: { config: { - getActiveExtensions: () => [], + getExtensions: () => [], }, }, }); @@ -36,13 +36,14 @@ describe('extensionsCommand', () => { it('should list active extensions when they are found', async () => { const mockExtensions = [ - { name: 'ext-one', version: '1.0.0' }, - { name: 'ext-two', version: '2.1.0' }, + { name: 'ext-one', version: '1.0.0', isActive: true }, + { name: 'ext-two', version: '2.1.0', isActive: true }, + { name: 'ext-three', version: '3.0.0', isActive: false }, ]; mockContext = createMockCommandContext({ services: { config: { - getActiveExtensions: () => mockExtensions, + getExtensions: () => mockExtensions, }, }, }); diff --git a/packages/cli/src/ui/commands/extensionsCommand.ts b/packages/cli/src/ui/commands/extensionsCommand.ts index 87d23afb..09241e5f 100644 --- a/packages/cli/src/ui/commands/extensionsCommand.ts +++ b/packages/cli/src/ui/commands/extensionsCommand.ts @@ -11,7 +11,9 @@ export const extensionsCommand: SlashCommand = { name: 'extensions', description: 'list active extensions', action: async (context: CommandContext): Promise<void> => { - const activeExtensions = context.services.config?.getActiveExtensions(); + const activeExtensions = context.services.config + ?.getExtensions() + .filter((ext) => ext.isActive); if (!activeExtensions || activeExtensions.length === 0) { context.ui.addItem( { diff --git a/packages/cli/src/ui/commands/mcpCommand.test.ts b/packages/cli/src/ui/commands/mcpCommand.test.ts index 0a8d8306..f23cf3ab 100644 --- a/packages/cli/src/ui/commands/mcpCommand.test.ts +++ b/packages/cli/src/ui/commands/mcpCommand.test.ts @@ -63,6 +63,7 @@ describe('mcpCommand', () => { let mockConfig: { getToolRegistry: ReturnType<typeof vi.fn>; getMcpServers: ReturnType<typeof vi.fn>; + getBlockedMcpServers: ReturnType<typeof vi.fn>; }; beforeEach(() => { @@ -83,6 +84,7 @@ describe('mcpCommand', () => { getAllTools: vi.fn().mockReturnValue([]), }), getMcpServers: vi.fn().mockReturnValue({}), + getBlockedMcpServers: vi.fn().mockReturnValue([]), }; mockContext = createMockCommandContext({ @@ -419,6 +421,61 @@ describe('mcpCommand', () => { ); } }); + + it('should display the extension name for servers from extensions', async () => { + const mockMcpServers = { + server1: { command: 'cmd1', extensionName: 'my-extension' }, + }; + mockConfig.getMcpServers = vi.fn().mockReturnValue(mockMcpServers); + + const result = await mcpCommand.action!(mockContext, ''); + + expect(isMessageAction(result)).toBe(true); + if (isMessageAction(result)) { + const message = result.content; + expect(message).toContain('server1 (from my-extension)'); + } + }); + + it('should display blocked MCP servers', async () => { + mockConfig.getMcpServers = vi.fn().mockReturnValue({}); + const blockedServers = [ + { name: 'blocked-server', extensionName: 'my-extension' }, + ]; + mockConfig.getBlockedMcpServers = vi.fn().mockReturnValue(blockedServers); + + const result = await mcpCommand.action!(mockContext, ''); + + expect(isMessageAction(result)).toBe(true); + if (isMessageAction(result)) { + const message = result.content; + expect(message).toContain( + '🔴 \u001b[1mblocked-server (from my-extension)\u001b[0m - Blocked', + ); + } + }); + + it('should display both active and blocked servers correctly', async () => { + const mockMcpServers = { + server1: { command: 'cmd1', extensionName: 'my-extension' }, + }; + mockConfig.getMcpServers = vi.fn().mockReturnValue(mockMcpServers); + const blockedServers = [ + { name: 'blocked-server', extensionName: 'another-extension' }, + ]; + mockConfig.getBlockedMcpServers = vi.fn().mockReturnValue(blockedServers); + + const result = await mcpCommand.action!(mockContext, ''); + + expect(isMessageAction(result)).toBe(true); + if (isMessageAction(result)) { + const message = result.content; + expect(message).toContain('server1 (from my-extension)'); + expect(message).toContain( + '🔴 \u001b[1mblocked-server (from another-extension)\u001b[0m - Blocked', + ); + } + }); }); describe('schema functionality', () => { diff --git a/packages/cli/src/ui/commands/mcpCommand.ts b/packages/cli/src/ui/commands/mcpCommand.ts index 5ff77c4b..891227b0 100644 --- a/packages/cli/src/ui/commands/mcpCommand.ts +++ b/packages/cli/src/ui/commands/mcpCommand.ts @@ -49,8 +49,9 @@ const getMcpStatus = async ( const mcpServers = config.getMcpServers() || {}; const serverNames = Object.keys(mcpServers); + const blockedMcpServers = config.getBlockedMcpServers() || []; - if (serverNames.length === 0) { + if (serverNames.length === 0 && blockedMcpServers.length === 0) { const docsUrl = 'https://goo.gle/gemini-cli-docs-mcp'; if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') { return { @@ -118,9 +119,13 @@ const getMcpStatus = async ( // Get server description if available const server = mcpServers[serverName]; + let serverDisplayName = serverName; + if (server.extensionName) { + serverDisplayName += ` (from ${server.extensionName})`; + } // Format server header with bold formatting and status - message += `${statusIndicator} \u001b[1m${serverName}\u001b[0m - ${statusText}`; + message += `${statusIndicator} \u001b[1m${serverDisplayName}\u001b[0m - ${statusText}`; // Add tool count with conditional messaging if (status === MCPServerStatus.CONNECTED) { @@ -192,6 +197,14 @@ const getMcpStatus = async ( message += '\n'; } + for (const server of blockedMcpServers) { + let serverDisplayName = server.name; + if (server.extensionName) { + serverDisplayName += ` (from ${server.extensionName})`; + } + message += `🔴 \u001b[1m${serverDisplayName}\u001b[0m - Blocked\n\n`; + } + // Add helpful tips when no arguments are provided if (showTips) { message += '\n'; diff --git a/packages/cli/src/ui/components/ContextSummaryDisplay.tsx b/packages/cli/src/ui/components/ContextSummaryDisplay.tsx index 00a95e19..314e8ebd 100644 --- a/packages/cli/src/ui/components/ContextSummaryDisplay.tsx +++ b/packages/cli/src/ui/components/ContextSummaryDisplay.tsx @@ -13,6 +13,7 @@ interface ContextSummaryDisplayProps { geminiMdFileCount: number; contextFileNames: string[]; mcpServers?: Record<string, MCPServerConfig>; + blockedMcpServers?: Array<{ name: string; extensionName: string }>; showToolDescriptions?: boolean; } @@ -20,11 +21,17 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({ geminiMdFileCount, contextFileNames, mcpServers, + blockedMcpServers, showToolDescriptions, }) => { const mcpServerCount = Object.keys(mcpServers || {}).length; + const blockedMcpServerCount = blockedMcpServers?.length || 0; - if (geminiMdFileCount === 0 && mcpServerCount === 0) { + if ( + geminiMdFileCount === 0 && + mcpServerCount === 0 && + blockedMcpServerCount === 0 + ) { return <Text> </Text>; // Render an empty space to reserve height } @@ -39,10 +46,27 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({ }`; })(); - const mcpText = - mcpServerCount > 0 - ? `${mcpServerCount} MCP server${mcpServerCount > 1 ? 's' : ''}` - : ''; + const mcpText = (() => { + if (mcpServerCount === 0 && blockedMcpServerCount === 0) { + return ''; + } + + const parts = []; + if (mcpServerCount > 0) { + parts.push( + `${mcpServerCount} MCP server${mcpServerCount > 1 ? 's' : ''}`, + ); + } + + if (blockedMcpServerCount > 0) { + let blockedText = `${blockedMcpServerCount} blocked`; + if (mcpServerCount === 0) { + blockedText += ` MCP server${blockedMcpServerCount > 1 ? 's' : ''}`; + } + parts.push(blockedText); + } + return parts.join(', '); + })(); let summaryText = 'Using '; if (geminiMdText) { |
