第三节:MCP 工具集成
工具注册与调用

作者:小学子 📚 | 日期:2026年4月2日 | 第七阶段 · 模块七


# 第七阶段 · 模块七 · 第三节:MCP 工具集成

核心问题

MCP 工具如何注册为 Claude Code 工具?工具调用的参数和结果如何转换?MCP 工具的权限模型是什么?


◇ 本节位置


        Claude Code 全局架构
        
        ┌─────────────────────────────────────────────────────────────────────┐
        │  MCP 系统                                                            │
        │                                                                      │
        │  MCP 工具集成 ← 本节                                               │
        │  ├── MCPTool ──> 工具包装器                                        │
        │  ├── 工具注册 ──> 注册为 Claude Code 工具                           │
        │  └── 权限模型 ──> MCP 工具权限                                     │
        └─────────────────────────────────────────────────────────────────────┘
        


一、MCPTool 包装器

1.1 工具定义

源码位置:`src/tools/MCPTool/MCPTool.ts`


        import { buildTool, type ToolDef } from '../../Tool.js';
        
        export const MCPTool = buildTool({
          isMcp: true,
          name: 'mcp',           // 会被 MCPConnectionManager 覆盖
          maxResultSizeChars: 100_000,
        
          async call() {
            return { data: '' };  // 会被实际处理器覆盖
          },
        
          async checkPermissions(): Promise<PermissionResult> {
            return {
              behavior: 'passthrough',
              message: 'MCPTool requires permission.',
            };
          },
        } satisfies ToolDef<InputSchema, Output>);
        

1.2 通用 MCP 工具


        // MCPTool 是一个通用包装器
        // 具体的 MCP 工具由 MCPConnectionManager 动态创建
        
        // 工具名格式:mcp__{server}__{tool}
        // 例如:mcp__github__create_issue
        

1.3 五问分析

问 1:为什么需要 MCPTool 包装器?


        // 问题:MCP 工具有各种不同的输入输出
        // 解决:统一的包装器处理
        
        // MCPTool 的职责:
        // 1. 统一的工具接口
        // 2. 权限检查
        // 3. 结果渲染
        // 4. 错误处理
        


二、工具注册

2.1 MCPConnectionManager


        // MCPConnectionManager 负责:
        // 1. 管理 MCP 连接
        // 2. 注册 MCP 工具
        
        class MCPConnectionManager {
          private tools: Map<string, MCPTool> = new Map();
        
          async connect(server: MCPServerConfig): Promise<void> {
            // 1. 创建 MCP 客户端
            const client = await this.createClient(server);
        
            // 2. 发现工具
            const { tools } = await client.request(
              { method: 'tools/list' },
              ListToolsResultSchema
            );
        
            // 3. 注册每个工具
            for (const tool of tools) {
              await this.registerTool(server.name, tool);
            }
          }
        }
        

2.2 工具注册


        async registerTool(
          serverName: string,
          mcpTool: { name: string; description: string; inputSchema: object }
        ): Promise<void> {
          // 创建工具处理器
          const toolHandler = async (input: unknown, context: ToolContext) => {
            // 调用 MCP 服务器上的工具
            const result = await this.callTool(serverName, mcpTool.name, input);
            return result;
          };
        
          // 工具名:mcp__{server}__{tool}
          const toolName = `mcp__${serverName}__${mcpTool.name}`;
        
          // 注册
          this.tools.set(toolName, {
            name: toolName,
            description: mcpTool.description,
            inputSchema: mcpTool.inputSchema,
            call: toolHandler,
          });
        }
        

2.3 五问分析

问 1:工具名为什么用双下划线?


        // 格式:mcp__{server}__{tool}
        // 双下划线避免与普通工具名冲突
        
        // 普通工具:Read, Write, Grep
        // MCP 工具:mcp__github__create_issue, mcp__filesystem__read
        


三、工具调用

3.1 调用流程


        ┌─────────────────────────────────────────────────────────────────────┐
        │  MCP 工具调用流程                                                    │
        │                                                                      │
        │  1. Claude Code 调用 mcp__github__create_issue                    │
        │                                                                      │
        │  2. MCPTool.call(input)                                             │
        │                                                                      │
        │  3. MCPConnectionManager.callTool(                                  │
        │       serverName: 'github',                                         │
        │       toolName: 'create_issue',                                     │
        │       arguments: input                                              │
        │     )                                                               │
        │                                                                      │
        │  4. MCP Client 发送 tools/call 请求                                 │
        │                                                                      │
        │  5. 返回结果                                                        │
        └─────────────────────────────────────────────────────────────────────┘
        

3.2 callTool 实现


        async callTool(
          serverName: string,
          toolName: string,
          arguments: Record<string, unknown>
        ): Promise<string> {
          // 1. 获取连接
          const connection = await this.getConnection(serverName);
        
          // 2. 调用工具
          const result = await connection.client.request(
            {
              method: 'tools/call',
              params: {
                name: toolName,
                arguments: arguments,
              },
            },
            CallToolResultSchema
          );
        
          // 3. 格式化结果
          if (isError(result)) {
            throw new MCPToolError(result.content);
          }
        
          return formatResult(result);
        }
        

3.3 五问分析

问 1:MCP 工具的错误如何处理?


        // MCP 工具可能返回错误内容
        if (result.isError) {
          throw new MCPToolError(result.content);
        }
        
        // MCPToolError 会被捕获并显示给用户
        class MCPToolError extends Error {
          constructor(public readonly content: string) {
            super(content);
          }
        }
        


四、权限模型

4.1 MCP 工具权限


        // MCP 工具需要单独授权
        // 默认行为:需要用户授权
        
        async checkPermissions(
          input: unknown,
          context: ToolUseContext
        ): Promise<PermissionResult> {
          // 检查是否已授权该工具
          const authorized = await this.isAuthorized(
            context.getAppState().authorizedMcpTools,
            this.name
          );
        
          if (authorized) {
            return { behavior: 'allow' };
          }
        
          // 需要授权
          return {
            behavior: 'prompt',
            message: `Authorize MCP tool: ${this.name}`,
          };
        }
        

4.2 授权管理


        // 授权存储在 AppState
        interface AppState {
          authorizedMcpTools: Set<string>;  // 已授权的工具
        }
        
        // 添加授权
        function authorizeTool(toolName: string): void {
          const state = getAppState();
          state.authorizedMcpTools.add(toolName);
        }
        

4.3 五问分析

问 1:MCP 工具和内置工具的权限区别?


        // 内置工具:基于权限规则
        // MCP 工具:基于授权列表
        
        // 内置工具权限
        if (permissionRules.allows(toolName)) {
          return { behavior: 'allow' };
        }
        
        // MCP 工具权限
        if (state.authorizedMcpTools.has(toolName)) {
          return { behavior: 'allow' };
        }
        


五、结果渲染

5.1 renderToolResultMessage

源码位置:`src/tools/MCPTool/UI.js`


        // MCP 工具结果渲染
        function renderToolResultMessage(output: string): RenderResult {
          return {
            title: 'MCP Tool Result',
            content: output,
            isError: output.includes('Error:'),
          };
        }
        

5.2 进度显示


        // 工具执行中的进度
        function renderToolUseProgressMessage(progress: MCPProgress): RenderResult {
          return {
            title: `MCP: ${progress.tool}`,
            content: progress.message,
            partial: true,
          };
        }
        


六、思考题

思考题 1:如何调试 MCP 工具调用?

答案


        // 1. 启用 verbose 模式
        claude --verbose
        
        // 2. 查看工具调用日志
        console.log('Calling MCP tool:', toolName, arguments);
        
        // 3. 测试 MCP 工具
        claude mcp call github.create_issue --args '{"title": "Test"}'
        


思考题 2:MCP 工具的结果大小有限制吗?

答案


        // 有,maxResultSizeChars = 100,000
        // 超出会被截断
        
        MCPTool = buildTool({
          maxResultSizeChars: 100_000,
          // ...
        });
        
        // 如果截断,显示警告
        if (output.length > maxResultSizeChars) {
          return output.slice(0, maxResultSizeChars) + '\n[Truncated]';
        }
        


思考题 3:MCP 工具调用失败怎么办?

答案


        // 1. 检查连接状态
        claude mcp list
        
        // 2. 重新连接
        claude mcp remove <server>
        claude mcp add <server>
        
        // 3. 查看服务器日志
        // MCP 服务器可能需要重启
        


七、延伸阅读

文件核心内容
`src/tools/MCPTool/MCPTool.ts`MCP 工具包装器
`src/services/mcp/MCPConnectionManager.tsx`连接管理器


八、下节预告

下一节我们将深入 MCP 服务器开发

- 创建 MCP 服务器

- 实现工具端点

- 注册和发布服务器


*- 第一轮:□ 事实准确性*

*- 第二轮:□ 深度与洞见*

*- 第三轮:□ 可读性与价值*