尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FastAPI-MCP 实战:让 AI 轻松“对话”你的后端服务

FastAPI-MCP 实战:让 AI 轻松“对话”你的后端服务 1. 从一次真实需求说起为什么 FastAPI 项目需要 MCP我手头有个跑了半年的 FastAPI 订单服务接口不多二十来个Swagger 文档写得也算清楚。上个月产品提了个需求能不能让 AI 助手直接查订单、改状态而不是每次都要人肉去后台点。第一反应是写个 function calling 的适配层把每个接口再包一遍。真动手才发现二十个接口就是二十个工具定义参数模型、描述、错误处理全得重写一遍接口一改还得同步维护两套纯属给自己找活干。后来接触到 MCPModel Context Protocol思路就变了。MCP 是 Anthropic 提出的开放协议你可以把它理解成 AI 和外部工具之间的一套标准对话规则。AI 不需要知道你的接口长什么样只要按 MCP 的格式拿到工具清单就能发起调用。而 FastAPI-MCP 这个库做的事情更省事它直接读你现有的 FastAPI 路由、Pydantic 模型和 docstring自动转成 MCP 工具业务代码一行不用改。这篇文章要解决的就是一个具体问题你有一个现成的 FastAPI 后端服务怎么用最短路径让它被 AI 调用起来。适合谁看已经写过 FastAPI、对 Pydantic 不陌生、想让 AI Agent 接入自己业务接口的开发者。全程本地可跑不需要公网部署最后我会给出完整的配置片段和验证命令照着敲就能看到 AI 成功调用你的接口。核心检索词先摆出来FastAPI-MCP 是一个把 FastAPI 路由自动暴露为 MCP 工具的适配器让 AI 助手能够以标准协议调用你的后端服务。它解决的是AI 与后端服务对话的最后一公里问题。在动手之前先把整体链路想清楚不然后面容易懵。链路是这样的AI 客户端比如支持 MCP 的对话工具→ 通过 MCP 协议连接到你本地启动的 MCP 服务端点 → FastAPI-MCP 把请求翻译成对 FastAPI 路由的内部调用 → 你的业务逻辑正常执行 → 结果按 MCP 格式返回给 AI。整条链路里你的 FastAPI 代码完全不知道 AI 的存在它只是被正常调用而已。这里有个容易踩的坑很多人以为 FastAPI-MCP 是把 OpenAPI 文档转成 MCP其实不是。它是深度集成在 FastAPI 的 ASGI 框架内部的走的是应用内部调用不是发 HTTP 请求绕一圈。这意味着性能更好也意味着你的依赖注入、中间件、认证逻辑全都会原样生效。这一点后面讲认证的时候会重点说。2. 前置准备TaoToken 与本地环境怎么配在写代码之前得先把两样东西准备好一个是能调用模型的入口一个是本地 Python 环境。先说模型入口这块。AI 要调用你的 MCP 工具前提是它本身能跑起来、能理解工具描述。如果你用的是支持 MCP 的客户端通常需要配置一个模型服务地址。我这边习惯用 TaoToken 来做统一入口它的 API 地址是 https://taotoken.net/api兼容常见的接口格式配置起来不折腾。你需要在控制台创建一个 API Key这个 Key 后面会填到客户端的配置里。具体操作路径先到官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册登录然后进控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建密钥。创建完记得复制保存页面刷新后就看不到了。如果你只是想先验证模型通不通可以直接用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 发一条消息试试确认 Key 有效再往下走。这里要强调一点TaoToken 在这里的角色是模型调用入口不是代理你的 FastAPI 服务。你的 FastAPI 服务始终跑在本地MCP 客户端通过本地地址连接它模型只是负责理解用户意图并决定调用哪个工具。这两条链路是分开的别搞混。再说本地环境。Python 版本建议 3.10 以上FastAPI-MCP 对类型注解的解析依赖较新的 typing 特性。依赖装三个就够pip install fastapi uvicorn fastapi-mcp如果你打算用 Claude Code 这类编码工具来辅助调试可以顺带把 Coding Plan 配好地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 它适合长时间写代码、跑 Agent 的场景。不过这一步不是必须的纯手工敲也完全没问题。环境验证很简单跑一句python -c import fastapi, fastapi_mcp; print(fastapi.__version__, fastapi_mcp.__version__)能打印出版本号就说明装好了。如果报 ModuleNotFoundError八成是 pip 装到了别的 Python 环境里用which python和which pip对一下路径。还有一点提前说MCP 客户端连接本地服务时很多客户端要求走 stdio 或者 HTTP 两种模式之一。FastAPI-MCP 的mount_http()挂的是 HTTP 端点适合支持 HTTP 传输的客户端如果你的客户端只支持 stdio那就得用独立部署方式再套一层。这个区别在第四节验证的时候会具体讲先有个印象。3. 可复制配置把 FastAPI 路由变成 MCP 工具这一节是全文的核心我会给出一份完整可跑的main.py然后逐段解释关键参数。你直接复制就能用。先看完整代码# main.py from fastapi import FastAPI, Depends, HTTPException, Header from pydantic import BaseModel, Field from typing import List, Optional from fastapi_mcp import FastApiMCP app FastAPI(title订单管理服务) # ---------- 数据模型 ---------- class Order(BaseModel): id: int user: str Field(description下单用户姓名) amount: float Field(description订单金额单位元) status: str Field(defaultpending, description订单状态pending/paid/shipped) class OrderCreate(BaseModel): user: str Field(description下单用户姓名) amount: float Field(description订单金额单位元) # ---------- 简易内存库 ---------- fake_orders_db: List[Order] [ Order(id1, user张三, amount199.0, statuspaid), Order(id2, user李四, amount88.5, statuspending), ] # ---------- 认证依赖 ---------- def verify_token(authorization: Optional[str] Header(None)): if authorization ! Bearer demo-token-123: raise HTTPException(status_code401, detailinvalid token) return True # ---------- 业务路由 ---------- app.get(/orders, response_modelList[Order], summary查询订单列表, tags[订单]) async def list_orders(user: Optional[str] None): 查询订单列表。可以传入 user 参数按用户名过滤不传则返回全部订单。 if user: return [o for o in fake_orders_db if o.user user] return fake_orders_db app.post(/orders, response_modelOrder, summary创建新订单, tags[订单]) async def create_order(order: OrderCreate): 创建一笔新订单。需要提供用户姓名和金额返回创建后的完整订单信息。 new_id len(fake_orders_db) 1 new_order Order(idnew_id, userorder.user, amountorder.amount) fake_orders_db.append(new_order) return new_order app.delete(/orders/{order_id}, summary删除订单, tags[订单], dependencies[Depends(verify_token)]) async def delete_order(order_id: int): 删除指定 ID 的订单。此接口需要认证AI 调用时也必须携带有效 token。 global fake_orders_db before len(fake_orders_db) fake_orders_db [o for o in fake_orders_db if o.id ! order_id] if len(fake_orders_db) before: raise HTTPException(status_code404, detailorder not found) return {deleted: order_id} # ---------- MCP 集成核心就这几行 ---------- mcp FastApiMCP( app, nameorder-service-mcp, description订单管理服务的 MCP 工具集支持查询、创建、删除订单, include_tags[订单], # 只暴露带订单标签的路由 # exclude_operations[delete_order], # 如需屏蔽高危接口取消注释 ) mcp.mount_http() if __name__ __main__: import uvicorn uvicorn.run(app, host127.0.0.1, port8000)启动命令uvicorn main:app --reload --port 8000现在逐段拆解关键点。关于include_tags和exclude_operations。这是控制哪些接口对 AI 可见的开关。默认情况下FastAPI-MCP 会把所有路由都暴露出去这在生产环境是危险的。我的建议是用include_tags做白名单只放你确认安全的接口对于删除、改状态这类高危操作要么用exclude_operations单独屏蔽要么像上面代码那样加认证依赖。注意exclude_operations用的是路由的 operation id默认由函数名生成所以delete_order就是它的 id。关于认证的继承。上面delete_order挂了Depends(verify_token)这个依赖在 MCP 调用时同样生效。也就是说AI 想删订单必须带上Authorization: Bearer demo-token-123这个头。这是 FastAPI-MCP 最让我满意的地方——安全规则不用重写原样继承。如果你的项目用的是 OAuth2 或 JWT同样直接生效不需要为 MCP 单独做一套鉴权。关于 docstring 和 Field description。这两处是 AI 理解工具的主要依据。list_orders的 docstring 里写了可以传入 user 参数按用户名过滤AI 就知道这个参数怎么用。Order模型里amount的Field(description订单金额单位元)会变成工具参数的说明。写得越清楚AI 调用越准。我踩过的坑是早期偷懒不写 description结果 AI 把user参数理解成用户 ID传了个数字进来直接报错。关于mount_http()。它把 MCP 端点挂在/mcp路径下。启动后你可以访问http://127.0.0.1:8000/mcp看看正常会返回 MCP 协议的握手信息。同时原来的 Swagger 文档http://127.0.0.1:8000/docs照常可用两者互不干扰。如果你需要独立部署比如主服务和 MCP 服务分开跑把mount_http()换成独立 ASGI 应用的写法即可但大多数本地验证场景挂载方式就够了。4. 验证请求确认 AI 真的能调通你的接口代码跑起来只是第一步得实际验证 MCP 端点可用、工具清单正确、调用链路通畅。这一节给你三个层次的验证方法从简单到完整。第一层确认 MCP 端点活着。启动服务后另开一个终端curl -s http://127.0.0.1:8000/mcp | head -c 500如果返回一段包含协议版本、serverInfo 之类的 JSON说明端点正常。如果返回 404检查mount_http()是不是漏了或者路径被别的路由占了。第二层确认工具清单正确。MCP 协议里有个tools/list方法可以用 curl 模拟一次 JSON-RPC 调用curl -s -X POST http://127.0.0.1:8000/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d {jsonrpc:2.0,id:1,method:tools/list,params:{}}返回里应该能看到list_orders、create_order、delete_order三个工具每个都带 inputSchema。重点检查两件事一是delete_order的 schema 里有没有把认证头暴露出来通常不会因为 Header 依赖不进入参数模型二是list_orders的user参数描述是不是你写的那句。如果工具数量不对回去看include_tags配置。第三层完整调用一次。用tools/call方法实际调list_orderscurl -s -X POST http://127.0.0.1:8000/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d {jsonrpc:2.0,id:2,method:tools/call,params:{name:list_orders,arguments:{user:张三}}}预期返回里包含张三那笔 199 元的订单。如果返回的是空数组检查内存库初始化数据如果报参数错误检查arguments的键名和 schema 是否一致。三层都通了之后就可以接到真实 AI 客户端了。以支持 HTTP 传输的 MCP 客户端为例配置片段大致长这样不同客户端字段名略有差异以官方文档为准{ mcpServers: { order-service: { url: http://127.0.0.1:8000/mcp, headers: { Authorization: Bearer demo-token-123 } } } }注意这里的headers是给需要认证的工具用的。如果你的客户端不支持在 MCP 配置里加 header那就得把认证信息通过环境变量或者客户端自身的凭证管理来传。配置好重启客户端你应该能在工具列表里看到这三个订单工具。然后直接对 AI 说帮我查一下张三的订单它就会调用list_orders并返回结果。到这一步AI 与后端服务的对话闭环就跑通了。如果你在客户端里用的是 TaoToken 作为模型入口记得把 API Key 配到客户端对应的模型设置里API 地址填 https://taotoken.net/api。Key 的创建入口在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 接入细节可以对照文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 来配。5. 常见报错排查401、local proxy failed 与 reading choices这一节把我实际遇到过的报错整理出来对照着查能省不少时间。报错一401 Unauthorized。这个最常见分两种场景。场景 A你调delete_order时没带认证头。MCP 客户端配置里如果漏了Authorization调用会直接 401。解决方法是把 header 补上或者临时把delete_order从暴露列表里去掉先验证其他工具。场景 B认证头格式不对。FastAPI 的Header(None)拿到的是原始字符串Bearer demo-token-123里的Bearer前缀不能少少一个空格都会失败。建议在verify_token里加一行日志把收到的 header 打出来对比。报错二local proxy failed / connection refused。这个通常出现在客户端连不上本地 MCP 端点的时候。原因有几个一是服务没启动或者启动在了别的端口用lsof -i :8000确认一下二是客户端配置里写的是localhost但服务绑的是127.0.0.1某些环境下这俩解析不一致统一写成127.0.0.1最稳三是客户端本身要求 HTTPS而你本地是 HTTP这种情况要么换支持 HTTP 的客户端要么本地起个自签证书不推荐麻烦。还有一种隐蔽情况你的服务启动时mount_http()挂载失败但没报错导致/mcp路径实际不存在用第四节的 curl 先确认端点活着。报错三reading choices of undefined。这个报错不是 FastAPI-MCP 抛的而是模型调用层的问题。通常出现在客户端把 MCP 工具结果回传给模型时模型接口返回了非预期结构。排查方向先确认模型 API 地址和 Key 配置正确用模型对话页面单独发一条消息验证 Key 有效再检查客户端是不是把 MCP 的返回格式和模型的期望格式搞混了。如果你用的是兼容接口确认请求体里的model字段填的是服务端支持的模型 ID。这个错和 MCP 本身关系不大但很容易让人误以为是工具注册出了问题别被带偏。报错四OAuth 相关错误。如果你的 FastAPI 项目用的是 OAuth2 密码流或授权码流MCP 调用时可能会卡在 token 获取环节。原因是 OAuth2 的交互需要浏览器跳转而 MCP 调用是无头的。解决办法是给 MCP 场景单独签发一个长期 token或者在verify_token里对 MCP 来源的请求走简化校验。不要试图让 AI 去完成 OAuth 跳转那条路走不通。报错五工具列表为空。tools/list返回空数组八成是include_tags写错了。检查你的路由tags参数和include_tags里的字符串是否完全一致大小写敏感。另一个可能是路由定义在FastApiMCP(app)之后导致注册时还没看到这些路由。记住顺序先定义所有路由再初始化FastApiMCP最后mount_http()。排查的时候有个通用技巧把uvicorn的日志级别调到 debuguvicorn main:app --log-level debugMCP 的请求和响应都会打出来比盲猜快得多。6. 把链路用起来从验证到日常开发跑通验证只是起点真正有价值的是把它用进日常开发。我现在的做法是本地起服务MCP 客户端常驻写代码的时候直接让 AI 帮我查数据、造测试订单省去来回切终端调 curl 的时间。如果你打算长期这么用有几个实践建议。第一把 MCP 配置和 FastAPI 服务做成一个启动脚本一条命令拉起全部避免每次手动开两个终端。第二给不同环境准备不同的 token本地开发用一个联调用另一个别混。第三定期 review 暴露给 AI 的工具列表业务迭代后有些接口可能不该再暴露了include_tags要跟着更新。对于需要长时间跑 Agent、批量处理任务的场景可以考虑用 Coding Plan 来承载地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 它更适合持续性的编码和自动化任务。而如果你只是想快速验证某个模型对工具调用的理解能力直接用模型对话页面发指令就行不用配客户端。最后说个我自己的习惯每次改完 FastAPI 路由先跑一遍第四节的tools/listcurl确认工具清单符合预期再去客户端里试。这样能把路由改了但 MCP 没同步这类问题挡在前面。MCP 工具的描述质量直接决定 AI 调用的准确率docstring 和 Field description 值得多花几分钟写清楚这个投入回报比很高。
返回列表