
1. 为什么要把 FastAPI 接口改造成 MCP 工具如果你手里已经有一堆跑得好好的 FastAPI 接口现在想让大模型直接调用它们最直接的想法可能是重写一套 MCP Server。但真动手就会发现参数描述、鉴权、异步并发、部署方式全都要再写一遍维护两套代码非常痛苦。FastAPI-MCP 解决的正是这个问题它把已有的 FastAPI 路由自动识别并暴露成 MCP 工具你几乎不用改业务逻辑就能让 Claude、Cline、Cherry Studio 这类支持 MCP 协议的客户端调用你的接口。FastAPI-MCP 是一个基于 Python FastAPI 框架的开源项目核心能力是自动扫描 FastAPI 的operation_id和路由信息生成对应的 MCP 工具描述。它继承了 FastAPI 的全部优点异步高并发、可独立远程部署、自带 OpenAPI 文档。传输层支持 SSE 和 mcp-remote 两种接入方式也支持设置授权访问适配各种支持 MCP 协议的客户端。简单说你写接口的方式不变只是多挂载了一个 MCP 服务。这篇内容适合三类人一是已经有 FastAPI 项目、想快速接入 MCP 的后端开发者二是想搭建私有 MCP 工具集、又不想从零写协议层的工程师三是正在用 uv 管理 Python 依赖、希望部署流程干净可复现的团队。下面我会从项目结构、工具注册代码、SSE 传输配置、uv 依赖管理到客户端调用与返回校验完整跑通一条自定义 MCP 工具链路。整个过程你可以直接复制命令和代码跟做。需要提前说明的是MCP 工具的本质是让模型知道「有哪些能力可以调用、参数是什么、返回什么」。FastAPI-MCP 通过operation_id和函数签名自动生成这些元信息所以接口命名和参数类型标注越清晰模型调用越准确。这也是为什么后面代码里我会强调operation_id必须显式设置。2. 用 uv 管理依赖并搭建 FastAPI-MCP 项目结构先说环境。FastAPI-MCP 要求 Python 3.10 及以上依赖管理我推荐用 uv因为它安装快、锁文件清晰、虚拟环境隔离干净。如果你还没装 uv可以用官方脚本安装装好后uv --version能输出版本号即可。这里不展开系统级安装细节重点放在项目初始化。我习惯的项目结构是这样的一个app包放业务接口一个mcp_server.py负责挂载 MCP根目录放pyproject.toml和uv.lockfastapi-mcp-demo/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 实例与路由 │ └── auth.py # 鉴权依赖 ├── mcp_server.py # 挂载 MCP 并启动 ├── pyproject.toml └── uv.lock初始化命令如下uv init会生成基础pyproject.toml然后添加依赖uv init fastapi-mcp-demo cd fastapi-mcp-demo uv add fastapi fastapi-mcp uvicorn执行完uv add后pyproject.toml里会出现类似下面的依赖声明版本号以你实际拉到的为准[project] name fastapi-mcp-demo version 0.1.0 requires-python 3.10 dependencies [ fastapi0.115.0, fastapi-mcp0.3.0, uvicorn0.30.0, ]这里有个容易踩的坑fastapi-mcp的版本迭代比较快早期版本和 0.3 之后的 API 在mount参数上有差异。如果你照着旧教程写mcp.mount(/mcp)报参数错误先确认版本。用uv tree可以看清实际安装的版本链路。接下来写业务接口。我在app/main.py里定义两个工具一个获取当前时间不需要授权一个模拟获取用户信息需要授权。注意每个路由都要显式写operation_id这是模型理解工具用途的关键。# app/main.py from datetime import datetime from fastapi import FastAPI, Depends, HTTPException, Header app FastAPI(titleFastAPI-MCP Demo) async def verify_token(authorization: str | None Header(None)): valid_tokens {123456, abcdef} if authorization not in valid_tokens: raise HTTPException(status_code403, detailInvalid Token) return True app.get(/getCurrentTime, operation_idget_current_time) async def get_current_time(): return {current_time: datetime.now().strftime(%Y-%m-%d %H:%M:%S)} app.get(/users/{user_id}, operation_idget_user_info) async def get_user_info(user_id: int, is_auth: bool Depends(verify_token)): data { user_id: user_id, name: 小狗狗, sex: 男, birthday: 2002-07-06, } return dataoperation_id用下划线命名模型读起来更自然。参数类型标注user_id: int会被转成 MCP 工具的输入 schema模型就知道这里要传整数。鉴权用 FastAPI 的Depends注入MCP 调用时会带上请求头逻辑和普通接口完全一致。3. 挂载 SSE 传输并写出可复制的 MCP 配置业务接口写好后新建mcp_server.py把 FastAPI 实例交给 FastApiMCP然后挂载。默认挂载路径是/mcpSSE 传输就绪后客户端通过这个地址建立长连接。# mcp_server.py import uvicorn from app.main import app from fastapi_mcp import FastApiMCP mcp FastApiMCP(app) mcp.mount() if __name__ __main__: uvicorn.run(app, host0.0.0.0, port8000)启动命令用 uv 运行保证依赖来自项目锁文件uv run python mcp_server.py启动后你会看到 uvicorn 监听 8000 端口。此时有两套入口同时可用普通 REST 接口在http://localhost:8000/getCurrentTimeMCP SSE 端点在http://localhost:8000/mcp。Swagger 文档在http://localhost:8000/docs可以先用它确认接口本身正常。接下来是客户端配置。以支持 SSE 的 MCP 客户端为例配置片段如下注意baseUrl指向/mcpAuthorization头填有效 token{ mcpServers: { fastapi-mcp: { name: fastapi-mcp, type: sse, description: 本地 FastAPI 接口封装的 MCP 工具, isActive: true, baseUrl: http://localhost:8000/mcp, headers: { Content-Type: application/json, Authorization: 123456 } } } }这里三件套必须齐全Base URL 是http://localhost:8000/mcpKey 是Authorization头里的123456Model ID 取决于你客户端里选的对话模型和 MCP 服务本身无关但调用工具时需要模型支持 function calling。如果你用的是 Cline 或 Claude Code 这类工具配置字段名可能略有不同但 Base URL、鉴权头、传输类型这三个信息是一致的。如果你希望把 MCP 服务部署到远程让团队共用可以把uvicorn.run的 host 保持0.0.0.0然后用反向代理暴露 HTTPS。此时客户端配置里的baseUrl换成你的域名加/mcp。注意 SSE 是长连接反向代理要关闭缓冲、拉长超时否则连接会被掐断。另外FastAPI-MCP 也支持 mcp-remote 接入方式适合客户端只支持 stdio 的场景。原理是在本地起一个转发进程把 stdio 转成 SSE。配置里用npx mcp-remote http://localhost:8000/mcp这类命令即可具体参数看客户端文档。我实测下来SSE 直连最省事mcp-remote 适合临时兼容。4. 验证请求与返回校验从 Swagger 到模型链式调用配置完成后先做两层验证。第一层验证接口本身第二层验证 MCP 工具是否被正确识别。第一层直接访问 REST 接口。获取时间不需要鉴权curl http://localhost:8000/getCurrentTime返回{current_time: 2025-01-15 14:32:07}获取用户信息需要带 tokencurl -H Authorization: 123456 http://localhost:8000/users/888888返回{user_id: 888888, name: 小狗狗, sex: 男, birthday: 2002-07-06}如果 token 不对会返回 403 和Invalid Token。这一步确认业务逻辑没问题。第二层在 MCP 客户端里刷新工具列表。正常情况下客户端会自动请求/mcp并拉取到两个工具get_current_time和get_user_info。你可以在对话里直接说「现在几点了」模型会调用get_current_time并返回时间。再说「帮我查一下用户 888888 的信息」模型会调用get_user_info并在请求头带上配置里的Authorization。更值得演示的是链式调用。你可以问「帮我看看用户 ID 为 888888 的用户多少岁了」。模型会先调用get_user_info拿到birthday是2002-07-06然后自己计算年龄。这个过程里MCP 工具只负责返回原始数据推理和计算由模型完成。这就是把接口封装成工具的价值模型按需组合能力而不是你写死一个「算年龄」的接口。返回校验要注意两点。一是工具返回必须是 JSON 可序列化的结构FastAPI 默认会做但如果你返回了datetime对象或自定义类要手动转成字符串。二是错误处理接口抛HTTPException时MCP 客户端会收到错误信息模型能感知到调用失败。建议在detail里写清楚原因比如Invalid Token方便模型决定是否重试或提示用户。如果你在客户端里看到工具列表为空先确认/mcp端点能返回 SSE 事件流可以用curl -N http://localhost:8000/mcp观察是否有event: endpoint之类的输出。没有输出说明挂载没生效检查mcp.mount()是否在uvicorn.run之前执行。5. 常见报错排查401、local proxy failed 与 reading choices实际接入时报错基本集中在鉴权、传输和客户端解析三类。下面按真实错误信息对照排查。401 Unauthorized 或 403 Invalid Token这是鉴权头没带上或 token 不对。检查客户端配置里的headers.Authorization是否和verify_token里的有效集合一致。注意有些客户端会把 header 名小写化FastAPI 的Header(None)默认大小写不敏感一般没问题。如果你用的是 Bearer 格式记得在verify_token里去掉Bearer前缀再比对。local proxy failed / connection refusedSSE 连接建立失败。先确认uvicorn还在运行端口没被占用。如果客户端和 MCP 服务不在同一台机器baseUrl不能写localhost要写实际 IP 或域名。反向代理场景下检查是否关闭了响应缓冲Nginx 需要proxy_buffering off;和较长的proxy_read_timeout。reading choices / unexpected end of JSON input客户端解析模型返回时出错通常不是 MCP 服务本身的问题而是模型输出被截断或格式不合法。排查顺序是先确认模型支持 function calling再确认工具返回的 JSON 没有超长字段最后看客户端日志里模型原始输出。如果工具返回了嵌套很深的 JSON某些客户端解析会出问题可以精简返回结构。OAuth 相关报错如果你给 MCP 服务加了 OAuth 鉴权客户端需要走授权码流程。报错通常是invalid_client或redirect_uri mismatch。检查客户端注册的回调地址和服务端配置是否完全一致包括末尾斜杠。本地调试时OAuth 回调地址用http://localhost通常可以但部分客户端要求 HTTPS。工具列表为空除了前面说的挂载顺序还要确认 FastAPI 路由是在FastApiMCP(app)之前注册的。如果你在挂载之后才include_router新路由不会被扫描到。解决办法是把 MCP 挂载放在所有路由注册之后。uv 依赖冲突uv add时如果报版本冲突先看uv tree找出冲突链路。FastAPI-MCP 对 FastAPI 版本有下限要求太老的 FastAPI 缺少某些 OpenAPI 字段会导致工具描述生成不全。升级 FastAPI 到 0.115 以上通常能解决。排查时我习惯先隔离变量用 curl 直接打 REST 接口确认业务正常再用 curl 打/mcp确认 SSE 正常最后才在客户端里调。这样能快速定位是服务端、传输层还是客户端的问题。6. 长期编码与 Agent 场景下的接入建议如果你只是临时验证本地跑通就够了。但如果你打算把 FastAPI-MCP 用在长期编码或 Agent 场景里有几个点值得提前规划。第一工具粒度要控制。不要把几十个接口一股脑全暴露模型面对太多工具会选错。按业务域拆分多个 MCP 服务或者用include_operations参数只暴露必要接口。FastAPI-MCP 支持筛选具体参数看版本文档。第二鉴权要可轮换。示例里的valid_tokens是硬编码生产环境应该接数据库或 JWT。MCP 客户端配置里的 Key 也要能定期更换避免泄露。如果你用 TaoToken 这类平台做模型接入API Key 和 MCP 服务的鉴权是两套东西别混在一起。第三部署要独立。MCP 服务建议和业务接口分开部署因为 SSE 长连接会占用连接数和普通 REST 请求的资源模型不同。用 uv 的锁文件保证部署环境一致配合容器化uv sync --frozen可以精确还原依赖。第四日志要可观测。MCP 调用失败时客户端往往只显示一句「工具调用失败」具体原因在服务端日志里。建议在verify_token和每个工具函数里加结构化日志记录operation_id、参数和耗时方便排查。如果你需要长期跑编码 Agent可以考虑用 Coding Plan 这类方案管理模型调用额度把 MCP 服务和模型接入分开配置。模型对话调试可以用模型对话页面快速验证工具是否被正确调用。接入文档里有完整的 Base URL、Key 和 Model ID 配置说明照着填即可。最后说个实用技巧FastAPI-MCP 生成的工具描述来自路由的summary和description。你可以在装饰器里补上summary获取当前时间模型理解会更准。这个细节在工具多了以后特别明显值得一开始就养成习惯。