Spring AI 学习指南(九)MCP模型上下文协议 & 工具调用实战
一、前言:为什么需要 MCP 工具调用?
普通的对话问答模式下,大模型存在几个天生短板:
- 知识存在时间断层,无法获取最新实时数据;
- 无法访问业务数据库、业务接口,不能查询订单、用户、业务数据;
- 不会执行精确计算、代码运算,容易出现算术错误;
- 不能调用外部服务,无法完成联网查询、文件读取、天气查询等任务。
想要让 AI 真正落地业务,就必须赋予它调用外部工具的能力,而支撑这套能力的行业标准就是 MCP 模型上下文协议(Model Context Protocol)。
简单理解:
MCP 就是一套标准化的规则,让大模型知道自己 “有哪些工具可用、工具入参是什么、执行完如何带回结果”,并自动完成「思考→选工具→填参数→调用→整合结果回答用户」全流程。
Spring AI 基于 MCP 协议做了完整封装,我们不需要手动拼接工具描述、不需要解析工具调用格式,通过简单注解即可完成工具注册,让大模型自动调用。
本篇从概念到实战,完整拆解 MCP 协议、本地工具调用、第三方接口调用、多工具混合编排,带你打通 AI 智能体的核心能力。
二、MCP 模型上下文协议核心概念
2.1 什么是 MCP?
MCP(Model Context Protocol,模型上下文协议),是用于大模型与外部工具之间通信的通用标准化协议。 它定义了一套通用 JSON 格式,用来描述:
- 工具名称、功能说明
- 工具入参类型、是否必填、参数说明
- 工具调用格式规范
- 工具返回结果格式规范
主流大模型(智谱、通义、文心、OpenAI)全部兼容 MCP 协议,是当前工具调用的行业通用标准。
2.2 工具调用完整执行流程(MCP 标准流程)
整个流程分为 4 步,也是所有智能体的通用逻辑:
- 思考阶段(Reason):大模型分析用户问题,判断是否需要调用工具、需要调用哪个工具;
- 工具调用(Act):大模型按照 MCP 协议,生成标准化工具调用参数;
- 本地执行:Spring AI 框架解析 MCP 格式,调用我们注册的业务工具;
- 结果回填:将工具执行结果重新塞回对话上下文,大模型整合信息后给出最终回答。
整个过程全自动完成,用户无感知,这就是经典的 ReAct 智能体模式。
2.3 Spring AI 中工具相关核心注解
Spring AI 对 MCP 做了高度封装,核心依靠两个注解完成工具定义:
@Tool:标记一个方法为 AI 可调用工具,配置工具名称、功能描述;@ToolParam:对方法入参进行说明,告诉大模型参数含义、是否必填。
框架会自动根据注解,生成符合 MCP 规范的工具描述,交给大模型进行决策调用。
三、实战一:基础本地工具调用(入门必学)
先通过最简单的天气查询工具,演示完整的 MCP 工具调用流程,理解整体运行逻辑。
3.1 编写工具类,注册 AI 可调用方法
创建 WeatherTool 工具类,通过注解定义工具功能和参数:
package demo.ai.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
/**
* 天气查询工具
* 遵循 MCP 协议,可供大模型自动调用
*/
@Component
public class WeatherTool {
/**
* 根据城市名称查询天气
* @param city 城市名称
* @return 天气信息
*/
@Tool(description = "根据城市名称,查询该城市当前实时天气状况")
public String getWeather(@ToolParam(description = "用户需要查询的城市名称,例如:北京、上海、广州") String city) {
// 模拟第三方天气接口调用
return city + " 今日天气:多云,气温 18~26℃,微风,空气质量良好";
}
}
3.2 ChatClient 注册工具,开启 MCP 调用能力
在全局配置类ChatClientConfiguration中,将工具注入 ChatClient,框架会自动生成 MCP 工具描述:
package demo.ai.conf;
import demo.ai.tool.WeatherTool;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import jakarta.annotation.Resource;
@Configuration
public class ChatClientConfiguration {
@Resource
private WeatherTool weatherTool;
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
// 注册工具,开启 MCP 工具调用能力
.defaultTools(weatherTool)
.build();
}
}
3.3 编写对话测试接口
package demo.ai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
@RestController
@RequestMapping("/mcp")
public class McpController {
@Resource
private ChatClient chatClient;
@GetMapping("/chat")
public String chat(@RequestParam String msg) {
return chatClient.prompt()
.system("你是智能助手,遇到需要查询天气的问题,自动调用天气工具获取数据,再整理回答用户")
.user(msg)
.call()
.content();
}
}
3.4 测试验证
访问接口:
http://localhost:8080/mcp/chat?msg=帮我查一下成都的天气
执行流程:
- 大模型识别用户需要查天气,触发 MCP 工具调用;
- 按照 MCP 协议生成调用参数:
city=成都; - Spring AI 解析 MCP 数据,执行
getWeather("成都"); - 将结果回填上下文,大模型整合后给出最终回答。
四、实战二:多工具混合编排(业务常用)
真实业务场景中,通常存在多个工具,大模型会根据用户问题,自动选择对应工具,甚至连续调用多个工具。
我们新增两个工具:日期计算工具、汇率换算工具,实现多工具智能调度。
4.1 新增两个业务工具
package demo.ai.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
@Component
public class BusinessTool {
/**
* 计算两个日期之间相差的天数
*/
@Tool(description = "计算两个日期之间相差的天数,日期格式为 yyyy-MM-dd")
public long calcDay(
@ToolParam(description = "开始日期,格式yyyy-MM-dd") String startDate,
@ToolParam(description = "结束日期,格式yyyy-MM-dd") String endDate
) {
LocalDate start = LocalDate.parse(startDate);
LocalDate end = LocalDate.parse(endDate);
return ChronoUnit.DAYS.between(start, end);
}
/**
* 人民币兑美元汇率换算
*/
@Tool(description = "人民币兑换美元,返回换算后的金额")
public String rmbToUsd(@ToolParam(description = "人民币金额") Double money) {
double rate = 0.138;
return "换算后美元金额:" + String.format("%.2f", money * rate);
}
}
4.2 配置类注册多个工具
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultTools(weatherTool, businessTool)
.build();
}
4.3 多工具场景测试
测试问题:
http://localhost:8080/mcp/chat?msg=现在是2025-01-01,距离国庆节还有多少天?另外帮我把5000元人民币换成美元
大模型会自动识别,连续调用日期计算工具、汇率工具,再整合结果统一回答,无需人工干预。
五、实战三:结合记忆上下文,实现多轮工具调用
MCP 工具调用可以和前面学习的对话记忆无缝结合,实现多轮对话连续调用工具,这是智能客服的核心模式。
5.1 配置记忆 + 工具
新增ScoreTool工具类
package demo.ai.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
public class ScoreTool {
/**
* 返回学生的成绩列表
*/
@Tool(description = "返回学生的成绩列表")
public String getScoreList() {
return "张三:90分,李四:85分,王五:95分,赵六:88分,王二:92分";
}
@Tool(description = "生成学生成绩报告")
public String report(@ToolParam(description = "最高分") Integer max,
@ToolParam(description = "最低分你") Integer min,
@ToolParam(description = "平均分") Integer avg) {
String grade = "";
if (avg > 90) grade = "优";
else if (avg > 80) grade = "良";
else if (avg > 70) grade = "中";
else if (avg > 60) grade = "及格";
else grade = "不及格";
return "本次考试,最高分:" + max + ",最低分:" + min + ",平均分:" + avg + ",等级:" + grade;
}
}
沿用第 之前的 内存记忆配置,在 ChatClient 中同时注入记忆顾问、工具列
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder, MessageWindowChatMemory chatMemory) {
return chatClientBuilder
.defaultTools(weatherTool, businessTool, scoreTool)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
5.2 多轮对话测试
第一轮提问:
http://localhost:8080/mcp/chat?msg=我要查询学生的成绩列表
第二轮接着提问:
http://localhost:8080/mcp/chat?msg=统计最高分、最低分、平均分
第二轮接着提问:
http://localhost:8080/mcp/chat?msg=统计学生成绩报告
模型会保留上一轮对话上下文,自动连续调用不同工具,完成连贯问答。
六、MCP 底层原理:Spring AI 如何生成工具描述?
很多同学好奇:为什么只加注解,大模型就能知道工具信息?
核心流程:
- 启动项目时,Spring AI 扫描所有带
@Tool的 Bean; - 读取方法注解、参数注解,自动生成符合 MCP 协议的 JSON 工具描述;
- 每次对话请求时,框架自动把工具描述拼接到 Prompt 头部,告诉大模型可用工具;
- 大模型思考后,返回 MCP 格式的工具调用指令;
- Spring AI 解析 MCP JSON,反射执行对应工具方法,回填结果。
简化后的 MCP 工具描述结构:
json
{
"type": "function",
"function": {
"name": "getWeather",
"description": "根据城市名称查询实时天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
}
},
"required": ["city"]
}
}
}
所有兼容 MCP 的大模型,都能识别这套格式,这也是 Spring AI 跨模型通用的底层基础。
七、生产级最佳实践 & 避坑指南
✅ 推荐规范
- 工具职责单一:一个工具只做一件事,不要在一个方法里混合多个业务逻辑;
- 描述尽量详细:
@Tool和@ToolParam的描述越清晰,大模型调用准确率越高; - 参数尽量使用简单类型:优先使用 String、Integer、Double,复杂对象容易解析失败;
- 工具统一归类:按业务模块划分工具类,例如订单工具、用户工具、支付工具;
- 结合会话记忆使用:智能客服场景必须开启记忆,保证对话连贯性;
- 工具执行加日志:结合前面的日志 Advisor,记录工具调用入参、出参,方便问题排查。
❌ 高频踩坑点
- 描述过于简略:大模型无法理解工具用途,导致不调用或错误调用;
- 参数命名不规范:参数名和业务含义不匹配,MCP 解析后传参错误;
- 工具抛出异常未处理:工具内部异常会导致整个对话中断,建议内部 try‑catch 并返回错误文本;
- 大量工具一次性注册:工具过多会造成 Prompt 过长,Token 消耗暴增,建议按场景动态注册;
- 混淆工具调用和普通问答:必须在 system 提示词中引导大模型 “需要时自动调用工具”。
八、完整可运行控制器代码
package demo.ai.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
/**
* 天气查询工具
* 遵循 MCP 协议,可供大模型自动调用
*/
@Component
public class WeatherTool {
/**
* 根据城市名称查询天气
* @param city 城市名称
* @return 天气信息
*/
@Tool(description = "根据城市名称,查询该城市当前实时天气状况")
public String getWeather(@ToolParam(description = "用户需要查询的城市名称,例如:北京、上海、广州") String city) {
// 模拟第三方天气接口调用
return city + " 今日天气:多云,气温 18~26℃,微风,空气质量良好";
}
}
package demo.ai.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
@Component
public class BusinessTool {
/**
* 计算两个日期之间相差的天数
*/
@Tool(description = "计算两个日期之间相差的天数,日期格式为 yyyy-MM-dd")
public long calcDay(
@ToolParam(description = "开始日期,格式yyyy-MM-dd") String startDate,
@ToolParam(description = "结束日期,格式yyyy-MM-dd") String endDate
) {
LocalDate start = LocalDate.parse(startDate);
LocalDate end = LocalDate.parse(endDate);
return ChronoUnit.DAYS.between(start, end);
}
/**
* 人民币兑美元汇率换算
*/
@Tool(description = "人民币兑换美元,返回换算后的金额")
public String rmbToUsd(@ToolParam(description = "人民币金额") Double money) {
double rate = 0.138;
return "换算后美元金额:" + String.format("%.2f", money * rate);
}
}
package demo.ai.tool;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
public class ScoreTool {
/**
* 返回学生的成绩列表
*/
@Tool(description = "返回学生的成绩列表")
public String getScoreList() {
return "张三:90分,李四:85分,王五:95分,赵六:88分,王二:92分";
}
@Tool(description = "生成学生成绩报告")
public String report(@ToolParam(description = "最高分") Integer max,
@ToolParam(description = "最低分你") Integer min,
@ToolParam(description = "平均分") Integer avg) {
String grade = "";
if (avg > 90) grade = "优";
else if (avg > 80) grade = "良";
else if (avg > 70) grade = "中";
else if (avg > 60) grade = "及格";
else grade = "不及格";
return "本次考试,最高分:" + max + ",最低分:" + min + ",平均分:" + avg + ",等级:" + grade;
}
}
package demo.ai.conf;
import demo.ai.tool.BusinessTool;
import demo.ai.tool.ScoreTool;
import demo.ai.tool.WeatherTool;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ChatClientConfiguration {
@Resource
private WeatherTool weatherTool;
@Resource
private BusinessTool businessTool;
@Resource
private ScoreTool scoreTool;
/**
* 注册内存存储仓库(默认存储方案)
*/
@Bean
public InMemoryChatMemoryRepository chatMemoryRepository() {
return new InMemoryChatMemoryRepository();
}
/**
* 注册滑动窗口记忆策略
* maxMessages:最大保留消息条数,超出自动裁剪旧消息,保留系统消息
*/
@Bean
public MessageWindowChatMemory chatMemory(InMemoryChatMemoryRepository repository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(repository)
.maxMessages(10) //最多保留10条对话消息
.build();
}
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder, MessageWindowChatMemory chatMemory) {
return chatClientBuilder
.defaultTools(weatherTool, businessTool, scoreTool)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
}
package demo.ai.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
@RestController
@RequestMapping("/mcp")
public class McpController {
@Resource
private ChatClient chatClient;
/**
* http://localhost:8080/mcp/chat?msg=帮我查询重庆的天气
* http://localhost:8080/mcp/chat?msg=现在是2026-05-01,距离国庆节还有多少天?另外帮我把5000元人民币换成美元
* http://localhost:8080/mcp/chat?msg=我要查询学生的成绩列表
* http://localhost:8080/mcp/chat?msg=统计最高分、最低分、平均分
* http://localhost:8080/mcp/chat?msg=统计学生成绩报告
*/
@GetMapping("/chat")
public String chat(@RequestParam String msg, HttpServletRequest request) {
return chatClient.prompt()
.system("你是智能助手,遇到需要查询天气的问题,自动调用天气工具获取数据,再整理回答用户")
.user(msg)
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, request.getSession().getId()))
.call()
.content();
}
}
九、本篇学习总结
本篇完整讲解了 MCP 模型上下文协议 + Spring AI 工具调用 核心能力,核心收获:
- 理解 MCP 是大模型调用外部工具的通用行业协议,是智能体的底层基础;
- 掌握
@Tool、@ToolParam注解的使用方式,快速定义 AI 工具; - 理解 ReAct 思考 + 执行流程,大模型全自动完成工具调用;
- 掌握单工具、多工具、结合记忆的实战用法;
- 看懂 MCP 底层 JSON 结构,理解 Spring AI 自动生成工具描述的原理;
- 掌握生产环境下工具调用的最佳实践与常见坑点。
掌握 MCP 工具调用,意味着你已经从普通问答开发,正式进入 AI Agent 智能体开发 阶段,是 Spring AI 进阶的核心分水岭。
十、结语
普通大模型只能回答知识类问题,而具备 MCP 工具调用能力的 AI,才能真正落地业务场景。
无论是智能客服、数据分析、自动化办公、企业知识库,底层全部依靠 MCP 工具调用实现。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)