Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/core/src/tools/mcp-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { AuthProviderType, isSdkMcpServerConfig } from '../config/config.js';
import { GoogleCredentialProvider } from '../mcp/google-auth-provider.js';
import { ServiceAccountImpersonationProvider } from '../mcp/sa-impersonation-provider.js';
import { DiscoveredMCPTool } from './mcp-tool.js';
import type { McpToolAnnotations } from './mcp-tool.js';
import { SdkControlClientTransport } from './sdk-control-client-transport.js';

import type { FunctionDeclaration } from '@google/genai';
Expand Down Expand Up @@ -638,6 +639,23 @@ export async function discoverTools(
return [];
}

// Fetch raw tool list from MCP client to get annotations (readOnlyHint, etc.)
// that are not preserved by mcpToTool's functionDeclarations conversion.
const annotationsMap = new Map<string, McpToolAnnotations>();
try {
const listToolsResult = await mcpClient.listTools();
for (const mcpTool of listToolsResult.tools) {
if (mcpTool.annotations) {
annotationsMap.set(mcpTool.name, mcpTool.annotations);
}
}
} catch {
// If listTools fails, proceed without annotations — non-critical
debugLogger.error(
`Failed to fetch tool annotations from MCP server '${mcpServerName}'`,
);
}

const mcpTimeout = mcpServerConfig.timeout ?? MCP_DEFAULT_TIMEOUT_MSEC;
const discoveredTools: DiscoveredMCPTool[] = [];
for (const funcDecl of tool.functionDeclarations) {
Expand All @@ -658,6 +676,7 @@ export async function discoverTools(
cliConfig,
mcpClient, // raw MCP Client for direct callTool with progress
mcpTimeout,
annotationsMap.get(funcDecl.name!),
),
);
} catch (error) {
Expand Down
24 changes: 23 additions & 1 deletion packages/core/src/tools/mcp-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ type McpContentBlock =
| McpResourceBlock
| McpResourceLinkBlock;

/**
* MCP Tool Annotations as defined in the MCP specification.
* These provide hints about a tool's behavior to help clients make decisions
* about tool approval and safety.
*/
export interface McpToolAnnotations {
readOnlyHint?: boolean;
destructiveHint?: boolean;
idempotentHint?: boolean;
openWorldHint?: boolean;
}

class DiscoveredMCPToolInvocation extends BaseToolInvocation<
ToolParams,
ToolResult
Expand All @@ -110,6 +122,7 @@ class DiscoveredMCPToolInvocation extends BaseToolInvocation<
private readonly cliConfig?: Config,
private readonly mcpClient?: McpDirectClient,
private readonly mcpTimeout?: number,
private readonly annotations?: McpToolAnnotations,
) {
super(params);
}
Expand All @@ -124,6 +137,12 @@ class DiscoveredMCPToolInvocation extends BaseToolInvocation<
return false; // server is trusted, no confirmation needed
}

// MCP tools annotated with readOnlyHint: true are safe to execute
// without confirmation, especially important for plan mode support
if (this.annotations?.readOnlyHint === true) {
return false;
}

if (
DiscoveredMCPToolInvocation.allowlist.has(serverAllowListKey) ||
DiscoveredMCPToolInvocation.allowlist.has(toolAllowListKey)
Expand Down Expand Up @@ -341,13 +360,14 @@ export class DiscoveredMCPTool extends BaseDeclarativeTool<
private readonly cliConfig?: Config,
private readonly mcpClient?: McpDirectClient,
private readonly mcpTimeout?: number,
private readonly annotations?: McpToolAnnotations,
) {
super(
nameOverride ??
generateValidName(`mcp__${serverName}__${serverToolName}`),
`${serverToolName} (${serverName} MCP Server)`,
description,
Kind.Other,
annotations?.readOnlyHint === true ? Kind.Read : Kind.Other,
parameterSchema,
true, // isOutputMarkdown
true, // canUpdateOutput — enables streaming progress for MCP tools
Expand All @@ -366,6 +386,7 @@ export class DiscoveredMCPTool extends BaseDeclarativeTool<
this.cliConfig,
this.mcpClient,
this.mcpTimeout,
this.annotations,
);
}

Expand All @@ -382,6 +403,7 @@ export class DiscoveredMCPTool extends BaseDeclarativeTool<
this.cliConfig,
this.mcpClient,
this.mcpTimeout,
this.annotations,
);
}
}
Expand Down