/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ // File for 'gemini mcp add' command import type { CommandModule } from 'yargs'; import { loadSettings, SettingScope } from '../../config/settings.js'; import { MCPServerConfig } from '@google/gemini-cli-core'; async function addMcpServer( name: string, commandOrUrl: string, args: Array | undefined, options: { scope: string; transport: string; env: string[] | undefined; header: string[] | undefined; timeout?: number; trust?: boolean; description?: string; includeTools?: string[]; excludeTools?: string[]; }, ) { const { scope, transport, env, header, timeout, trust, description, includeTools, excludeTools, } = options; const settingsScope = scope === 'user' ? SettingScope.User : SettingScope.Workspace; const settings = loadSettings(process.cwd()); let newServer: Partial = {}; const headers = header?.reduce( (acc, curr) => { const [key, ...valueParts] = curr.split(':'); const value = valueParts.join(':').trim(); if (key.trim() && value) { acc[key.trim()] = value; } return acc; }, {} as Record, ); switch (transport) { case 'sse': newServer = { url: commandOrUrl, headers, timeout, trust, description, includeTools, excludeTools, }; break; case 'http': newServer = { httpUrl: commandOrUrl, headers, timeout, trust, description, includeTools, excludeTools, }; break; case 'stdio': default: newServer = { command: commandOrUrl, args: args?.map(String), env: env?.reduce( (acc, curr) => { const [key, value] = curr.split('='); if (key && value) { acc[key] = value; } return acc; }, {} as Record, ), timeout, trust, description, includeTools, excludeTools, }; break; } const existingSettings = settings.forScope(settingsScope).settings; const mcpServers = existingSettings.mcpServers || {}; const isExistingServer = !!mcpServers[name]; if (isExistingServer) { console.log( `MCP server "${name}" is already configured within ${scope} settings.`, ); } mcpServers[name] = newServer as MCPServerConfig; settings.setValue(settingsScope, 'mcpServers', mcpServers); if (isExistingServer) { console.log(`MCP server "${name}" updated in ${scope} settings.`); } else { console.log( `MCP server "${name}" added to ${scope} settings. (${transport})`, ); } } export const addCommand: CommandModule = { command: 'add [args...]', describe: 'Add a server', builder: (yargs) => yargs .usage('Usage: gemini mcp add [options] [args...]') .parserConfiguration({ 'unknown-options-as-args': true, // Pass unknown options as server args 'populate--': true, // Populate server args after -- separator }) .positional('name', { describe: 'Name of the server', type: 'string', demandOption: true, }) .positional('commandOrUrl', { describe: 'Command (stdio) or URL (sse, http)', type: 'string', demandOption: true, }) .option('scope', { alias: 's', describe: 'Configuration scope (user or project)', type: 'string', default: 'project', choices: ['user', 'project'], }) .option('transport', { alias: 't', describe: 'Transport type (stdio, sse, http)', type: 'string', default: 'stdio', choices: ['stdio', 'sse', 'http'], }) .option('env', { alias: 'e', describe: 'Set environment variables (e.g. -e KEY=value)', type: 'array', string: true, }) .option('header', { alias: 'H', describe: 'Set HTTP headers for SSE and HTTP transports (e.g. -H "X-Api-Key: abc123" -H "Authorization: Bearer abc123")', type: 'array', string: true, }) .option('timeout', { describe: 'Set connection timeout in milliseconds', type: 'number', }) .option('trust', { describe: 'Trust the server (bypass all tool call confirmation prompts)', type: 'boolean', }) .option('description', { describe: 'Set the description for the server', type: 'string', }) .option('include-tools', { describe: 'A comma-separated list of tools to include', type: 'array', string: true, }) .option('exclude-tools', { describe: 'A comma-separated list of tools to exclude', type: 'array', string: true, }) .middleware((argv) => { // Handle -- separator args as server args if present if (argv['--']) { const existingArgs = (argv['args'] as Array) || []; argv['args'] = [...existingArgs, ...(argv['--'] as string[])]; } }), handler: async (argv) => { await addMcpServer( argv['name'] as string, argv['commandOrUrl'] as string, argv['args'] as Array, { scope: argv['scope'] as string, transport: argv['transport'] as string, env: argv['env'] as string[], header: argv['header'] as string[], timeout: argv['timeout'] as number | undefined, trust: argv['trust'] as boolean | undefined, description: argv['description'] as string | undefined, includeTools: argv['includeTools'] as string[] | undefined, excludeTools: argv['excludeTools'] as string[] | undefined, }, ); }, };