1. 为什么我要用 FastMCP 搭第一个 MCP 服务
如果你最近在折腾 AI Agent、Claude Desktop 或者 Cursor 这类工具,大概率听过 MCP(Model Context Protocol)这个词。简单说,MCP 就是一套让大模型能"调用外部工具"的协议标准——模型本身不会查数据库、不会读文件、不会调你的内部接口,但通过 MCP 服务,它就能像插了 USB 一样把这些能力接进来。FastMCP 则是 Python 生态里把这件事做得最省心的库:几行代码就能把一个普通 Python 函数注册成 MCP 工具,还自带 Streamable HTTP、stdio 等多种传输方式。
这篇面向的是第一次上手 FastMCP 的 Python 开发者。我会带你从零跑通一个最小可用的 MCP 服务:先写服务端,再写客户端调用,最后把 TaoToken 的统一 Key/API 通道接进config.toml骨架里,让整个链路从本地调试到模型调用形成闭环。全程 Python 3.10+ 验证过,代码可以直接复制。
场景很具体:你本地有个小工具函数(比如查天气、算汇率、读配置),想让它被 AI 客户端调用。以前你得写一堆 HTTP 路由、参数校验、错误处理,现在用 FastMCP 一个装饰器搞定。下面按"装环境 → 写服务 → 写客户端 → 接 TaoToken → 排错"的顺序走一遍。
2. 环境准备与 TaoToken 前置配置
2.1 安装 FastMCP 与依赖
先建个干净的虚拟环境,避免和系统里的包打架:
python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install fastmcpFastMCP 会自动带上httpx、pydantic这些依赖。装完可以验证一下版本:
python -c "import fastmcp; print(fastmcp.__version__)"我实测下来,0.4.x 之后的版本对 Streamable HTTP 支持比较稳,如果你装到的是更老的版本,建议pip install -U fastmcp升一下。
2.2 TaoToken 是什么,为什么这里要接它
MCP 服务本身只是"工具提供方",真正调用它的是模型。而模型调用需要一个统一的 API 通道——TaoToken 就是干这个的:它把不同模型的调用收敛成一套 Key 和一套 API 地址,你在config.toml里配一次,后面换模型、加模型都不用改业务代码。官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。
对 MCP 场景来说,TaoToken 的价值在于:你的 MCP 服务负责"提供工具",TaoToken 负责"让模型能调这些工具",两边解耦。下面先把 Key 拿到手。
2.3 获取 API Key
登录后进控制台,在 API Keys 页面创建一个新 Key。建议按用途命名,比如mcp-local-dev,方便后面区分。创建后立刻复制保存——多数平台只显示一次。
拿到 Key 后,先别急着写代码,用一条 curl 验证通道是否通:
curl https://taotoken.net/api/v1/models \ -H "Authorization: Bearer sk-你的Key"返回一个模型列表的 JSON 就说明 Key 和网络都没问题。这一步很关键,很多人后面 MCP 调不通,其实是 Key 或网络的问题,提前排掉能省不少时间。
3. 可复制的 FastMCP 服务端与客户端配置
3.1 服务端最小示例(server.py)
先写一个"打招呼 + 算加法"的双工具服务,覆盖字符串和数值两种参数类型:
# server.py from fastmcp import FastMCP mcp = FastMCP(name="MyFirstServer") @mcp.tool def greet(name: str) -> str: """Greet a user by name.""" return f"Hello, {name}!" @mcp.tool def add(a: int, b: int) -> int: """Add two integers.""" return a + b if __name__ == "__main__": mcp.run( transport="streamable-http", host="127.0.0.1", port=9000, )几个要点:FastMCP(name=...)里的名字会出现在客户端日志里,起个能认出来的;@mcp.tool装饰器会把函数签名自动转成 MCP 的 JSON Schema,所以类型注解必须写全,name: str不能省成name;mcp.run()里transport="streamable-http"是重点,这是目前推荐的 HTTP 传输方式,比老的 SSE 更省连接。
启动服务:
python server.py看到类似Uvicorn running on http://127.0.0.1:9000的日志就说明起来了。注意 MCP 的 HTTP 端点在/mcp路径下,不是根路径。
3.2 客户端调用脚本(client.py)
# client.py import asyncio from fastmcp import Client config = { "mcpServers": { "local": { "url": "http://127.0.0.1:9000/mcp", "transport": "streamable-http", } } } client = Client(config) async def main(): async with client: tools = await client.list_tools() print("available tools:", [t.name for t in tools]) greet_result = await client.call_tool("greet", {"name": "world"}) print("greet:", greet_result) add_result = await client.call_tool("add", {"a": 3, "b": 4}) print("add:", add_result) if __name__ == "__main__": asyncio.run(main())async with client:会自动管理连接生命周期,退出时释放资源,别手动去 close。call_tool的第一个参数是工具名,第二个是参数字典,键名必须和服务端函数参数名一致。
3.3 TaoToken 的 config.toml 配置骨架
MCP 客户端(比如 Claude Desktop、Cursor)通常读一个config.toml或mcp.json来知道有哪些服务。把 TaoToken 作为模型通道接进来时,骨架长这样:
# config.toml [model] provider = "taotoken" base_url = "https://taotoken.net/api" api_key = "sk-你的Key" default_model = "你的默认模型名" [mcp_servers.local] url = "http://127.0.0.1:9000/mcp" transport = "streamable-http" [mcp_servers.local.env] TAOTOKEN_BASE_URL = "https://taotoken.net/api" TAOTOKEN_API_KEY = "sk-你的Key"这里把模型通道和 MCP 服务分开配:[model]段管模型调用,[mcp_servers.*]段管工具服务。env段是给 MCP 服务进程注入环境变量用的,如果你的 MCP 工具内部也要调模型,就从这里读 Key,别硬编码在代码里。
注意:
api_key不要提交到 Git。本地开发用.env或系统环境变量,config.toml加进.gitignore。
4. 验证请求与成功结果
4.1 先验证 MCP 服务本身
服务端跑起来后,用 curl 探一下端点是否活着:
curl -i http://127.0.0.1:9000/mcpStreamable HTTP 端点对 GET 的响应可能不是 200,但只要不是Connection refused,就说明端口通了。更靠谱的验证是直接跑客户端:
python client.py预期输出:
available tools: ['greet', 'add'] greet: Hello, world! add: 7看到available tools列出两个工具名,说明服务端注册成功;greet和add都返回正确结果,说明调用链路通了。
4.2 再验证 TaoToken 通道
单独测一下模型通道,确认 Key 有效:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{ "model": "你的模型名", "messages": [{"role": "user", "content": "ping"}] }'返回带choices的 JSON 就说明通道正常。这一步和 MCP 服务是独立的——MCP 管工具,TaoToken 管模型,两边都通,整个闭环才算成立。
4.3 端到端串起来
把 MCP 服务地址填进你的 AI 客户端(Claude Desktop 的claude_desktop_config.json或 Cursor 的 MCP 设置),模型通道指向 TaoToken。然后在对话里让模型调用greet工具,比如输入"用 greet 工具跟 alice 打个招呼"。模型会通过 TaoToken 通道发起请求,TaoToken 转发给模型,模型决定调用 MCP 的greet工具,MCP 服务返回结果,整条链路跑通。
5. 本篇常见错误排查
5.1 Connection refused
最常见。按顺序查:服务端是否真的在跑(看终端有没有 Uvicorn 日志);端口是不是 9000(被占用就换 9001);URL 有没有带/mcp路径——很多人写成http://127.0.0.1:9000就报这个错。防火墙一般本地回环不拦,但如果用了容器,要确认端口映射。
5.2 工具列表为空
list_tools()返回空数组,通常是装饰器没生效。检查两点:@mcp.tool有没有写在函数正上方(中间不能隔空行或注释);函数有没有类型注解。FastMCP 靠类型注解生成 Schema,def greet(name):这种没注解的会被跳过。
5.3 参数校验失败
调用时报ValidationError,多半是参数名或类型对不上。服务端是def add(a: int, b: int),客户端就得传{"a": 3, "b": 4},传{"x": 3}会直接报错。类型也要匹配,传字符串"3"给int参数,Pydantic 有时能转有时不能,别赌,老老实实传对类型。
5.4 TaoToken 返回 401
Key 错了或没带。检查Authorization: Bearer sk-xxx格式,Bearer 后面有个空格,别漏。Key 前后有没有多余空格或换行,复制时容易带上。如果 Key 是在别的环境生成的,确认它没被删除或过期。
5.5 Streamable HTTP 握手超时
客户端连上了但一直卡住,可能是传输方式配错。服务端用streamable-http,客户端也必须写streamable-http,两边不一致会握手失败。另外确认 FastMCP 版本够新,老版本可能不支持这个 transport。
6. 下一步:把 MCP 服务接进你的工作流
跑通最小示例后,你可以往几个方向扩:给工具加更复杂的参数(列表、嵌套对象),FastMCP 会自动生成 Schema;用@mcp.resource注册资源而不只是工具;把服务部署到内网让团队共用。模型通道这边,TaoToken 的 Coding Plan 适合长期编码和 Agent 场景,接入文档在 https://taotoken.net/api-keys 和 https://taotoken.net/doc 可以查到更细的配置项。
我踩过的一个坑是:一开始把 Key 硬编码在server.py里,后来换环境忘了改,调了半天才发现。现在统一走config.toml的env段注入,代码里只读环境变量,清爽很多。你如果只是本地玩,先跑通greet和add这两个工具,再往上加复杂度,别一上来就搞多服务编排。