MCP 定义
MCP(Model Context Protocol) 是由 Anthropic 在 2024 年底开源的一个通用协议标准。
没有 MCP 之前的痛点(M×N 问题)
假设你有 5 个不同的 AI 应用(ChatGPT、Claude、Cursor、自研Agent等),要对接 10 个外部服务(GitHub、Slack、数据库、文件系统等)。
- 每个 AI 应用都要为每个服务写一套专属集成代码
- 总共需要 5×10=50 个连接器
- 每新增一个服务,所有 AI 应用都要重新开发
有了 MCP 之后(M+N 问题)
MCP 定义了一套统一的 Client-Server 架构:
- MCP Host:AI 应用(如 Claude Desktop、你的 Agent)
- MCP Client:Host 内置的协议客户端
- MCP Server:轻量级服务,封装具体能力(文件系统、GitHub API、数据库查询等)
现在只需要:
- 5 个 Host 各自实现 1 个 MCP Client
- 10 个服务各自实现 1 个 MCP Server
- 总共 5+10=15 个实现,且即插即用
要彻底理解 MCP,请忘掉复杂的 AI 概念,把它想象成 “餐厅点餐”:
- MCP Server(后厨):拥有具体的能力(炒菜、煮饭)。它把菜单(Tools)打印出来贴在窗口,只管按标准格式接单和出菜,不关心是谁点的。
- MCP Client(服务员):手里拿着标准点餐平板。他不需要会炒菜,只需要看懂菜单,把客人的需求翻译成标准订单发给后厨,再把做好的菜端给客人。
- Agent/LLM(顾客):只看服务员提供的菜单来决定吃什么,完全不跟后厨直接打交道。
核心本质:Client 和 Server 之间通过一套固定的 JSON-RPC 格式通信。只要遵守这个格式,任何 Client 都能连任何 Server。
MCP 和 Agent 是如何一起工作的?
MCP 对 Agent 来说,是一个更优雅、更安全的工具调用层。工作流程如下:
用户提问 → Agent(LLM) 思考 → 决定调用工具
↓
MCP Client 发送标准请求
↓
MCP Server 执行操作
(读文件/查DB/调API)
↓
返回结构化结果给 Agent
↓
Agent 观察结果 → 继续思考或输出答案
经典MCP Demo
下面我们用 Python 官方 SDK 编写一个最简单的 “问候服务”,让你亲眼看到这套机制是如何运转的。
编写 MCP Server(提供能力的后厨)
Server 的职责是:声明自己有什么工具,并实现工具的逻辑。
# server.py
from mcp.server.fastmcp import FastMCP
# 创建一个名为 "Greeter" 的 MCP Server
mcp = FastMCP("Greeter")
# 使用装饰器注册一个工具。
# 注意:函数的 docstring 会自动成为工具的描述,LLM 靠它来判断何时调用!
@mcp.tool()
def say_hello(name: str) -> str:
"""向指定的人发送热情的问候。当用户想要打招呼时使用此工具。"""
return f" 你好, {name}! 欢迎来到 MCP 的世界!"
if __name__ == "__main__":
# 启动 Server,默认使用 stdio(标准输入输出)传输
# 这意味着它通过控制台管道与 Client 通信
mcp.run(transport="stdio")
编写 MCP Client(调用能力的服务员)
Client 的职责是:连接 Server,获取工具列表,然后调用工具。注意看代码中完全没有硬编码 say_hello 的逻辑,全是动态发现的。
# client.py
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
# 1. 定义如何连接到 Server(就像配置后厨的地址)
server_params = StdioServerParameters(
command="python",
args=["server.py"]
)
# 2. 建立连接并创建会话
async with stdio_client(server_params) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session:
# 3. 初始化握手(Client 和 Server 确认协议版本)
await session.initialize()
# 4. 【关键】动态获取 Server 提供的所有工具列表
tools_result = await session.list_tools()
print(" 发现以下工具:")
for tool in tools_result.tools:
print(f" – {tool.name}: {tool.description}")
# 5. 【关键】通过名称动态调用工具(无需提前知道函数签名)
result = await session.call_tool(
name="say_hello",
arguments={"name": "开发者"}
)
print(f"\\n 调用结果: {result.content[0].text}")
if __name__ == "__main__":
asyncio.run(main())
运行与验证
在终端执行 Client,它会自动拉起 Server 进程并完成交互:
python client.py
预期输出:
发现以下工具:
– say_hello: 向指定的人发送热情的问候。当用户想要打招呼时使用此工具。
调用结果: 你好, 开发者! 欢迎来到 MCP 的世界!
这种经典的MCP Demo可以上生产吗?
直接给结论:不能!
如果直接把它部署到生产环境,大概率会在几小时甚至几分钟内出现崩溃、资源泄漏或安全漏洞。
要将其升级为生产级应用,你至少需要在以下 6 个维度进行系统性完善:
1. 生命周期与连接韧性(最致命的问题)
教学代码假设 Server 永远在线且不会出错,但现实中进程会崩溃、网络会抖动。
- 自动重连机制:当 stdio 进程退出或 SSE/HTTP 连接断开时,Client 必须能自动重启 Server 或重新建立连接,而不是直接抛异常。
- 优雅关闭:使用 AsyncExitStack 确保在程序退出、异常中断时,所有 Server 子进程都能被正确 kill 掉,避免产生僵尸进程吃光服务器内存。
- 健康检查:定期发送 ping 请求,对长时间无响应的 Server 主动断开并重建。
2. 工具路由与命名空间隔离
教学代码用简单的字典合并工具,这在真实场景中必然出问题。
- 命名空间前缀:强制为每个 Server 的工具加上 {server_name}__ 前缀(如 weather__get_weather),从根本上杜绝多 Server 工具名冲突。
- 动态工具加载/卸载:不要启动时一次性加载所有工具。应支持运行时按需挂载新 Server,或在检测到某 Server 不可用时自动摘除其工具,防止 LLM 调用死工具。
- 工具数量治理:当聚合工具超过 30-50 个时,LLM 选择准确率会断崖式下降。需要引入“工具索引”或“两阶段检索”:先让 LLM 选 Server/类别,再加载具体工具。
3. 安全性加固(生产环境的红线)
MCP Server 拥有执行代码和访问系统资源的权限,是攻击面最大的环节。
- 参数校验与沙箱:永远不要信任 LLM 传来的参数。在 Server 端对所有输入做严格的 Schema 校验;文件操作、命令执行类 Server 必须在 Docker 容器或受限用户下运行。
- 权限分级:区分只读工具和写入/删除工具。对高危工具增加二次确认机制或人工审批节点。
- Prompt Injection 防御:工具返回的结果可能包含恶意指令。应对返回内容做清洗或标记,避免 LLM 被工具返回值劫持。
4. 可观测性
没有监控的 Agent 就是黑盒,出了问题无法排查。
- 全链路 Trace:每次工具调用都要记录 trace_id、耗时、输入参数、输出结果、Token 消耗。推荐接入 LangSmith、Arize Phoenix 或 OpenTelemetry。
- 结构化日志:Server 和 Client 的日志必须关联同一个 trace_id,方便跨进程追踪问题。
- 指标监控:监控工具调用成功率、P99 延迟、Server 重启次数等核心指标,设置告警。
5. 性能优化
- 并发调用:当 LLM 在一次思考中决定调用多个独立工具时,必须用 asyncio.gather() 并行执行,而非串行等待。
- 结果压缩:工具返回的大段文本(如网页内容、长文档)应在 Server 端或 Client 层做摘要/截断,避免撑爆 LLM 上下文窗口。
- 缓存层:对幂等的查询类工具(如天气、汇率)加 TTL 缓存,减少重复调用和 Token 消耗。
6. 工程化规范
- 配置外置:Server 列表、超时时间、重试策略等全部从环境变量或配置文件读取,禁止硬编码。
- 类型安全:使用 Pydantic 定义工具参数和返回值的 Schema,而非依赖裸字典。
- 集成测试:编写端到端测试,模拟 Server 崩溃、超时、返回异常数据等边界情况,验证 Client 的容错能力。
两种架构模式的对比
模式A:简单直连(Demo/小规模)
编排层(脑) → MCP Client(手) → MCP Server(柜子)
- MCP Client 直接嵌入在编排层代码中
- 每个 Agent 进程都自带一个 Client 实例
- 鉴权、连接管理、限流全在 Client 里做
- 问题:多 Agent 时 Token 混乱、无法统一审计、连接数爆炸
模式B:企业级网关架构(生产环境)

编排层(脑) → [Gateway 内置的 MCP Client] → MCP Gateway → MCP Server
↑ 这个Client是Gateway的一部分
- 顶层编排层:专注业务意图,完全解耦底层协议细节。对编排层来说,Gateway 就是一个“超级 MCP Client”
- 中间 Gateway 层:内置 MCP Client 集群承担"手"的职责,同时集成企业级治理能力(鉴权、限流、审计等)
- 底层 Server 层:各业务域独立部署,通过标准 MCP 协议与 Gateway 通信
如果把企业级 MCP 架构比作一家现代化公司:
- MCP Server = 各个专业部门(财务部、IT部、市场部),各自拥有专业技能但只听从指令。
- MCP Gateway = 公司前台/安保,负责验证身份、转接电话、限流。
- LLM Agent 编排层 = 总经理/项目经理,负责理解老板(用户)的需求,拆解任务,协调各部门工作,并对最终结果负责。
MCP Client和Server 普通demo和企业级构建对比
| 连接管理 | 每次调用新建连接,stdio/SSE 直连 | 连接池 + 会话复用 + 自动重连 | 避免高频握手开销,防止 Server 端连接数爆炸 |
| 身份认证 | 无 / 硬编码 API Key | OAuth2/OIDC + Token 自动刷新 + Vault 集成 | 凭证安全、过期自动续期、零信任访问 |
| 流量治理 | 无限制直接透传 | 限流、熔断、降级、超时控制、重试策略 | 防止 LLM 幻觉导致无限循环调用打垮后端服务 |
| 多租户隔离 | 单用户/单租户 | 租户级路由、配额、数据隔离、RBAC | SaaS 化部署,防止跨租户数据泄露和资源抢占 |
| 可观测性 | print() / 控制台日志 | OpenTelemetry Trace + Metrics + 结构化审计日志 | 全链路追踪,Token 消耗归因,合规审计 |
| 工具发现 | 启动时加载静态列表 | 动态注册/注销 + 版本管理 + Schema 校验 | 热更新工具无需重启,灰度发布,防止脏 Schema 污染 Agent |
| 传输协议 | stdio / 基础 SSE | Streamable HTTP + gRPC + 双向心跳保活 | 适应云原生网关、负载均衡器,支持长连接流式响应 |
| 容错设计 | 报错即终止 | 优雅降级 + 兜底工具 + 错误语义化封装 | LLM 能理解错误并自主恢复,而非直接抛出堆栈 |
为什么 Python 仍是企业级 MCP 的首选?
- 生态垄断地位:MCP 官方 SDK 中 Python 和 TypeScript 是一等公民,但 AI/Agent 生态(LangChain、LlamaIndex、AutoGen、CrewAI)几乎全部是 Python 原生。用其他语言写 MCP Server 意味着你要自己造轮子对接这些框架。
- 开发效率与迭代速度:MCP Server 本质是“胶水代码”,核心逻辑是调用现有 API、查数据库、做数据转换。Python 的开发效率远高于 Go/Java,在 Agent 这种需求变化极快的领域,快比省资源更重要。
- 人才储备:AI 工程师、算法工程师、数据工程师普遍精通 Python。让一个 AI 团队去写 Go 或 Rust 的 MCP Server,沟通成本和招聘成本远高于多开几台服务器的成本。
- 性能瓶颈不在语言本身:MCP Server 90% 的时间在等待 I/O(等 LLM 响应、等数据库查询、等外部 API)。Python 的 asyncio 处理 I/O 密集型任务的性能完全够用,真正的瓶颈在网络延迟和 LLM 推理速度,而非 CPU 计算。




