# MCP 工具内联渲染修改说明 本文件提供了将 MCP 工具调用从消息顶部固定位置改为内联渲染的修改步骤。您需要修改以下两个文件: 1. `src/renderer/src/pages/home/Messages/MessageContent.tsx` 2. `src/renderer/src/pages/home/Markdown/Markdown.tsx` **重要提示:** 在进行修改之前,请确保您已经备份了这两个文件。 ## 步骤 1: 修改 `src/renderer/src/pages/home/Messages/MessageContent.tsx` 这个文件主要负责处理消息内容的整体布局和数据准备。我们需要在这里: * 移除 MCP 工具块的顶部容器。 * 将 MCP 工具的相关状态(如 `activeKeys`, `copiedMap`, `editingToolId`, `editedParams`)和处理函数(如 `copyContent`, `handleRerun`, `handleEdit`, `handleCancelEdit`, `handleSaveEdit`, `handleParamsChange`)移动或传递给 `Markdown` 组件。 * 修改 `processedContent` 的生成逻辑,将工具 XML 标记替换为自定义的占位符,以便 `Markdown` 组件能够识别并在正确的位置渲染工具块。 以下是需要修改的部分: 1. **移除 `MessageTools` 导入和相关的 JSX:** 找到并删除以下导入: ```typescript import { default as MessageTools } from './MessageTools' // Change to named import (using default alias) ``` 找到并删除以下 JSX 结构: ```jsx
{/* Only display thought info at the top */} {/* Render MessageTools to display tool blocks based on metadata */}
``` 保留 `MessageThought` 组件,它应该独立于工具块渲染。 2. **将工具相关的状态和处理函数移动或传递:** 将 `MessageTools.tsx` 中的以下状态和处理函数定义复制到 `MessageContent.tsx` 中: * `activeKeys` state 和 `setActiveKeys` * `copiedMap` state 和 `setCopiedMap` * `editingToolId` state 和 `setEditingToolId` * `editedParams` state 和 `setEditedParams` * `localToolResponses` state 和 `setLocalToolResponses` * `useEffect` 钩子,用于同步 `localToolResponses` * `copyContent` useCallback * `handleRerun` useCallback * `handleEdit` useCallback * `handleCancelEdit` useCallback * `handleSaveEdit` useCallback * `handleParamsChange` useCallback * 监听 `onToolRerunUpdate` 的 `useEffect` 钩子 这些状态和函数需要作为 props 传递给 `Markdown` 组件。 3. **修改 `processedContent` 逻辑:** 在 `processedContent` 的 `useMemo` 钩子中,在处理引用标记之后,添加逻辑来查找 `...` 标记,并将其替换为自定义的占位符,例如 ``。 首先,在文件顶部导入 `MCPToolResponse` 类型: ```typescript import { Message, Model, MCPToolResponse } from '@renderer/types' ``` 然后,在 `processedContent` 的 `useMemo` 内部,在处理完引用标记后,添加以下代码: ```typescript // ... (之前的引用标记处理逻辑) // 处理 MCP 工具调用标记 const toolResponses = message.metadata?.mcpTools || []; if (toolResponses.length > 0) { toolResponses.forEach(toolCall => { const toolTagRegex = new RegExp(`(?:[^<]*?${toolCall.id}[\\s\\S]*?)<\\/tool_use>`, 'gi'); content = content.replace(toolTagRegex, ``); }); } return content; ``` 请注意,这里的正则表达式 `toolTagRegex` 是一个示例,您可能需要根据实际的工具 XML 格式进行调整,以确保能够准确匹配包含特定 `toolCall.id` 的 `` 标记。 4. **更新 `Markdown` 组件的 props:** 在渲染 `Markdown` 组件的地方,将新移动过来的状态和处理函数作为 props 传递下去: ```jsx ``` 请注意,我在这里使用了新的 prop 名称(例如 `toolResponses`, `activeToolKeys` 等),以避免与 `Markdown` 组件内部可能已有的 props 冲突。您需要在 `Markdown.tsx` 中接收这些新的 props。 ## 步骤 2: 修改 `src/renderer/src/pages/home/Markdown/Markdown.tsx` 这个文件负责将 Markdown 内容渲染为 HTML。我们需要在这里: * 接收从 `MessageContent` 传递过来的工具相关 props。 * 添加一个自定义的渲染器,用于处理我们在步骤 1 中创建的 `` 占位符。 * 在自定义渲染器中,根据占位符中的 `id` 查找对应的工具响应数据,并渲染 `SingleToolCallBlock` 组件。 以下是需要修改的部分: 1. **更新 Props 接口:** 修改 `Props` 接口,添加从 `MessageContent` 传递过来的新 props: ```typescript import { MCPToolResponse, Message } from '@renderer/types' // 导入 MCPToolResponse interface Props { message: Message toolResponses: MCPToolResponse[] // 添加工具响应数据 prop activeToolKeys: string[] // 添加 activeKeys prop copiedToolMap: Record // 添加 copiedMap prop editingToolId: string | null // 添加 editingToolId prop editedToolParamsString: string // 添加 editedParams prop onToolToggle: React.Dispatch> // 添加 onToolToggle prop onToolCopy: (content: string, toolId: string) => void // 添加 onToolCopy prop onToolRerun: (toolCall: MCPToolResponse, currentParamsString: string) => void // 添加 onToolRerun prop onToolEdit: (toolCall: MCPToolResponse) => void // 添加 onToolEdit prop onToolSave: (toolCall: MCPToolResponse) => void // 添加 onToolSave prop onToolCancel: () => void // 添加 onToolCancel prop onToolParamsChange: (newParams: string) => void // 添加 onToolParamsChange prop } ``` 2. **导入 `SingleToolCallBlock` 组件:** 在文件顶部导入 `SingleToolCallBlock` 组件: ```typescript import SingleToolCallBlock from '../Messages/SingleToolCallBlock' // 导入 SingleToolCallBlock ``` 3. **添加自定义渲染器:** 在 `components` 的 `useMemo` 钩子中,添加一个针对 `tool-block` 标签的渲染器: ```typescript const components = useMemo(() => { const baseComponents = { // ... (其他现有渲染器) // 添加 tool-block 渲染器 'tool-block': (props: any) => { const toolCallId = props.id; // 获取占位符中的 id const toolResponse = toolResponses.find(tr => tr.id === toolCallId); // 查找对应的工具响应数据 if (!toolResponse) { return null; // 如果找不到对应的工具响应,则不渲染 } // 渲染 SingleToolCallBlock 组件,并传递必要的 props return ( onToolToggle(prev => prev.includes(toolCallId) ? prev.filter(k => k !== toolCallId) : [...prev, toolCallId])} // 传递 onToolToggle 函数 onCopy={onToolCopy} // 传递 onToolCopy 函数 onRerun={onToolRerun} // 传递 onToolRerun 函数 onEdit={onToolEdit} // 传递 onToolEdit 函数 onSave={onToolSave} // 传递 onToolSave 函数 onCancel={onToolCancel} // 传递 onToolCancel 函数 onParamsChange={onToolParamsChange} // 传递 onToolParamsChange 函数 /> ); }, } as Partial; return baseComponents; }, [ // ... (其他现有依赖) toolResponses, // 添加 toolResponses 依赖 activeToolKeys, // 添加 activeToolKeys 依赖 copiedToolMap, // 添加 copiedToolMap 依赖 editingToolId, // 添加 editingToolId 依赖 editedToolParamsString, // 添加 editedToolParamsString 依赖 onToolToggle, // 添加 onToolToggle 依赖 onToolCopy, // 添加 onToolCopy 依赖 onToolRerun, // 添加 onToolRerun 依赖 onToolEdit, // 添加 onToolEdit 依赖 onToolSave, // 添加 onToolSave 依赖 onToolCancel, // 添加 onToolCancel 依赖 onToolParamsChange, // 添加 onToolParamsChange 依赖 t, // 添加 t 依赖 messageFont // 添加 messageFont 依赖 ]); ``` 请确保在 `useMemo` 的依赖数组中包含了所有使用到的外部变量和函数。 ## 步骤 3: 修改 `src/renderer/src/pages/home/Messages/MessageTools.tsx` 这个文件将不再负责渲染工具块,它的主要作用将变为处理工具相关的状态和逻辑。 1. **移除渲染工具块的 JSX:** 找到并删除 `MessageTools` 组件中的以下 JSX 结构: ```jsx return ( <> {collapseItems.map((item) => ( { setActiveKeys((prev) => prev.includes(item.key as string) ? prev.filter((k) => k !== item.key) : [...prev, item.key as string] ) }}> {item.children} ))} ) ``` 以及相关的 `ToolsContainer` 样式定义。 2. **保留工具相关的状态和逻辑:** 保留 `MessageTools` 组件中所有与工具相关的状态(`activeKeys`, `copiedMap`, `editingToolId`, `editedParams`, `localToolResponses`)和处理函数(`copyContent`, `handleRerun`, `handleEdit`, `handleCancelEdit`, `handleSaveEdit`, `handleParamsChange`),以及监听 `onToolRerunUpdate` 的 `useEffect` 钩子。这些状态和逻辑将需要被提升到 `MessageContent.tsx` 中。 3. **修改 `MessageTools` 组件的用途:** `MessageTools` 组件可能不再需要作为一个 React 组件存在,或者可以修改其用途,例如只包含工具相关的逻辑和状态管理,并通过钩子或上下文提供给其他组件使用。考虑到您希望自己修改,您可以选择将这些逻辑完全移动到 `MessageContent.tsx` 中,然后删除 `MessageTools.tsx` 文件。 完成以上修改后,重新运行您的应用程序,MCP 工具块应该会以内联的方式显示在消息内容中对应的位置。 请仔细按照步骤进行修改,并根据您的实际代码结构进行调整。如果在修改过程中遇到任何问题,或者需要进一步的帮助,请随时告诉我。