这三个概念不在同一个层级,是配合起来用的: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 的订单详情如下: