From 0152759dfbd409213da38648d35157bbf3686457 Mon Sep 17 00:00:00 2001 From: christine betts Date: Fri, 19 Sep 2025 21:15:40 -0400 Subject: [PATCH] Filter out the 'trust' attribute from extension MCP server configs (#8809) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/extension.md | 3 ++- packages/cli/src/config/extension.test.ts | 20 ++++++++++++++++++++ packages/cli/src/config/extension.ts | 15 +++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/extension.md b/docs/extension.md index 714fa6dd70..00f65d553a 100644 --- a/docs/extension.md +++ b/docs/extension.md @@ -107,8 +107,9 @@ The `gemini-extension.json` file contains the configuration for the extension. T - `name`: The name of the extension. This is used to uniquely identify the extension and for conflict resolution when extension commands have the same name as user or project commands. The name should be lowercase and use dashes instead of underscores or spaces. This is how users will refer to your extension in the CLI. Note that we expect this name to match the extension directory name. - `version`: The version of the extension. - `mcpServers`: A map of MCP servers to configure. The key is the name of the server, and the value is the server configuration. These servers will be loaded on startup just like MCP servers configured in a [`settings.json` file](./cli/configuration.md). If both an extension and a `settings.json` file configure an MCP server with the same name, the server defined in the `settings.json` file takes precedence. + - Note that all MCP server configuration options are supported except for `trust`. - `contextFileName`: The name of the file that contains the context for the extension. This will be used to load the context from the extension directory. If this property is not used but a `GEMINI.md` file is present in your extension directory, then that file will be loaded. -- `excludeTools`: An array of tool names to exclude from the model. You can also specify command-specific restrictions for tools that support it, like the `run_shell_command` tool. For example, `"excludeTools": ["run_shell_command(rm -rf)"]` will block the `rm -rf` command. +- `excludeTools`: An array of tool names to exclude from the model. You can also specify command-specific restrictions for tools that support it, like the `run_shell_command` tool. For example, `"excludeTools": ["run_shell_command(rm -rf)"]` will block the `rm -rf` command. Note that this differs from the MCP server `excludeTools` functionality, which can be listed in the MCP server config. When Gemini CLI starts, it loads all the extensions and merges their configurations. If there are any conflicts, the workspace configuration takes precedence. diff --git a/packages/cli/src/config/extension.test.ts b/packages/cli/src/config/extension.test.ts index a7e32fe8b0..1de6eadd6a 100644 --- a/packages/cli/src/config/extension.test.ts +++ b/packages/cli/src/config/extension.test.ts @@ -372,6 +372,26 @@ describe('extension tests', () => { expect(serverConfig.env!.MISSING_VAR).toBe('$UNDEFINED_ENV_VAR'); expect(serverConfig.env!.MISSING_VAR_BRACES).toBe('${ALSO_UNDEFINED}'); }); + + it('should filter trust out of mcp servers', () => { + createExtension({ + extensionsDir: userExtensionsDir, + name: 'test-extension', + version: '1.0.0', + mcpServers: { + 'test-server': { + command: 'node', + args: ['server.js'], + trust: true, + }, + }, + }); + + const extensions = loadExtensions(); + expect(extensions).toHaveLength(1); + const loadedConfig = extensions[0].config; + expect(loadedConfig.mcpServers?.['test-server'].trust).toBeUndefined(); + }); }); describe('annotateActiveExtensions', () => { diff --git a/packages/cli/src/config/extension.ts b/packages/cli/src/config/extension.ts index 282cf62b62..857c034db0 100644 --- a/packages/cli/src/config/extension.ts +++ b/packages/cli/src/config/extension.ts @@ -239,6 +239,15 @@ export function loadExtension(context: LoadExtensionContext): Extension | null { config = resolveEnvVarsInObject(config); + if (config.mcpServers) { + config.mcpServers = Object.fromEntries( + Object.entries(config.mcpServers).map(([key, value]) => [ + key, + filterMcpConfig(value), + ]), + ); + } + const contextFiles = getContextFileNames(config) .map((contextFileName) => path.join(effectiveExtensionPath, contextFileName), @@ -261,6 +270,12 @@ export function loadExtension(context: LoadExtensionContext): Extension | null { } } +function filterMcpConfig(original: MCPServerConfig): MCPServerConfig { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { trust, ...rest } = original; + return Object.freeze(rest); +} + export function loadInstallMetadata( extensionDir: string, ): ExtensionInstallMetadata | undefined {