summaryrefslogtreecommitdiff
path: root/packages/cli/src/commands/mcp/add.ts
diff options
context:
space:
mode:
authorJack Wotherspoon <[email protected]>2025-08-06 11:52:29 -0400
committerGitHub <[email protected]>2025-08-06 15:52:29 +0000
commitca4c745e3b620e3ac4eca24b610cc7b936c0a50d (patch)
treeeb1b9cfb46bd4fa2e7f9631828b0704df1050eb7 /packages/cli/src/commands/mcp/add.ts
parentb38f377c9a2672e88dd15119b38d908c0f00b54a (diff)
feat(mcp): add `gemini mcp` commands for `add`, `remove` and `list` (#5481)
Diffstat (limited to 'packages/cli/src/commands/mcp/add.ts')
-rw-r--r--packages/cli/src/commands/mcp/add.ts211
1 files changed, 211 insertions, 0 deletions
diff --git a/packages/cli/src/commands/mcp/add.ts b/packages/cli/src/commands/mcp/add.ts
new file mode 100644
index 00000000..9537e131
--- /dev/null
+++ b/packages/cli/src/commands/mcp/add.ts
@@ -0,0 +1,211 @@
+/**
+ * @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<string | number> | 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<MCPServerConfig> = {};
+
+ 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<string, string>,
+ );
+
+ 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<string, string>,
+ ),
+ 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 <name> <commandOrUrl> [args...]',
+ describe: 'Add a server',
+ builder: (yargs) =>
+ yargs
+ .usage('Usage: gemini mcp add [options] <name> <commandOrUrl> [args...]')
+ .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,
+ }),
+ handler: async (argv) => {
+ await addMcpServer(
+ argv.name as string,
+ argv.commandOrUrl as string,
+ argv.args as Array<string | number>,
+ {
+ 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,
+ },
+ );
+ },
+};