summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui')
-rw-r--r--packages/cli/src/ui/App.test.tsx8
-rw-r--r--packages/cli/src/ui/App.tsx1
-rw-r--r--packages/cli/src/ui/commands/extensionsCommand.test.ts9
-rw-r--r--packages/cli/src/ui/commands/extensionsCommand.ts4
-rw-r--r--packages/cli/src/ui/commands/mcpCommand.test.ts57
-rw-r--r--packages/cli/src/ui/commands/mcpCommand.ts17
-rw-r--r--packages/cli/src/ui/components/ContextSummaryDisplay.tsx34
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) {