Tools 是 Model Context Protocol (MCP) 中的一个强大原语,它使 servers 能够向 clients 暴露可执行功能。通过 tools,LLMs 可以与外部系统交互、执行计算并在现实世界中采取行动。

Tools 被设计为模型控制,这意味着 tools 从 servers 暴露给 clients,目的是 AI 模型能够自动调用它们(需要人工介入以授予批准)。

概述

MCP 中的 Tools 允许 servers 暴露可执行函数,这些函数可以被 clients 调用,并被 LLMs 用于执行操作。Tools 的关键方面包括:

  • Discovery (发现):Clients 可以通过 tools/list endpoint 列出可用的 tools
  • Invocation (调用):Tools 使用 tools/call endpoint 调用,其中 servers 执行请求的操作并返回结果
  • Flexibility (灵活性):Tools 的范围可以从简单的计算到复杂的 API 交互

resources 一样,tools 通过唯一的名称进行标识,并且可以包含描述以指导其使用。但是,与 resources 不同的是,tools 代表可以修改状态或与外部系统交互的动态操作。

Tool 定义结构

每个 tool 都使用以下结构定义:

{
  name: string;          // Tool 的唯一标识符
  description?: string;  // 供人阅读的描述
  inputSchema: {         // Tool 参数的 JSON Schema
    type: "object",
    properties: { ... }  // Tool 特定的参数
  }
}

实现 tools

以下是在 MCP server 中实现一个基本 tool 的示例:

const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {
    tools: {}
  }
});

// 定义可用的 tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [{
      name: "calculate_sum",
      description: "将两个数字加在一起",
      inputSchema: {
        type: "object",
        properties: {
          a: { type: "number" },
          b: { type: "number" }
        },
        required: ["a", "b"]
      }
    }]
  };
});

// 处理 tool 执行
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "calculate_sum") {
    const { a, b } = request.params.arguments;
    return {
      content: [
        {
          type: "text",
          text: String(a + b)
        }
      ]
    };
  }
  throw new Error("Tool not found");
});

示例 tool 模式

以下是 server 可以提供的 tool 类型的示例:

系统操作

与本地系统交互的 tools:

{
  name: "execute_command",
  description: "运行 shell 命令",
  inputSchema: {
    type: "object",
    properties: {
      command: { type: "string" },
      args: { type: "array", items: { type: "string" } }
    }
  }
}

API 集成

封装外部 APIs 的 tools:

{
  name: "github_create_issue",
  description: "创建 GitHub issue",
  inputSchema: {
    type: "object",
    properties: {
      title: { type: "string" },
      body: { type: "string" },
      labels: { type: "array", items: { type: "string" } }
    }
  }
}

数据处理

转换或分析数据的 tools:

{
  name: "analyze_csv",
  description: "分析 CSV 文件",
  inputSchema: {
    type: "object",
    properties: {
      filepath: { type: "string" },
      operations: {
        type: "array",
        items: {
          enum: ["sum", "average", "count"]
        }
      }
    }
  }
}

最佳实践

在实现 tools 时:

  1. 提供清晰、描述性的名称和描述
  2. 为参数使用详细的 JSON Schema 定义
  3. 在 tool 描述中包含示例,以演示模型应如何使用它们
  4. 实现适当的错误处理和验证
  5. 对长时间运行的操作使用进度报告
  6. 保持 tool 操作的 focus 和原子性
  7. 记录预期的返回值结构
  8. 实现适当的超时
  9. 考虑对资源密集型操作进行速率限制
  10. 记录 tool 使用情况以进行调试和监控

安全考虑

在暴露 tools 时:

输入验证

  • 根据 schema 验证所有参数
  • 清理文件路径和系统命令
  • 验证 URL 和外部标识符
  • 检查参数大小和范围
  • 阻止命令注入

访问控制

  • 在需要时实现身份验证
  • 使用适当的授权检查
  • 审计 tool 使用情况
  • 限制请求速率
  • 监控滥用情况

错误处理

  • 不要向 clients 暴露内部错误
  • 记录与安全相关的错误
  • 适当地处理超时
  • 在出错后清理资源
  • 验证返回值

Tool 发现和更新

MCP 支持动态 tool 发现:

  1. Clients 可以在任何时候列出可用的 tools
  2. Servers 可以在 tools 更改时使用 notifications/tools/list_changed 通知 clients
  3. 可以在运行时添加或删除 tools
  4. 可以更新 tool 定义(尽管应该谨慎进行)

错误处理

Tool 错误应在 result 对象中报告,而不是作为 MCP 协议级别的错误。这允许 LLM 看到并可能处理错误。当 tool 遇到错误时:

  1. 在 result 中将 isError 设置为 true
  2. content 数组中包含错误详细信息

以下是 tools 的正确错误处理示例:

try {
  // Tool operation
  const result = performOperation();
  return {
    content: [
      {
        type: "text",
        text: `Operation successful: ${result}`
      }
    ]
  };
} catch (error) {
  return {
    isError: true,
    content: [
      {
        type: "text",
        text: `Error: ${error.message}`
      }
    ]
  };
}

这种方法允许 LLM 看到发生了错误,并可能采取纠正措施或请求人工干预。

测试 tools

MCP tools 的综合测试策略应涵盖:

  • 功能测试:验证 tools 是否使用有效输入正确执行,并适当地处理无效输入
  • 集成测试:使用真实和模拟的依赖项测试 tool 与外部系统的交互
  • 安全测试:验证身份验证、授权、输入清理和速率限制
  • 性能测试:检查负载下的行为、超时处理和资源清理
  • 错误处理:确保 tools 通过 MCP 协议正确报告错误并清理资源