FastAPI-08.实战-HTTP与MCP共进程

本系列:00 导读 · 01 心智模型 · 02 路由与数据模型 · 03 依赖注入与分层 · 04 中间件异常日志 · 05 异步后台与流式 · 06 鉴权与安全 · 07 测试与项目骨架 · 08 实战 HTTP↔MCP(本文)

行文:T4 项目篇 | 本篇方法:70-20-10 + 科尔布 | 辅助:极度学习、费曼复盘

邻系:MCP 协议与 Server 写法见 MCP 系列Python 框架选型 §7 已预告本模式。


1. 为什么要共进程

模式 优点 缺点
stdio MCP 独立进程 Cursor 配置简单 与 REST 服务两套部署
HTTP MCP 独立服务 解耦 多端口、多健康检查
FastAPI + MCP 同进程 一套 uvicorn、共享配置与工具函数 需理解 ASGI mount;故障域合并

适用:已有 HTTP 服务,要把同一套业务能力给 Agent 当 Tool(内网 API、蛋白设计流水线、数据查询等)。

段末注释MCP(Model Context Protocol,模型上下文协议)规范 Agent 如何发现与调用外部工具;FastMCP 是官方 Python SDK 推荐的 Server 高层 API。


2. 成功标准

  • GET /health 返回 200
  • GET /docs 仍是 FastAPI 文档(可选关闭)
  • MCP 端点可挂载(如 /mcp),Cursor/Inspector 能列出 Tool
  • 业务逻辑只写一份:HTTP 与 MCP 共用 service 层(03)
  • 能费曼解释:「REST 与 MCP 在进程里各走哪条 URL」

3. 架构(双重编码)

1
2
3
4
5
6
7
8
9
10
11
12
        uvicorn (单进程)

FastAPI app
/ \
/health, /api/* mount /mcp
(APIRouter) │
MCP ASGI 子应用
(Streamable HTTP)

@mcp.tool 业务

services/* ← 共用

原则:MCP Tool 函数薄封装,调用 services/;不要在 Tool 里复制 HTTP 路由逻辑。


4. 直接任务:最小共进程示例

以下以官方 MCP Python SDKFastMCP 为参照;具体 mount API 以当前 python-sdk 文档为准(合并期可能有小幅变动)。

4.1 共用业务

1
2
3
# app/services/echo.py
def echo_message(text: str) -> str:
return text.strip()

4.2 FastMCP 定义 Tool

1
2
3
4
5
6
7
8
9
10
11
12
# app/mcp_server.py
from mcp.server.fastmcp import FastMCP

from app.services.echo import echo_message

mcp = FastMCP("demo-tools", json_response=True)


@mcp.tool()
def echo(text: str) -> str:
"""回显文本,供 Agent 调试。"""
return echo_message(text)

4.3 FastAPI 挂载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# app/main.py
from contextlib import asynccontextmanager

from fastapi import FastAPI

from app.mcp_server import mcp
from app.routers import health

# 以 SDK 当前版本为准:常见为 mcp.streamable_http_app() 或等价 ASGI 应用
mcp_app = mcp.streamable_http_app()


@asynccontextmanager
async def lifespan(app: FastAPI):
# 若 SDK 要求 lifespan 转发,见文档将 mcp 的 lifespan 与 FastAPI 合并
yield


app = FastAPI(lifespan=lifespan)
app.include_router(health.router)

# REST 在 /,MCP 在 /mcp(路径以 SDK 默认为准,可配置)
app.mount("/mcp", mcp_app)


@app.get("/api/echo")
async def api_echo(q: str):
return {"result": echo_message(q)}

运行:

1
2
3
uvicorn app.main:app --host 0.0.0.0 --port 8000
curl -s http://127.0.0.1:8000/health
curl -s "http://127.0.0.1:8000/api/echo?q=hi"

MCP 侧:在 Cursor mcp.json 或 Inspector 中配置 Streamable HTTP 指向 http://127.0.0.1:8000/mcp(路径与传输名以 SDK 文档为准)。

4.4 与 stdio 的取舍

场景 建议
本地 Cursor 快速试 Tool stdio 独立进程仍最省事
服务已部署、Agent 远程连 HTTP MCP + 本篇共进程或独立 MCP 服务
强隔离 MCP 单独容器,REST 通过内网调

5. 70-20-10 本篇落地

比例 动作
70% 把 07 骨架加上 mcp_server.pymount、1 个真实 Tool
20% 读 MCP SDK 文档中 Streamable HTTP / lifespan 两节
10% 读本篇 + MCP-02 选型

6. 科尔布复盘模板

做完后填空:

  1. 具体经验:mount 后 /docs/mcp 是否都正常?Inspector 能否 list tools?
  2. 反思:卡在 import 循环、lifespan 还是路径前缀?
  3. 抽象:提炼 3 条——例如「Tool 只调 service」「mount 子应用不经过 APIRouter 前缀」「生产对 /mcp 做鉴权」。
  4. 再实验:给 /mcp 加 API Key 网关,或把 Tool 改成调异步 DB。

7. 生产注意

  • 鉴权/mcp 不应公网裸奔;API Key、mTLS 或内网-only(06)。
  • 观测:中间件 request_id 对 REST 有效;MCP 子应用日志需单独确认是否继承。
  • 版本mcp SDK pin 版本;合并期关注 changelog。
  • 故障域:同进程时 MCP 崩溃可能影响整服务——高隔离场景拆进程。

8. 系列收束:费曼总复盘

合上全系列,应能回答 00 §6 的四条:

系列自测 对应篇章
请求生命周期 01
分层 + 校验 + 鉴权 + pytest 骨架 02–07
阻塞 vs 异步 05
HTTP ↔ MCP 共进程 08 + MCP 系列

闪卡收官包:ASGI 三参数 · Depends 与中间件分工 · 422/401/500 谁处理 · async 假异步 · mount vs include_router · MCP Tool 与 REST 共用 service。


小结

  • 08 不是另学一套 MCP,而是把 ASGI 可组合(01)落到「REST + MCP 同进程」。
  • 业务写在 services;HTTP 与 Tool 各做薄入口。
  • 系列完结;后续深入 MCP 协议与部署请沿 MCP 系列 继续。
-------------本文结束感谢您的阅读-------------