Tools 与 JSON Schema 2020-12
学习目标
- 从 tools/list 和 tools/call 还原一个 MCP Tool 的发现、校验、执行、返回数据流。
- 正确理解 inputSchema、outputSchema、content、structuredContent 和 isError。
- 知道没有参数的工具也要使用有效的 object Schema。
- 区分 Schema 形状校验、业务校验、授权和普通 Tool Calling。
前置知识
需要先读 server/discover 与能力发现 以及第 04 章的 Function Calling。本文所有可运行代码只使用 Python 标准库;生产环境应使用明确支持 JSON Schema 2020-12 的校验器。
核心知识点
Tool 是服务器发布的契约
一个 Tool 至少包含:
- name:Server 内唯一的标识;
- description:给人和模型理解用途的说明;
- inputSchema:期望 arguments 的 JSON Schema;
- 可选 outputSchema:structuredContent 的 JSON Schema;
- 可选 title、icons、annotations 等展示或行为提示。
Tool 是“可调用契约”,不是 Python 函数指针,也不授予调用方权限。Host 先通过 tools/list 获取定义,再决定是否将它转换为模型的普通 Tool Calling schema。
inputSchema 的 2020-12 约束
规范建议并要求实现支持 JSON Schema 2020-12。缺少 $schema 时,默认按 2020-12 解释;显式写 draft-07 等其他方言时,双方必须按实现支持情况处理,不能静默当作另一种方言。
最重要的边界是:
- JSON 解析只说明文本是合法 JSON;
- Schema 校验只说明形状、字段和类型符合;
- 业务校验还要检查允许的日期、金额、资源范围;
- 授权检查决定当前主体是否可以做;
- 审批和沙箱才决定是否产生副作用。
没有参数的 Tool 不应使用空对象 {} 作为 inputSchema。规范要求一个有效 Schema 对象,推荐:
{
"type": "object",
"additionalProperties": false
}调用与结果
Client 发送 tools/call:
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "calculate_sum",
"arguments": {"a": 2, "b": 3},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}成功结果可以同时提供:
- content:给模型或用户消费的内容块列表,常见是 text;
- structuredContent:给程序消费的任意 JSON 值,可按 outputSchema 校验;
- resultType:现代结果的 complete;
- isError:工具执行失败时为 true,但仍是一个 Tool 结果。
未知工具、坏 arguments 形状或不支持的方法是 JSON-RPC error;外部 API 失败、业务规则失败和可由模型修正的参数错误,更适合返回 isError 为 true 的结果。
x-mcp-header 与普通参数
2026-07-28 的 Streamable HTTP 允许在 inputSchema 的 primitive 属性上标注 x-mcp-header。Client 可把值镜像到 Mcp-Param-Name,网关因此能路由而不解析 body;Server 必须比较 header 和 body,不一致时返回 -32020。它是路由优化和一致性校验,不是绕过 JSON Schema 或授权的通道。
Tool 调用数据流
用标准库验证一个最小输入 Schema
用途:标准库没有完整 JSON Schema 2020-12 校验器,下面只实现课程示例需要的 object、required、integer 和 additionalProperties。它帮助理解“形状校验在执行前发生”,不应冒充生产级校验器。
def validate_add(schema: dict, arguments: dict) -> list[str]:
errors = []
if schema.get("type") != "object" or not isinstance(arguments, dict):
return ["arguments must be an object"]
for name in schema.get("required", []):
if name not in arguments:
errors.append(f"missing required property: {name}")
properties = schema.get("properties", {})
if schema.get("additionalProperties") is False:
extra = set(arguments) - set(properties)
errors.extend(f"unexpected property: {name}" for name in sorted(extra))
for name, definition in properties.items():
if name not in arguments:
continue
value = arguments[name]
if definition.get("type") == "integer" and (
isinstance(value, bool) or not isinstance(value, int)
):
errors.append(f"{name} must be integer")
return errors
schema = {
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"},
},
"required": ["a", "b"],
"additionalProperties": False,
}
for args in ({"a": 2, "b": 3}, {"a": 2, "c": 3}):
print(args, "OK" if not validate_add(schema, args) else validate_add(schema, args))输出:
{'a': 2, 'b': 3} OK
{'a': 2, 'c': 3} ['missing required property: b', 'unexpected property: c']普通 Tool Calling 的映射边界
Host 可以把:
{
"name": "calculate_sum",
"description": "Add two integers",
"inputSchema": {
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"]
}
}映射成模型供应商的 function/tool 定义。但模型返回 arguments 以后,仍需走 MCP Client、JSON-RPC、Server 端校验和授权。JSON Schema 只描述形状,不能让模型直接拥有文件、数据库或网络权限。
术语与白话
inputSchema
术语:Tool arguments 的 JSON Schema。 白话:填写订单的表格格式,说明哪些栏位必填、类型是什么。
outputSchema
术语:structuredContent 的可选 JSON Schema。 白话:厨房给机器读的标准出餐单;content 仍可提供人能读的说明。
content
术语:ToolResult 的内容块列表。 白话:给模型或用户看的“说明纸”,可以是文字、图片、音频或资源引用。
isError
术语:表示 Tool 已执行到结果层,但失败信息可反馈给模型。 白话:订单格式没错,但厨房说库存不足;模型可以据此调整,不是协议本身坏了。
易混点
description 不是授权策略
工具说明写“只读”不会阻止函数删文件。授权、审批和执行器必须由 Host/Server 强制执行。
Schema 通过不是业务通过
字符串符合 pattern、整数在 range 内,只代表形状规则满足。资源是否属于当前租户、目标账号是否允许操作,还要由业务层验证。
content 与 structuredContent 不是二选一
程序可读取 structuredContent,模型和 UI 可读取 content。outputSchema 如果存在,Server 应让 structuredContent 与它一致;不应把未校验的任意对象当作结构化契约。
Tool list 与模型工具列表不同
tools/list 是 MCP Server 的协议目录;Host 还可以按风险、上下文预算、租户和模型供应商限制进行裁剪。
课后小问(含解析)
问题 1:为什么无参数 Tool 仍需要 type 为 object 的 Schema?
解析:Schema 必须告诉 Client/校验器 arguments 的根形状。用 type object、additionalProperties false 可以表达“只接受空对象”,比 {} 清晰且符合 2020-12 约束。
问题 2:工具抛异常时应该总是返回 JSON-RPC error 吗?
解析:未知方法、协议结构错误等应是 JSON-RPC error;可供模型理解和修正的业务失败应是 result 中 isError true。异常分类要由 SDK 适配层和业务代码明确决定。
小结
MCP Tool 是由 Server 发布的结构化能力契约。inputSchema 采用 JSON Schema 2020-12 语义,调用要经过解析、Schema、业务、授权和执行多层边界;结果可同时包含 content 与 structuredContent。它可以被映射成普通 Tool Calling,却不等于模型直接获得执行权限。
快速回顾
- tools/list 返回 Tool 定义;tools/call 执行一次调用。
- inputSchema 默认 2020-12;无参数工具推荐 object + additionalProperties false。
- Schema 只检查形状,不能代替业务校验和授权。
- content 面向内容消费,structuredContent 面向程序消费。
- Tool 执行失败可以用 isError true;协议失败用 JSON-RPC error。
阿源的知识库