4.1 输入 Schema:从类型注解到 JSON Schema


4.1 输入 Schema:从类型注解到 JSON Schema

本节摘要:本章的核心命题是「类型即契约」,本节先讲输入这一半——函数的参数类型注解,如何变成给模型的输入 JSON Schema。背后是 SDK 的函数元数据(func_metadata)机制:它在你用 @mcp.tool() 装饰的那一刻,扫描函数签名,把 Python 类型映射成 JSON Schema 的类型,把参数名映射成 Schema 的字段,把文档字符串映射成描述。我们会讲透这套映射规则——基础类型、容器类型、可选类型、Pydantic 模型各自怎么映射,以及为什么这套机制能替代手写 Schema。读完本节,你能向别人讲清楚「为什么 a: int 就够」。

一、func_metadata:扫描函数的引擎

先认识幕后的主角:函数元数据(func_metadata)。它是 SDK 内部的一个工具函数,负责扫描任意 Python 函数,提取出「这个函数需要什么参数、什么类型、有没有默认值」的完整描述。

@mcp.tool() 装饰时发生的事 │ ▼ 调用 func_metadata(add) │ ▼ 扫描函数签名 提取出: - 参数名:[a, b] - 类型:{a: int, b: int} - 必填性:{a: 必填, b: 必填} - 默认值:{} │ ▼ 转成 JSON Schema { "type": "object", "properties": { "a": {"type": "integer"}, "b": {"type": "integer"} }, "required": ["a", "b"] } │ ▼ 这个 Schema 发给模型

整个流程在你装饰的那一刻就完成了,不需要运行时反复扫描。这就是为什么第 1.2 节说「你不写 Schema,SDK 从函数读出来」——读的动作,就是 func_metadata 干的。

二、基础类型的映射

Python 的基础类型,直接映射到 JSON Schema 的基础类型:

Python 类型注解 映射成的 JSON Schema 示例参数
int {"type": "integer"} a: int
float {"type": "number"} rate: float
str {"type": "string"} name: str
bool {"type": "boolean"} verbose: bool

这几个映射是 1:1 的、可预测的。模型看到的 Schema 与你写的注解完全对应:

@mcp.tool() def set_rate(name: str, rate: float, enabled: bool) -> str: """Set a rate for a named target.""" ...

模型看到的 Schema 大致是:

{ "name": {"type": "string"}, "rate": {"type": "number"}, "enabled": {"type": "boolean"} }

三、容器类型:list、dict、tuple

容器类型的映射稍复杂,但仍是规则化的:

Python 类型注解 映射成的 JSON Schema
list[str] {"type": "array", "items": {"type": "string"}}
list[int] {"type": "array", "items": {"type": "integer"}}
dict[str, int] {"type": "object", "additionalProperties": {"type": "integer"}}
tuple[str, int] 定长数组(每个位置独立类型)
@mcp.tool() def batch_process(items: list[str], mapping: dict[str, int]) -> list[str]: """Process a batch of items with a mapping.""" ...

模型据此知道:items 是字符串数组、mapping 是「键字符串、值整数」的对象。这种映射让模型能正确构造嵌套结构的参数,而不会传错类型。

四、可选类型与默认值

可选类型(Optional / X | None)与默认值,决定参数的「必填性」:

@mcp.tool() def search( query: str, # 必填,无默认值 limit: int = 10, # 可选,有默认值 offset: int = 0, # 可选 filter: str | None = None, # 可选,可为 None ) -> list: """Search items.""" ...

映射规则:

写法 必填性 Schema 里的体现
query: str(无默认值) 必填 required 数组里
limit: int = 10(有默认值) 可选 不在 required 里,Schema 标注 default: 10
filter: str | None = None 可选 类型是 ["string", "null"]

这套「默认值 = 可选参数」的规则,让模型知道「哪些参数必须填、哪些可以省略」。这是减少模型错误调用的关键——必填参数少了模型会瞎填,可选参数多了模型会困惑,清晰的必填性让模型准确判断。

💡 技巧:把「这个参数模型必须显式提供」的设为必填(无默认值),把「模型可以省略、用默认即可」的设为可选(有默认值)。这个判断直接影响模型调用的准确率。

五、Pydantic 模型:结构化参数

当参数本身是个复杂结构,用 Pydantic BaseModel:

from pydantic import BaseModel class SearchQuery(BaseModel): text: str limit: int = 10 tags: list[str] = [] @mcp.tool() def advanced_search(query: SearchQuery) -> list: """Advanced search with structured query.""" ...

func_metadata 会把 SearchQuery 展开成嵌套的 JSON Schema:

{ "query": { "type": "object", "properties": { "text": {"type": "string"}, "limit": {"type": "integer", "default": 10}, "tags": {"type": "array", "items": {"type": "string"}} }, "required": ["text"] } }

模型据此能构造出嵌套的对象参数。这种「参数即模型」的写法,让你能用 Pydantic 表达任意复杂的参数结构,而不必手写嵌套 Schema。

六、为什么这套机制能替代手写 Schema

现在退一步,看 func_metadata 替你省了什么。如果不用它,你要手写:

# 手写方式(不用 SDK 推断) tool_schema = { "name": "search", "description": "Search items.", "inputSchema": { "type": "object", "properties": { "query": {"type": "string"}, "limit": {"type": "integer", "default": 10}, # ... 几十行 }, "required": ["query"] } }

而用 SDK,你只写:

@mcp.tool() def search(query: str, limit: int = 10) -> list: """Search items.""" ...

手写 Schema 的所有信息,都在类型注解里func_metadata 把它们读出来、转成 Schema,你不用维护两份(类型一份、Schema 一份),永远不会出现「类型改了、Schema 忘改」的割裂。

⚠️ 注意:这套机制的代价是——类型注解必须准确。如果你写 a: int 但实际接受字符串,SDK 生成的 Schema 会告诉模型「这是整数」,模型传整数进来,你的代码可能出错。所以「类型即契约」也意味着「类型注解就是承诺」,必须如实写。

本节要点回顾

  1. func_metadata 是扫描函数、提取元数据的引擎,装饰时运行一次。
  2. 基础类型 1:1 映射:int→integerfloat→numberstr→stringbool→boolean
  3. 容器类型规则映射:list[str]→arraydict[str, int]→object
  4. 默认值决定必填性:无默认值→必填(进 required),有默认值→可选。
  5. Pydantic 模型展开成嵌套 Schema,表达任意复杂的参数结构。
  6. 整套机制替代手写 Schema,避免「类型一份、Schema 一份」的割裂。
  7. 「类型即契约」意味着类型注解就是承诺,必须如实写,否则模型传错类型。

输入这一半清楚了,下一节讲输出那一半——返回类型注解如何变成结构化输出。


作者与出处
原作者: 灏天文库
来源:modelcontextprotocol
许可证:MIT
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: 灏天文库 转发
评论区 (0)
U