尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

FastMCP 工具管理实战:用 @tool 装饰器与 Context 上下文构建结构化输出服务

发布时间:2026/9/29 5:28:20

资讯中心
01
ARTICLE

FastMCP 工具管理实战:用 @tool 装饰器与 Context 上下文构建结构化输出服务

FastMCP 工具管理实战:用 @tool 装饰器与 Context 上下文构建结构化输出服务
1. 从一次工具注册失败说起FastMCP 工具管理到底管什么如果你正在把本地脚本、内部 API 或者一堆零散函数封装成 MCP 工具多半会遇到这几个问题函数写好了但客户端list_tools看不到、参数校验报一堆 Pydantic 警告、工具里想打日志却拿不到请求上下文、返回一个 dict 客户端解析出来结构对不上。这些都不是业务逻辑的问题而是 FastMCP 的工具管理没配对。FastMCP 是 MCP 生态里上手成本比较低的服务器框架它把「函数注册成工具」这件事收敛到了tool装饰器上把「工具执行时能拿到什么」收敛到了Context上下文注入把「返回什么格式」收敛到了结构化输出配置。这三块合起来就是工具管理的核心。适合谁看已经写过一两个 MCP 工具、但工具一多就开始乱、想搞清楚注册机制和返回结构的开发者。下面我会给一套可复制的服务骨架把tool注册、Context注入、结构化输出三件事串起来最后用客户端实际调用验证工具列表和返回结构。2. 前置准备TaoToken 接入与 FastMCP 环境2.1 为什么这里要提 TaoTokenFastMCP 本身是服务器框架但你在本地调试工具时往往需要一个能稳定调用模型的入口来做端到端验证比如让模型决定调用哪个工具。TaoToken 提供统一的 API 入口兼容常见的模型调用方式适合放在调试链路里。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个不加 UTM。你需要先去控制台拿一个 API Key后面客户端验证时会用到。2.2 安装依赖FastMCP 的包名是fastmcp建议用虚拟环境隔离python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install fastmcp pydantic装完之后确认版本tool的参数在不同小版本里有细微差异python -c import fastmcp; print(fastmcp.__version__)2.3 目录结构我习惯把工具和服务入口分开工具一多不至于全堆在一个文件里fastmcp-demo/ ├── server.py # FastMCP 实例与启动入口 ├── tools/ │ ├── __init__.py │ ├── math_tools.py # 示例工具 │ └── text_tools.py └── client_test.py # 客户端验证脚本3. 可复制配置tool 注册、Context 注入与结构化输出3.1 服务骨架先写server.py把 FastMCP 实例建好工具模块导入进来触发注册# server.py from fastmcp import FastMCP mcp FastMCP( namedemo-tools, instructions演示 FastMCP 工具管理注册、上下文、结构化输出, ) # 导入即注册装饰器在模块加载时执行 from tools import math_tools, text_tools # noqa: E402,F401 if __name__ __main__: mcp.run()这里有个容易踩的点tool装饰器是在模块被导入时执行的所以工具模块必须被 import 一次否则list_tools里什么都没有。很多人把工具写在server.py里没事一拆文件就忘了导入。3.2 tool 装饰器注册tool支持同步和异步函数框架通过inspect.iscoroutinefunction自动识别执行模式。常用参数对照如下参数作用默认值name工具的程序化名称函数名titleUI 展示用的可读标题无description工具功能描述函数 docstringannotations附加注解信息无structured_output是否强制结构化输出None自动检测写一个带完整元数据的工具# tools/math_tools.py from fastmcp import FastMCP from pydantic import BaseModel, Field mcp FastMCP(namedemo-tools) class AddResult(BaseModel): 加法结果的结构化模型 sum: int Field(description两数之和) expression: str Field(description原始表达式) mcp.tool( nameadd_numbers, title整数加法, description对两个整数求和返回结构化结果, ) def add_numbers(a: int, b: int) - AddResult: 计算 a b 并返回结构化结果。 return AddResult(suma b, expressionf{a} {b})注意装饰器必须写成mcp.tool()带括号的形式。如果写成mcp.tool框架会抛TypeError因为它期望先调用再返回装饰器。这个错误信息不算特别直观第一次遇到容易懵。3.3 Context 上下文注入想让工具里能打日志、报进度、读资源就在函数签名里加一个Context类型注解的参数。框架的find_context_parameter会用typing.get_type_hints解析签名找到这个参数后在Tool.run里作为关键字参数注入。它支持Optional[Context]这类泛型写法。# tools/text_tools.py import asyncio from fastmcp import FastMCP from fastmcp.server.context import Context mcp FastMCP(namedemo-tools) mcp.tool( namesummarize_text, title文本摘要模拟, description模拟一个耗时任务演示进度上报与日志, ) async def summarize_text(text: str, ctx: Context) - dict: 对文本做模拟摘要过程中上报进度。 await ctx.info(f收到文本长度: {len(text)}) total 3 for i in range(1, total 1): await asyncio.sleep(0.2) await ctx.report_progress(progressi, totaltotal, messagef第 {i} 步) await ctx.debug(摘要流程结束) return {summary: text[:20] ..., steps: total}Context提供的能力包括debug/info/warning/error日志方法、report_progress进度上报、read_resource资源访问、elicit用户交互以及request_id、client_id属性。这些只在请求处理期间有效别把ctx存到全局变量里跨请求用。3.4 结构化输出配置结构化输出由structured_output参数控制三种模式None按返回类型注解自动检测True强制创建结构化工具False无条件非结构化。返回类型到输出模型的映射规则大致是返回类型注解输出模型处理方式BaseModel子类直接使用该类str/int等基本类型包装进含result字段的模型TypedDict转换为 Pydantic 模型list/dict等泛型包装进result字段上面add_numbers返回AddResult框架会直接用它作为outputSchema。而summarize_text返回dict会被包装成{result: {...}}的结构。如果你希望客户端拿到扁平结构就显式定义BaseModel返回类型别偷懒返回裸 dict。4. 验证请求启动服务并检查工具列表与返回结构4.1 启动服务python server.py默认走 stdio 传输日志会打到 stderr。看到类似Starting MCP server demo-tools就说明起来了。4.2 客户端验证工具列表写一个client_test.py用 FastMCP 自带的客户端连上去# client_test.py import asyncio from fastmcp import Client async def main(): async with Client(server.py) as client: tools await client.list_tools() for t in tools: print(f- {t.name} | {t.title} | schema{bool(t.outputSchema)}) result await client.call_tool(add_numbers, {a: 3, b: 4}) print(add_numbers -, result) result2 await client.call_tool(summarize_text, {text: FastMCP 工具管理实战}) print(summarize_text -, result2) if __name__ __main__: asyncio.run(main())运行后你应该看到两个工具都出现在列表里add_numbers的outputSchema为真summarize_text返回的是带result字段的包装结构。如果list_tools是空的先回去检查工具模块有没有被 import。4.3 用模型驱动工具调用做端到端验证工具列表对了再验证模型能不能正确选工具。把 TaoToken 的 API Key 配到环境变量export TAOTOKEN_API_KEY你的key然后在客户端里把工具暴露给模型让它根据用户问题决定调用哪个工具。这一步能验证的不只是工具注册还有description和参数 schema 是否足够清晰——模型选错工具八成是描述写得太含糊。5. 本篇常见错排查5.1 工具没出现在 list_tools最常见的原因是工具模块没被导入。tool是导入时执行的副作用拆文件后忘了 import 就静默失败。其次是装饰器写成了mcp.tool而不是mcp.tool()这种情况通常会抛TypeError但如果你在异常处理里吞掉了就看不到。5.2 同名工具被静默忽略ToolManager内部维护一个_tools字典add_tool时用get检查同名。如果warn_on_duplicate_tools为 True会记录警告但不覆盖已有工具优先保留先注册的。想更新工具必须先remove_tool再重新注册。这个策略保证了注册顺序的确定性但也意味着你改了工具代码却没生效时先怀疑是不是有同名旧工具占着位置。5.3 参数校验报 Pydantic 警告func_metadata会为参数创建ArgModelBase子类。如果参数名和BaseModel的属性冲突比如叫schema、copy框架会用别名规避但会打警告。另外StrictJsonSchema会把 JSON Schema 生成过程中的警告直接转成异常所以 schema 不合法时不是警告而是直接报错看 traceback 里的 schema 字段定位。5.4 Context 注入失败find_context_parameter依赖类型注解解析。如果你用了from __future__ import annotations又没正确解析或者参数注解写成了字符串但没在可解析作用域里就找不到 Context 参数ctx会变成必填参数导致调用报缺参。确保Context是从fastmcp.server.context正确导入的。5.5 结构化输出结构对不上返回裸dict会被包装成{result: ...}客户端如果按扁平结构解析就会失败。要么在客户端按包装结构取要么在服务端定义BaseModel返回类型。convert_result的行为还受structured_output配置影响False时直接返回非结构化内容True时返回非结构化加结构化的元组调试时打印原始CallToolResult最直观。6. 继续往下走工具管理跑通之后下一步通常是把它接到真实的编码或 Agent 流程里。如果你要长期跑编码类任务可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 想先在对话里手动验证工具调用效果用模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 需要管理多个 Key 或查看用量去控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite Key 的创建和权限配置在 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 接入细节和协议说明看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。工具注册这块我的经验是先把description和返回类型写清楚比事后调 schema 省事得多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。