1. 造数服务为什么要接 MCP:从六步手工到一句话搞定
社区造数服务接入 MCP 这件事,本质上解决的是一个很具体的痛点:测试数据准备太碎、太依赖人。你回想一下日常联调场景,要造一个「团长拉新」的账号,得先确认这个号 30 天内没发过动态,再把它加进团队 A,然后发一条动态,接着过一审风控、过二审标注,最后还得把阅读数刷到 300。这一串操作分散在造数工厂、接口自动化平台、脚本仓库里,六个步骤走下来,不熟悉业务的人根本串不起来。
MCP(Model Context Protocol)能做什么?它把工具能力标准化暴露给 AI Agent,让 Agent 自己决定调哪个接口、按什么顺序调。适合谁?适合所有手里有一堆内部工具、想让 AI 帮忙编排的研发和测试同学。我这次落地的就是把基于 FastAPI 的社区造数服务,通过 fastapi-mcp 这个库暴露成 MCP 工具,Agent 在本地开发与联调时可以直接调用。
整条链路的关键词是:FastAPI 提供 HTTP 接口,fastapi-mcp 把这些接口自动转成 MCP 工具,uv 负责依赖和环境管理,AI Agent(比如 Cursor 里的 Agent)负责按自然语言编排调用。你不需要重写业务逻辑,只需要在启动入口挂载一下 MCP,再给每个接口补上清晰的 operation_id 和文档注释。下面我按「环境准备 → 配置挂载 → 验证调用 → 排错」的顺序,把可复制的步骤全部摊开。
2. 用 uv 初始化造数服务环境与依赖锁定
造数服务原本用 pip 或 poetry 管理依赖,这次我换成 uv,实测下来环境隔离更干净,装包速度也快。第一步是安装 uv,官方脚本一行搞定:
curl -LsSf https://astral.sh/uv/install.sh | sh装完之后创建虚拟环境,指定 Python 3.12,因为 fastapi-mcp 对版本有要求:
uv venv tools_venv --python 3.12 source tools_venv/bin/activate激活后安装依赖。这里有个坑:如果你直接uv pip install -r pyproject.toml,某些版本的 uv 对 pyproject 的解析行为和 requirements.txt 不完全一致,建议用uv sync或者明确指定依赖文件。我这边是把核心库版本锁死在 pyproject.toml 里,避免 fastapi-mcp 和 mcp 版本错配:
[project] name = "community-tools-mcp" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "fastapi==0.115.12", "fastapi-mcp==0.3.1", "mcp==1.7.0", "pydantic>=2.11.0", "pydantic-settings>=2.2.0", "uvicorn>=0.30.0", "httpx>=0.27.0", ]锁定版本这件事别偷懒。fastapi-mcp 0.3.1 依赖的 mcp 是 1.7.0,如果你环境里已经装了别的 mcp 版本,启动时会报ImportError: cannot import name 'FastMCP'之类的错。用 uv 的好处是它会按 lock 文件解析,冲突会提前告诉你。
项目结构保持原样即可,MCP 改造不涉及目录大改:
├── main.py # 启动入口,MCP 挂载在这里 ├── pyproject.toml # 依赖与版本锁定 ├── app │ ├── api # 造数接口目录 │ ├── core # 全局配置 │ ├── crud # 增删改查 │ ├── models # 表结构 │ └── schemas # pydantic 模型 └── alembic # 数据库迁移环境准备好之后,先别急着改 MCP,跑一遍原有服务确认基线正常。启动命令:
uvicorn main:app --host 0.0.0.0 --port 8023 --reload访问http://localhost:8023/docs能看到 Swagger 文档,说明造数服务本身没问题。这一步是后面排错的参照物,如果 MCP 挂了但 Swagger 正常,问题一定出在挂载层。
3. 可复制配置:main.py 挂载 fastapi-mcp 与 settings 片段
真正接入只需要三步。第一步引入库,第二步在 main.py 里创建 FastApiMCP 实例并 mount,第三步给造数接口补 operation_id 和文档注释。先看 main.py 的完整可复制片段:
from fastapi import FastAPI import uvicorn from fastapi_mcp import FastApiMCP app = FastAPI(title="社区造数服务", description="社区测试数据构造工具集") # 注册原有造数路由 from app.api import tools_router app.include_router(tools_router, prefix="/tools") # 基于 FastAPI app 创建 MCP server mcp = FastApiMCP( app, name="community-tools-mcp", description="社区造数 MCP 服务,支持账号、动态、风控等造数能力", describe_all_responses=True, describe_full_response_schema=True, ) # 挂载到 /mcp 路径 mcp.mount() if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8023, reload=True, workers=2)这里几个参数值得说清楚。name和description会出现在 MCP 客户端的工具列表里,写清楚有助于 Agent 判断该不该用这个服务。describe_all_responses=True会把所有可能的响应 schema 塞进工具描述,Agent 能更准确理解返回结构,代价是描述变长、token 消耗增加。describe_full_response_schema=True类似,把完整 JSON schema 带上。我建议本地开发环境两个都开,线上按需关掉省 token。
如果你只想暴露部分接口,用 include/exclude 控制:
mcp = FastApiMCP( app, include_tags=["tools", "public"], exclude_operations=["delete_account"], )注意include_operations和exclude_operations不能同时用,include_tags和exclude_tags也不能同时用,但 operations 和 tags 可以组合,匹配任一条件即暴露。
第三步是工作量最大但最影响效果的一步:给每个造数接口显式命名 operation_id,并写清楚文档注释。FastAPI 路由如果不指定 operation_id,框架会自动生成类似read_user_users__user_id__get的名字,Agent 看了容易懵。显式命名后工具名就是 operation_id:
@router.post("/create-account", operation_id="create_account", summary="创建测试账号") async def create_account( env: str = Body(..., description="环境,默认 t1"), phonenumber: str = Body(None, description="手机号,非必填,不填自动生成"), pwd: str = Body("test123", description="密码,默认 test123"), usernum: str = Body(None, description="数量"), ): """ 创建测试账号,默认 111 开头,不用填写 111,只需要后面 8 位。 不传 phonenumber 时默认随机生成手机号。 """ ...注释里把「默认值」「是否必填」「格式要求」写全,Agent 的调用准确率会明显提升。这部分后面排错章节会展开讲。
4. 验证请求:MCP 工具发现与一次造数调用链路
配置挂载完,启动服务:
uvicorn main:app --host 0.0.0.0 --port 8023 --reload控制台无报错后,MCP server 就在http://localhost:8023/mcp。接下来用 MCP 客户端连接。以 Cursor 为例,在项目根目录建.cursor/mcp.json:
{ "mcpServers": { "community-tools-mcp": { "url": "http://localhost:8023/mcp", "description": "本地社区造数 MCP 服务" } } }打开 Cursor Settings → MCP,能看到community-tools-mcp加载出来,开关打开显示绿色,说明工具发现成功。点开工具列表,应该能看到create_account、publish_post、pass_risk_review等 operation_id 对应的工具。
现在做一次真实调用验证。在 Cursor 的 Agent 对话里输入:
给手机号 11120210001 的用户发布一个点评动态,并且通过风控一审。注意 11120210001 是手机号不是 userId。
Agent 会自动编排三步:先调get_user_info用手机号换 userId,再调publish_post用 userId 发动态,最后调pass_risk_review用动态 id 过一审。你可以在 Cursor 的调用日志里看到每次 tool call 的入参和返回。如果三步都返回成功,说明工具发现和调用链路完全打通。
再验证一个批量场景:
随机创建 10 个测试账号。
如果 Agent 报错,大概率是注释没写清楚。我第一次跑这个需求时,Agent 自作主张传了个手机号进去,结果接口 500。排查发现 t1 环境测试账号默认填了 111 前缀,只需要传后 8 位,而 Agent 不知道这个规则。把注释改成「默认 111 开头,不用填写 111,只需要后面 8 位;不传 phonenumber 默认随机生成」之后,再跑就成功了。这个案例说明:MCP 接入的准确率,七分靠注释,三分靠模型。
5. 本篇常见错排查:401、local proxy failed 与 reading choices
接入过程中我踩了几个典型报错,这里按现象、原因、解法列出来,你遇到时可以直接对照。
报错一:401 Unauthorized。如果你在 Cursor 里连的是线上造数服务,而线上挂了鉴权,MCP 请求会返回 401。fastapi-mcp 支持 auth_config 参数,但更简单的做法是本地开发连本地服务,线上服务单独配一个带 token 的 header。检查你的 mcp.json 里 url 是否指向了需要鉴权的地址。
报错二:local proxy failed / connection refused。这个通常是 MCP server 没起来,或者端口不对。先确认uvicorn进程还在,再curl http://localhost:8023/mcp看是否有响应。如果 Swagger 能访问但 /mcp 404,说明mcp.mount()没执行到,检查 main.py 里 mount 是否在 include_router 之后。
报错三:Error reading choices / unexpected token。这个多出现在 Agent 调用工具后解析返回时。原因是接口返回了非 JSON 结构,或者返回体太大超出模型上下文。解法是给造数接口统一返回结构,并在 FastApiMCP 里关掉describe_full_response_schema,减少描述体积。
报错四:OAuth / auth.json 相关。如果你用的是 Codex 或 Claude Code 这类需要 auth.json 的客户端,MCP 配置要写全三件套:Base URL、Key、Model ID。以 Codex 的 auth.json 为例:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的key", "model": "claude-sonnet-4-5" }Base URL 填https://taotoken.net/api,Key 在控制台生成,Model ID 按你实际用的模型填。三者缺一,客户端会报 OAuth 或 401。如果你用的是 Claude Code 做代码润色类任务,同样需要先完成这套接入配置,否则「连上就能用」只是错觉。
报错五:工具列表为空。Cursor 里 MCP 显示绿色但工具列表空,通常是 operation_id 重复或路由没注册。检查是否有两个接口用了同一个 operation_id,FastAPI 会静默覆盖。
6. 语义一致 CTA:把造数 MCP 接到你的 Agent 工作流
造数服务接入 MCP 之后,下一步就是把它接到你日常的 Agent 工作流里。如果你只是本地联调验证,直接用 Cursor 连本地/mcp就够了。如果你想让团队共用,可以把服务部署到内网,然后在 mcp.json 里换成内网地址。
需要生成和管理 API Key 的话,去控制台创建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=console
接入文档里有完整的 Base URL 和鉴权说明:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc
如果你要长期跑编码类 Agent 任务,比如让 Agent 自动编排造数、跑回归,Coding Plan 更划算:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding-plan
想先验证模型对话效果,可以直接在模型对话页试:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=chat
API Key 管理入口:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api-keys
Claude Code 接入参考:https://taotoken.net/claude-code?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=claudecode
最后说个实操经验:造数接口的文档注释不是写一次就完事。每次 Agent 调用出错,先去看它传了什么参数、接口返回了什么,然后回头改注释。我前后改了四版注释,create_account的调用成功率才从一半提到稳定。注释即规范,这句话在 MCP 场景下是实打实的。