メインコンテンツまでスキップ

工具格式适配

MCP 模块需要将 MCP 服务器提供的工具转换为不同 LLM 提供商所需的格式。这个过程由两个组件负责:

  • ToolsLoader — 将 MCPTool 转换为 OpenAI/Anthropic/Gemini 等提供商的工具定义格式
  • McpToolAdapter — 将 MCPTool 封装为 TinyElf 引擎的 ITool 接口实现

ToolsLoader

文件路径packages/desktop/app/main/services/capabilities/tools/mcp-users/ToolsLoader.ts

职责

ToolsLoader 提供了完整的 MCP 工具加载和格式转换流程:

graph LR
fetchMcpTools --> MCPTool_Array["MCPTool[]"]
MCPTool_Array --> convertOpenAI["convertMcpToolsToOpenAI"]
MCPTool_Array --> convertAnthropic["convertMcpToolsToAnthropic"]
MCPTool_Array --> convertGemini["convertMcpToolsToGemini"]
convertOpenAI --> OpenAI_Format["OpenAITool[]"]
convertAnthropic --> Anthropic_Format["AnthropicTool[]"]
convertGemini --> Gemini_Format["GeminiTools"]

入口函数:setupMcpTools

async function setupMcpTools(
mcpService: McpService,
config?: {
enabled?: boolean;
mode?: 'auto' | 'manual';
selectedServers?: string[];
},
apiFormat?: 'openai' | 'anthropic' | 'google'
): Promise<{ tools: OpenAITool[] | AnthropicTool[] | GeminiTools; mcpTools: MCPTool[] } | undefined>

执行流程

  1. 调用 fetchMcpTools() 获取原始 MCPTool 列表
  2. 如果没有可用工具,返回 undefined
  3. 按服务器分组打印日志
  4. 根据 apiFormat 调用对应的转换函数
  5. 返回转换后的工具定义和原始 MCPTool 列表

fetchMcpTools — 获取工具

async function fetchMcpTools(
mcpService: McpService,
config?: { enabled?: boolean; mode?: 'auto' | 'manual'; selectedServers?: string[] }
): Promise<MCPTool[]>

根据配置决定加载策略:

条件行为
enabledfalse 或未设置返回空数组
modeauto 或未设置调用 mcpService.listAllActiveServerTools()
modemanual遍历 selectedServers,逐个调用 mcpService.listServerTools()

输出格式对比

OpenAI 格式

interface OpenAITool {
type: 'function';
function: {
name: string; // MCPTool.id
description: string; // MCPTool.description
parameters: { // 处理后的 JSON Schema
type: 'object';
properties: Record<string, any>;
required: string[];
};
};
}

Anthropic 格式

interface AnthropicTool {
name: string; // MCPTool.id
description: string; // MCPTool.description
input_schema: { // 处理后的 JSON Schema
type: 'object';
properties: Record<string, any>;
required: string[];
};
}

Gemini 格式

type GeminiTools = Array<{
functionDeclarations: Array<{
name: string; // MCPTool.id
description: string; // MCPTool.description
parameters: { // 处理后的 JSON Schema
type: 'object';
properties: Record<string, any>;
required: string[];
};
}>;
}>;

Gemini 格式特点:所有工具包装在一个 functionDeclarations 数组中,外层再包一层数组。

processJsonSchema — Schema 处理

function processJsonSchema(schema: any): any

确保 JSON Schema 与各 LLM API 兼容:

  • 如果 schema 为空或不是对象,返回默认的空 object schema
  • 如果已有 type: 'object',提取 propertiesrequireddescription
  • 否则包装为 object 类型

McpToolAdapter

文件路径packages/desktop/app/main/services/agent-core/engine/tinyelf/tools/McpToolAdapter.ts

职责

将 MCPTool 封装为 TinyElf 引擎的 ITool 接口实现,使 TinyElf 可以直接调用 MCP 工具。

ITool 接口映射

class McpToolAdapter implements ITool {
readonly name: string; // ← MCPTool.id
readonly description: string; // ← MCPTool.description
readonly parameters: JsonSchema; // ← MCPTool.inputSchema

constructor(
private mcpTool: MCPTool,
private mcpService: McpService
);

async execute(params: Record<string, unknown>): Promise<string>;
}

execute 方法

sequenceDiagram
participant TinyElf
participant Adapter as McpToolAdapter
participant Service as McpService
participant Server as MCP Server

TinyElf->>Adapter: execute(params)
Adapter->>Adapter: 生成 callId
Adapter->>Service: callTool(serverId, toolName, args, callId)
Note over Adapter,Service: Promise.race([callTool, timeout(120s)])
Service->>Server: client.callTool()
Server-->>Service: MCPCallToolResponse
Service-->>Adapter: { isError, content[] }
Adapter->>Adapter: flattenContent(content)
Adapter-->>TinyElf: 字符串结果

超时处理:使用 Promise.race() 实现 120 秒超时,与 ShellTool 超时保持一致。

flattenContent — 内容扁平化

MCPToolResponseContent[] 转换为单个字符串:

function flattenContent(content: MCPToolResponseContent[]): string
内容类型转换方式
text直接使用 c.text
image输出 [Image: ${mimeType}] 占位符
audio输出 [Audio: ${mimeType}] 占位符

多个内容项用 \n 连接。

loadMcpTools — 批量加载

async function loadMcpTools(
mcpService: McpService,
serverIds: string[]
): Promise<McpToolAdapter[]>

遍历服务器 ID 列表,对每个服务器调用 listServerTools(),将每个 MCPTool 封装为 McpToolAdapter。单个服务器失败不影响其他服务器。

DirectMcpToolAdapter

用途:用于内置 MCP 服务器(如 Agent 预设的本地 MCP 服务),绕过 McpService 直接持有 Client 引用。

与 McpToolAdapter 的区别

特性McpToolAdapterDirectMcpToolAdapter
连接管理委托给 McpService直接持有 Client
适用场景用户配置的 MCP 服务器内置/预设的 MCP 服务器
生命周期跟随 McpService跟随会话(session-level)
工具名格式mcp__serverName__toolNameserverName__toolName
进程追踪不需要通过 McpProcessTracker

loadDirectMcpTools — 直连加载

async function loadDirectMcpTools(
servers: ResolvedMcpServer[]
): Promise<{
tools: DirectMcpToolAdapter[];
connections: DirectMcpConnection[];
}>

执行流程:

  1. 对每个服务器创建 StdioClientTransport
  2. 创建 MCP Client 并连接
  3. 注册到 McpProcessTracker 进行 PID 追踪
  4. 发现工具并创建 DirectMcpToolAdapter 实例
  5. 返回工具列表和连接句柄(用于会话结束时清理)

McpProcessTracker

文件路径packages/desktop/app/main/services/agent-core/engine/tinyelf/tools/McpProcessTracker.ts

职责

全局单例,追踪 DirectMcpToolAdapter 创建的 stdio 子进程 PID,确保异常退出时也能清理。

安全层

层级方法说明
正常清理closeConnection()优雅关闭(3 秒超时)→ 强制 kill
批量清理closeAll()关闭所有追踪的连接
安全网process.on('exit')进程退出时同步强制 kill 所有 PID

使用方式

// 追踪
McpProcessTracker.getInstance().track(connection);

// 清理单个
await McpProcessTracker.getInstance().closeConnection(connection);

// 清理全部
await McpProcessTracker.getInstance().closeAll();

集成点

CompletionService 集成

CompletionService 通过 setupMcpTools() 在每次聊天请求前加载 MCP 工具:

const mcpResult = await setupMcpTools(
this.mcpService,
{ enabled: true, mode: 'auto' },
'openai' // 根据当前提供商选择
);

if (mcpResult) {
requestPayload.tools = mcpResult.tools;
}

TinyElf 引擎集成

TinyElf 引擎通过 loadMcpTools()loadDirectMcpTools() 加载工具:

// 用户配置的 MCP 服务器
const mcpTools = await loadMcpTools(mcpService, serverIds);

// 内置 MCP 服务器
const { tools: directTools, connections } = await loadDirectMcpTools(builtinServers);

// 注册到 ToolRegistry
for (const tool of [...mcpTools, ...directTools]) {
toolRegistry.register(tool);
}

相关文件