From bc365f1eaa39c0414b4d70e600d733eb0867aec6 Mon Sep 17 00:00:00 2001 From: christine betts Date: Wed, 26 Nov 2025 13:08:47 -0500 Subject: [PATCH] Add support for MCP server instructions behind config option (#13432) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/tools/mcp-server.md | 2 + packages/cli/src/config/settingsSchema.ts | 5 ++ packages/core/src/config/config.ts | 2 + .../core/src/tools/mcp-client-manager.test.ts | 51 +++++++++++++++++++ packages/core/src/tools/mcp-client-manager.ts | 16 ++++++ packages/core/src/tools/mcp-client.ts | 4 ++ .../core/src/utils/memoryDiscovery.test.ts | 39 ++++++++++++++ packages/core/src/utils/memoryDiscovery.ts | 7 ++- schemas/settings.schema.json | 4 ++ 9 files changed, 129 insertions(+), 1 deletion(-) diff --git a/docs/tools/mcp-server.md b/docs/tools/mcp-server.md index 3436f7fc31..633d433700 100644 --- a/docs/tools/mcp-server.md +++ b/docs/tools/mcp-server.md @@ -156,6 +156,8 @@ Each server configuration supports the following properties: - **`targetServiceAccount`** (string): The email address of the Google Cloud Service Account to impersonate. Used with `authProviderType: 'service_account_impersonation'`. +- **`useInstructions`** (boolean): If true will include the MCP server's + initialization instructions in the system instructions. ### OAuth Support for Remote MCP Servers diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index b2b1abcc4b..56d9333c2f 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -1516,6 +1516,11 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record< description: 'Service account email to impersonate (name@project.iam.gserviceaccount.com).', }, + useInstructions: { + type: 'boolean', + description: + 'If true, instructions from this server will be included in the system prompt.', + }, }, }, TelemetrySettings: { diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index fb767e666d..cc8e316c77 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -203,6 +203,8 @@ export class MCPServerConfig { readonly targetAudience?: string, /* targetServiceAccount format: @.iam.gserviceaccount.com */ readonly targetServiceAccount?: string, + // Include the MCP server initialization instructions in the system instructions + readonly useInstructions?: boolean, ) {} } diff --git a/packages/core/src/tools/mcp-client-manager.test.ts b/packages/core/src/tools/mcp-client-manager.test.ts index 0cfddb61ff..01039edc56 100644 --- a/packages/core/src/tools/mcp-client-manager.test.ts +++ b/packages/core/src/tools/mcp-client-manager.test.ts @@ -193,4 +193,55 @@ describe('McpClientManager', () => { ); }); }); + + describe('getMcpInstructions', () => { + it('should only return instructions from servers with useInstructions: true', async () => { + // Override McpClient mock for this test to return distinct objects based on config + vi.mocked(McpClient).mockImplementation( + (name, config) => + ({ + connect: vi.fn(), + discover: vi.fn(), + disconnect: vi.fn(), + getServerConfig: vi.fn().mockReturnValue(config), + getInstructions: vi + .fn() + .mockReturnValue(`Instructions for ${name}`), + }) as unknown as McpClient, + ); + + const manager = new McpClientManager({} as ToolRegistry, mockConfig); + + // 1. Configured server with useInstructions: true + mockConfig.getMcpServers.mockReturnValue({ + 'enabled-server': { + useInstructions: true, + }, + 'disabled-server': { + useInstructions: false, + }, + 'default-server': { + // undefined should be treated as false + }, + }); + await manager.startConfiguredMcpServers(); + + const instructions = manager.getMcpInstructions(); + + expect(instructions).toContain( + "# Instructions for MCP Server 'enabled-server'", + ); + expect(instructions).toContain('Instructions for enabled-server'); + + expect(instructions).not.toContain( + "# Instructions for MCP Server 'disabled-server'", + ); + expect(instructions).not.toContain('Instructions for disabled-server'); + + expect(instructions).not.toContain( + "# Instructions for MCP Server 'default-server'", + ); + expect(instructions).not.toContain('Instructions for default-server'); + }); + }); }); diff --git a/packages/core/src/tools/mcp-client-manager.ts b/packages/core/src/tools/mcp-client-manager.ts index aaf5fedac6..1d7ca2f5d6 100644 --- a/packages/core/src/tools/mcp-client-manager.ts +++ b/packages/core/src/tools/mcp-client-manager.ts @@ -323,4 +323,20 @@ export class McpClientManager { } return mcpServers; } + + getMcpInstructions(): string { + const instructions: string[] = []; + for (const [name, client] of this.clients) { + // Only include instructions if explicitly enabled in config + if (client.getServerConfig().useInstructions) { + const clientInstructions = client.getInstructions(); + if (clientInstructions) { + instructions.push( + `# Instructions for MCP Server '${name}'\n${clientInstructions}`, + ); + } + } + } + return instructions.join('\n\n'); + } } diff --git a/packages/core/src/tools/mcp-client.ts b/packages/core/src/tools/mcp-client.ts index f484d73b21..0968dc8702 100644 --- a/packages/core/src/tools/mcp-client.ts +++ b/packages/core/src/tools/mcp-client.ts @@ -218,6 +218,10 @@ export class McpClient { getServerConfig(): MCPServerConfig { return this.serverConfig; } + + getInstructions(): string | undefined { + return this.client?.getInstructions(); + } } /** diff --git a/packages/core/src/utils/memoryDiscovery.test.ts b/packages/core/src/utils/memoryDiscovery.test.ts index 0804561fac..8196300adf 100644 --- a/packages/core/src/utils/memoryDiscovery.test.ts +++ b/packages/core/src/utils/memoryDiscovery.test.ts @@ -934,4 +934,43 @@ included directory memory expect(config.getGeminiMdFilePaths()).equals(refreshResult.filePaths); expect(mockEventListener).toHaveBeenCalledExactlyOnceWith(refreshResult); }); + + it('should include MCP instructions in user memory', async () => { + const mockConfig = { + getWorkingDir: vi.fn().mockReturnValue(cwd), + shouldLoadMemoryFromIncludeDirectories: vi.fn().mockReturnValue(false), + getDebugMode: vi.fn().mockReturnValue(false), + getFileService: vi + .fn() + .mockReturnValue(new FileDiscoveryService(projectRoot)), + getExtensionLoader: vi + .fn() + .mockReturnValue(new SimpleExtensionLoader([])), + isTrustedFolder: vi.fn().mockReturnValue(true), + getImportFormat: vi.fn().mockReturnValue('tree'), + getFileFilteringOptions: vi.fn().mockReturnValue(undefined), + getDiscoveryMaxDirs: vi.fn().mockReturnValue(200), + setUserMemory: vi.fn(), + setGeminiMdFileCount: vi.fn(), + setGeminiMdFilePaths: vi.fn(), + getMcpClientManager: vi.fn().mockReturnValue({ + getMcpInstructions: vi + .fn() + .mockReturnValue( + "\n\n# Instructions for MCP Server 'extension-server'\nAlways be polite.", + ), + }), + } as unknown as Config; + + await refreshServerHierarchicalMemory(mockConfig); + + expect(mockConfig.setUserMemory).toHaveBeenCalledWith( + expect.stringContaining( + "# Instructions for MCP Server 'extension-server'", + ), + ); + expect(mockConfig.setUserMemory).toHaveBeenCalledWith( + expect.stringContaining('Always be polite.'), + ); + }); }); diff --git a/packages/core/src/utils/memoryDiscovery.ts b/packages/core/src/utils/memoryDiscovery.ts index 6a74abc8a6..55b2354d9c 100644 --- a/packages/core/src/utils/memoryDiscovery.ts +++ b/packages/core/src/utils/memoryDiscovery.ts @@ -569,7 +569,12 @@ export async function refreshServerHierarchicalMemory(config: Config) { config.getFileFilteringOptions(), config.getDiscoveryMaxDirs(), ); - config.setUserMemory(result.memoryContent); + const mcpInstructions = + config.getMcpClientManager()?.getMcpInstructions() || ''; + const finalMemory = [result.memoryContent, mcpInstructions.trimStart()] + .filter(Boolean) + .join('\n\n'); + config.setUserMemory(finalMemory); config.setGeminiMdFileCount(result.fileCount); config.setGeminiMdFilePaths(result.filePaths); coreEvents.emit(CoreEvent.MemoryChanged, result); diff --git a/schemas/settings.schema.json b/schemas/settings.schema.json index 5974be4fe9..0ebe3e3a33 100644 --- a/schemas/settings.schema.json +++ b/schemas/settings.schema.json @@ -1460,6 +1460,10 @@ "targetServiceAccount": { "type": "string", "description": "Service account email to impersonate (name@project.iam.gserviceaccount.com)." + }, + "useInstructions": { + "type": "boolean", + "description": "If true, instructions from this server will be included in the system prompt." } } },