50行Python,把你的REST API包装成Claude能直接调的MCP工具
上周 Finix 发了个公告:他们的支付 API 现在可以通过 MCP 直接在 ChatGPT、Claude、Gemini 里调用了。开发者在聊天窗口里说一句"帮我查一下昨天的交易流水",AI 就能直接调 Finix 的 API 返回结果。
说白了,MCP 正在变成 AI 调用外部服务的标准协议。不光是 Finix,GitHub 也上线了官方 MCP Registry,各种数据库、云服务、内部工具都在往 MCP 上靠。
问题来了——你自己的 API 怎么办?
下面不讲概念,直接上手。我会用一个真实场景(封装一个 TODO 待办事项 API)演示怎么用 Python 写一个 MCP Server,让 Claude Code 或 Cursor 在对话中直接调用你的接口。
你需要准备什么
- Python 3.10+
- 一个终端
- Claude Code 或 Cursor(用来测试连接)
安装依赖只需要一行:
pip install "mcp[cli]" httpx
mcp[cli] 是 Anthropic 官方的 Python SDK,自带一个 dev inspector 方便调试。httpx 用来发 HTTP 请求,比 requests 多了个原生异步支持。
先跑通最小 demo
创建 server.py:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-api-tools")
@mcp.tool()
def ping() -> str:
"""检查服务是否存活。"""
return "pong"
if __name__ == "__main__":
mcp.run(transport="stdio")
这 8 行代码就是一个合法的 MCP Server。运行 mcp dev server.py,浏览器会弹出一个调试界面,你能看到 ping 工具已经注册好了,点一下就能调用。
几个注意点:
@mcp.tool()装饰器把普通函数注册为 MCP 工具,函数名就是工具名- 函数的 type hint 会被 SDK 自动转成 JSON Schema,AI 读这个 schema 决定传什么参数
- docstring 是工具描述,Claude 靠它判断什么时候该调这个工具。写得烂,AI 就不知道该不该用
封装一个真实的 REST API
光写 ping 没啥用。假设你有一个待办事项 REST API 跑在 http://localhost:8000,接口长这样:
| 接口 | 方法 | 说明 |
|---|---|---|
| /todos | GET | 获取全部待办 |
| /todos | POST | 新建待办 |
| /todos/{id} | PUT | 更新状态 |
| /todos/{id} | DELETE | 删除待办 |
我们要做的事情很直白:给每个接口写一个对应的 MCP tool。
import httpx
from mcp.server.fastmcp import FastMCP
API_BASE = "http://localhost:8000"
mcp = FastMCP("todo-mcp")
@mcp.tool()
async def list_todos() -> str:
"""获取所有待办事项,返回 JSON 列表。"""
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.get(f"{API_BASE}/todos")
resp.raise_for_status()
return resp.text
@mcp.tool()
async def add_todo(title: str, priority: str = "medium") -> str:
"""新建一条待办事项。
Args:
title: 待办内容
priority: 优先级,可选 low / medium / high
"""
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.post(
f"{API_BASE}/todos",
json={"title": title, "priority": priority}
)
resp.raise_for_status()
return resp.text
@mcp.tool()
async def complete_todo(todo_id: int) -> str:
"""把一条待办标记为已完成。
Args:
todo_id: 待办事项的 ID
"""
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.put(
f"{API_BASE}/todos/{todo_id}",
json={"completed": True}
)
resp.raise_for_status()
return resp.text
@mcp.tool()
async def delete_todo(todo_id: int) -> str:
"""删除一条待办事项。
Args:
todo_id: 待办事项的 ID
"""
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.delete(f"{API_BASE}/todos/{todo_id}")
resp.raise_for_status()
return resp.text
if __name__ == "__main__":
mcp.run(transport="stdio")
50 行出头。每个函数做的事情都一样:接收参数,调 REST API,拿到结果返回给 AI。
写法上有几个细节。
参数的 type hint 必须写。 MCP SDK 拿 type hint 生成 JSON Schema,没有 hint 的参数 AI 不知道该传什么类型。priority: str = "medium" 这种写法,SDK 会自动把 medium 设为默认值。
docstring 决定 AI 会不会调用你的工具。 我在 add_todo 的 docstring 里写了"新建一条待办事项",Claude 在对话里听到用户说"帮我加个任务",就会匹配到这个工具。如果 docstring 写成"POST request to /todos endpoint",AI 大概率不知道什么时候该用它。
用 async。 MCP 的 stdio 传输本身是同步的,但 SDK 内部有事件循环。用 async 函数可以避免 HTTP 请求阻塞其他工具的调用。如果你的 API 响应慢(超过 2 秒),这个差别更明显。
连接到 Claude Code
写完 server.py,下一步是让 Claude Code 识别它。
在你的项目根目录创建 .claude/settings.json:
{
"mcpServers": {
"todo-mcp": {
"command": "/path/to/your/venv/bin/python",
"args": ["server.py"],
"cwd": "/path/to/your/mcp-server"
}
}
}
两个坑要提前踩掉:
-
command 必须用 venv 里的 python 绝对路径。 写
python3或者python会用系统默认的解释器,找不到你 pip install 的 mcp 包。用which python(在你的 venv 激活状态下)拿到完整路径。 -
cwd 填 server.py 所在目录的绝对路径。 Claude Code 启动子进程时不一定在你预期的工作目录。
保存后重启 Claude Code。在对话里输入 /mcp,你应该能看到 todo-mcp 和它下面注册的 4 个工具。
试一下:"帮我看看现在有哪些待办事项"——Claude 会调用 list_todos,返回你 API 的 JSON 数据。
如果你用 Cursor
Cursor 的 MCP 配置稍有不同。打开 Settings → MCP Servers,点 Add Server,填:
{
"name": "todo-mcp",
"command": "/path/to/venv/bin/python",
"args": ["server.py"],
"cwd": "/path/to/mcp-server"
}
跟 Claude Code 的字段基本一样。Cursor 会在 Agent 模式下自动发现你的 MCP 工具。
实际用的时候会踩哪些坑
我自己封装了好几个内部 API 做 MCP Server,下面是踩过的坑:
超时问题。 httpx 默认超时是 5 秒,如果你的 API 比较慢,改成 timeout=30 或者按需调整。MCP 协议本身没有超时限制,但 Claude Code 等客户端有自己的耐心上限——大概 60 秒,超了会认为工具挂了。
错误处理不能只 raise。 如果你的工具抛了异常,Claude 会收到一个模糊的错误消息。更好的做法是 try/except 之后返回一条人能读懂的错误信息:
@mcp.tool()
async def list_todos() -> str:
"""获取所有待办事项。"""
try:
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.get(f"{API_BASE}/todos")
resp.raise_for_status()
return resp.text
except httpx.HTTPStatusError as e:
return f"API 返回错误: {e.response.status_code}"
except httpx.ConnectError:
return "连不上 API 服务,确认一下 localhost:8000 是否在运行"
这样 Claude 拿到的是一段中文说明,它可以告诉用户哪里出了问题,甚至自己尝试修复。
返回数据太大。 如果你的 API 一次返回几万条数据,全塞给 AI 会撑爆上下文窗口。两个办法:一是在工具函数里做分页(加 limit 和 offset 参数),二是只返回摘要信息。
认证怎么处理。 如果你的 API 需要 token,别硬编码在代码里。用环境变量:
import os
API_TOKEN = os.environ.get("API_TOKEN", "")
# 在请求里带上
headers = {"Authorization": f"Bearer {API_TOKEN}"}
resp = await client.get(url, headers=headers)
启动 MCP Server 前 export API_TOKEN=xxx 就行。Claude Code 的配置也支持 env 字段:
{
"mcpServers": {
"todo-mcp": {
"command": "python",
"args": ["server.py"],
"env": {
"API_TOKEN": "your-token-here"
}
}
}
}
调试利器:mcp dev
开发过程中别直接连 Claude 测。先用 SDK 自带的 inspector:
mcp dev server.py
浏览器打开后能看到所有注册的工具,点进去可以填参数、手动调用、看返回结果。JSON Schema 也在这个页面上,能直接检查 AI 会看到什么样的参数定义。
我一般的开发流程是:写工具函数 → mcp dev 验证 → 确认没问题了再改 Claude Code 配置 → 重启 Claude Code 测试完整对话。比在聊天里来回调试效率高不少。
总结
整个流程回头看不复杂:
pip install "mcp[cli]" httpx- 用
@mcp.tool()把你的 API 调用包成函数 - 给每个函数写好 type hint 和 docstring
- 配置 Claude Code 或 Cursor 的 MCP 连接
- 对话里直接用
MCP 的 Python SDK 文档在 https://github.com/modelcontextprotocol/python-sdk ,FastMCP 的 API 在 https://gofastmcp.com/getting-started/welcome 。想看更多实际项目可以逛 GitHub 的 MCP Registry(https://github.com/mcp),上面已经有几百个社区做的 MCP Server 了。
如果你的公司有内部 API,花半小时包一个 MCP Server 出来,让 AI 助手能直接操作业务数据。比在聊天窗口里反复复制粘贴 curl 结果,舒服得多。
更多推荐



所有评论(0)