MCP 协议:把工具标准化,让生态流动起来
系列:ToyAgent 智能体知识库 09/26
一句话核心:工具不再写死在智能体里——把工具拆成独立 Server,智能体作为 Client 通过标准协议动态发现与调用。这是智能体世界的 USB-C 接口。
为什么需要
在基础的 Function Calling 实现里,工具是"编译期写死"的:
// 工具硬编码在智能体内部——想换天气供应商?改代码重新部署。
if ("get_weather".equals(toolName)) { return mockWeather.query(args); }
这种写法有几个天花板:
- 换供应商要改代码:天气 API 从 A 换到 B,智能体代码跟着动,重新构建部署。
- 上帝类:接 100 个工具,智能体代码里就有 100 个分支、100 份 API 凭据、100 种错误处理。
- 无法复用:你为自家智能体写的天气工具,别的智能体团队要用,只能复制粘贴。
MCP(Model Context Protocol)的解法:把工具拆成独立服务(Server),智能体作为 Client 通过标准协议使用它们。工具的开发、部署、升级与智能体彻底解耦。
核心机制
协议核心只需要两个方法——tools/list 发现 + tools/call 调用:
// MCP Server:工具的标准化宿主
public interface McpServer {
List<ToolDescriptor> tools(); // tools/list —— 工具发现
String call(String tool, String args); // tools/call —— 工具调用
}
// 工具描述符:标准"名片",模型靠 description 选工具
record ToolDescriptor(String name,
String description,
String parameters) {} // 生产中为 JSON Schema
// 智能体侧:动态发现,而不是硬编码
List<ToolDescriptor> tools = client.listTools(); // 启动时/按需拉取
String result = client.callTool("get_weather", "{\"city\": \"杭州\"}");
架构上是三方结构:
智能体 (Client) ⇄ 标准协议 ⇄ 工具服务 (Server)
解耦带来三件事:
- 热插拔:Server 换实现(Mock 换真实天气 API),Client 零改动——名片不变,名片背后的实现随便换。
- 生态复用:一个合规的 MCP Server 可以被所有智能体接入,写一次到处用。
- 权限收敛:执行与凭据都在 Server 侧,Client 只能拿到协议允许暴露的能力。
Client 与多个 MCP Server 之间的发现与调用时序:
sequenceDiagram
participant U as 用户
participant C as Agent Client
participant E as Exa Server
participant J as Jina Server
U->>C: 调研 MCP 协议最新规范
C->>E: tools/list
E-->>C: web_search_exa 工具名片
C->>J: tools/list
J-->>C: read_url 工具名片
C->>E: tools/call web_search_exa
E-->>C: 5 条搜索结果
C->>J: tools/call read_url
J-->>C: 网页正文
C-->>U: 汇总后的调研报告
图示:Client 先对各 Server 做 tools/list 拉取名片,对话中按需 tools/call 调用——Client 代码不含任何具体 Server 的实现。
举例
结合生产场景:假设你的学习平台智能体要接三个 MCP 预置服务——Exa(联网搜索)、Jina(网页阅读)、Firecrawl(网页抓取)。
[启动阶段]
Agent Client ── tools/list ──► Exa Server 返回 [{name:"web_search_exa", ...}]
Agent Client ── tools/list ──► Jina Server 返回 [{name:"read_url", ...}]
Agent Client ── tools/list ──► Firecrawl Server 返回 [{name:"scrape", ...}]
(注册表把三个服务的工具合并渲染进系统提示词清单)
[对话阶段]
用户:帮我调研一下 MCP 协议的最新规范
模型思考:需要联网搜索 → 选 web_search_exa
Agent Client ── tools/call("web_search_exa", {"query":"MCP spec 2026"}) ──► Exa Server
模型思考:搜到 5 条链接,逐条读正文 → 选 read_url
Agent Client ── tools/call("read_url", {"url":"https://modelcontextprotocol.io/..."}) ──► Jina Server
模型输出:汇总后的调研报告
关键观察:智能体代码里没有一行 Exa 或 Jina 的代码。它只知道"我有一批工具,名片如下"。哪天把 Exa 换成 Tavily,只要新 Server 提供同名/同语义的 tools/list + tools/call,智能体零改动。反过来,一个自研的"知识库检索 MCP Server"写好之后,公司里任何智能体都能挂载,无需各自实现一遍。
LangChain4j 视角:langchain4j-mcp 模块提供 McpToolProvider,把远程 MCP Server 的工具自动桥接成框架的 ToolProvider,与内置工具同场注册——MCP 工具和本地工具对模型来说没有区别。
教学版 vs 真实 MCP
| 维度 | 教学版 | 真实 MCP |
|---|---|---|
| 通信 | 进程内 Java 接口 | JSON-RPC 2.0(stdio / Streamable HTTP) |
| Server 形态 | 嵌套接口模拟 | 独立进程 / 远程服务 |
| 核心方法 | 完全一致:tools/list + tools/call | 同左,另有资源、提示词等扩展能力 |
教学版把传输层抽掉,只保留协议语义——因为 MCP 的价值不在传输,而在这两个方法构成的契约。
面试考点
- MCP 解决了什么问题? M×N 集成问题。没有 MCP 时 M 个智能体对接 N 个工具要写 M×N 份胶水代码;有了 MCP 各自实现协议一侧,只需 M+N 份。
- MCP 的两个核心方法是什么?
tools/list(能力发现)与tools/call(调用执行)。发现与调用分离,是"动态"的关键——Client 启动时不需要知道有哪些工具。 - MCP 和 Function Calling 的关系? 不竞争而是分层:Function Calling 是模型与宿主之间的调用格式;MCP 是宿主与工具服务之间的连接协议。MCP 工具最终仍以 Function Calling 形式暴露给模型。
生产化路径
- 鉴权双链路:MCP 工具经 Adapter 桥接进框架时,注意 Adapter ≠ Registry——注册层与适配层的鉴权、禁用名单要分别配置,否则出现"配置了禁用但工具仍然可用"的缝隙。
- 超时预算:远程 MCP Server 的超时要按其真实响应特征设(搜索类几秒,抓取类可能几十秒),一刀切短超时会误杀慢工具,长超时会拖垮对话。
- 错误体透传:Server 返回的非 2xx 错误体不要吞掉,里面有模型重试所需的语义(限流、参数错、无结果各不相同)。
- 预置 Server 种子化:团队常用 Server(搜索/抓取/内部 KB)做成幂等 seed,随部署自动挂载,新 Agent 开箱即用。
- 降级策略:MCP 全连失败时智能体应能降级到内置工具或如实告知,而不是整轮对话断流。