mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-24 22:55:13 +00:00
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>
This commit is contained in:
@@ -156,6 +156,8 @@ Each server configuration supports the following properties:
|
|||||||
- **`targetServiceAccount`** (string): The email address of the Google Cloud
|
- **`targetServiceAccount`** (string): The email address of the Google Cloud
|
||||||
Service Account to impersonate. Used with
|
Service Account to impersonate. Used with
|
||||||
`authProviderType: 'service_account_impersonation'`.
|
`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
|
### OAuth Support for Remote MCP Servers
|
||||||
|
|
||||||
|
|||||||
@@ -1516,6 +1516,11 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
|
|||||||
description:
|
description:
|
||||||
'Service account email to impersonate (name@project.iam.gserviceaccount.com).',
|
'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: {
|
TelemetrySettings: {
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ export class MCPServerConfig {
|
|||||||
readonly targetAudience?: string,
|
readonly targetAudience?: string,
|
||||||
/* targetServiceAccount format: <service-account-name>@<project-num>.iam.gserviceaccount.com */
|
/* targetServiceAccount format: <service-account-name>@<project-num>.iam.gserviceaccount.com */
|
||||||
readonly targetServiceAccount?: string,
|
readonly targetServiceAccount?: string,
|
||||||
|
// Include the MCP server initialization instructions in the system instructions
|
||||||
|
readonly useInstructions?: boolean,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -323,4 +323,20 @@ export class McpClientManager {
|
|||||||
}
|
}
|
||||||
return mcpServers;
|
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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,6 +218,10 @@ export class McpClient {
|
|||||||
getServerConfig(): MCPServerConfig {
|
getServerConfig(): MCPServerConfig {
|
||||||
return this.serverConfig;
|
return this.serverConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getInstructions(): string | undefined {
|
||||||
|
return this.client?.getInstructions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -934,4 +934,43 @@ included directory memory
|
|||||||
expect(config.getGeminiMdFilePaths()).equals(refreshResult.filePaths);
|
expect(config.getGeminiMdFilePaths()).equals(refreshResult.filePaths);
|
||||||
expect(mockEventListener).toHaveBeenCalledExactlyOnceWith(refreshResult);
|
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.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -569,7 +569,12 @@ export async function refreshServerHierarchicalMemory(config: Config) {
|
|||||||
config.getFileFilteringOptions(),
|
config.getFileFilteringOptions(),
|
||||||
config.getDiscoveryMaxDirs(),
|
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.setGeminiMdFileCount(result.fileCount);
|
||||||
config.setGeminiMdFilePaths(result.filePaths);
|
config.setGeminiMdFilePaths(result.filePaths);
|
||||||
coreEvents.emit(CoreEvent.MemoryChanged, result);
|
coreEvents.emit(CoreEvent.MemoryChanged, result);
|
||||||
|
|||||||
@@ -1460,6 +1460,10 @@
|
|||||||
"targetServiceAccount": {
|
"targetServiceAccount": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Service account email to impersonate (name@project.iam.gserviceaccount.com)."
|
"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."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user