1. 为什么要在 FastMCP v2 里接 TaoToken
FastMCP v2 是 Python 生态里把 MCP 服务端和客户端都封装得比较顺手的库,你写几个装饰器就能把工具、资源、提示词暴露给 LLM。但真到本地工具链落地时,很多人会卡在同一个地方:模型通道怎么统一。每个 MCP 工具如果各自去读环境变量、各自拼 base_url、各自管 Key,配置会散落在十几个文件里,换一次 Key 要改一圈。
TaoToken 在这里扮演的角色就是统一 Key 和 API 通道。它提供 OpenAI 兼容的接口形态,你只需要在配置里写一次base_url和api_key,FastMCP 服务端里所有需要调用模型的地方(比如ctx.sample采样、工具内部的补全请求)都走同一个出口。对本地 LLM 工具链来说,这意味着config.toml和settings.json可以成为唯一的配置源,而不是让 Key 到处漂。
这篇面向的是已经在用 Python 搭本地 MCP 工具链、想让模型交互配置收敛的人。我会给出可直接复制的config.toml骨架、settings.json关键字段,然后跑一次真实的模型交互请求验证整条链路通不通。FastMCP v2 负责协议层,TaoToken 负责模型通道层,两者拼起来才是完整的本地智能交互。
2. TaoToken 前置:Key 与通道准备
在写配置之前,先把 TaoToken 这边的两样东西拿到手:API Key 和 base_url。API Key 在控制台的 API Keys 页面创建,建议按用途分 Key,比如本地开发一个、CI 一个,方便后面排查问题时定位。
base_url 用https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 OpenAI 兼容客户端的base_url使用。FastMCP v2 本身不绑定某一家模型供应商,它通过采样回调把请求交给你的客户端逻辑,所以你在客户端侧配置 TaoToken 即可。
创建 Key 的入口在这里:
控制台 API Keys:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite
拿到 Key 之后不要直接写进代码,先放进环境变量或者本地配置文件。我习惯用.env加python-dotenv,但 FastMCP 的config.toml也支持直接读环境变量占位,两种方式下面都会给。
如果你还没确认模型通道是否可用,可以先用模型对话页面做一次最小验证,确认 Key 有效再往下配:
模型对话:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite
这一步的意义是隔离变量。如果后面 FastMCP 请求失败,你能确定是配置问题而不是 Key 本身的问题。
3. 可复制配置:config.toml 骨架与 settings.json
FastMCP v2 的配置分两层:config.toml描述 MCP 服务端和客户端的连接关系,settings.json描述模型通道和运行时参数。下面这份骨架可以直接复制,改掉 Key 占位即可。
3.1 config.toml 骨架
# config.toml # FastMCP v2 + TaoToken 本地工具链配置骨架 [server] name = "taotoken-mcp-server" transport = "http" host = "127.0.0.1" port = 8000 [server.logging] level = "INFO" format = "text" [client] # 客户端默认连接的 MCP 服务端 default_server = "local" [client.servers.local] url = "http://127.0.0.1:8000/mcp" transport = "http" timeout = 30 [client.servers.local_stdio] command = "python" args = ["./mcp_server.py"] transport = "stdio" [model] # TaoToken 统一通道 provider = "openai-compatible" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" default_model = "claude-3-5-sonnet" timeout = 60 max_retries = 2 [model.sampling] temperature = 0.7 max_tokens = 1024几个关键点说明。[model]段里的base_url固定写https://taotoken.net/api,api_key_env指向环境变量名而不是明文 Key,这样配置文件可以进版本库。default_model按你实际开通的模型填,这里只是占位。[client.servers]同时给了 HTTP 和 STDIO 两种连接方式,本地调试用 STDIO 更省事,跨进程用 HTTP。
3.2 settings.json 关键字段
有些 FastMCP 的宿主环境(比如某些编辑器插件或本地 Agent 框架)读的是settings.json,字段和config.toml有对应关系:
{ "mcpServers": { "taotoken-local": { "url": "http://127.0.0.1:8000/mcp", "transport": "http", "env": { "TAOTOKEN_API_KEY": "${TAOTOKEN_API_KEY}", "TAOTOKEN_BASE_URL": "https://taotoken.net/api" } } }, "model": { "provider": "openai-compatible", "baseUrl": "https://taotoken.net/api", "apiKeyEnv": "TAOTOKEN_API_KEY", "defaultModel": "claude-3-5-sonnet" }, "sampling": { "temperature": 0.7, "maxTokens": 1024 } }${TAOTOKEN_API_KEY}这种写法在多数宿主里会被环境变量替换,如果你的宿主不支持,就改成直接读.env。注意baseUrl和base_url的命名差异,settings.json用驼峰,config.toml用下划线,这是两套配置体系的习惯,别写混。
3.3 环境变量落地
# .env TAOTOKEN_API_KEY=sk-你的Key TAOTOKEN_BASE_URL=https://taotoken.net/api# load_env.py from dotenv import load_dotenv import os load_dotenv() def get_taotoken_config(): return { "base_url": os.getenv("TAOTOKEN_BASE_URL", "https://taotoken.net/api"), "api_key": os.environ["TAOTOKEN_API_KEY"], }这样配置层就收敛完了。接下来写服务端和客户端代码,让它们读这份配置。
4. 验证请求:跑通一次模型交互
配置写完必须验证,否则你不知道是配置错了还是代码错了。这一步分两段:先起一个带采样能力的 FastMCP 服务端,再用客户端发一次真实请求,观察模型返回。
4.1 服务端:暴露一个会调用模型的工具
# mcp_server.py from fastmcp import FastMCP, Context import asyncio mcp = FastMCP(name="taotoken-mcp-server") @mcp.tool async def summarize_text(text: str, ctx: Context) -> str: """调用模型对输入文本做摘要。""" prompt = f"请用一句话总结下面这段内容:\n{text}" response = await ctx.sample(prompt) return response.text.strip() @mcp.tool def add(a: int, b: int) -> int: """两数相加,用于验证工具链路。""" return a + b if __name__ == "__main__": mcp.run(transport="http", host="127.0.0.1", port=8000)ctx.sample是 FastMCP v2 的采样入口,它会把请求交给客户端侧的采样回调,而采样回调里我们接 TaoToken。这样服务端代码完全不碰 Key,职责干净。
4.2 客户端:把采样回调接到 TaoToken
# mcp_client.py from fastmcp import Client from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext from openai import OpenAI import asyncio import os client_llm = OpenAI( base_url=os.getenv("TAOTOKEN_BASE_URL", "https://taotoken.net/api"), api_key=os.environ["TAOTOKEN_API_KEY"], ) async def sampling_handler( messages: list[SamplingMessage], params: SamplingParams, context: RequestContext, ) -> str: """把 FastMCP 的采样请求转发到 TaoToken。""" oai_messages = [ {"role": m.role, "content": m.content.text} for m in messages ] resp = client_llm.chat.completions.create( model="claude-3-5-sonnet", messages=oai_messages, temperature=params.temperature or 0.7, max_tokens=params.max_tokens or 1024, ) return resp.choices[0].message.content client = Client( "http://127.0.0.1:8000/mcp", sampling_handler=sampling_handler, ) async def main(): async with client: await client.ping() tools = await client.list_tools() print("可用工具:", [t.name for t in tools]) result = await client.call_tool("add", {"a": 3, "b": 4}) print("add 结果:", result.content[0].text) result = await client.call_tool( "summarize_text", {"text": "FastMCP v2 让 MCP 服务端开发变得简单,配合统一模型通道可以快速搭建本地工具链。"}, ) print("摘要结果:", result.content[0].text) if __name__ == "__main__": asyncio.run(main())4.3 预期输出
先起服务端:
python mcp_server.py再跑客户端:
python mcp_client.py正常的话你会看到类似输出:
可用工具: ['summarize_text', 'add'] add 结果: 7 摘要结果: FastMCP v2 简化了 MCP 服务端开发,配合统一模型通道能快速搭建本地工具链。add走的是纯工具链路,不碰模型,用来确认 MCP 协议层通。summarize_text走的是采样链路,经过 TaoToken 到模型再回来,用来确认模型通道通。两条都通,说明config.toml和settings.json的配置真正生效了。
5. 本篇常见错排查
配置跑不通时,错误信息往往指向几个固定位置。下面按我实际遇到的顺序列。
5.1 401 或 invalid api key
最常见。先确认TAOTOKEN_API_KEY环境变量在当前 shell 里真的存在:
echo $TAOTOKEN_API_KEY如果为空,说明.env没被加载,或者你是在服务端进程里读的但服务端没加载.env。FastMCP 服务端和客户端是两个进程,环境变量要各自可见。用python-dotenv的话,两个入口文件都要load_dotenv()。
5.2 base_url 拼错导致 404
base_url必须是https://taotoken.net/api,不要在后面加/v1或/chat/completions。OpenAI SDK 会自己拼路径,你多写一段就变成双路径。如果报 404,先打印实际请求 URL 确认。
5.3 采样回调没被触发
如果summarize_text卡住或报sampling not supported,检查Client初始化时sampling_handler是否传进去了。FastMCP v2 的采样是客户端能力,服务端只负责发起,客户端不注册回调就不会有响应。
5.4 端口占用
mcp_server.py起不来,报Address already in use,说明 8000 被占了。改config.toml里的port和客户端 URL 里的端口,两处要一致。或者先查一下:
lsof -i :80005.5 STDIO 和 HTTP 混用
config.toml里同时配了local和local_stdio,客户端连哪个要明确。STDIO 模式下服务端不能同时用 HTTP 起,否则连接方式对不上。本地调试建议先用 STDIO,跨进程再用 HTTP。
5.6 模型名不存在
default_model填了一个你没开通的模型,会报 model not found。先用模型对话页面确认可用模型列表,再回填配置。
接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
文档里有完整的接口说明和错误码对照,排障时对着看比猜快。
6. 长期编码与 Agent 场景的配置收敛
如果你不只是跑一次验证,而是要把 FastMCP v2 当成日常编码或 Agent 的底座,配置收敛就更重要。我的做法是把config.toml作为唯一事实源,settings.json只做宿主适配,Key 永远走环境变量。这样换机器、换 Key、加新 MCP 服务端,都只动一处。
长期跑的话,Coding Plan 这类按周期计费的通道比按次调用更划算,适合高频的工具链场景:
Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite
另外几个实践点。第一,给每个 MCP 服务端起独立进程,别把多个服务端塞一个进程,出问题时隔离困难。第二,采样回调里加超时和重试,max_retries在config.toml里配了但客户端侧也要兜底。第三,日志级别在开发时开DEBUG,生产降到INFO,FastMCP 的ctx.info会走日志回调,别让它刷屏。
配置骨架给到这里,剩下的就是按你的工具链往里填工具。FastMCP v2 负责把协议层做薄,TaoToken 负责把模型通道做统一,两者之间的粘合点就是那份config.toml。把它写对,后面加工具就是加装饰器的事。