news 2026/10/7 7:20:27

MCP Parameters 增加描述:用 Annotated 与 Pydantic 让工具调用参数自解释

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MCP Parameters 增加描述:用 Annotated 与 Pydantic 让工具调用参数自解释

1. 为什么 Cline 里 Parameters 总是 No description

如果你最近在本地写 MCP Server,大概率踩过这个坑:工具函数写完了,参数类型也标了,结果在 Cline 或 Windsurf 的工具面板里一看,Parameters 那一栏齐刷刷写着No description。模型拿到这个工具定义,只能靠参数名去猜——image_url到底是网络地址还是本地路径?width单位是像素还是百分比?猜错了就报错,猜对了也是运气。

这个问题的本质不在 MCP 协议,而在你给工具函数加的类型注解方式。MCP Server 在启动时会读取函数的签名,把它转成 JSON Schema 暴露给客户端。如果你只写了image_url: str,那生成的 schema 里就只有type: string,没有description字段。客户端拿不到描述,自然显示No description,模型也就失去了判断依据。

我试过最直接的对比:同一个deal_image工具,第一版用裸类型注解,Cline 面板里三个参数全是No description;改成Annotated[str, Field(description="...")]之后重新加载,描述立刻出现在面板上,模型调用时传参的准确率肉眼可见地提升。这不是玄学,是 schema 里多了一个字段带来的确定性差异。

这篇面向的是已经在写 MCP Server、并且用 Cline MCP 或 Windsurf BYOK 做客户端的人。你需要的不只是「加个 description」这么简单,而是搞清楚 Annotated 和 Pydantic Field 两种写法各自生成的 schema 长什么样、在什么场景下选哪种、以及怎么通过一次真实工具调用验证描述确实被读进去了。下面从环境准备开始,一步步把 Parameters 描述补全,最后用 TaoToken 的统一通道发一次请求,看返回的入参 JSON 里描述字段是否到位。

2. TaoToken 统一 Key 与 API 通道准备

在动手改代码之前,先把调用通道理顺。MCP Server 本身是本地进程,但你要验证「描述是否被正确读取」,需要一个能发起工具调用的模型端。这里用 TaoToken 的统一 Key 和 API 通道,好处是 Base URL 和 Key 一套配置通吃 Claude、GPT 等模型,不用为每个模型单独换 endpoint。

TaoToken 的定位是统一模型接入层,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。你需要在控制台创建一个 API Key,然后把它填到客户端的配置里。对于 Cline MCP 和 Windsurf BYOK 这类场景,关键三件套是 Base URL、API Key、Model ID,缺一不可。

具体操作路径:打开 https://taotoken.net/api-keys ,新建一个 Key,复制出来先存好。然后到 https://taotoken.net/console 确认账户状态正常。模型 ID 可以在 https://taotoken.net/doc 的模型列表里查,比如claude-sonnet-4-20250514这类标识。如果你打算长期跑编码类 Agent 任务,可以看下 https://taotoken.net/coding-plan ,按用量规划比单次调用更划算。

配置的时候有个细节容易忽略:Base URL 要填到/api这一层,不要多加/v1或/chat/completions,客户端会自动拼接。Key 的权限范围建议只勾选需要的模型,避免一个 Key 泄露影响全部额度。Model ID 必须和文档里完全一致,大小写和连字符都不能错,否则会返回模型不存在的错误。

准备好这三样之后,先别急着改 MCP 代码。你可以用 https://taotoken.net/model-chat 做一次纯文本对话,确认 Key 和通道是通的。这一步能排除掉网络和鉴权问题,后面调试 MCP 参数描述时就不会把「Key 错了」和「描述没生效」混在一起排查。通道通了,再回到 MCP Server 里补 Parameters 描述,验证链路才干净。

3. 用 Annotated 与 Pydantic Field 补全 Parameters 描述

这一节是核心,直接给可复制的配置片段。MCP Server 的工具体系基于 Pydantic,所以参数描述的写法本质上就是 Pydantic 的写法。两种主流方式:Annotated 和 Field 默认值。先看 Annotated,这是更推荐的现代写法,把类型提示和元数据分开,可读性更好。

from typing import Annotated, Literal, Any from pydantic import Field from mcp.server.fastmcp import FastMCP mcp = FastMCP("image-tools") @mcp.tool( name="deal_image", description="处理图片,支持缩放和格式转换" ) def deal_image( image_url: Annotated[str, Field(description="要处理的图片 URL,支持 http/https")], resize: Annotated[bool, Field(description="是否缩放图片")] = False, width: Annotated[int, Field(description="目标宽度,单位像素", ge=1, le=2000)] = 800, format: Annotated[ Literal["jpeg", "png", "webp"], Field(description="输出图片格式") ] = "jpeg" ) -> dict: """处理图片并返回结果路径。""" # 实现略 return {"ok": True, "width": width, "format": format}

这段代码里,每个参数都被Annotated[类型, Field(...)]包住,Field里的description就是最终会出现在 JSON Schema 里的描述文本。ge和le是数值范围约束,也会一并写进 schema,模型看到minimum: 1, maximum: 2000就知道不能传 0 或 9999。Literal配合Field会生成enum字段,模型只能在三个格式里选,不会瞎编一个gif出来。

第二种写法是直接用Field作为默认值,适合参数不多、想写得更紧凑的场景:

@mcp.tool( name="getDataFromSql", description="从数据库查询数据" ) def get_data_from_sql( query: str = Field(description="搜索查询字符串"), limit: int = Field(10, description="返回结果最大条数", ge=1, le=100) ) -> list: """根据查询条件检索数据库。""" # 实现略 return []

注意这里query没有默认值,Field(description=...)直接放在类型后面作为参数默认值。这种写法在 Pydantic v2 里是合法的,生成的 schema 和 Annotated 写法完全一致。区别在于 Annotated 把元数据和类型绑定,Field 默认值把元数据和默认值绑定。如果参数有默认值且你想强调默认值,Field 写法更直观;如果参数是必填且元数据复杂,Annotated 更清晰。

再看一个实际项目里的注册函数,把工具名、描述和参数描述都写全:

def register(mcp): @mcp.tool( name="text2Voice", description="文字转语音" ) def text2Voice( text: Annotated[str, Field(description="传入文本,建议不超过 500 字")] ) -> dict[str, Any] | None: """文字转语音并返回音频文件信息。""" res = utils.Text2Voice(text=text, download=True) logging.info(f"text2Voice res: {res}") return res

这里text参数的描述是「传入文本,建议不超过 500 字」,模型在调用时会把这个描述作为提示,知道要控制文本长度。如果你不写描述,模型可能塞进去一整篇文章,导致 TTS 接口超时或截断。描述不只是给人看的,它是模型决策的一部分。

关于Field的常用参数,可以对照下面这张表:

参数作用生成的 schema 字段
description参数说明description
ge大于等于minimum
le小于等于maximum
gt / lt严格大于/小于exclusiveMinimum / exclusiveMaximum
min_length / max_length字符串长度minLength / maxLength
pattern正则约束pattern
default默认值default

把这些填全,MCP 客户端拿到的 Parameters 就不再是No description,而是每个参数都有明确说明、约束和默认值。模型调用时传参的准确率会明显提升,尤其是参数名有歧义的时候,描述就是唯一的消歧依据。

4. 发起工具调用验证描述是否被读取

代码改完,重启 MCP Server,接下来要验证描述真的进了 schema。最直接的方式是让模型发起一次工具调用,然后看返回的入参 JSON。这里用 TaoToken 的模型对话入口配合 Cline MCP 做一次实测。

先在 Cline 的 MCP 配置里加上你的本地 Server。Cline 的 MCP 配置文件通常在~/.cline/mcp_settings.json或项目下的.cline/mcp.json,格式如下:

{ "mcpServers": { "image-tools": { "command": "python", "args": ["-m", "your_mcp_server"], "env": { "PYTHONUNBUFFERED": "1" } } } }

保存后重启 Cline,在工具面板里找到deal_image,展开 Parameters,应该能看到每个参数的描述文本。如果还是No description,说明 Server 没重启或者 schema 没刷新,先排查这个。

确认面板显示正常后,在 Cline 对话框里发一条指令:「帮我把 https://example.com/a.jpg 缩放到宽度 1200,输出 webp 格式」。模型会调用deal_image工具,Cline 会弹出工具调用确认,里面显示实际传入的参数 JSON。你要看的就是这个 JSON 里每个字段的值是否符合描述约束:image_url是完整 URL,width是 1200 且在 1 到 2000 之间,format是webp而不是别的。

如果你想更直接地看 schema,可以在 MCP Server 启动后,用 TaoToken 的 API 发一次请求,让模型返回它看到的工具定义。用 curl 示例:

curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [ {"role": "user", "content": "列出 deal_image 工具的参数描述"} ], "tools": [ { "type": "function", "function": { "name": "deal_image", "description": "处理图片,支持缩放和格式转换", "parameters": { "type": "object", "properties": { "image_url": {"type": "string", "description": "要处理的图片 URL,支持 http/https"}, "resize": {"type": "boolean", "description": "是否缩放图片", "default": false}, "width": {"type": "integer", "description": "目标宽度,单位像素", "minimum": 1, "maximum": 2000, "default": 800}, "format": {"type": "string", "enum": ["jpeg", "png", "webp"], "description": "输出图片格式", "default": "jpeg"} }, "required": ["image_url"] } } } ] }'

返回的choices[0].message.tool_calls里会包含模型决定调用的参数。如果描述被正确读取,模型传的width不会超出 2000,format不会出现gif。你可以故意把描述去掉再发一次同样的请求,对比模型传参的差异——没有描述时,模型更容易传错格式或超范围的值。

实测下来,描述补全后模型一次调用成功的概率从「经常要重试」变成「基本一次过」。尤其是format这种枚举参数,有描述和没描述的差别最明显:没描述时模型可能传jpg,有描述时它知道只能从jpeg/png/webp里选。

5. 常见报错与排查对照

改完代码到验证成功之间,通常会撞上几个典型报错。下面按真实遇到的顺序列出来,对照排查。

401 Unauthorized:这个最常见,出现在用 TaoToken 发请求时。原因通常是 Key 没填对、Key 被禁用、或者 Base URL 写错。检查Authorization: Bearer后面的 Key 是否和控制台里的一致,Base URL 是否是https://taotoken.net/api而不是别的路径。如果 Key 刚创建,等几秒再试,有时候有缓存延迟。

local proxy failed / connection refused:Cline 连不上本地 MCP Server。先确认 Server 进程在跑,ps aux | grep your_mcp_server看一下。然后检查mcp_settings.json里的command和args是否指向正确的 Python 解释器和模块路径。如果 Server 启动时报了 import 错误,Cline 这边只会显示连接失败,实际原因在 Server 的 stderr 里,把PYTHONUNBUFFERED=1加上能看到实时日志。

reading 'choices' of undefined:这个报错说明 API 返回的结构和你预期的不一样。常见原因是 Model ID 写错了,TaoToken 返回了一个错误对象而不是正常的 chat completion 结构。去 https://taotoken.net/doc 核对模型 ID,确保和文档里完全一致。另一个可能是请求体里tools字段格式不对,检查parameters里的 JSON Schema 是否合法。

OAuth 相关报错:如果你用的是 Claude Code 或某些需要 OAuth 的客户端,可能会遇到 token 过期或 scope 不足。这种情况建议改用 API Key 方式接入,在 https://taotoken.net/api-keys 创建一个专用 Key,填到客户端的 API Key 字段,避开 OAuth 流程。Claude Code 的接入文档在 https://taotoken.net/doc 里有说明,按步骤配 Base URL、Key、Model ID 三件套即可。

Parameters 仍然显示 No description:代码改了但面板没变。先确认 Server 真的重启了,Python 进程是新的。然后检查@mcp.tool装饰器是否在函数定义之前,Annotated和Field是否正确导入。如果用的是Field默认值写法,确认 Pydantic 版本是 v2,v1 的Field行为不同。最后,有些客户端会缓存工具 schema,清一下客户端缓存或重启客户端。

模型传参仍然出错:描述写了但模型不遵守。检查描述文本是否足够明确,比如「宽度」不如「目标宽度,单位像素,范围 1-2000」清晰。枚举参数一定要用Literal或enum,光靠描述文字约束力不够。如果参数之间有依赖关系,在工具描述里说明,比如「format 为 webp 时 width 不能超过 1600」。

排查顺序建议从外到内:先确认 TaoToken 通道通(用 model-chat 发一条纯文本),再确认 MCP Server 能启动(看日志),再确认 schema 里有描述(看客户端面板),最后确认模型调用时传参正确(看入参 JSON)。一层层排除,不要跳步。

6. 把描述写进工具定义,让调用不再靠猜

回到最开始的问题:Cline 里 Parameters 显示No description,模型调用靠猜。根因是工具函数的类型注解没有携带描述元数据,MCP Server 生成的 JSON Schema 里缺了description字段。解法就是用Annotated或Field把描述、约束、默认值补全,让 schema 自解释。

具体操作上,优先用Annotated[类型, Field(description="...")]的写法,把类型和元数据分开,可读性和可维护性都更好。参数有默认值时可以用Field默认值写法,更紧凑。枚举参数一定用Literal配合Field,数值参数把ge/le填上,字符串参数考虑min_length/max_length。这些元数据会原样进入 JSON Schema,客户端面板和模型都能读到。

验证环节用 TaoToken 的统一通道发一次工具调用,看返回的入参 JSON 是否符合描述约束。Base URL 用https://taotoken.net/api,Key 在 https://taotoken.net/api-keys 创建,Model ID 在 https://taotoken.net/doc 核对。三件套配好之后,Cline MCP 和 Windsurf BYOK 都能用同一套配置。

最后给一个实用建议:把工具描述当成 API 文档来写,但读者是模型。模型不会问你「这个参数什么意思」,它只会根据描述做决策。描述写得越明确,调用越准。我现在的习惯是每个参数至少写清楚「是什么、什么格式、什么范围」,枚举参数把可选值列全,有依赖关系的在工具描述里说明。这样下来,工具调用的返工率能降一大截。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 7:20:13

健康照明技术解析:从光源参数到桌面微环境的技术拆解

本文面向关注桌面照明技术的开发者、产品经理及有读写照明需求的用户,系统拆解健康桌面照明的核心技术指标与实现方案,涵盖光生物安全、光学结构设计与桌面微环境调节三个维度。 在居家办公与在线学习成为常态的当下,人们在桌面前的停留时间显…

作者头像 李华
网站建设 2026/10/7 7:19:15

个人AI助理选型建议:OpenClaw、NullClaw 与 TaoToken 统一接入实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华