一、每个 Agent 都在重复造轮子
我们之前写过 LangGraph vs AutoGen 的对比(那篇讲了 Agent 的"编排层"怎么选)。但有个问题那篇没展开:不管你用哪个框架,只要你的 Agent 要连真实世界——查数据库、读文件、调 API、操作浏览器——你都得写一堆"工具适配器"。更糟的是,这套适配器是框架绑定的。
举个真实例子。你想让模型查一下 MySQL 里的订单表:
- 用 LangChain,你要写一个
@tool装饰的函数,按照 LangChain 的 schema 描述参数; - 用 AutoGen,你要写
FunctionSpec和配套的execute_function; - 用原生 OpenAI,你要写 JSON Schema 的
tools数组,自己解析返回; - 用 Claude 原生,又是另一套
tools格式。
同一份"查数据库"的能力,因为接入的模型/框架不同,要写四遍,而且彼此不兼容。这就是 MCP 出现前的状态:工具能力的"接线"是点对点、私有、重复的。MCP 的愿景是——工具只实现一次(一个 MCP Server),任何支持 MCP 的模型/客户端都能即插即用。
┌────────────────────────────────────────────────────────┐ │ 前 MCP 时代: 工具是"点对面"的私有对接 │ │ │ │ [查DB适配器-LangChain] ──> LangChain ──> 模型 A │ │ [查DB适配器-AutoGen] ──> AutoGen ──> 模型 B │ │ [查DB适配器-原生] ──> 原生API ──> 模型 C │ │ (同一能力, 写三遍, 互不兼容) │ ├────────────────────────────────────────────────────────┤ │ MCP 时代: 工具实现一次, 即插即用 │ │ │ │ [MySQL MCP Server] ──标准协议──> [MCP Client] ──> 任意模型 │ │ [文件系统 MCP Server] ──标准协议──> [MCP Client] ──> 任意模型│ └────────────────────────────────────────────────────────┘二、MCP 是什么:AI 世界的 USB-C
MCP 由 Anthropic 在 2024 年 11 月开源提出,是一个开放的标准化协议,用于让大模型应用(Host)以统一方式连接外部数据源和工具(Server)。官方比喻是"AI 应用的 USB-C 接口"——USB-C 统一了充电和数据传输的接口,MCP 想统一"模型 ↔ 外部能力"的接口。
它解决三个具体问题:
- 接口标准化:工具怎么描述、怎么调用、怎么返回,有统一格式;
- 传输标准化:本地进程间(stdio)和远程(HTTP+SSE)都有标准传输;
- 能力标准化:把"能做什么"拆成三类原语,覆盖绝大多数工具场景。
2.1 三层架构:Host / Client / Server
┌─────────────────────────────────────────────────────────┐ │ MCP Host (宿主应用) │ │ 例如: Claude Desktop, IDE 插件, 自研 Agent 应用 │ │ 职责: 运行 LLM, 管理多个 MCP Client, 决定何时调哪个工具 │ │ │ │ │ ├── MCP Client ① ──stdTransport──> MCP Server ① │ │ │ (例如: 文件系统服务器) │ │ ├── MCP Client ② ──stdio/HTTP───> MCP Server ② │ │ │ (例如: GitHub 服务器) │ │ └── MCP Client ③ ──stdTransport──> MCP Server ③ │ │ (例如: 数据库服务器) │ └─────────────────────────────────────────────────────────┘- Host:真正跑模型、和用户交互的应用。一个 Host 同时管理多个 Client。
- Client:Host 内的连接管理器,一个 Client 只连一个 Server,负责协议握手、消息收发。
- Server:提供具体能力的进程/服务。它可以暴露工具、数据、提示模板。
注意这个设计:MCP 本身不碰模型推理,它只管"模型应用 ↔ 工具"之间的通信。模型怎么决策调哪个工具、怎么理解返回,是 Host 的事。MCP 把"工具层"从"编排层"彻底解耦——这正是它和 Agent 框架(LangGraph/AutoGen)不冲突反而互补的原因。
2.2 传输层:JSON-RPC 2.0
MCP 的线格式基于JSON-RPC 2.0,支持两种传输:
- stdio(标准输入输出):用于本地 Server(同一台机器起个子进程),最简单、零网络配置,适合桌面应用连本地工具;
- Streamable HTTP / SSE:用于远程 Server,支持流式响应,适合 SaaS 化部署。
每个 MCP 会话遵循固定生命周期:initialize(握手,交换能力)→notifications/initialized→ 正常request/response→ 关闭。
三、三大原语:Tools / Resources / Prompts
MCP 把 Server 能提供的东西分成三类,这是它设计上最清楚的地方。
3.1 Tools(工具)—— 模型可调用、有副作用的操作
Tool 是给模型主动调用的"动作",比如"查询数据库""发送邮件""调用 API"。每个 Tool 必须声明:
name:唯一名;description:模型靠这个决定"什么时候该用"(所以描述要写得像给人看的说明,不能太简略);inputSchema:JSON Schema 格式的参数定义;- 一个
execute实现。
Tool 是有副作用的(写数据、发请求),所以调用前要经模型决策 + 通常需要用户确认。
3.2 Resources(资源)—— 只读的数据源
Resource 是模型可以读取的上下文数据,比如文件内容、数据库某张表的快照、API 返回的 JSON。它没有副作用,更像"把资料喂给模型"。Resource 用 URI 寻址(如file:///logs/app.log、db://users/123)。
关键区别:Tool 是"动作",Resource 是"资料"。一个查数据库可以是 Tool(执行 SQL,有副作用),也可以暴露成 Resource(只读返回某张表,供模型引用)。
3.3 Prompts(提示模板)—— 可复用的交互流程
Prompt 是预定义的、参数化的提示词模板,比如"把这段代码转成带单元测试的版本"这种固定工作流。它让用户/应用一键触发标准化交互,而不是每次手写。
MCP 三大原语对照: ┌──────────┬──────────┬───────────┬───────────────────────┐ │ 原语 │ 谁触发 │ 有无副作用 │ 例子 │ ├──────────┼──────────┼───────────┼───────────────────────┤ │ Tools │ 模型决策 │ 有 │ 查SQL/发邮件/调API │ │ Resources│ 应用/模型 │ 无(只读) │ 读文件/拉数据快照 │ │ Prompts │ 用户触发 │ 无 │ 标准化工作流模板 │ └──────────┴──────────┴───────────┴───────────────────────┘四、MCP vs Function Calling:别混为一谈
很多人把 MCP 和 Function Calling 当成一回事,其实它们是不同层级:
| 维度 | Function Calling | MCP |
|---|---|---|
| 提出方 | 各模型厂商(OpenAI 最早) | Anthropic(开放标准) |
| 层级 | 模型能力(模型"会"调工具) | 通信协议(工具"怎么接") |
| 范围 | 定义工具 schema + 模型输出调用请求 | 定义 Host/Client/Server 全链路如何交互 |
| 跨模型 | 每家的 schema 格式略有差异 | 与模型无关,任何模型只要 Host 支持即可 |
| 跨框架 | 绑定具体 SDK | 一次实现,处处可用 |
一句话:Function Calling 是"模型怎么表达'我要调工具'";MCP 是"工具和模型应用之间怎么通信"。现实中它们是配合的——MCP Server 提供的 Tools,最终会被 Host 翻译成某个模型的 Function Calling 格式(或直接走协议调用)。MCP 补的是 Function Calling 缺的那块"标准化接线"。
五、实战:写一个可运行的 MCP Server + Client
用官方 Python SDK(pip install mcp),我们写一个暴露两个能力的 Server:一个 Tool(查 SQLite 订单总额)+ 一个 Resource(读取配置文件)。
5.1 MCP Server(FastMCP 高层 API,最简洁)
python
# server.py —— 用 FastMCP 高层封装, 几行就能起一个 MCP Server from mcp.server.fastmcp import FastMCP import sqlite3, json mcp = FastMCP("order-tools") @mcp.tool() def query_order_total(user_id: int) -> float: """查询指定用户的累计订单总额。 参数 user_id: 用户ID。返回该用户所有订单的合计金额。""" conn = sqlite3.connect("orders.db") cur = conn.cursor() cur.execute("SELECT SUM(amount) FROM orders WHERE user_id=?", (user_id,)) total = cur.fetchone()[0] or 0.0 conn.close() return round(total, 2) @mcp.tool() def list_recent_orders(limit: int = 10) -> str: """返回最近 limit 条订单的 JSON 列表。""" conn = sqlite3.connect("orders.db") cur = conn.cursor() cur.execute( "SELECT order_id, user_id, amount FROM orders " "ORDER BY id DESC LIMIT ?", (limit,)) rows = [dict(zip(["order_id","user_id","amount"], r)) for r in cur.fetchall()] conn.close() return json.dumps(rows, ensure_ascii=False) @mcp.resource("config://app") def get_app_config() -> str: """暴露应用配置作为只读资源。""" return json.dumps({"db": "orders.db", "env": "prod"}) if __name__ == "__main__": mcp.run() # 默认 stdio 传输注意@mcp.tool()的 docstring极其重要——模型完全靠它来判断"什么时候该调这个工具、参数怎么填"。写得含糊,模型就会瞎调或干脆不调。
5.2 MCP Client(连接并调用)
python
# client.py —— 连接上面的 Server 并列出、调用工具 import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): # 启动 Server 子进程 (stdio 传输) server_params = StdioServerParameters( command="python", args=["server.py"]) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: # ① 初始化握手 await session.initialize() # ② 列出 Server 提供的工具 tools = await session.list_tools() print("可用工具:", [t.name for t in tools.tools]) # ③ 调用工具 (模型决策后由 Host 发起, 这里演示直接调用) result = await session.call_tool( "query_order_total", {"user_id": 1001}) print("订单总额:", result.content[0].text) if __name__ == "__main__": asyncio.run(main())跑python client.py,你会看到它列出了query_order_total/list_recent_orders两个工具,并成功调用拿到结果。这套 Server 不需要任何改动,就能被 Claude Desktop、任何支持 MCP 的 IDE、或你自己写的 Host 直接接入——这就是"实现一次,处处可用"。
5.3 在 Claude Desktop 里即用
如果你装了 Claude Desktop,只要在配置文件claude_desktop_config.json里加一段:
json
{ "mcpServers": { "order-tools": { "command": "python", "args": ["/绝对路径/server.py"] } } }重启 Claude Desktop,它就能在对话里直接"查订单""读配置",而模型侧你完全不用写接入代码。
六、为什么它可能成为 Agent 时代的"HTTP"
我的判断:MCP 的价值不只是"少写几行适配器",而是它可能成为Agent 生态的底层标准协议,类比 Web 时代的 HTTP。
- 解耦产生网络效应。工具实现一次、任意 Host 可用,意味着会涌现出大量"即插即用"的 MCP Server(数据库、GitHub、Slack、浏览器、云服务商……),形成一个能力市场。工具的供给越多,用 MCP 的 Host 越值钱,反过来也成立——正循环。
- 降低 Agent 落地门槛。企业想给内部 Agent 接 ERP、CRM、数据仓库,过去要定制开发;现在只要这些系统提供 MCP Server,Agent 就能直接消费。
- 安全边界清晰。MCP 把"工具能做什么"显式声明、把调用权交给 Host 和用户确认,天然适合权限管控(每个 Server 管一类资源,最小权限)。
但也要泼盆冷水,当前落地的真实痛点:
6.1 当前痛点(不回避)
- 认证/授权标准还在成熟中。stdio 模式适合本地,远程 Server 的 OAuth、多租户、密钥管理尚无定论,企业落地有顾虑。
- "描述即接口"的双刃剑。Tool 的 description 直接影响模型决策质量,写得不好模型就乱用;而自动生成的 description 往往太简陋。
- 调试困难。协议层错误、JSON-RPC 异常、Server 崩溃,排查链路比普通 API 长。
- 标准快速演进。2025 年传输层从 SSE 切到 Streamable HTTP,早期 SSE Server 已不推荐,迁移成本存在。
七、结论
MCP 不是又一个 Agent 框架,它是 Agent 的"工具层 USB-C"。它把"模型能连什么"和"框架怎么编排"彻底解耦——这正好解释了为什么我们之前对比的 LangGraph / AutoGen 都不排斥 MCP,反而在往里集成。
给工程师的判断建议:
- 如果你的团队在搭 Agent 平台,现在就该把"工具接入"统一到 MCP,别再手写框架私有的适配器,否则半年后标准定了,你那套私有接线全成技术债。
- 优先用 MCP 暴露内部系统能力(数据库、内部 API、文件系统),而不是每个 Agent 项目重复造。
- 写 Tool 时把 description 当产品文档写,这是模型调用正确率的最高杠杆点。
- 本地场景先上 stdio,远程场景等 Streamable HTTP 认证方案成熟再规模化。
MCP 会不会真的成为"Agent 的 HTTP"还不好说(标准还在动、大厂站队也未定),但它指的方向——工具能力的标准化、即插即用——几乎是确定性趋势。早半年上车,你的 Agent 架构就少背一块债。
你现在的 Agent 项目是怎么接工具的?是手写框架私有适配器,还是已经切到 MCP?有没有踩过 description 写不好导致模型乱调工具、或者远程 Server 认证难落地的坑?
往期回顾:
- LangGraph vs AutoGen 深度对比:多 Agent 协作框架该选谁
- RAG 评测体系搭建:用 RAGAS 科学量化你的检索增强系统