工具

社区译文;来源: MCP 80041ab0

模型上下文协议(MCP)允许服务器暴露可由语言模型调用的工具。工具使模型能够与外部系统交互,例如查询数据库、调用 API 或执行计算。每个工具都由名称唯一标识,并包含描述其 schema 的元数据。

为简洁起见,本页请求示例省略了 _meta 请求元数据(io.modelcontextprotocol/protocolVersion、io.modelcontextprotocol/clientInfo 和 io.modelcontextprotocol/clientCapabilities)。每个请求 MUST 包含必需的 _meta 字段;请参阅 _meta。

用户交互模型

MCP 中的工具被设计为由模型控制,即语言模型可以根据其对上下文的理解和用户的提示词,自动发现并调用工具。

不过,实现可以通过任何符合自身需求的界面模式暴露工具;协议本身并不强制规定特定的用户交互模型。

出于信任、安全与防护考虑,流程中始终 SHOULD 有人类参与,并能够拒绝工具调用。

应用 SHOULD:

  • 提供明确展示哪些工具已暴露给 AI 模型的 UI
  • 调用工具时显示清晰的视觉指示
  • 针对操作向用户显示确认提示,确保有人类参与流程

能力

支持工具的服务器 MUST 声明 tools 能力:

{
  "capabilities": {
    "tools": {
      "listChanged": true
    }
  }
}

listChanged 表示服务器是否会在可用工具列表发生变化时发出通知。

声明 tools 能力的服务器 MUST 响应 tools/list 请求,返回当前可供发起请求的客户端使用的工具集合。该集合 MAY 为空,也 MAY 随时间变化(参见列表变更通知),但 MUST NOT 因连接不同而变化,也不得因同一连接上的其他请求产生副作用而变化。由于凭据是逐请求输入,而非连接状态,该集合 MAY 根据请求携带的授权而变化,例如只返回调用者获授作用域允许的工具。

服务器 SHOULD 以确定性顺序返回工具,即底层工具集合未发生变化时,各次请求中的顺序保持一致。确定性顺序使客户端能够可靠缓存工具列表,并在工具被纳入模型上下文时提高 LLM 提示词缓存命中率。

协议消息

列出工具

客户端通过发送 tools/list 请求发现可用工具。此操作支持分页和缓存。

请求:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
    "cursor": "optional-cursor-value"
  }
}

响应:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "complete",
    "tools": [
      {
        "name": "get_weather",
        "title": "Weather Information Provider",
        "description": "Get current weather information for a location",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City name or zip code"
            }
          },
          "required": ["location"]
        },
        "icons": [
          {
            "src": "https://example.com/weather-icon.png",
            "mimeType": "image/png",
            "sizes": ["48x48"]
          }
        ]
      }
    ],
    "nextCursor": "next-page-cursor",
    "ttlMs": 300000,
    "cacheScope": "public"
  }
}

调用工具

客户端通过发送 tools/call 请求调用工具:

请求:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "location": "New York"
    }
  }
}

响应:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resultType": "complete",
    "content": [
      {
        "type": "text",
        "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
      }
    ],
    "isError": false
  }
}

需要输入的工具结果

服务器 MAY 使用 tools/call 的 InputRequiredResult 响应,表示完成工具调用之前需要额外输入。此过程遵循多轮往返请求机制。

使用输入响应重试请求时,客户端在请求参数中包含 inputResponses,并在服务器提供了 requestState 时一并包含该字段:

需要输入的响应:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resultType": "input_required",
    "inputRequests": {
      "github_login": {
        "method": "elicitation/create",
        "params": {
          "mode": "form",
          "message": "Please provide your GitHub username",
          "requestedSchema": {
            "type": "object",
            "properties": {
              "name": { "type": "string" }
            },
            "required": ["name"]
          }
        }
      }
    },
    "requestState": "eyJsb2NhdGlvbiI6Ik5ldyBZb3JrIn0..."
  }
}

使用输入响应重试:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "location": "New York"
    },
    "inputResponses": {
      "github_login": {
        "action": "accept",
        "content": {
          "name": "octocat"
        }
      }
    },
    "requestState": "eyJsb2NhdGlvbiI6Ik5ldyBZb3JrIn0..."
  }
}

请注意,初始请求与重试请求的 JSON-RPC id MUST 不同。

列表变更通知

当可用工具列表发生变化时,声明了 listChanged 能力的服务器 SHOULD 向已打开 subscriptions/listen 流并设置 toolsListChanged: true 的客户端发送通知:

{
  "jsonrpc": "2.0",
  "method": "notifications/tools/list_changed"
}

消息流

  sequenceDiagram
    participant LLM
    participant Client
    participant Server

    Note over Client,Server: Discovery
    Client->>Server: tools/list
    Server-->>Client: List of tools

    Note over Client,LLM: Tool Selection
    LLM->>Client: Select tool to use

    Note over Client,Server: Invocation
    Client->>Server: tools/call
    Server-->>Client: Tool result
    Client->>LLM: Process result

    opt listChanged
      Client->>Server: subscriptions/listen (toolsListChanged: true)
      Server--)Client: notifications/subscriptions/acknowledged
      Note over Client,Server: Updates
      Server--)Client: notifications/tools/list_changed
      Client->>Server: tools/list
      Server-->>Client: Updated tools
    end

数据类型

Tool

工具定义包括:

  • name:工具的唯一标识符
  • title:用于展示的可选、人类可读的工具名称
  • description:对功能的人类可读描述
  • icons:用于在用户界面中展示的可选图标数组
  • inputSchema:定义预期参数的 JSON Schema
    • 遵循 JSON Schema 使用指南
    • 未提供 $schema 字段时,默认为 2020-12
    • MUST 是有效的 JSON Schema 对象(不能为 null)
    • 对于没有参数的工具,使用以下任一有效方式:
      • { "type": "object", "additionalProperties": false } - 推荐:明确只接受空对象
      • { "type": "object" } - 接受任何对象(包括带属性的对象)
    • 属性 MAY 包含 x-mcp-header 注解,以将参数值暴露为 HTTP Header
  • outputSchema:定义预期输出结构的可选 JSON Schema
  • annotations:描述工具行为的可选属性
出于信任、安全与防护考虑,除非工具注解来自可信服务器,否则客户端 MUST 将其视为不可信输入。

工具名称

  • 工具名称长度 SHOULD 为 1 到 128 个字符(含边界)。
  • 工具名称 SHOULD 区分大小写。
  • SHOULD 只允许以下字符:ASCII 大写和小写字母(A-Z、a-z)、数字(0-9)、下划线(_)、连字符(-)和点(.)。
  • 工具名称 SHOULD NOT 包含空格、逗号或其他特殊字符。
  • 工具名称在单个服务器内 SHOULD 唯一。
  • 有效工具名称示例:
    • getUser
    • DATA_EXPORT_v2
    • admin.tools.list

工具名称只要求在单个服务器范围内唯一。聚合多个服务器工具的客户端或代理 MAY 遇到名称冲突(例如两个服务器都暴露名为 search 的工具),并 SHOULD 实现消歧策略,例如为工具名称添加服务器标识符前缀。

服务器 name(来自 serverInfo)不保证在不同服务器间唯一,因此 SHOULD NOT 依赖它进行消歧。

x-mcp-header

x-mcp-header 扩展属性允许服务器指定特定工具参数,在使用可流式 HTTP 传输(英文)时将这些参数镜像到 HTTP Header。这使网络中间设备(负载均衡器、代理、WAF)无需解析请求正文,即可根据参数值路由和处理请求。

x-mcp-header 属性直接放在待镜像属性的 JSON Schema 中。其值指定生成的 Mcp-Param-{name} HTTP Header 中的名称部分。

x-mcp-header 值的约束:

  • MUST NOT 为空
  • MUST 匹配 HTTP 字段名称 token 语法(1*tchar,RFC 9110 第 5.1 节)
  • MUST NOT 包含控制字符,包括回车(CR,\r)或换行(LF,\n)
  • 在 inputSchema 的所有 x-mcp-header 值中,MUST 不区分大小写地保持唯一
  • MUST 仅应用于原始类型(integer、string、boolean)的参数。不允许用于 number 类型的参数。integer 值 MUST 位于 IEEE754 双精度浮点数可安全表示的整数范围内(−253+1 到 253−1)
  • MUST 仅应用于从 schema 根节点_静态可达_的属性,其定义见从工具参数生成自定义 Header(英文);该章节也定义了如何从调用参数中提取 Header 值

使用可流式 HTTP 传输的客户端 MUST 拒绝任何 x-mcp-header 值违反这些约束的工具定义。拒绝意味着客户端 MUST 从 tools/list 的结果中排除无效工具。客户端拒绝工具定义时 SHOULD 记录警告,其中包括工具名称和拒绝原因。这样可以确保单个格式错误的工具定义不会妨碍其他有效工具的使用。使用其他传输(例如 stdio)的客户端 MAY 完全忽略 x-mcp-header 注解。

带 x-mcp-header 的工具定义示例:

{
  "name": "execute_sql",
  "description": "Execute SQL on Google Cloud Spanner",
  "inputSchema": {
    "type": "object",
    "properties": {
      "region": {
        "type": "string",
        "description": "The region to execute the query in",
        "x-mcp-header": "Region"
      },
      "query": {
        "type": "string",
        "description": "The SQL query to execute"
      }
    },
    "required": ["region", "query"]
  }
}

在此示例中,当使用 "region": "us-west1" 调用工具时,客户端会向 HTTP 请求添加 Header Mcp-Param-Region: us-west1。

服务器开发者 SHOULD NOT 使用 x-mcp-header 标记敏感参数(密码、API key、token、PII),因为网络中间设备可以看到 Header 值。

工具结果

工具结果可以包含结构化或非结构化内容。

非结构化内容通过结果的 content 字段返回,可以包含多个不同类型的内容项:

所有内容类型(文本、图像、音频、资源链接和嵌入式资源)都支持可选的注解,用于提供受众、优先级和修改时间等元数据。这与资源和提示词使用的注解格式相同。

文本内容

{
  "type": "text",
  "text": "Tool result text"
}

图像内容

{
  "type": "image",
  "data": "base64-encoded-data",
  "mimeType": "image/png",
  "annotations": {
    "audience": ["user"],
    "priority": 0.9
  }
}

音频内容

{
  "type": "audio",
  "data": "base64-encoded-audio-data",
  "mimeType": "audio/wav"
}

资源链接

工具 MAY 返回指向资源的链接,以提供额外上下文或数据。此时,工具返回一个可由客户端订阅或获取的 URI:

{
  "type": "resource_link",
  "uri": "file:///project/src/main.rs",
  "name": "main.rs",
  "description": "Primary application entry point",
  "mimeType": "text/x-rust"
}

资源链接支持与常规资源相同的资源注解,帮助客户端理解如何使用这些资源。

工具返回的资源链接不保证会出现在 resources/list 请求的结果中。

嵌入式资源

资源 MAY 使用适当的 URI scheme嵌入,以提供额外上下文或数据。使用嵌入式资源的服务器 SHOULD 实现 resources 能力:

{
  "type": "resource",
  "resource": {
    "uri": "file:///project/src/main.rs",
    "mimeType": "text/x-rust",
    "text": "fn main() {\n    println!(\"Hello world!\");\n}",
    "annotations": {
      "audience": ["user", "assistant"],
      "priority": 0.7,
      "lastModified": "2025-05-03T14:30:00Z"
    }
  }
}

嵌入式资源支持与常规资源相同的资源注解,帮助客户端理解如何使用这些资源。

结构化内容

结构化内容以 JSON 值的形式在结果的 structuredContent 字段中返回。如果工具定义了 outputSchema,该值可以是符合此 schema 的任意 JSON 值(对象、数组、字符串、数字、布尔值或 null)。

为保持向后兼容,返回结构化内容的工具 SHOULD 同时在 TextContent 块中返回序列化后的 JSON。

structuredContent 是服务器生成的结果数据,与 LLM 的“结构化输出”(受 schema 约束的模型生成)无关。

输出 Schema

工具还可以提供输出 schema,用于验证结构化结果。若提供输出 schema:

  • 服务器 MUST 提供符合该 schema 的结构化结果。
  • 客户端 SHOULD 根据该 schema 验证结构化结果。

带输出 schema 的工具示例:

{
  "name": "get_weather_data",
  "title": "Weather Data Retriever",
  "description": "Get current weather data for a location",
  "inputSchema": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City name or zip code"
      }
    },
    "required": ["location"]
  },
  "outputSchema": {
    "type": "object",
    "properties": {
      "temperature": {
        "type": "number",
        "description": "Temperature in celsius"
      },
      "conditions": {
        "type": "string",
        "description": "Weather conditions description"
      },
      "humidity": {
        "type": "number",
        "description": "Humidity percentage"
      }
    },
    "required": ["temperature", "conditions", "humidity"]
  }
}

此工具的有效响应示例:

{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "resultType": "complete",
    "content": [
      {
        "type": "text",
        "text": "{\"temperature\": 22.5, \"conditions\": \"Partly cloudy\", \"humidity\": 65}"
      }
    ],
    "structuredContent": {
      "temperature": 22.5,
      "conditions": "Partly cloudy",
      "humidity": 65
    }
  }
}

带数组输出 schema 的工具示例:

{
  "name": "list_users",
  "title": "User List",
  "description": "Returns a list of all users",
  "inputSchema": {
    "type": "object",
    "properties": {}
  },
  "outputSchema": {
    "type": "array",
    "items": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string" },
        "email": { "type": "string" }
      },
      "required": ["id", "name", "email"]
    }
  }
}

带数组输出的工具的有效响应示例:

{
  "jsonrpc": "2.0",
  "id": 6,
  "result": {
    "resultType": "complete",
    "content": [
      {
        "type": "text",
        "text": "Found 2 users: Alice ([email protected]) and Bob ([email protected])."
      }
    ],
    "structuredContent": [
      { "id": "1", "name": "Alice", "email": "[email protected]" },
      { "id": "2", "name": "Bob", "email": "[email protected]" }
    ]
  }
}

提供输出 schema 可通过以下方式帮助客户端和 LLM 理解并正确处理结构化工具输出:

  • 对响应启用严格的 schema 验证
  • 提供类型信息,以便更好地与编程语言集成
  • 引导客户端和 LLM 正确解析并使用返回的数据
  • 改善文档和开发者体验

Schema 示例

使用默认 2020-12 schema 的工具:

{
  "name": "calculate_sum",
  "description": "Add two numbers",
  "inputSchema": {
    "type": "object",
    "properties": {
      "a": { "type": "number" },
      "b": { "type": "number" }
    },
    "required": ["a", "b"]
  }
}

显式使用 draft-07 schema 的工具:

{
  "name": "calculate_sum",
  "description": "Add two numbers",
  "inputSchema": {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
      "a": { "type": "number" },
      "b": { "type": "number" }
    },
    "required": ["a", "b"]
  }
}

无参数工具:

{
  "name": "get_current_time",
  "description": "Returns the current server time",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false
  }
}

有状态工具

本节为工具设计提供非规范性指南。协议没有状态句柄这一概念;从线上协议的角度看,句柄只是工具结果中的普通字符串,也是后续工具调用的普通参数。

MCP 没有协议级会话,因此服务器不能依赖隐式的逐连接状态来关联连续的工具调用。需要跨调用维护状态(例如购物车、已打开的浏览器上下文或数据库事务)的服务器,应由创建工具返回显式句柄,并在后续调用中接受该句柄作为参数。

例如,管理购物车的服务器可以暴露:

// → tools/call
{ "name": "create_basket", "arguments": {} }

// ← result
{
  "content": [{ "type": "text", "text": "Created basket bsk_a1b2c3" }],
  "structuredContent": { "basket_id": "bsk_a1b2c3" }
}

// → tools/call
{
  "name": "add_item",
  "arguments": { "basket_id": "bsk_a1b2c3", "sku": "..." }
}

模型负责在后续调用中传递 basket_id;服务器以该 key 存储购物车内容,并在每次调用时进行查找。

设计句柄时,服务器应考虑:

  • 授权。 对于需要身份验证的服务器,句柄是名称,而不是 capability。服务器应在每次调用时针对句柄验证调用者的授权。对于无需身份验证的服务器,句柄必然是 bearer token,因此应使用足够的熵生成(例如 UUIDv4),并设置有限的生命周期。
  • 不透明性。 编码内部结构的句柄容易被解析或猜测;不透明标识符不会暴露这些信息。
  • 生命周期。 由于句柄的存续时间超过任何单个连接,服务器应在创建工具的描述中说明保留策略(例如“购物车在 24 小时无活动后过期”),使模型在决定创建状态时能够看到该策略。
  • 过期错误。 针对已过期或未知句柄的调用应返回明确说明情况的工具执行错误,使模型能够通过创建新句柄恢复。

错误处理

工具使用两种错误报告机制:

  1. 协议错误表示请求结构本身存在问题,模型通常不太可能修复这些问题:

    这些错误以标准 JSON-RPC 错误返回:

    {
      "jsonrpc": "2.0",
      "id": 3,
      "error": {
        "code": -32602,
        "message": "Unknown tool: invalid_tool_name"
      }
    }
  2. 工具执行错误包含可操作的反馈,语言模型可以据此自行修正,并使用调整后的参数重试:

    • API 失败
    • 输入验证错误(例如日期格式错误、值超出范围)
    • 业务逻辑错误

    这些错误通过工具结果中的 isError: true 报告:

    {
      "jsonrpc": "2.0",
      "id": 4,
      "result": {
        "resultType": "complete",
        "content": [
          {
            "type": "text",
            "text": "Invalid departure date: must be in the future. Current date is 08/08/2025."
          }
        ],
        "isError": true
      }
    }

客户端 MAY 将协议错误提供给语言模型,但这类错误通常不太可能成功恢复。客户端 SHOULD 将工具执行错误提供给语言模型,以支持自行修正。

安全注意事项

  1. 服务器 MUST:

    • 验证所有工具输入
    • 实施适当的访问控制
    • 对工具调用实施速率限制
    • 清理工具输出
  2. 客户端 SHOULD:

    • 对敏感操作提示用户确认
    • 调用服务器前向用户展示工具输入,以避免恶意或意外的数据泄露
    • 将工具结果传递给 LLM 前进行验证
    • 根据 inputSchema 和 outputSchema 验证工具输入与输出时,遵循 $ref 解析要求
    • 为工具调用实现超时
    • 记录工具使用情况以供审计