1. 从一次工具调用失败说起:FastMCP 的 Tool 与 Prompt 到底怎么协作
如果你正在用 FastMCP 写 MCP 服务,大概率遇到过这种场景:工具函数写好了,客户端也能列出 tools 列表,但模型调用时参数对不上,或者提示词模板渲染出来是空的。问题往往不在业务逻辑,而在对 FastMCP 两个核心组件——工具(Tool)和提示词(Prompt)——的调用链路理解不够。
FastMCP 把 MCP 规范里的原语(Primitive)统一抽象成组件,基类是FastMCPComponent。其中和资源相关的两个组件(静态资源、动态资源模板)负责“读数据”,而工具和提示词负责“驱动行为”:工具让模型能执行动作,提示词让模型知道该怎么组织上下文。这两个组件配合起来,才构成一个可用的 AI 应用闭环。
这篇面向正在构建 MCP 服务的开发者,拆解 Tool 和 Prompt 的字段设计、子类差异和协作机制,并给出可复制的注册代码。最后用 TaoToken 的统一 Key/API 通道把本地服务跑通,验证工具调用和提示词渲染是否真的生效。核心检索词就是 FastMCP 工具与提示词组件,适合已经写过一两个 MCP demo、想搞清楚内部机制的人。
我试过把工具和提示词分开调试,结果发现单独看都没问题,合起来调用就报参数缺失。后来才明白,Tool 的parameters和 Prompt 的arguments是两套独立的 Schema,客户端在调用时分别走tools/call和prompts/get两条路径,不能混用。
2. TaoToken 前置准备:统一 Key 与 API 通道
在验证 FastMCP 服务之前,需要先有一个能访问模型的通道。TaoToken 提供统一的 API 入口,把不同模型的调用收敛到一个 Base URL 和一把 Key 上,这样本地 MCP 服务在测试工具调用时不用来回切换配置。
你需要准备三样东西:Base URL、API Key、Model ID。Base URL 用https://taotoken.net/api,注意这个地址不带任何查询参数。API Key 在控制台的 API Keys 页面创建,创建后复制保存,页面只显示一次。Model ID 根据你要验证的模型填写,比如做工具调用测试时选一个支持 function calling 的模型。
如果你用的是 Claude Code 这类编码工具,或者 Cline、Codex 这类支持 MCP 的客户端,配置逻辑是一样的:把 Base URL 指向 TaoToken 的 API 地址,Key 填进去,Model ID 选好。这三件套缺一不可,尤其是 Model ID,填错会直接导致 404 或模型不存在。
对于长期做编码和 Agent 开发的场景,可以考虑 Coding Plan,它在调用额度和并发上更适合持续跑 MCP 服务的验证。如果只是临时验证模型对话效果,用模型对话页面直接测就行。接入文档里有各客户端的详细配置示例,遇到路径问题可以先对照文档。
这里要强调一点:TaoToken 是统一的 API 通道,不是让你替换编辑器或客户端本身。你的 FastMCP 服务还是跑在本地,只是模型请求通过 TaoToken 转发。配置的时候把 Base URL 和 Key 写对,剩下的就是本地服务的调试。
3. 可复制配置:Tool 注册与 Prompt 模板
先看工具注册。FastMCP 里用函数定义工具是最常见的方式,底层会转成FunctionTool。FunctionTool的fn字段不是原始函数,而是剔除了注入参数后的版本,所以run方法传入的arguments能直接对齐输入 Schema。
下面是一个可复制的工具注册示例,包含参数 Schema 和超时设置:
from fastmcp import FastMCP from fastmcp.tools import Tool mcp = FastMCP("demo-server") @mcp.tool( name="search_docs", description="根据关键词搜索文档,返回匹配的片段", timeout=10.0, ) def search_docs(query: str, top_k: int = 3) -> dict: """搜索文档并返回结果""" # 实际业务逻辑替换这里 return {"query": query, "top_k": top_k, "results": []}这段代码注册后,FastMCP 会解析函数签名生成parameters,query是必填字符串,top_k是带默认值的整数。timeout字段对应Tool的timeout,单位是秒,None表示不限时。
如果你需要在不改原工具代码的前提下调整对外暴露的参数,用TransformedTool。它通过transform_args做参数重命名、隐藏和类型转换。比如把a、b改成x、y并限制为 float:
from fastmcp.tools.tool_transform import ArgTransform, TransformedTool def divide(a: int, b: int) -> float: """Divide a by b""" return a / b transform_args = { "a": ArgTransform(name="x", description="1st operand", type=float), "b": ArgTransform(name="y", description="2nd operand", type=float), } safe_tool = TransformedTool.from_tool( tool=Tool.from_function(fn=divide, name="Divide"), transform_args=transform_args, name="SafeDivide", description="Divide two numbers", )ArgTransform的hide=True配合default可以把某个参数从输入 Schema 里隐藏,实现硬编码效果。required字段能显式标记必填性,examples给 LLM 提供参数示例,提高调用准确率。
再看提示词模板。Prompt组件的arguments是PromptArgument列表,每个参数对应模板里的占位符。render方法用参数填充模板,返回字符串、Message列表或PromptResult。
from fastmcp.prompts import Prompt @mcp.prompt( name="code_review", description="生成代码审查提示词", ) def code_review(code: str, language: str = "python") -> str: return f"请审查以下 {language} 代码,指出潜在问题:\n\n{code}"注册后,客户端调用prompts/get时传入code和language,render会返回填充后的完整提示词。PromptArgument的required字段决定参数是否必填,description帮助模型理解占位符含义。
如果你用 Claude Code 或 Cline 这类客户端,配置 MCP 服务时需要在 settings 里写清楚服务启动命令。以 Cline 的 MCP 配置为例,JSON 片段如下:
{ "mcpServers": { "demo-server": { "command": "python", "args": ["-m", "demo_server"], "env": { "TAOTOKEN_API_KEY": "你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_MODEL": "你的ModelID" } } } }这段配置里 Base URL、Key、Model ID 三件套齐全,服务启动后客户端就能列出工具和提示词。注意env里的变量名要和你的服务代码读取的变量名一致,否则会出现 Key 读不到的情况。
4. 验证请求:本地服务跑通与结果确认
配置写好后,启动本地服务,用 MCP 客户端或直接发请求验证。先确认工具列表能正常返回:
python -m demo_server服务启动后,在客户端里执行tools/list,应该能看到search_docs和SafeDivide。如果列表为空,检查@mcp.tool装饰器是否生效,以及服务是否真的注册到了mcp实例上。
接着验证工具调用。用tools/call传入参数:
{ "name": "search_docs", "arguments": { "query": "FastMCP 工具", "top_k": 5 } }返回结果里content是ContentBlock列表,structured_content是结构化输出。如果返回isError: true,看错误信息是参数校验失败还是业务逻辑抛异常。参数校验失败通常是 Schema 不匹配,比如top_k传了字符串。
再验证提示词渲染。调用prompts/get:
{ "name": "code_review", "arguments": { "code": "def add(a, b): return a + b", "language": "python" } }返回的messages里应该包含填充后的完整提示词。如果messages为空,检查render方法是否返回了有效值,以及arguments里的参数名是否和PromptArgument的name一致。
模型调用这一层,通过 TaoToken 的 API 通道验证。用 curl 发一个请求:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "'"$TAOTOKEN_MODEL"'", "messages": [{"role": "user", "content": "测试"}] }'如果返回正常,说明 Key 和 Base URL 配置正确。这一步很关键,因为 MCP 服务本身不直接调模型,模型调用是客户端通过 TaoToken 通道完成的。工具和提示词只是把上下文准备好,真正驱动 AI 应用的是模型请求。
实测下来,工具调用和提示词渲染都通过后,整个链路就通了。客户端拿到工具列表,模型决定调用哪个工具,工具执行返回结果,提示词模板负责组织对话历史。这个闭环里任何一环配置错误,都会表现为模型“不听话”或“答非所问”。
5. 常见报错排查:401、local proxy failed 与 reading choices
配置过程中最容易碰到几类报错,逐个说清楚。
401 Unauthorized:Key 没传对或过期。检查Authorization头是不是Bearer加 Key,注意空格。如果 Key 是从控制台复制的,确认没有多余换行。用 TaoToken 的 API Keys 页面重新生成一个再试。
local proxy failed:本地服务启动失败或端口被占用。检查启动命令的路径是否正确,python -m demo_server要求demo_server是可导入的模块。如果用了虚拟环境,确认依赖装在了当前环境里。端口冲突的话换一个端口。
reading choices 报错:模型返回结构不符合预期,通常是 Model ID 填错或模型不支持当前请求格式。检查TAOTOKEN_MODEL是否和 TaoToken 支持的模型列表一致。如果做工具调用,确认模型支持 function calling,否则返回里没有tool_calls字段。
OAuth 相关报错:如果客户端要求 OAuth 认证,检查是不是把 API Key 和 OAuth 流程混用了。TaoToken 的 API 通道用 Bearer Token 认证,不需要额外的 OAuth 步骤。客户端配置里如果同时填了 OAuth 和 API Key,可能会冲突。
工具参数缺失:模型调用工具时少传了必填参数。检查parameters里的required列表,确认必填参数没有默认值。如果用了TransformedTool的hide=True,被隐藏的参数必须有default,否则模型无法提供值。
提示词渲染为空:render返回了空字符串或空列表。检查模板里的占位符和PromptArgument的name是否一一对应,参数名拼写错误会导致填充失败。另外确认arguments传的是字典,不是 JSON 字符串。
排查的时候建议先单独测工具,再单独测提示词,最后合起来测。分开测能快速定位是哪个组件的问题。如果工具和提示词都正常,但模型调用失败,那问题就在 TaoToken 的 Key、Base URL 或 Model ID 上。
6. 继续验证与接入:把链路跑稳
工具和提示词跑通后,下一步是把配置固化下来。把 Base URL、Key、Model ID 写进环境变量或配置文件,避免每次手动输入。Cline 的 MCP 配置、Claude Code 的 settings、Codex 的 auth.json 都是常见的落地点,三件套填全就能复用。
如果要做长期编码或 Agent 开发,Coding Plan 在调用稳定性和额度上更适合持续验证。临时测模型对话效果,用模型对话页面直接发请求就行。接入文档里有各客户端的完整配置示例,遇到路径或参数问题先对照文档。
最后提醒一点:FastMCP 的 Tool 和 Prompt 是两套独立的 Schema,客户端调用时走不同路径。工具负责执行动作,提示词负责组织上下文,两者通过模型请求串联起来。把 TaoToken 的通道配好,本地服务就能稳定验证这条链路。