这三个概念不在同一个层级,是配合起来用的:Function Calling 是模型"决定调用哪个工具"的能力,Tool 是具体可执行的功能,MCP 是统一连接工具和模型的标准协议。
可以把模型想象成一个聪明的助理:
- Function Calling :助理的"决策和表达",听懂需求后,判断该用哪个工具、需要什么参数,并生成结构化指令(比如
{"name": "getWeather", "arguments": {"city": "成都"}})。它本身不执行,只负责"说清楚要干什么"。 - **Tool(工具)**:助理实际能用的"家伙什",比如查天气的接口、发邮件的功能、计算器。开发者把工具定义好,告诉模型"你有这些可以用"。
- MCP:连接助理和工具的"标准插座"(像 USB-C)。以前每家模型对接工具的方式都不一样,MCP 统一了接口标准,让同一个工具能被不同模型直接使用,不用反复适配。
它们怎么配合工作
- 注册工具 :开发者定义好工具(Tool),比如
getWeather,用 JSON 描述它的功能和参数。 - 模型决策 :你问"成都明天天气",模型通过 Function Calling 判断需要调用天气工具,并生成指令:
{"name": "getWeather", "arguments": {"city": "成都", "date": "明天"}}。 - 执行与返回:你的程序(或 MCP Client)收到指令,真正去执行工具,拿到天气数据后再交给模型,由模型整理成自然语言回复你。
关键点:模型不直接执行代码,它只负责"发号施令",真正的执行由后端完成。
Function Calling 和 MCP 的核心区别
| 对比维度 | Function Calling | MCP |
|---|---|---|
| 本质 | 模型的输出能力 | 连接工具与应用的协议 |
| 作用层 | 模型层 | 传输/协议层 |
| 解决的问题 | 模型怎么表达调用意图 | 应用怎么发现和连接工具 |
| 工具发现 | 开发者写死工具列表 | 运行时动态发现 |
| 跨平台 | 各家格式不兼容 | 统一标准,一次接入到处可用 |
简单说:**Function Calling 是"大脑"的决策,MCP 是让决策能落地的"高速公路"**,两者互补,不是替代关系。
什么时候用哪个
- 只服务单一模型:直接用 Function Calling 就够了,更简单直接。
- 小项目、仅需3-5个固定工具:直接用Function Calling对接Tool即可,代码量小、链路简单,完全不需要引入MCP
- 要支持多个模型、工具会动态增减,或涉及数据库、支付等敏感操作:建议上 MCP,协议层有更好的标准化和安全机制。
- 实际项目里,两者通常结合使用:模型用 Function Calling 表达意图,MCP 负责把意图路由到正确的工具并执行。
- 企业级多Agent系统、需要对接十几个内部业务系统:用MCP统一封装所有Tool,再通过Function Calling触发调用,能减少60%以上的重复开发代码,工具支持热插拔,迭代效率大幅提升。
各自的核心定位
- **Tool(工具)**:是最底层的"执行单元",就是一个个能完成具体任务的功能,比如查天气的接口、发邮件的函数、查询数据库的能力,本身是一段可执行的代码或API,不直接和模型交互。
- Function Calling :是大模型的原生输出能力,它的核心作用是让模型在生成文本时,能输出结构化的JSON调用指令,明确告诉应用"我要调用哪个工具、传什么参数",解决的是"单次调用怎么准确表达意图"的问题。
- MCP(模型上下文协议) :是Anthropic推出的标准化中间层协议,相当于工具调用领域的"USB-C通用接口",它不替代任何一方,而是把所有外部工具统一封装成可被自动发现、连接的标准服务,解决的是"工具怎么跨应用复用、统一治理"的问题。
三者的完整协作链路
在实际生产系统中,三者会按固定流程配合工作,完全不存在谁替代谁的情况:
- 工具标准化封装:开发者把所有Tool(天气查询、数据库操作、文件读取等)封装成独立的MCP Server,通过统一的JSON-RPC协议暴露工具清单、参数规则。
- 工具自动发现:AI应用作为MCP Client,连接各个MCP Server,自动拉取所有可用工具的完整描述,不需要开发者手动为每个工具写重复的Schema定义。
- 模型生成调用指令:用户发起请求后,大模型通过Function Calling能力,判断需要调用的工具,输出标准的结构化调用JSON,明确工具名和入参。
- 协议层路由执行:应用侧把模型输出的Function Calling指令,转发给对应的MCP Server,由MCP Server完成参数校验、实际Tool执行,再把结果格式化返回给模型。
- 结果整合输出:大模型拿到工具返回的结果,整理成自然语言回复给用户,MCP还会自动维护多轮调用的上下文状态,不需要开发者手动传递参数。
核心差异对比
| 维度 | Tool | Function Calling | MCP |
|---|---|---|---|
| 所处层级 | 最底层执行层 | 模型侧交互层 | 中间协议层 |
| 核心目标 | 完成具体业务动作 | 让模型准确表达调用意图 | 统一工具接入标准,实现跨应用复用 |
| 核心能力 | 执行具体逻辑 | 输出结构化调用JSON | 工具自动发现、状态自动维护、跨模型兼容 |
| 复用范围 | 仅在当前代码库可用 | 绑定特定大模型的输出格式 | 支持所有兼容MCP的AI应用/模型直接复用 |
| 适用场景 | 所有需要外部操作的场景 | 工具少、单应用的轻量场景 | 多Agent协作、大量工具需要统一治理的复杂场景 |
简单总结:**Tool是"干活的手",Function Calling是"大脑发出的动作指令",MCP是连接大脑和手的标准化"神经通路"**,三者配合才能让大模型真正安全、高效地和真实业务系统交互。
生活场景:智能家居管家
- Tool:家里的空调、灯、扫地机器人------具体能执行动作的设备。
- Function Calling:管家(模型)的能力,你说"有点热",他能判断出该开除湿还是制冷、定几度,输出明确指令。
- MCP :家里的智能中控网关。以前小米、海尔、格力的设备协议各不相同,管家得分别学着控制;有了统一网关,管家只要对着网关说"把客厅降温",网关自动翻译成各品牌设备听得懂的信号。
于是流程变成:你说"到家了,有点热" → 管家(模型)判断要开空调+开灯 → 通过网关(MCP)分别下发指令 → 空调、灯(Tool)各自执行 → 管家告诉你"已开空调 26 度和客厅灯 (模型整合结果,回复你)"
如果哪天你新买了一台新品牌的风扇,只要它接入了这个网关,管家自动就能控制它,你什么都不用改------这就是 MCP 带来的"热插拔"
代码示例
一、假设有一个订单服务正常运行
二、搭建一个MCP Server 去调用订单服务查询订单详情
1、引入依赖
<!--引入依赖 spring-ai-mcp-server-webflux-spring-boot-starter-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、配置文件
#配置服务端口
server:
port: 8084
spring:
application:
name: order-mcp-server
#配置mcp的服务
ai:
mcp:
server:
name: my-order-server
version: 1.0.0
3、实现:往Spring 注册Bean(ToolCallbackProvider)
@Data
public class Order implements Serializable {
private String orderId;
private String userId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date orderTime;
private BigDecimal totalAmount;
/**
* 订单状态
* 0:待付款
* 1:已付款
* 2:已发货
* 3:已完成
* 4:已取消
*/
private Integer orderStatus;
private String paymentMethod;
private String shippingAddress;
private String contactPhone;
private List<OrderDetail> orderDetails;
}
@Data
public class OrderDetail {
private Integer detailId;
private String orderId;
private String productId;
private String productName;
private Integer quantity;
private BigDecimal unitPrice;
private BigDecimal totalPrice;
private String specification;
}
public enum OrderStatus {
/**
* 待付款 (0)
*/
PENDING(0, "待付款"),
/**
* 已付款 (1)
*/
PAID(1, "已付款"),
/**
* 已发货 (2)
*/
SHIPPED(2, "已发货"),
/**
* 已完成 (3)
*/
COMPLETED(3, "已完成"),
/**
* 已取消 (4)
*/
CANCELLED(4, "已取消");
private final int code;
private final String description;
/**
* 构造函数
* @param code 状态码
* @param description 状态描述
*/
OrderStatus(int code, String description) {
this.code = code;
this.description = description;
}
/**
* 获取状态码
* @return 状态码
*/
public int getCode() {
return code;
}
/**
* 获取状态描述
* @return 状态描述
*/
public String getDescription() {
return description;
}
/**
* 根据状态码获取枚举实例
* @param code 状态码
* @return 对应的枚举实例
* @throws IllegalArgumentException 如果找不到对应的枚举值
*/
public static OrderStatus fromCode(int code) {
for (OrderStatus status : values()) {
if (status.code == code) {
return status;
}
}
throw new IllegalArgumentException("无效的订单状态码: " + code);
}
/**
* 检查当前状态是否允许取消订单
* @return 是否允许取消
*/
public boolean canBeCancelled() {
return this == PENDING || this == PAID;
}
/**
* 检查当前状态是否已完成(包括已完成和已取消)
* @return 是否已完成
*/
public boolean isFinalStatus() {
return this == COMPLETED || this == CANCELLED;
}
}
@Service
public class OpenOrderService {
//订单服务访问地址
private static final String BASE_URL = "http://localhost:8083/orders";
private final RestTemplate restTemplate;
public OpenOrderService() {
this.restTemplate = new RestTemplate();
}
@Tool(description = "获取所有订单信息" )
public List<Order> getOrders() {
// 尝试远程调用
String url = BASE_URL+"/list";
return restTemplate.getForObject(url, List.class);
}
@Tool(description = "根据用户ID获取用户订单列表信息" )
public List<Order> getOrdersByUserId(String userId) {
// 尝试远程调用
String url = BASE_URL+"/user/" + userId;
return restTemplate.getForObject(url, List.class);
}
@Tool(description = "根据订单ID获取订单详情" )
public Order getOrderById(String orderId) {
String url = BASE_URL+"/{orderId}";
return restTemplate.getForObject(url, Order.class, orderId);
}
@Tool(description = "根据订单ID取消订单" )
public boolean cancelOrder(String orderId) {
String url = BASE_URL+"/cancel/{orderId}";
return restTemplate.postForObject(url, null, Boolean.class, orderId);
}
}
@SpringBootApplication
public class OMSApplication {
public static void main(String[] args) {
SpringApplication.run(OMSApplication.class,args);
}
//通过ToolCallbackProvider 提供远程服务调用(mcp client)
@Bean
public ToolCallbackProvider orderTools(OpenOrderService openOrderService) {
return MethodToolCallbackProvider.builder().toolObjects(openOrderService).build();
}
}
三、搭建一个MCP Client 去获取订单服务详情数据
1、引入依赖
<dependencies>
<!--引入依赖 spring-ai-mcp-client-webflux-spring-boot-starter-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId>
</dependency>
<!--引入依赖 sspring-ai-alibaba-starter-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
</dependency>
</dependencies>
2、配置mcp的服务
spring:
application:
name: order-mcp-client
main:
web-application-type: none
ai:
ollama:
base-url: http://localhost:11434 #本地ollama服务地址
chat:
model: deepseek-r1:1.5b #本地大模型
#ai:
#dashscope:
#api-key: ${AI_DASHSCOPE_API_KEY}
mcp:
client:
sse:
connections:
server1:
url: http://localhost:8084 #连接服务
mandatory-file-encoding: UTF-8
# 调试日志
logging:
level:
io:
modelcontextprotocol:
client: DEBUG
spec: DEBUG
ai:
user:
input: 查询订单ID为ORD20250414001的订单详情
3、实现:通过ToolCallbackProvider 调用远程的服务
@SpringBootApplication(exclude = {
org.springframework.ai.autoconfigure.mcp.client.SseHttpClientTransportAutoConfiguration.class
})
public class OMCApplication {
public static void main(String[] args) {
SpringApplication.run(OMCApplication.class,args);
}
private String userInput = "查询订单ID为ORD20250414001的订单详情";
/**
* @param chatClientBuilder
* @param tools 通过ToolCallbackProvider 调用远程的服务
* @param context
* @return
*/
@Bean
public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools,
ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
//通过tool方式接入
.defaultTools(tools)
.build();
System.out.println("\n>>> QUESTION: " + userInput);
System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput).call().content());
context.close();
};
}
}
启动服务(数据库服务,本地大模型服务,应用服务)
启动应用服务顺序:1、订单服务 2、MCP Server 3、 MCP Client
MCP Client 端执行结果
执行查询命令
>>> QUESTION: 查询订单ID为ORD20250414001的订单详情
返回查询结果(大模型整合返回结果)
>>> ASSISTANT: 订单ID为 ORD20250414001 的订单详情如下:
