| | |
| | |
| | |
| | |
| | |
| |
|
| | import { |
| | BaseTool, |
| | ToolResult, |
| | ToolCallConfirmationDetails, |
| | ToolConfirmationOutcome, |
| | ToolMcpConfirmationDetails, |
| | } from './tools.js'; |
| | import { CallableTool, Part, FunctionCall } from '@google/genai'; |
| |
|
| | type ToolParams = Record<string, unknown>; |
| |
|
| | export class DiscoveredMCPTool extends BaseTool<ToolParams, ToolResult> { |
| | private static readonly allowlist: Set<string> = new Set(); |
| |
|
| | constructor( |
| | private readonly mcpTool: CallableTool, |
| | readonly serverName: string, |
| | readonly name: string, |
| | readonly description: string, |
| | readonly parameterSchema: Record<string, unknown>, |
| | readonly serverToolName: string, |
| | readonly timeout?: number, |
| | readonly trust?: boolean, |
| | ) { |
| | super( |
| | name, |
| | `${serverToolName} (${serverName} MCP Server)`, |
| | description, |
| | parameterSchema, |
| | true, |
| | false, |
| | ); |
| | } |
| |
|
| | async shouldConfirmExecute( |
| | _params: ToolParams, |
| | _abortSignal: AbortSignal, |
| | ): Promise<ToolCallConfirmationDetails | false> { |
| | const serverAllowListKey = this.serverName; |
| | const toolAllowListKey = `${this.serverName}.${this.serverToolName}`; |
| |
|
| | if (this.trust) { |
| | return false; |
| | } |
| |
|
| | if ( |
| | DiscoveredMCPTool.allowlist.has(serverAllowListKey) || |
| | DiscoveredMCPTool.allowlist.has(toolAllowListKey) |
| | ) { |
| | return false; |
| | } |
| |
|
| | const confirmationDetails: ToolMcpConfirmationDetails = { |
| | type: 'mcp', |
| | title: 'Confirm MCP Tool Execution', |
| | serverName: this.serverName, |
| | toolName: this.serverToolName, |
| | toolDisplayName: this.name, |
| | onConfirm: async (outcome: ToolConfirmationOutcome) => { |
| | if (outcome === ToolConfirmationOutcome.ProceedAlwaysServer) { |
| | DiscoveredMCPTool.allowlist.add(serverAllowListKey); |
| | } else if (outcome === ToolConfirmationOutcome.ProceedAlwaysTool) { |
| | DiscoveredMCPTool.allowlist.add(toolAllowListKey); |
| | } |
| | }, |
| | }; |
| | return confirmationDetails; |
| | } |
| |
|
| | async execute(params: ToolParams): Promise<ToolResult> { |
| | const functionCalls: FunctionCall[] = [ |
| | { |
| | name: this.serverToolName, |
| | args: params, |
| | }, |
| | ]; |
| |
|
| | const responseParts: Part[] = await this.mcpTool.callTool(functionCalls); |
| |
|
| | return { |
| | llmContent: responseParts, |
| | returnDisplay: getStringifiedResultForDisplay(responseParts), |
| | }; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | function getStringifiedResultForDisplay(result: Part[]) { |
| | if (!result || result.length === 0) { |
| | return '```json\n[]\n```'; |
| | } |
| |
|
| | const processFunctionResponse = (part: Part) => { |
| | if (part.functionResponse) { |
| | const responseContent = part.functionResponse.response?.content; |
| | if (responseContent && Array.isArray(responseContent)) { |
| | |
| | const allTextParts = responseContent.every( |
| | (p: Part) => p.text !== undefined, |
| | ); |
| | if (allTextParts) { |
| | return responseContent.map((p: Part) => p.text).join(''); |
| | } |
| | |
| | return responseContent; |
| | } |
| |
|
| | |
| | return part.functionResponse; |
| | } |
| | return part; |
| | }; |
| |
|
| | const processedResults = |
| | result.length === 1 |
| | ? processFunctionResponse(result[0]) |
| | : result.map(processFunctionResponse); |
| | if (typeof processedResults === 'string') { |
| | return processedResults; |
| | } |
| |
|
| | return '```json\n' + JSON.stringify(processedResults, null, 2) + '\n```'; |
| | } |
| |
|