diff --git a/packages/core/src/services/shellExecutionService.ts b/packages/core/src/services/shellExecutionService.ts index 23ac63f772..96cae8c269 100644 --- a/packages/core/src/services/shellExecutionService.ts +++ b/packages/core/src/services/shellExecutionService.ts @@ -32,6 +32,18 @@ const { Terminal } = pkg; const MAX_CHILD_PROCESS_BUFFER_SIZE = 16 * 1024 * 1024; // 16MB +/** + * An environment variable that is set for shell executions. This can be used + * by downstream executables and scripts to identify that they were executed + * from within Gemini CLI. + */ +export const GEMINI_CLI_IDENTIFICATION_ENV_VAR = 'GEMINI_CLI'; + +/** + * The value of {@link GEMINI_CLI_IDENTIFICATION_ENV_VAR} + */ +export const GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE = '1'; + // We want to allow shell outputs that are close to the context window in size. // 300,000 lines is roughly equivalent to a large context window, ensuring // we capture significant output from long-running commands. @@ -302,7 +314,8 @@ export class ShellExecutionService { detached: !isWindows, env: { ...sanitizeEnvironment(process.env, sanitizationConfig), - GEMINI_CLI: '1', + [GEMINI_CLI_IDENTIFICATION_ENV_VAR]: + GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE, TERM: 'xterm-256color', PAGER: 'cat', GIT_PAGER: 'cat', diff --git a/packages/core/src/tools/mcp-client.test.ts b/packages/core/src/tools/mcp-client.test.ts index 39165bde45..3f289f1732 100644 --- a/packages/core/src/tools/mcp-client.test.ts +++ b/packages/core/src/tools/mcp-client.test.ts @@ -1639,6 +1639,28 @@ describe('mcp-client', () => { }); }); + it('sets an env variable GEMINI_CLI=1 for stdio MCP servers', async () => { + const mockedTransport = vi + .spyOn(SdkClientStdioLib, 'StdioClientTransport') + .mockReturnValue({} as SdkClientStdioLib.StdioClientTransport); + + await createTransport( + 'test-server', + { + command: 'test-command', + args: ['--foo', 'bar'], + env: {}, + cwd: 'test/cwd', + }, + false, + EMPTY_CONFIG, + ); + + const callArgs = mockedTransport.mock.calls[0][0]; + expect(callArgs.env).toBeDefined(); + expect(callArgs.env!['GEMINI_CLI']).toBe('1'); + }); + it('should exclude extension settings with undefined values from environment', async () => { const mockedTransport = vi .spyOn(SdkClientStdioLib, 'StdioClientTransport') diff --git a/packages/core/src/tools/mcp-client.ts b/packages/core/src/tools/mcp-client.ts index 2588d54dba..7902d8953a 100644 --- a/packages/core/src/tools/mcp-client.ts +++ b/packages/core/src/tools/mcp-client.ts @@ -67,6 +67,10 @@ import { sanitizeEnvironment, type EnvironmentSanitizationConfig, } from '../services/environmentSanitization.js'; +import { + GEMINI_CLI_IDENTIFICATION_ENV_VAR, + GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE, +} from '../services/shellExecutionService.js'; export const MCP_DEFAULT_TIMEOUT_MSEC = 10 * 60 * 1000; // default to 10 minutes @@ -1897,10 +1901,11 @@ export async function createTransport( let transport: Transport = new StdioClientTransport({ command: mcpServerConfig.command, args: mcpServerConfig.args || [], - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion env: { ...sanitizeEnvironment(process.env, sanitizationConfig), ...(mcpServerConfig.env || {}), + [GEMINI_CLI_IDENTIFICATION_ENV_VAR]: + GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE, } as Record, cwd: mcpServerConfig.cwd, stderr: 'pipe',