diff options
| author | Brian Ray <[email protected]> | 2025-07-15 22:35:05 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-16 02:35:05 +0000 |
| commit | 0903421b1a1c5b43f4271e779aafa440ffec595c (patch) | |
| tree | 00c289e3cb67076093e1c5523bcff892301be359 /packages/cli/src/ui/commands/mcpCommand.ts | |
| parent | b72e3dfb43acc3faf8099fa758a4283b83f32ff6 (diff) | |
Move MCP slash command to new system (#3678)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Abhi <[email protected]>
Diffstat (limited to 'packages/cli/src/ui/commands/mcpCommand.ts')
| -rw-r--r-- | packages/cli/src/ui/commands/mcpCommand.ts | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/packages/cli/src/ui/commands/mcpCommand.ts b/packages/cli/src/ui/commands/mcpCommand.ts new file mode 100644 index 00000000..fc266362 --- /dev/null +++ b/packages/cli/src/ui/commands/mcpCommand.ts @@ -0,0 +1,236 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + SlashCommand, + SlashCommandActionReturn, + CommandContext, +} from './types.js'; +import { + DiscoveredMCPTool, + getMCPDiscoveryState, + getMCPServerStatus, + MCPDiscoveryState, + MCPServerStatus, +} from '@google/gemini-cli-core'; +import open from 'open'; + +const COLOR_GREEN = '\u001b[32m'; +const COLOR_YELLOW = '\u001b[33m'; +const COLOR_CYAN = '\u001b[36m'; +const RESET_COLOR = '\u001b[0m'; + +const getMcpStatus = async ( + context: CommandContext, + showDescriptions: boolean, + showSchema: boolean, + showTips: boolean = false, +): Promise<SlashCommandActionReturn> => { + const { config } = context.services; + if (!config) { + return { + type: 'message', + messageType: 'error', + content: 'Config not loaded.', + }; + } + + const toolRegistry = await config.getToolRegistry(); + if (!toolRegistry) { + return { + type: 'message', + messageType: 'error', + content: 'Could not retrieve tool registry.', + }; + } + + const mcpServers = config.getMcpServers() || {}; + const serverNames = Object.keys(mcpServers); + + if (serverNames.length === 0) { + const docsUrl = 'https://goo.gle/gemini-cli-docs-mcp'; + if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') { + return { + type: 'message', + messageType: 'info', + content: `No MCP servers configured. Please open the following URL in your browser to view documentation:\n${docsUrl}`, + }; + } else { + // Open the URL in the browser + await open(docsUrl); + return { + type: 'message', + messageType: 'info', + content: `No MCP servers configured. Opening documentation in your browser: ${docsUrl}`, + }; + } + } + + // Check if any servers are still connecting + const connectingServers = serverNames.filter( + (name) => getMCPServerStatus(name) === MCPServerStatus.CONNECTING, + ); + const discoveryState = getMCPDiscoveryState(); + + let message = ''; + + // Add overall discovery status message if needed + if ( + discoveryState === MCPDiscoveryState.IN_PROGRESS || + connectingServers.length > 0 + ) { + message += `${COLOR_YELLOW}⏳ MCP servers are starting up (${connectingServers.length} initializing)...${RESET_COLOR}\n`; + message += `${COLOR_CYAN}Note: First startup may take longer. Tool availability will update automatically.${RESET_COLOR}\n\n`; + } + + message += 'Configured MCP servers:\n\n'; + + const allTools = toolRegistry.getAllTools(); + for (const serverName of serverNames) { + const serverTools = allTools.filter( + (tool) => + tool instanceof DiscoveredMCPTool && tool.serverName === serverName, + ) as DiscoveredMCPTool[]; + + const status = getMCPServerStatus(serverName); + + // Add status indicator with descriptive text + let statusIndicator = ''; + let statusText = ''; + switch (status) { + case MCPServerStatus.CONNECTED: + statusIndicator = '🟢'; + statusText = 'Ready'; + break; + case MCPServerStatus.CONNECTING: + statusIndicator = '🔄'; + statusText = 'Starting... (first startup may take longer)'; + break; + case MCPServerStatus.DISCONNECTED: + default: + statusIndicator = '🔴'; + statusText = 'Disconnected'; + break; + } + + // Get server description if available + const server = mcpServers[serverName]; + + // Format server header with bold formatting and status + message += `${statusIndicator} \u001b[1m${serverName}\u001b[0m - ${statusText}`; + + // Add tool count with conditional messaging + if (status === MCPServerStatus.CONNECTED) { + message += ` (${serverTools.length} tools)`; + } else if (status === MCPServerStatus.CONNECTING) { + message += ` (tools will appear when ready)`; + } else { + message += ` (${serverTools.length} tools cached)`; + } + + // Add server description with proper handling of multi-line descriptions + if (showDescriptions && server?.description) { + const descLines = server.description.trim().split('\n'); + if (descLines) { + message += ':\n'; + for (const descLine of descLines) { + message += ` ${COLOR_GREEN}${descLine}${RESET_COLOR}\n`; + } + } else { + message += '\n'; + } + } else { + message += '\n'; + } + + // Reset formatting after server entry + message += RESET_COLOR; + + if (serverTools.length > 0) { + serverTools.forEach((tool) => { + if (showDescriptions && tool.description) { + // Format tool name in cyan using simple ANSI cyan color + message += ` - ${COLOR_CYAN}${tool.name}${RESET_COLOR}`; + + // Handle multi-line descriptions by properly indenting and preserving formatting + const descLines = tool.description.trim().split('\n'); + if (descLines) { + message += ':\n'; + for (const descLine of descLines) { + message += ` ${COLOR_GREEN}${descLine}${RESET_COLOR}\n`; + } + } else { + message += '\n'; + } + // Reset is handled inline with each line now + } else { + // Use cyan color for the tool name even when not showing descriptions + message += ` - ${COLOR_CYAN}${tool.name}${RESET_COLOR}\n`; + } + if (showSchema && tool.parameterSchema) { + // Prefix the parameters in cyan + message += ` ${COLOR_CYAN}Parameters:${RESET_COLOR}\n`; + + const paramsLines = JSON.stringify(tool.parameterSchema, null, 2) + .trim() + .split('\n'); + if (paramsLines) { + for (const paramsLine of paramsLines) { + message += ` ${COLOR_GREEN}${paramsLine}${RESET_COLOR}\n`; + } + } + } + }); + } else { + message += ' No tools available\n'; + } + message += '\n'; + } + + // Add helpful tips when no arguments are provided + if (showTips) { + message += '\n'; + message += `${COLOR_CYAN}💡 Tips:${RESET_COLOR}\n`; + message += ` • Use ${COLOR_CYAN}/mcp desc${RESET_COLOR} to show server and tool descriptions\n`; + message += ` • Use ${COLOR_CYAN}/mcp schema${RESET_COLOR} to show tool parameter schemas\n`; + message += ` • Use ${COLOR_CYAN}/mcp nodesc${RESET_COLOR} to hide descriptions\n`; + message += ` • Press ${COLOR_CYAN}Ctrl+T${RESET_COLOR} to toggle tool descriptions on/off\n`; + message += '\n'; + } + + // Make sure to reset any ANSI formatting at the end to prevent it from affecting the terminal + message += RESET_COLOR; + + return { + type: 'message', + messageType: 'info', + content: message, + }; +}; + +export const mcpCommand: SlashCommand = { + name: 'mcp', + description: 'list configured MCP servers and tools', + action: async (context: CommandContext, args: string) => { + const lowerCaseArgs = args.toLowerCase().split(/\s+/).filter(Boolean); + + const hasDesc = + lowerCaseArgs.includes('desc') || lowerCaseArgs.includes('descriptions'); + const hasNodesc = + lowerCaseArgs.includes('nodesc') || + lowerCaseArgs.includes('nodescriptions'); + const showSchema = lowerCaseArgs.includes('schema'); + + // Show descriptions if `desc` or `schema` is present, + // but `nodesc` takes precedence and disables them. + const showDescriptions = !hasNodesc && (hasDesc || showSchema); + + // Show tips only when no arguments are provided + const showTips = lowerCaseArgs.length === 0; + + return getMcpStatus(context, showDescriptions, showSchema, showTips); + }, +}; |
