From 636a430eb9b36ca9f2d3e76e55ee088f9b3d4de8 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Mon, 4 Aug 2025 23:08:37 +0800 Subject: [PATCH] fix: better mcp tool match and feedback (#8825) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(mcp): 添加多工具匹配时的警告提示 当匹配到多个MCP工具或未匹配到所需工具时,显示相应的警告信息。同时在i18n中添加对应的翻译字段。 * feat(i18n): 添加MCP工具警告信息和模型列表刷新功能的多语言支持 * fix(mcp): 修复工具调用解析错误并优化日志消息 添加对工具调用解析错误的处理,当解析失败时显示错误信息 统一工具调用相关的日志消息格式,使用字符串插值替代拼接 * feat(i18n): 为MCP工具添加解析错误的多语言支持 --- src/renderer/src/i18n/locales/en-us.json | 9 ++++ src/renderer/src/i18n/locales/ja-jp.json | 9 ++++ src/renderer/src/i18n/locales/ru-ru.json | 9 ++++ src/renderer/src/i18n/locales/zh-cn.json | 9 ++++ src/renderer/src/i18n/locales/zh-tw.json | 9 ++++ src/renderer/src/i18n/translate/el-gr.json | 10 +++++ src/renderer/src/i18n/translate/es-es.json | 10 +++++ src/renderer/src/i18n/translate/fr-fr.json | 10 +++++ src/renderer/src/i18n/translate/pt-pt.json | 10 +++++ src/renderer/src/utils/mcp-tools.ts | 52 +++++++++++++++++----- 10 files changed, 126 insertions(+), 11 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 04cdec7246..87ddc434ec 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -383,6 +383,15 @@ "settings": "Web Search Settings" } }, + "mcp": { + "error": { + "parse_tool_call": "Unable to convert to a valid tool call format: {{toolCall}}" + }, + "warning": { + "multiple_tools": "Multiple matching MCP tools exist, {{tool}} has been selected", + "no_tool": "No matching MCP tool found for {{tool}}" + } + }, "message": { "new": { "branch": { diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 0ed513d6e3..10b0d27913 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -383,6 +383,15 @@ "settings": "ウェブ検索設定" } }, + "mcp": { + "error": { + "parse_tool_call": "有効なツール呼び出し形式に変換できません:{{toolCall}}" + }, + "warning": { + "multiple_tools": "複数の一致するMCPツールが存在するため、{{tool}} が選択されました", + "no_tool": "必要なMCPツール {{tool}} が見つかりません" + } + }, "message": { "new": { "branch": { diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 665a80a565..f30681c3b5 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -383,6 +383,15 @@ "settings": "Настройки веб-поиска" } }, + "mcp": { + "error": { + "parse_tool_call": "Не удалось преобразовать в действительный формат вызова инструмента: {{toolCall}}" + }, + "warning": { + "multiple_tools": "Существует несколько совпадающих инструментов MCP, выбран {{tool}}", + "no_tool": "Не удалось сопоставить требуемый инструмент MCP {{tool}}" + } + }, "message": { "new": { "branch": { diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 63174f25eb..4b056b0e2d 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -383,6 +383,15 @@ "settings": "网络搜索设置" } }, + "mcp": { + "error": { + "parse_tool_call": "无法转换为有效的工具调用格式:{{toolCall}}" + }, + "warning": { + "multiple_tools": "存在多个匹配的MCP工具,已选择 {{tool}}", + "no_tool": "未匹配到所需的MCP工具 {{tool}}" + } + }, "message": { "new": { "branch": { diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index e83594eea1..3429f92b24 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -383,6 +383,15 @@ "settings": "網路搜尋設定" } }, + "mcp": { + "error": { + "parse_tool_call": "無法轉換為有效的工具呼叫格式:{{toolCall}}" + }, + "warning": { + "multiple_tools": "存在多個匹配的MCP工具,已選擇 {{tool}}", + "no_tool": "未匹配到所需的MCP工具 {{tool}}" + } + }, "message": { "new": { "branch": { diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 91aae56868..e703e4f82d 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -383,6 +383,15 @@ "settings": "Ρυθμίσεις αναζήτησης στο διαδίκτυο" } }, + "mcp": { + "error": { + "parse_tool_call": "Δεν είναι δυνατή η μετατροπή σε έγκυρη μορφή κλήσης εργαλείου: {{toolCall}}" + }, + "warning": { + "multiple_tools": "Υπάρχουν πολλαπλά εργαλεία MCP που ταιριάζουν, επιλέχθηκε το {{tool}}", + "no_tool": "Δεν βρέθηκε το απαιτούμενο εργαλείο MCP {{tool}}" + } + }, "message": { "new": { "branch": { @@ -2991,6 +3000,7 @@ "label": "Προσθήκη μοντέλων από τη λίστα" }, "add_whole_group": "Προσθήκη ολόκληρης ομάδας", + "refetch_list": "Επαναλήψτε τη λήψη της λίστας μοντέλων", "remove_listed": "Αφαίρεση μοντέλων από τη λίστα", "remove_model": "Αφαίρεση Μοντέλου", "remove_whole_group": "Αφαίρεση ολόκληρης ομάδας" diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 5300a03e6b..59a1958d18 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -383,6 +383,15 @@ "settings": "Configuración de búsqueda en red" } }, + "mcp": { + "error": { + "parse_tool_call": "No se puede convertir al formato de llamada de herramienta válido: {{toolCall}}" + }, + "warning": { + "multiple_tools": "Existen múltiples herramientas MCP coincidentes, se ha seleccionado {{tool}}", + "no_tool": "No se encontró la herramienta MCP requerida {{tool}}" + } + }, "message": { "new": { "branch": { @@ -2991,6 +3000,7 @@ "label": "Agregar modelo en la lista" }, "add_whole_group": "Agregar todo el grupo", + "refetch_list": "Volver a obtener la lista de modelos", "remove_listed": "Eliminar modelo de la lista", "remove_model": "Eliminar modelo", "remove_whole_group": "Eliminar todo el grupo" diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 38b7c92e6e..b558c30240 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -383,6 +383,15 @@ "settings": "Paramètres de recherche en ligne" } }, + "mcp": { + "error": { + "parse_tool_call": "Impossible de convertir au format d'appel d'outil valide : {{toolCall}}" + }, + "warning": { + "multiple_tools": "Il existe plusieurs outils MCP correspondants, {{tool}} a été sélectionné", + "no_tool": "Aucun outil MCP requis {{tool}} n'a été trouvé" + } + }, "message": { "new": { "branch": { @@ -2991,6 +3000,7 @@ "label": "Ajouter le modèle dans la liste" }, "add_whole_group": "Ajouter tout le groupe", + "refetch_list": "Récupérer à nouveau la liste des modèles", "remove_listed": "Supprimer un modèle de la liste", "remove_model": "Supprimer le modèle", "remove_whole_group": "Supprimer tout le groupe" diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 4aeb1c3e11..c03ca04f8a 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -383,6 +383,15 @@ "settings": "Configurações de Pesquisa na Web" } }, + "mcp": { + "error": { + "parse_tool_call": "Não é possível converter para um formato de chamada de ferramenta válido: {{toolCall}}" + }, + "warning": { + "multiple_tools": "Existem várias ferramentas MCP correspondentes, a ferramenta {{tool}} foi selecionada", + "no_tool": "Nenhuma ferramenta MCP necessária correspondente encontrada {{tool}}" + } + }, "message": { "new": { "branch": { @@ -2991,6 +3000,7 @@ "label": "Adicionar modelo da lista" }, "add_whole_group": "Adicionar todo o grupo", + "refetch_list": "Obter novamente a lista de modelos", "remove_listed": "Remover modelo da lista", "remove_model": "Remover Modelo", "remove_whole_group": "Remover todo o grupo" diff --git a/src/renderer/src/utils/mcp-tools.ts b/src/renderer/src/utils/mcp-tools.ts index e2bf4e3f09..7e4dd868a7 100644 --- a/src/renderer/src/utils/mcp-tools.ts +++ b/src/renderer/src/utils/mcp-tools.ts @@ -18,6 +18,7 @@ import { import type { MCPToolCompleteChunk, MCPToolInProgressChunk, MCPToolPendingChunk } from '@renderer/types/chunk' import { ChunkType } from '@renderer/types/chunk' import { AwsBedrockSdkMessageParam, AwsBedrockSdkTool, AwsBedrockSdkToolCall } from '@renderer/types/sdk' +import { t } from 'i18next' import { isArray, isObject, pull, transform } from 'lodash' import { nanoid } from 'nanoid' import OpenAI from 'openai' @@ -253,20 +254,33 @@ export function openAIToolsToMcpTool( mcpTools: MCPTool[], toolCall: OpenAI.Responses.ResponseFunctionToolCall | ChatCompletionMessageToolCall ): MCPTool | undefined { - const tool = mcpTools.find((mcpTool) => { + let toolName = '' + try { if ('name' in toolCall) { - return mcpTool.id === toolCall.name || mcpTool.name === toolCall.name + toolName = toolCall.name } else { - return mcpTool.id === toolCall.function.name || mcpTool.name === toolCall.function.name + toolName = toolCall.function.name } + } catch (error) { + logger.error(`Error parsing tool call: ${toolCall}`, error as Error) + window.message.error(t('chat.mcp.error.parse_tool_call', { toolCall: toolCall })) + return undefined + } + const tools = mcpTools.filter((mcpTool) => { + return mcpTool.id === toolName || mcpTool.name === toolName }) + if (tools.length > 1) { + logger.warn(`Multiple MCP Tools found for tool call: ${toolName}`) + window.message.warning(t('chat.mcp.warning.multiple_tools', { tool: tools[0].name })) + } - if (!tool) { - logger.warn('No MCP Tool found for tool call:', toolCall) + if (tools.length === 0) { + logger.warn(`No MCP Tool found for tool call: ${toolName}`) + window.message.warning(t('chat.mcp.warning.no_tool', { tool: toolName })) return undefined } - return tool + return tools[0] } export async function callBuiltInTool(toolResponse: MCPToolResponse): Promise { @@ -358,11 +372,17 @@ export function mcpToolsToAnthropicTools(mcpTools: MCPTool[]): Array export function anthropicToolUseToMcpTool(mcpTools: MCPTool[] | undefined, toolUse: ToolUseBlock): MCPTool | undefined { if (!mcpTools) return undefined - const tool = mcpTools.find((tool) => tool.id === toolUse.name) - if (!tool) { + const tools = mcpTools.filter((tool) => tool.id === toolUse.name) + if (tools.length === 0) { + logger.warn(`No MCP Tool found for tool call: ${toolUse.name}`) + window.message.warning(t('chat.mcp.warning.no_tool', { tool: toolUse.name })) return undefined } - return tool + if (tools.length > 1) { + logger.warn(`Multiple MCP Tools found for tool call: ${toolUse.name}`) + window.message.warning(t('chat.mcp.warning.multiple_tools', { tool: tools[0].name })) + } + return tools[0] } /** @@ -424,9 +444,19 @@ export function geminiFunctionCallToMcpTool( const toolName = toolCall.name || toolCall.id if (!toolName) return undefined - const tool = mcpTools.find((tool) => tool.id.includes(toolName) || tool.name.includes(toolName)) + const tools = mcpTools.filter((tool) => tool.id.includes(toolName) || tool.name.includes(toolName)) + if (tools.length > 1) { + logger.warn(`Multiple MCP Tools found for tool call: ${toolName}`) + window.message.warning(t('chat.mcp.warning.multiple_tools', { tool: tools[0].name })) + } - return tool + if (tools.length === 0) { + logger.warn(`No MCP Tool found for tool call: ${toolName}`) + window.message.warning(t('chat.mcp.warning.no_tool', { tool: toolName })) + return undefined + } + + return tools[0] } export function upsertMCPToolResponse(