我用 Python 30 分钟搭了个 MCP 网关,把四个 AI 工具统一成了一套协议
手上同时维护着 Slack bot、微信机器人和内部 CLI 工具,每个都对接了不同的 AI API,接口风格五花八门,维护成本快赶上了业务代码本身。
问题是什么
三个月前我接手了一个内部工具平台,三个 AI 功能模块——代码审查、文档问答、日志分析——分别对接了 OpenAI、Claude 和本地跑的 LLaMA 模型。每个模块的调用方式五花八门:OpenAI 用 messages 数组,Claude 用 prompt 字符串,LLaMA 又是另一种 JSON 格式。
更头疼的是,每次新增一个工具(比如搜索、计算器、查数据库),就得在每个模块里单独实现一遍。那会儿我就琢磨,得有一个统一的工具调用网关。正好 MCP(Model Context Protocol)在这时候冒了出来。
解决思路
MCP 的核心说白了就一句话:用标准化的方式,让 AI 模型发现和调用外部工具。它定义了三种基本操作:
| 操作 | 含义 |
|---|---|
tools/list |
返回可用工具清单 |
tools/call |
调用指定工具并返回结果 |
tools/notify |
工具状态变化时主动通知 |
我的方案很简单:用 Python 写一个轻量 MCP 网关,后端工具以插件方式注册,前端对接任何支持 MCP 的 AI 客户端。不折腾重量级框架,只依赖标准库 + FastMCP。
操作步骤
步骤1:安装依赖,搭好项目骨架
mkdir mcp-gateway && cd mcp-gateway
python -m venv venv && source venv/bin/activate
pip install "mcp[cli]>=1.0.0"
pip install httpx # 调用外部 API 用
MCP 官方提供了 Python SDK,mcp 命令行工具可以直接拿来调试和测试。
项目结构:
mcp-gateway/
├── server.py # MCP 网关主入口
├── tools/ # 工具插件目录
│ ├── __init__.py
│ ├── weather.py # 天气查询工具
│ └── calculator.py # 计算器工具
└── config.py # 配置文件
步骤2:用 FastMCP 起一个网关服务
MCP Python SDK 提供了 FastMCP——一个类似 FastAPI 风格的高层封装,十来行就能跑起来。
# server.py
from mcp.server.fastmcp import FastMCP
# 创建 MCP 服务,名称会出现在客户端的能力列表里
mcp = FastMCP("my-tool-gateway")
# 注册一个简单的计算器工具
@mcp.tool()
def calculate(expression: str) -> str:
"""执行数学运算,传入表达式如 '1 + 2 * 3'"""
try:
# 安全起见用 ast 而不是 eval
import ast
import operator as op
allowed_ops = {
ast.Add: op.add, ast.Sub: op.sub,
ast.Mult: op.mul, ast.Div: op.truediv,
ast.Pow: op.pow, ast.USub: op.neg,
}
def eval_expr(node):
if isinstance(node, ast.Constant):
return node.value
elif isinstance(node, ast.BinOp):
return allowed_ops[type(node.op)](eval_expr(node.left), eval_expr(node.right))
elif isinstance(node, ast.UnaryOp):
return allowed_ops[type(node.op)](eval_expr(node.operand))
else:
raise ValueError("不支持的表达式")
result = eval_expr(ast.parse(expression.strip(), mode='eval').body)
return str(result)
except Exception as e:
return f"计算错误: {e}"
if __name__ == "__main__":
# stdio 传输模式,适合跟 AI 客户端配合
mcp.run(transport="stdio")
这段代码一跑起来,MCP 客户端就能通过 tools/list 拿到 calculate 工具的元信息——名称、描述、参数结构(从类型注解自动推断)。
注意:
transport="stdio"是最简单的对接方式,适合嵌入到 AI 助手的子进程里。如果要用 HTTP 接口,换成transport="sse"就行。
步骤3:搞个插件化注册机制
硬编码工具不是长久之计——我想要的是写一个工具就自动注册一个。下面是我搞的插件加载器:
# tools/__init__.py
import os
import importlib
import inspect
from typing import List
from mcp.server.fastmcp import FastMCP
def discover_and_register_tools(mcp: FastMCP, tools_dir: str = "tools"):
"""自动扫描 tools 目录,注册所有 tool 函数"""
tools_path = os.path.join(os.path.dirname(__file__), "..", tools_dir)
for f in os.listdir(tools_path):
if f.startswith("_") or not f.endswith(".py"):
continue
module_name = f"{tools_dir}.{f[:-3]}"
try:
module = importlib.import_module(module_name)
for name, obj in inspect.getmembers(module):
if getattr(obj, "_is_mcp_tool", False):
mcp.add_tool(obj)
print(f" ✓ 注册工具: {name}")
except Exception as e:
print(f" ✗ 加载 {module_name} 失败: {e}")
然后给每个工具函数加个装饰器标记:
# tools/calculator.py
from functools import wraps
def tool():
"""标记函数为 MCP 工具"""
def decorator(func):
func._is_mcp_tool = True
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
@tool()
def add(a: float, b: float) -> float:
"""两数相加"""
return a + b
@tool()
def multiply(a: float, b: float) -> float:
"""两数相乘"""
return a * b
# tools/weather.py
import httpx
from tools.calculator import tool
@tool()
def get_weather(city: str) -> str:
"""查询指定城市的当前天气"""
# 用 wttr.in 的免费 API 做演示
try:
resp = httpx.get(f"https://wttr.in/{city}?format=%C+%t", timeout=10)
return f"{city}: {resp.text.strip()}"
except Exception as e:
return f"查询天气失败: {e}"
插件化之后,加一个新工具只需要在 tools/ 下新建一个 .py 文件,写一个带 @tool() 的函数就行了。
步骤4:对接 AI 客户端——拿 Claude Code 举例
MCP 最实在的地方在于:主流 AI 工具已经开始原生支持它。Claude Code 就是其中之一。
在项目根目录创建 MCP 配置文件:
{
"mcpServers": {
"my-tool-gateway": {
"command": "python",
"args": ["server.py"],
"env": {},
"disabled": false,
"autoApprove": []
}
}
}
桌面端 Claude App 的话,配置文件路径是 ~/claude_desktop_config.json。Claude Code CLI 则放在项目目录的 .claude/settings.json 里。
配置好后,Claude 会自动拉起 server.py 子进程,通过 stdio 通信。对话中需要计算或查天气时,Claude 会通过 MCP 协议调用注册的工具。
踩坑记录: 我第一次测试时工具没被识别,排查了半天发现是
mcp版本问题。0.x和1.x的 API 完全不兼容,FastMCP是 1.0 才引入的。一定要pip install "mcp>=1.0.0"。
结果与总结
整个网关从零到跑通花了大概 30 分钟,核心代码不到 80 行。最终效果:
几个坑,记住了能省不少时间:
延伸思考
这个 MVP 还有很多可以折腾的空间:
如果你也在折腾多个 AI 工具对接,MCP 值得一试。协议本身不复杂,用起来顺不顺手全看 SDK 的成熟度——Python 的 FastMCP 已经是"开箱即用"的水平了。

评论已关闭!