Hotdry.

Article

Agent 工具网关:MCP inputSchema 与 OpenAI strict 对齐及执行前 JSON Schema 校验

依据 MCP 2025-11-25 的 JSON Schema 方言约定与 OpenAI Function Calling strict 模式要求,说明编排网关如何在 tools/list 归一化、向模型注册与 tools/call 执行前做双层校验,并给出 dialect 映射、拒绝策略与错误回灌参数表。

2026-07-06ai-systems

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: falseproperties 全部列入 required(可选字段用 typenull)。本文讨论客户端 / 网关在「注册 → 推理 → 执行」链路上的可落地校验,不评价某一 SDK 是否已内置全部步骤。

问题背景:三处 schema、两层信任边界

典型一轮 tool-use 的数据流:

  1. Catalog:MCP Server 在 tools/list 返回 inputSchema(常为 2020-12);网关还可能合并自研 OpenAPI 生成的 schema。
  2. Provider:网关把 catalog 映射为 OpenAI tools[].parameters、Anthropic input_schema 等;OpenAI Responses 路径会尝试把 schema 规范为 strict,无法兼容时回退为 strict: false 的尽力而为调用。
  3. 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 列出,直接送 OpenAI strict: true 会被 API 拒绝;若省略 strict,模型可能漏填或杜撰字段。
  • 执行前零校验:MCP 规范要求 Server MUST 校验工具输入,且将输入校验失败作为 Tool Execution ErrorisError: 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_idmcp_server_idinput_schema_dialectvalidator_idopenai_strict_eligible(布尔)、side_effect_classread / write / network)。

2. 向 OpenAI 注册时的 strict 规范化

OpenAI Function Calling 文档对 strict 的要求(摘要):

  1. parameters 中每个 object 的 additionalProperties 必须为 false
  2. 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 或本地函数前:

  1. tool_call_id 解析注册表条目;未知工具直接返回协议级错误映射(勿执行)。
  2. JSON.parse 失败:返回执行错误,isError: true,文案含 invalid_json 与解析位置(勿回显原始超长字符串)。
  3. validator.validate(parsed) 失败:收集 Ajv errors(限制条数 ≤ 5),生成简短英文或中文摘要;network/write 类工具校验失败时 不得 发起出站请求。
  4. 通过后再做业务级校验(枚举、路径 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 ErrorsTool Execution Errorsresult.isError: true)。网关映射建议:

场景 对模型暴露 对 MCP Server
未知 name 协议错误摘要 不发起 tools/call
参数不符合 inputSchema 执行错误(isError 不发起(本地拦截)
Server 返回业务校验失败 透传 isError 文本 原样
Server JSON-RPC error 可选透传(恢复率较低) 原样

Client SHOULD 把执行错误提供给模型做自修正;网关记录 tool_call_idvalidation_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:additionalPropertiesrequiredtype)。
  • 日志:默认不记录完整 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 后执行前闸门成为唯一防线。

参考来源

ai-systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com