开篇:一个常见的混淆

做 AI 应用开发时,”MCP” 和 “Function Calling” 这两个词经常被混用。有人觉得它们是同一个东西,有人说 MCP 就是 Function Calling 的升级版。

都不对。它们不是替代关系,而是分工关系。

先给一个一句话结论,然后我们再拆开细讲:

Function Calling 是 LLM 的”决策能力”(它决定要调哪个工具),MCP 是”通信协议”(定义了工具如何被发现和调用)。前者是脑力,后者是神经传导系统。


一、Function Calling:LLM 的”脑力”

1.1 它到底是什么?

Function Calling 是 LLM 的一项内置能力——模型在推理时,除了生成文本,还可以输出一个结构化的 “我想调这个函数” 的意图:

1
2
3
4
5
6
7
8
{
"function": {
"name": "searchKnowledge",
"arguments": {
"query": "三角函数公式"
}
}
}

注意:LLM 只是说出了这个意图,它自己没有执行这个函数。 真正执行是调用方(你的代码)的事。

1.2 工作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
用户:"帮我查一下三角函数的资料"


┌─────────────────────────────────┐
│ LLM 推理 │
│ → 这不是闲聊,需要查知识库 │
│ → 输出 function_call: │
│ name: "searchKnowledge" │
│ args: { query: "三角函数" } │
└─────────────────────────────────┘


你的代码拿到 function_call

├─→ 方案 A:本地直接调 searchKnowledge(query) 方法
├─→ 方案 B:通过 MCP 协议远程调工具
└─→ 方案 C:通过 HTTP REST API 调

关键认知:Function Calling 只是 LLM 给你了一个”指令”,至于你怎么执行这个指令,和 LLM 无关。

1.3 没有 MCP 也能用 Function Calling

以 LangChain4j 为例:

1
2
3
4
5
6
7
8
// 纯 Function Calling,不经过 MCP
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.tools(new KnowledgeSearchTool(), // ← 直接把 Java 对象传进去
new StudentStatsTool())
.build();

String answer = assistant.chat("张三最近成绩怎么样?");

这里的一切都在 同一个 JVM 进程 里——LLM 客户端持有工具对象的引用,Function Calling 的结果直接映射到 Java 方法调用。没有网络通信,没有 JSON-RPC,没有 /mcp 端点。

这在单体应用里完全够用。那为什么还需要 MCP?


二、MCP:标准化通信协议

2.1 MCP 的全称和定位

MCP = Model Context Protocol,由 Anthropic 于 2024 年底提出。

它解决的不是 “LLM 如何决定调工具”(那是 Function Calling 的事),而是 “工具提供方和工具调用方之间如何以标准方式通信”

2.2 MCP 定义了三个角色

1
2
3
4
5
6
7
┌──────────────┐      JSON-RPC 2.0       ┌──────────────┐
│ MCP Client │ ◄──────────────────────► │ MCP Server │
│ (Host 端) │ HTTP / SSE │ (工具提供方) │
└──────────────┘ └──────────────┘
↑ ↑
你的 LLM Gateway 你的 Spring Boot
(如 LLM 网关) (如 Spring Boot 服务)
  • MCP Host:运行 LLM 的应用,发起工具调用请求
  • MCP Client:Host 内部的协议客户端,负责与 Server 通信
  • MCP Server:暴露工具的轻量服务,处理 tools/listtools/call

2.3 MCP 协议规定的方法

方法 方向 作用
initialize Client → Server 握手,交换能力和协议版本
notifications/initialized Client → Server 通知握手完成
tools/list Client → Server 自动发现所有可用工具
tools/call Client → Server 调用指定工具,传入参数

每一步都是标准的 JSON-RPC 2.0 消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// → 请求 tools/list
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}

// ← 响应
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "searchKnowledge",
"description": "从教学知识库中搜索相关内容",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "搜索关键词" }
},
"required": ["query"]
}
}
]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// → 请求 tools/call
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "searchKnowledge",
"arguments": { "query": "三角函数" }
}
}

// ← 响应
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{ "type": "text", "text": "【数学第三章】\n三角函数包括正弦、余弦..." }
]
}
}

三、核心区别:十个维度的对照

维度 Function Calling MCP
本质 LLM 的能力(输出结构化调用意图) 通信协议(定义 Client-Server 交互格式)
谁做的 LLM 内部推理 应用层协议实现
解决什么问题 “LLM 如何表达它想调工具” “工具提供方和调用方如何标准化对接”
通信方式 无——只是 LLM 的输出格式 JSON-RPC 2.0 over HTTP/SSE
工具发现 取决于框架(手动注册或注解) tools/list 标准化自动发现
跨进程 不涉及 核心价值:跨进程、跨语言、跨网络
标准化程度 各 LLM 提供商格式略有差异 统一规范 2024-11-05
安全边界 无独立边界 /mcp 端点可统一鉴权、限流、日志
能否独立部署 不能,绑定在 LLM 调用链路里 能,Server 独立部署和扩缩
与 LLM 的关系 依赖 LLM 无关——理论上可以不走 LLM,直接用 REST 调工具

四、实际协作流程:它们怎么配合

来看一个完整的调用链路——用户在 AI 对话框里问 “最近张三成绩怎么样?”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
用户输入


┌──────────────────────────────────────────────────────────────┐
│ Step 1: LLM 推理(Function Calling 发挥作用) │
│ │
│ LLM 评估用户意图: │
│ → 这不是闲聊,也不是知识库问题 │
│ → 这是数据查询,需要调用 queryStudentStats 工具 │
│ → 输出 function_call: { name: "queryStudentStats", │
│ args: { studentName: "张三" } } │
└──────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────┐
│ Step 2: LLM 网关收到 function_call(MCP Client 发挥作用) │
│ │
│ LLM 网关已注册了你的 MCP Server (Spring Boot /mcp) │
│ → 构造 JSON-RPC 请求: │
│ { "method": "tools/call", │
│ "params": { "name": "queryStudentStats", ... } } │
│ → HTTP POST → http://your-app:8080/mcp │
└──────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────┐
│ Step 3: MCP Server 处理请求(工具执行) │
│ │
│ → handleToolsCall() 解析 method: "tools/call" │
│ → 从 Map<String, ToolDefinition> 中找到 queryStudentStats │
│ → 执行 StudentStatsTool.execute(arguments) │
│ → 查数据库:SELECT * FROM submission WHERE student_name=张三 │
│ → 聚合统计:平均分、提交次数、最近分数... │
│ → 返回 MCP 标准格式:{ content: [{ type: "text", text: ... }] }│
└──────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────┐
│ Step 4: 结果整合(Function Calling 再次发挥作用) │
│ │
│ LLM 拿到工具返回的文本结果 + 原来的用户问题 │
│ → 组织自然语言回复: │
│ "张三同学本学期共提交 8 次作业,平均分 82.5 分, │
│ 最近一次《三角函数》得分 78 分,趋势略有下降..." │
│ │
│ 最终返回给用户 │
└──────────────────────────────────────────────────────────────┘

分工总结

  • Function Calling 做了 Step 1(决策)和 Step 4(整合)
  • MCP 做了 Step 2(传输)和 Step 3(执行)
  • 少了任何一个,流程都跑不通

五、为什么需要 MCP?三个不可替代的理由

5.1 跨进程解耦

这是 MCP 最核心的价值。如果你的 LLM 网关和业务服务是两个独立的进程(甚至是两台机器),Function Calling 的 “本地方法调用” 模式就失效了。

1
2
3
4
5
6
7
8
9
10
11
12
13
❌ 单体模式(LLM 和工具在同一进程):

Spring Boot
├── LLM Client (LangChain4j)
├── Tool: KnowledgeSearchTool ← 直接 Java 对象调用
└── Tool: StudentStatsTool

✅ MCP 模式(LLM 和工具在不同进程):

LLM 网关 (独立进程) Spring Boot (独立进程)
├── LLM 推理 ├── MCP Server (/mcp)
├── Function Calling 决策 ├── KnowledgeSearchTool
└── MCP Client ── JSON-RPC ──→ └── StudentStatsTool

5.2 自动发现

你新增一个工具:

1
2
3
4
5
6
@Component
public class WeatherTool implements ToolDefinition {
public String name() { return "getWeather"; }
public String description() { return "查询天气"; }
// ...
}

不需要改任何其他代码。Spring 自动注入所有 ToolDefinition 实现类,tools/list 自动发现。LLM 不需要重新配置 Prompt,自动感知新工具的存在。

而在硬编码模式中,每加一个工具,你都要去 Controller 加 if-else 判断逻辑。

5.3 统一安全边界

所有工具调用都经过 /mcp 这一个入口:

1
2
3
4
5
6
7
8
9
@PostMapping("/mcp")
public Map<String, Object> handle(@RequestBody Map<String, Object> request) {
String method = (String) request.get("method");
// 这里可以统一做:
// → 认证鉴权
// → 限流(你在 SecurityConfig 里已经 permitAll,生产应加权限)
// → 日志审计(记录每次 tools/call)
// → 监控指标
}

所有工具调用可审计、可限流、可监控。硬编码的函数调用散落在各个 Controller 里,很难统一管理。


六、什么时候不该用 MCP

如果一个技术方案你只能说它好,不能说它不适合什么场景,那这篇文章就不够诚实。

6.1 单体应用不需要 MCP

你的 Spring Boot 项目是单体部署,LLM 调用和工具执行在同一个 JVM 进程里,LangChain4j 可以直接持有 Tool 对象的引用:

1
2
3
4
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.tools(new KnowledgeSearchTool(), new StudentStatsTool())
.build();

这种情况下引入 MCP 是过度设计

  • 本来是一次方法调用,变成一次 HTTP 请求;本来是一个断点能调试的,变成跨网络追日志
  • 多了一个需要监控、运维、保活的独立服务

判断标准:如果你的工具和 LLM Gateway 打包在同一个 jar 里部署,用 Function Calling 就够了。

6.2 工具数很少且不常变化

只有 1~3 个工具,而且半年不变一次。你用 if-else 或者简单 Map 分发的维护成本远低于引入一整套 MCP 协议栈。MCP 的自动发现优势在工具数量大、频繁增减时才会体现。

6.3 团队规模小、没有跨团队协作

MCP 解决的一个重要问题是:工具提供方和 LLM 应用方由不同团队维护。如果你的项目是个人或 2~3 人团队全栈负责,引入 MCP 带来的边界清晰化收益接近于零,反而增加了沟通成本——只不过这次沟通双方变成了你自己写的两个服务。

6.4 小结

1
2
3
4
5
6
7
8
9
10
需要 MCP:
✅ LLM 网关和业务工具在不同进程/不同机器
✅ 工具由不同团队独立开发维护
✅ 工具数量大且频繁增减(10+ 个)
✅ 需要统一的安全审计、限流、监控入口

不需要 MCP:
❌ 单 jar 部署,LLM 和工具在同一进程
❌ 只有 2~3 个工具且长期稳定
❌ 个人项目或小团队,没有跨服务通信需求

七、MCP 的代价——没有免费的午餐

每一层抽象都有它的账单。引入 MCP 不是零成本的:

7.1 网络延迟

纯 Function Calling 模式下,一次工具调用就是一次 Java 方法调用,耗时可以忽略。换成 MCP 之后:

1
2
本地方法调用:         ~0.01ms
MCP over HTTP: ~2-50ms(取决于网络和序列化)

你每次 tools/list 要发请求,每次 tools/call 也要发请求。在你需要连续调 3 个工具(比如先查知识库、再查学生成绩、再查提交记录),这个延迟会叠加。对于需要实时响应的对话场景,每一次额外的网络往返都是用户体验的损耗。

7.2 序列化开销

所有参数和返回值都要走 JSON-RPC 序列化/反序列化。对于简单的 String 参数还好,但如果你需要传复杂对象(比如一个多层嵌套的配置 Bean),你需要在 JSON 和 Java 对象之间来回转换,处理类型丢失、循环引用、特殊字符转义等一系列问题。

7.3 错误处理复杂度爆炸

本地函数调用失败,你 catch 一个异常就完事了。MCP 模式下可能的失败点:

1
2
3
4
5
6
7
8
调用方                        MCP Server                    工具本身
│ │ │
├── DNS 解析失败 ├── Server 进程挂了 ├── 工具抛异常
├── 连接超时(3s) ├── 数据库连不上 ├── 返回格式不符合 schema
├── 读超时(30s) ├── OOM 了 ├── 返回了不该返回的数据
├── HTTP 非 200 ├── 内存泄漏导致越来越慢 └── ...
├── JSON 解析失败 └── ...
└── response.content 为空

你需要为每一个失败点写降级逻辑——是重试?是返回默认值?是告诉用户”系统繁忙请稍后再试”?这些在本地调用里根本不用考虑的问题,在 MCP 下都需要单独处理。

7.4 排错成本

1
2
3
4
5
6
7
❌ 本地模式:
断点打在 Tool 实现类 → Debug → 1 分钟定位

✅ MCP 模式:
看 LLM 日志 → 看 MCP Client 日志 → 看 MCP Server 日志 →
对时间线 → 检查请求体 → 重放请求 → 怀疑网络 → tcpdump →
半个小时后发现是 Server 那边改了 inputSchema 格式

跨进程意味着你需要分布式链路追踪才能高效定位问题。没有 traceId 串起来的 MCP 调用链,排错就是灾难。

7.5 运维负担

多了一个需要部署、监控、扩容、保活的服务。MCP Server 挂了,你的 AI 功能就会降级——LLM 拿到 Function Calling 的指令后,发现工具调不通,只能回复”抱歉我无法获取这个信息”。你需要为这个服务配置健康检查、告警、自动重启,跟你的核心业务服务一样对待。

7.6 代价总结

1
2
3
4
5
引入 MCP 的隐性成本:
1. 每次工具调用 +2~50ms 网络延迟
2. 错误处理逻辑量增加 3~5 倍
3. 排错从打断点变成查分布式日志
4. 需要额外运维一个独立服务

这些代价不是说不该付,而是你决定引入 MCP 之前,心里要有这笔账。 如果你的场景确实需要跨进程解耦、工具自动发现、统一安全边界(如 Section 5 所述),那这些代价是值得的。如果只是想做本地函数调用,那是拿大炮打蚊子。


八、MCP 的现状与格局(2026 年中)

写这篇文章时是 2026 年 6 月——MCP 已经从一个实验性协议变成了 AI 基础设施层的关键拼图。保持客观:以下是目前的事实和正在进行的博弈。

8.1 治理:不再是 Anthropic 一家的东西

2025 年 12 月,Anthropic 将 MCP 捐赠给了 Linux 基金会旗下的 Agentic AI Foundation(AAIF)。联合创始方包括 OpenAI 和 Block,白金成员有 AWS、Google、Microsoft、Cloudflare、GitHub、Bloomberg。

这意味着 MCP 的治理模型对标的是 Kubernetes 和 PyTorch——不再由任何一家公司单独控制。对开发者来说,你不用担心”万一 Anthropic 改方向了怎么办”,协议的演进由社区共识驱动。

8.2 生态规模:千倍增长

指标 2024 年底 2026 年中 增长
月均 SDK 下载 ~10 万 ~9,700 万 ~970×
公开 MCP Server ~1,200 10,000~17,000+ ~10×+
原生支持 MCP 的客户端 Claude Claude、ChatGPT、Gemini、Cursor、Windsurf、VS Code + Copilot
企业生产部署 0 公开案例 Uber、亚马逊、Nordstrom、Bloomberg、Duolingo、PwC、Block

Block(原 Square)旗下的 Goose——基于 MCP 的企业级 AI Agent——已经有数千名员工日常使用,连接 Snowflake、GitHub、Jira、Slack、Google Drive,报告称节省 50%-75% 的时间。Gartner 预测到 2026 年底,40% 的企业应用将包含任务专属的 AI Agent。

8.3 协议架构:向无状态演进

MCP 正在经历自发布以来最大的架构改动——从有状态会话转向无状态请求(2026-07-28 候选版本已锁定):

  • 弃用 initialize 握手和 Mcp-Session-Id
  • 协议元数据通过 _meta 字段随每次请求传递
  • 支持标准 HTTP 负载均衡,不再需要 sticky routing

对你手写 MCP Server 的影响:目前基于 2024-11-05 的实现可以继续用——协议委员会承诺 12 个月的平滑过渡期。但长期看,如果你还没实现过 MCP Server,现在可以直接按无状态模式设计,跳过有状态的历史包袱。

8.4 MCP Apps:工具可以返回界面

2026 年 1 月,MCP 发布了第一个官方扩展 MCP Apps——工具不仅能返回文本,还能返回交互式的 HTML 界面,在对话中以沙箱 iframe 渲染。Figma 用它做内联组件编辑,Hex 用它做可交互的数据看板。Claude、ChatGPT、VS Code 首日即支持。

这是一个信号:MCP 的野心不只是”调工具”,而是成为 Agent 与用户交互界面的标准传输层

8.5 MCP 和 A2A:分工已基本明确

Google 主导的 **A2A(Agent-to-Agent)**协议在 2026 年发布了 v1.0,IBM 的 ACP 也并入了 A2A。两者都在 AAIF 下治理,分工现在很清晰:

1
2
MCP  = Agent ←→ 工具/数据/服务   (纵向连接)
A2A = Agent ←→ Agent (横向协作)

互不替代,各管一层。你的 Spring Boot 服务暴露的是工具接口,属于 MCP 的范畴;如果你的服务需要和另一个 AI Agent 进行任务协商和结果传递,那才是 A2A 的问题。

8.6 安全:最大的现实挑战

2026 年初,社区披露了 30+ 个 MCP 相关 CVE,包括一个 CVSS 9.6 的远程代码执行漏洞。核心威胁模型不是传统网络攻击,而是 工具投毒(Tool Poisoning)——恶意 MCP Server 在工具描述中嵌入隐藏指令,通过 LLM 的 Function Calling 机制间接操控 Agent 行为。

这在企业落地中催生了新的安全实践:网关层策略执行、工具元数据清理、高权限工具隔离、禁用”始终允许”按钮。MCP 2026 年路线图已将企业安全列为首位优先级。

8.7 一句话:MCP 现在的阶段

MCP 不再让人兴奋——它变得重要了。17 个月前,部署 MCP 意味着你是一个 early adopter;现在,部署 MCP 意味着你在用一套已被广泛验证的基础设施。 但它同时面临着无状态改造、安全治理、企业就绪等规模化问题——这些问题,恰恰证明它已经被用在了真正重要的地方。


九、一个常见误解:MCP Tool 不用写描述?

不是的。描述照样要手写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public String description() {
return "从教学知识库中搜索相关内容,用于回答学生问题或获取参考资料";
}

@Override
public Map<String, Object> inputSchema() {
return Map.of(
"type", "object",
"properties", Map.of(
"query", Map.of("type", "string", "description", "搜索关键词,越具体越好"),
"topK", Map.of("type", "integer", "description", "返回结果数量,默认3")
),
"required", List.of("query")
);
}

这段代码你用 Function Calling 本地调用也得写,用 MCP 也得写。MCP 不省描述,省的是通信标准化和工具发现自动化。


十、总结:一句话记住两个概念

Function Calling MCP
一句话 LLM 决定了”要调什么” MCP 规定了”怎么调”
类比 你的大脑决定要拿起杯子 你的神经系统传递信号给肌肉
如果没有它 LLM 无法表达工具调用意图 LLM 和服务之间通信靠 ad-hoc 编码,不可扩展

最后的最后:它们不是竞争对手,是搭档。Function Calling 让你有东西可调,MCP 让你能调得到——尤其是当 LLM 和工具不在同一个进程里的时候。


参考