* 执行 tools/call: * 先解析工具名,再按需唤起插件,最后调用 preload 中注册的 handler。
(params?: Record<string, unknown>)
| 364 | * 先解析工具名,再按需唤起插件,最后调用 preload 中注册的 handler。 |
| 365 | */ |
| 366 | private async handleToolCall(params?: Record<string, unknown>): Promise<unknown> { |
| 367 | const toolName = typeof params?.name === 'string' ? params.name : '' |
| 368 | if (!toolName) { |
| 369 | throw new McpProtocolError(-32602, 'tools/call 缺少 name 参数') |
| 370 | } |
| 371 | |
| 372 | const toolEntry = pluginToolsAPI |
| 373 | .getAllDeclaredToolEntries({ includeDisabled: false }) |
| 374 | .find((tool) => tool.mcpName === toolName) |
| 375 | if (!toolEntry) { |
| 376 | throw new McpProtocolError(-32602, `未找到工具: ${toolName}`) |
| 377 | } |
| 378 | |
| 379 | const webContents = await pluginToolsAPI.ensurePluginToolReady( |
| 380 | toolEntry.pluginPath, |
| 381 | toolEntry.toolName |
| 382 | ) |
| 383 | if (!webContents) { |
| 384 | throw new McpProtocolError(-32000, `插件 "${toolEntry.pluginName}" 未能就绪`) |
| 385 | } |
| 386 | |
| 387 | const result = await pluginToolsAPI.executeRegisteredTool( |
| 388 | webContents, |
| 389 | toolEntry.toolName, |
| 390 | params?.arguments ?? {} |
| 391 | ) |
| 392 | |
| 393 | // 插件可直接返回 MCP 标准 content 数组(支持 text/image 等多模态内容) |
| 394 | if (result && typeof result === 'object' && Array.isArray((result as any).content)) { |
| 395 | return { content: (result as any).content } |
| 396 | } |
| 397 | |
| 398 | return { |
| 399 | // 同时返回文本结果和结构化结果,方便标准 MCP 客户端和调试工具消费。 |
| 400 | content: [ |
| 401 | { |
| 402 | type: 'text', |
| 403 | text: this.stringifyToolResult(result) |
| 404 | } |
| 405 | ], |
| 406 | ...(result !== undefined ? { structuredContent: result } : {}) |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | /** |
| 411 | * 将任意工具返回值转成 MCP 文本内容,便于通用客户端展示。 |
no test coverage detected