Agent 编排器通常同时暴露三类工具:进程内函数、HTTP 适配器、以及从 MCP Server 同步的 tools/list。模型返回的 tool_calls 参数是不可信输入—— 可能多字段、类型漂移、或在 JSON 字符串里夹带路径与命令片段。仅依赖 MCP Server 侧「执行时再校验」会把无效请求打到外网或数据库;仅依赖 OpenAI 等提供商的 strict 模式又无法覆盖 Anthropic、自托管模型或「先注册 MCP schema、再转成各厂商 tools 形状」的适配层。MCP 基础章节规定:无 $schema 时默认 JSON Schema 2020-12,实现方 MUST 按声明或默认方言校验;OpenAI 文档则写明 strict 通过 structured outputs 约束生成,并要求 parameters 内每个 object 设 additionalProperties: false 且 properties 全部列入 required(可选字段用 type 含 null)。本文讨论客户端 / 网关在「注册 → 推理 → 执行」链路上的可落地校验,不评价某一 SDK 是否已内置全部步骤。
问题背景:三处 schema、两层信任边界
典型一轮 tool-use 的数据流:
- Catalog:MCP Server 在
tools/list返回inputSchema(常为 2020-12);网关还可能合并自研 OpenAPI 生成的 schema。 - Provider:网关把 catalog 映射为 OpenAI
tools[].parameters、Anthropicinput_schema等;OpenAI Responses 路径会尝试把 schema 规范为 strict,无法兼容时回退为strict: false的尽力而为调用。 - Execute:网关解析模型输出的 JSON 字符串,调用 MCP
tools/call或本地 handler。
断裂点包括:
- 方言不一致:MCP 允许工具显式使用 draft-07
$schema;同一工具在 Ajv 2020-12 与 draft-07 下对exclusiveMinimum、$ref解析可能不同。网关若只编译一种 dialect,会对部分 MCP 工具误报「不支持」或静默放过。 - strict 与 MCP 推荐形状不对齐:MCP 无参工具推荐
{"type":"object","additionalProperties":false};但若properties中部分字段在 MCP 里标为可选且未用required列出,直接送 OpenAIstrict: true会被 API 拒绝;若省略 strict,模型可能漏填或杜撰字段。 - 执行前零校验:MCP 规范要求 Server MUST 校验工具输入,且将输入校验失败作为 Tool Execution Error(
isError: true)回传以便模型自修正;Client 在网关层仍应 先于副作用 校验,避免一次恶意参数触发转账、删库或 SSRF 类 HTTP 工具。 - 协议错误与执行错误混用:对未知工具名应走 JSON-RPC 协议错误(如
-32602);对「JSON 可解析但不符合 inputSchema」应走执行错误并 SHOULD 喂回模型。网关若一律抛 500 或截断错误文案,会削弱自修正循环。
因此推荐把网关做成:注册期 schema 归一化 + 提供商适配(含 strict 规范化)+ 执行前 Ajv 校验 + 稳定错误码。
可落地实现:归一化、strict 适配与执行前闸门
1. Catalog 归一化(tools/list → 内部注册表)
| 步骤 | 建议 | 说明 |
|---|---|---|
解析 $schema |
无则视为 https://json-schema.org/draft/2020-12/schema |
对齐 MCP 默认方言 |
| 不支持方言 | 拒绝登记该工具,向运维告警 | MCP 要求对不支持方言返回明确错误,而非运行时崩溃 |
| 合并同名工具 | 禁止静默覆盖 | 多 MCP Server 时 (server_id, name) 作唯一键 |
| 预编译 validator | 登记时 compile(schema) 缓存 |
避免每次 tools/call 全量编译 |
| 体积上限 | 单工具 schema 序列化 ≤ 32 KiB | 防止超大 schema 拖垮模型上下文与编译内存 |
内部注册表建议字段:tool_id、mcp_server_id、input_schema_dialect、validator_id、openai_strict_eligible(布尔)、side_effect_class(read / write / network)。
2. 向 OpenAI 注册时的 strict 规范化
OpenAI Function Calling 文档对 strict 的要求(摘要):
parameters中每个 object 的additionalProperties必须为false。properties中列出的每个字段必须在required中出现;可选字段用type: ["string", "null"]等形式表达。
网关可做确定性变换(仅当工具标记为可走 OpenAI 且 schema 在支持子集内):
MCP optional 字段 "units"(不在 required)
→ required 增加 "units"
→ units.type 由 "string" 改为 ["string", "null"]
→ 根 object additionalProperties: false
| 配置项 | 生产建议 |
|---|---|
openai.strict_default |
true(对 eligible 工具) |
| strict 不可规范化时 | 显式 strict: false 并打点 tool.schema.strict_fallback;对 `side_effect_class=write |
| 多工具并行调用 | 注意 OpenAI 文档注明:部分微调快照在单轮多函数调用时会禁用 strict |
对 Chat Completions,文档写明默认非 strict;若产品仍走该 API,不能假设模型输出已受 strict 约束,执行前校验为必选项。
3. 执行前校验闸门(模型输出 → handler)
在调用 MCP tools/call 或本地函数前:
- 用
tool_call_id解析注册表条目;未知工具直接返回协议级错误映射(勿执行)。 JSON.parse失败:返回执行错误,isError: true,文案含invalid_json与解析位置(勿回显原始超长字符串)。validator.validate(parsed)失败:收集 Ajverrors(限制条数 ≤ 5),生成简短英文或中文摘要;network/write 类工具校验失败时 不得 发起出站请求。- 通过后再做业务级校验(枚举、路径 allowlist、SQL 只读角色等)——JSON Schema 无法表达的逻辑放第二层。
推荐默认参数:
| 项 | 建议值 | 说明 |
|---|---|---|
| 校验超时 | 5 ms / 工具(p99 预算) | 复杂 $ref schema 应登记时展开或拒绝 |
| 参数字节上限 | 16 KiB / tool_call | 与 OTel 采集 truncate 策略一致,防 DoS |
| 失败重试 | 不把「校验失败」计入提供商 tool 重试配额 | 避免模型死循环刷写接口 |
| 错误模板 | tool_input_invalid: {field} {keyword} |
稳定前缀便于日志聚合 |
伪代码(执行前闸门):
def dispatch_tool_call(tool_call_id: str, name: str, arguments_json: str) -> ToolResult:
entry = registry.get(name)
if entry is None:
raise ProtocolError(code=-32602, message=f"Unknown tool: {name}")
try:
args = json.loads(arguments_json)
except json.JSONDecodeError as e:
return ToolResult(is_error=True, text=f"invalid_json: {e.msg}")
if not entry.validator(args):
return ToolResult(is_error=True, text=format_ajv_errors(entry.validator.errors))
return entry.handler(args, tool_call_id=tool_call_id)
4. 与 MCP 错误语义对齐
MCP Tools 章节区分 Protocol Errors 与 Tool Execution Errors(result.isError: true)。网关映射建议:
| 场景 | 对模型暴露 | 对 MCP Server |
|---|---|---|
未知 name |
协议错误摘要 | 不发起 tools/call |
参数不符合 inputSchema |
执行错误(isError) |
不发起(本地拦截) |
| Server 返回业务校验失败 | 透传 isError 文本 |
原样 |
Server JSON-RPC error |
可选透传(恢复率较低) | 原样 |
Client SHOULD 把执行错误提供给模型做自修正;网关记录 tool_call_id、validation_stage=pre_gateway|mcp_server 便于区分责任域。
5. draft-07 与 2020-12 并存时的策略
| 策略 | 适用 | 风险 |
|---|---|---|
| 登记时升级到 2020-12 | 工具可控、schema 简单 | 复杂 draft-07 特性可能无等价 |
| 双 validator 缓存 | 需兼容历史 MCP Server | 内存与测试矩阵翻倍 |
| 拒绝登记 | 方言非 2020-12/07 | 工具不可用但边界清晰 |
MCP 实现要求 MUST 支持至少 2020-12;网关可声明仅保证 2020-12 + draft-07,其它 $schema 在 catalog 阶段拒绝。
6. 观测与审计
- 指标:
tool_validation_failed_total{tool,keyword}(低基数 keyword:additionalProperties、required、type)。 - 日志:默认不记录完整
arguments;失败时记录 schema 版本与tool_call_id。 - 与幂等 dedup 层:同一
tool_call_id校验失败后,重试应重复相同校验结果,不降级side_effect检查。
风险与边界
JSON Schema 不等于安全策略。 format: uri 无法阻止 SSRF;type: string 无法限制 shell 元字符。网络与写操作工具必须叠加 egress 网关、路径 allowlist、IAM 与审批策略(与 schema 校验正交)。
OpenAI strict 子集限制。 strict 依赖 structured outputs,部分 JSON Schema 特性不受支持(见 OpenAI Structured outputs 文档中的 supported schemas);强行规范化可能改变语义(例如丢失 oneOf 精细约束),应通过契约测试验证。
方言与 $ref 远程引用。 若 schema 含不可信的 $ref URL,编译阶段可能触发 SSRF;生产应禁止远程 ref,仅允许内联或注册表内 bundle。
性能与内存。 数千工具 × 每 schema 编译 validator 会占用可观内存;宜 LRU 缓存 validator,并对冷启动工具做异步预编译。
模型自修正循环。 反复校验失败仍可能消耗步数与 token;网关应对单 run 的 tool_input_invalid 次数设上限(例如 8 次),超限则终止 run 并升级人工。
MCP Server 信任级别。 受信 Server 仍可能返回过宽 inputSchema;网关可对 side_effect_class=network 强制叠加更严的附加 schema(例如禁止 url 字段使用内网 TLD 模式),这属于产品策略而非 MCP MUST。
多提供商漂移。 Anthropic、Gemini 等工具的 schema 形状与 strict 规则不同;「一次归一化、多端适配」需维护适配器测试夹具,避免某端 silent fallback 后执行前闸门成为唯一防线。
参考来源
- Model Context Protocol — JSON Schema usage(2025-11-25) — 默认 2020-12、方言 MUST/SHOULD、校验责任
- Model Context Protocol — Tools(2025-11-25) —
inputSchema/outputSchema、无参工具推荐形状、Protocol vs Tool Execution Errors - OpenAI API — Function calling(strict mode) —
strict: true条件、additionalProperties/required、Responses 与 Chat Completions 默认行为 - OpenAI API — Structured outputs — strict 底层机制与 supported schemas 限制
- JSON Schema Draft 2020-12 核心规范 — 方言语义基准
- JSON Schema Draft 2020-12 验证规范 — 关键字行为
- MCP 规范源码 —
schema/2025-11-25/schema.ts—CallToolRequest等协议消息形状
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。