MCP 协议:AI 世界的 USB 接口
作者:程序员马丁
Ragent AI —— 从 0 到 1 纯手工打造企业级 Agentic RAG,拒绝 Demo 玩具!AI 时代,助你拿个offer。
上一篇讲了 Function Call,你已经能让 RAG 系统从只能查知识库升级到能查数据、能调接口、能干活。模型自己判断什么时候该调工具,输出标准化的调用意图,你的代码执行函数——整个流程跑得很顺。
但文章最后留了一个尾巴:Function Call 很好用,工具一多就管不过来了。
假设你在一家公司做企业知识库助手,一开始只有两个工具:查年假、查订单。你手写两份 JSON Schema,代码里写两个 if-else 路由,没什么问题。但半年过去了,产品经理不断加需求:查考勤、查销售数据、查会议室、查报销进度、查项目排期、查库存、查物流、查合同……工具从 2 个变成了 20 个。
这时候你会发现:
- 20 个工具 × 每个工具 5~10 个参数 = 几百行 JSON Schema 要手写和维护
- Python 团队写了一个数据分析工具,你的 Java 系统调不了
- 新来的同事问“系统里有哪些工具可用”,你翻了半天代码才找全
- 某个工具的参数改了,JSON Schema 忘了同步更新,模型传了错误的参数,线上出了 bug
你需要的不是更多的 if-else,而是一个标准化的工具管理协议。这就是今天要讲的 MCP。
Function Call 的痛点:工具一多就管不过来
1. 回顾:Function Call 做对了什么
在展开痛点之前,先肯定一下 Function Call 的核心价值:
- 让模型自己判断什么时候该调工具:不用写规则匹配,模型理解自然语言,“我还剩几天年假”和“假期余额还有多少”都能识别
- 输出格式标准化:模型输出 JSON 格式的
tool_calls,易于解析,不会出现“模型在回答里夹了一段代码”的混乱情况 - 多轮对话机制成熟:定义工具 → 模型输出调用意图 → 执行函数 → 返回结果 → 生成答案,流程清晰
这些能力本身没问题,Function Call 解决了让模型调工具的核心问题。但当工具规模化之后,Function Call 协议本身没有覆盖的那些“管理层面”的问题就暴露出来了。
2. 工具规模化之后的四大痛点
2.1 工具定义的维护噩梦
上一篇的代码你应该还有印象,定义一个工具要写多少 JSON Schema:
JsonObject tool1 = new JsonObject();
tool1.addProperty("type", "function");
JsonObject function1 = new JsonObject();
function1.addProperty("name", "getUserAnnualLeave");
function1.addProperty("description", "查询用户的年假余额,包括总天数、已使用天数、剩余天数");
JsonObject parameters1 = new JsonObject();
parameters1.addProperty("type", "object");
JsonObject properties1 = new JsonObject();
JsonObject userId1 = new JsonObject();
userId1.addProperty("type", "string");
userId1.addProperty("description", "用户 ID");
properties1.add("userId", userId1);
parameters1.add("properties", properties1);
JsonArray required1 = new JsonArray();
required1.add("userId");
parameters1.add("required", required1);
function1.add("parameters", parameters1);
tool1.add("function", function1);
这只是一个工具、一个参数。如果一个工具有 5 个参数,代码量翻 5 倍。20 个工具就是几百行纯粹的 JSON Schema 构建代码。
更要命的是维护问题:
- 工具的参数改了(比如
getUserAnnualLeave新增了一个year参数),你要同步修改 JSON Schema,忘了改就会出 bug - 没有工具文档,新同事不知道系统里有哪些工具可用,只能翻代码
- 工具定义散落在代码各处,没有统一的注册中心
2.2 跨语言跨系统的集成困境
你的企业知识库助手需要调用的工具来自不同团队:
- Java 团队写的 HR 工具(查年假、查考勤)
- Python 团队写的数据分析工具(销售报表、用户画像)
- 第三方 HTTP API(物流查询、天气查询)
三种不同的调用方式,你的代码里要写三套集成逻辑:
if ("getUserAnnualLeave".equals(functionName)) {
// 直接调用本地 Java 方法
return hrService.getAnnualLeave(userId);
} else if ("analyzeSalesData".equals(functionName)) {
// 调用 Python 服务的 HTTP API
return httpClient.post("http://python-service:8000/analyze", params);
} else if ("getWeather".equals(functionName)) {
// 调用第三方 API
return httpClient.get("https://api.weather.com/v1/current?city=" + city);
}
每接入一 个新系统,就要写一套适配代码。而且每个系统的认证方式不同(有的用 Token,有的用 API Key,有的用 OAuth),错误处理方式也不同。
2.3 权限和安全的黑洞
Function Call 协议本身没有任何权限机制。所有的权限校验都要你自己写:
- 用户 A 能不能查用户 B 的年假?
- 实习生能不能调用删除订单这个工具?
- 某个工具只允许管理员使用,怎么控制?
工具越多,权限逻辑越复杂。而且权限代码和业务代码混在一起,容易出漏洞。
2.4 可观测性的缺失
20 个工具在线上跑,你需要知道:
- 每个工具被调用了多少次?
- 平均耗时多少?哪个工具最慢?
- 调用失败率是多少?失败的原因是什么?
- 某次调用的完整链路:用户问了什么 → 模型选了哪个工具 → 传了什么参数 → 返回了什么结果 → 最终答案是什么?
Function Call 协议不管这些,全靠你自己埋点、写日志、搭监控。工具调用链路一长(调用工具 A → 工具 A 内部调用工具 B → 工具 B 查数据库),排查问题就像大海捞针。
3. 我们需要的是一个标准化的工具管理协议
总结一下,Function Call 解决了让模型调工具的问题,但没有解决怎么管理工具的问题:
| 维度 | Function Call 解决了吗 |
|---|---|
| 模型判断是否调用工具 | 解决了 |
| 标准化的调用意图输出 | 解决了 |
| 工具定义的自动化管理 | 没有,手写 JSON Schema |
| 工具的动态发现和注册 | 没有,硬编码在代码里 |
| 跨语言跨系统的统一调用 | 没有,每个系统写一套适配 |
| 权限和安全控制 | 没有,自己实现 |
| 调用链路的可观测性 | 没有,自己埋点 |
我们需要的是在 Function Call 之上,加一层标准化的工具管理框架——统一工具的定义、注册、发现、调用、权限控制。
这就是 MCP 要做的事。
MCP 是什么
1. MCP 的核心思想:大模型世界的 USB 接口
回想一下 USB 出现之前的世界:打印机用并口,鼠标用 PS/2 接口,手机充电有 Micro USB、Lightning、Type-C 好几种。每换一个设备,就要换一根线。USB 出来之后,统一了接口标准——不管你是键盘、鼠标、U 盘还是手机,只要有 USB 接口,插上就 能用。
MCP 做的是同样的事,只不过它统一的不是硬件接口,而是大模型调用工具的接口。
MCP,全称 Model Context Protocol(模型上下文协议),由 Anthropic(Claude 的母公司)于 2024 年 11 月开源发布。它不是一个具体的产品或框架,而是一个开放的协议规范。核心思想用一句话概括:
任何工具只要实现了 MCP 协议,就能被任何支持 MCP 的客户端调用,不用关心对方是什么语言、什么平台。
打个比方:
- 没有 MCP 之前:你的 Java 工具只能被你的 Java 代码调用,Python 团队的工具只能被 Python 代码调用,每个系统都是一座孤岛
- 有了 MCP 之后:Java 工具、Python 工具、Node.js 工具都实现 MCP 协议,Claude Desktop、Cursor、你自己的企业助手都支持 MCP 协议,任意组合,即插即用
这就像 USB 接口一样——工具是“设备”,客户端是“电脑”,MCP 是“USB 标准”。
2. MCP 的三层架构:Host、Client、Server
MCP 的架构设计分三层:Host(宿主应用)、Client(MCP 客户端)、Server(MCP 服务端)。这三层的关系用一张图说明:

这张图最近在网上被广泛引用。不过坦白说,我刚开始学习时,看着它并没有完全理解,尤其是 MCP Server 和 Hosts 之间的关系,总觉得差点意思。
于是我重新画了一张图,结合我们自己的文章脉络重新拆解了一遍,希望能让结构更清晰,也方 便大家建立完整认知。

2.1 Host(宿主应用)
Host 是用户直接交互的应用,比如:
- Claude Desktop(Anthropic 的桌面客户端)
- Cursor(AI 编程 IDE)
- 你自己开发的企业知识库助手(比如咱们的 Ragent)
Host 的职责是:接收用户输入 → 调用大模型 → 根据模型的指令通过 MCP Client 调用工具 → 把结果返回给模型 → 展示最终答案给用户。
Host 内部包含一个或多个 MCP Client,每个 Client 负责和一个 MCP Server 通信。
2.2 Client(MCP 客户端)
Client 是 Host 内部的通信组件,负责和 MCP Server 建立连接、发送请求、接收响应。
关键点:一个 Client 只连接一个 Server,但一个 Host 可以有多个 Client,连接多个 Server。就像你的电脑有多个 USB 接口,每个接口插一个设备。
Client 的职责包括:
- 和 Server 建立连接(通过 Stdio 或 HTTP)
- 发现 Server 提供的工具列表
- 调用 Server 的工具并获取结果
- 管理连接的生命周期