🤖Spring AI MCP Server 开发指南
“如果说 MCP 是 AI 世界的 USB 接口,那 Spring AI 就是帮你焊好接口、包好外壳、还贴心地附赠了说明书的那个工具人。”
目录
写在前面:MCP 是个啥?
MCP (Model Context Protocol) 是 Anthropic 提出的一套开放协议,让 AI 模型能够像调用 API 一样调用外部工具。你可以把它理解为:
1 | 传统 Web 开发:浏览器 <--HTTP--> 服务器 |
以前你想让 AI 查个天气、算个数,得靠 prompt 里硬塞一堆上下文。现在有了 MCP,AI 可以主动说:”嘿,我需要查一下北京的天气”,然后你的 MCP Server 就屁颠屁颠地把结果递过去了。
一句话总结:MCP 让 AI 从”只会动嘴”进化成了”能动手”。
Spring AI 版本演进史
Spring AI 的发展速度堪比坐上了火箭——从最初的”实验品”到现在的”生产级框架”,每个版本都在疯狂堆料。
官网版本全景图 (截至 2026 年 4 月)
打开 Spring AI 官网,你会看到这样一幅”版本大家族合影”:
| 版本 | 标签 | 状态说明 |
|---|---|---|
| 1.1.4 | CURRENT |
1.1.x 系列最新稳定版,本项目正在使用 |
| 1.0.5 | CURRENT |
1.0.x 系列最新稳定版,长期维护分支 |
| 2.0.0-SNAPSHOT | SNAPSHOT |
2.0 开发快照,每日构建,喜欢刺激的可以试试 |
| 2.0.0-M4 | PRE |
2.0 第四个里程碑预览版,功能逐步冻结中 |
| 1.1.5-SNAPSHOT | SNAPSHOT |
1.1.x 的下一个补丁版快照 |
小科普:
CURRENT= 生产可用的稳定版;PRE= 预览版,功能基本成型但还在打磨;SNAPSHOT= 每日构建的开发版,随时可能翻天覆地,慎用于生产。
看到没?Spring AI 同时维护着 两条稳定线 (1.0.x 和 1.1.x),外加一条正在孵化的 2.0 线。这在 Spring 生态里很常见——老用户有安全网,新用户有最新功能,冒险家有尝鲜渠道。各取所需,和谐共处。
版本演进时间线
1 | 0.8.x 1.0.0-M1~M6 1.0.0 GA 1.0.5 未来 |
各版本核心特性详解
1.0.x 系列 —— “稳如老狗”
最新稳定版:1.0.5 CURRENT
Spring AI 1.0.x 是第一个正式发布的版本系列。如果你的项目已经在用 1.0.x 且跑得好好的,没必要急着升级——官方还在持续维护呢。
核心能力:
@Tool注解:终于不用手写 JSON Schema 了!一个注解搞定工具声明@ToolParam注解:给参数加上描述,让 AI 知道该传什么- SSE 传输协议:基于 Server-Sent Events 的实时通信,支持远程部署
- stdio 模式:通过标准输入输出通信,适合本地工具
MethodToolCallbackProvider:把普通 Java 方法变成 MCP 工具的魔法转换器
1 | // 1.0.x 时代的写法(至今仍然适用) |
1.0.x 的不足:SSE 协议在复杂网络环境下(代理、负载均衡)容易出问题,像是一个只能在实验室里跑的原型。也正因为这个痛点,才有了 1.1.x 的诞生。
1.1.x 系列 —— “好用且能打”
最新稳定版:1.1.4 CURRENT | 开发中:1.1.5-SNAPSHOT
1.1.0 是一次质的飞跃,也是目前新项目的最佳选择。三个重磅更新:
1. Streamable HTTP 协议
告别 SSE 的各种水土不服!Streamable HTTP 使用标准的 HTTP 请求/响应模型,天然兼容各种代理、网关和负载均衡器。
1 | # 一行配置,从此告别 SSE |
2. 注解扫描器 (Annotation Scanner)
1.1.0 引入了自动扫描机制——只要你的类是 Spring Bean 且方法标了 @Tool,框架就能自动发现并注册。不再需要手动配置 ToolCallbackProvider Bean(当然手动配置依然支持,看你心情)。
1 | # 默认就是 true,写不写都行,但写出来显得专业 |
3. MCP SDK 持续升级
从 0.8.x 一路升到 0.17.0,每个版本都在完善协议实现。Spring AI BOM 帮你管好了版本,不用操心依赖冲突。
2.0.0 系列 —— “未来已来”
里程碑版:2.0.0-M4 PRE | 开发快照:2.0.0-SNAPSHOT
2.0 是 Spring AI 的下一个大版本,目前已经推进到第四个里程碑 (M4)。虽然官方还没正式公布完整的 changelog,但从里程碑节奏来看,2.0 GA 正在加速靠近。
从 M4 的标签可以推测,2.0 大概率会带来:
- API 层面的重大重构和优化(大版本号升级的惯例)
- 更完善的 MCP 协议支持
- 可能的 breaking changes(所以才需要大版本号)
尝鲜提醒: 2.0.0-M4 (
PRE) 可以用于技术预研和 POC,但别拿来上生产——除非你喜欢半夜被 oncall 电话叫醒的感觉。2.0.0-SNAPSHOT 就更刺激了,每天一个惊喜(或惊吓),适合 Spring AI 贡献者和极限运动爱好者。
版本选择建议
1 | 你是什么类型的开发者? |
| 你的情况 | 推荐版本 | 理由 |
|---|---|---|
| 新项目,从零开始 | 1.1.4 | 最新稳定版,Streamable HTTP 是正确答案 |
| 老项目,已在用 1.0.x | 1.0.5 | 先升到 1.0.5 修补漏洞,再择机迁移到 1.1.x |
| 想提前适配 2.0 | 2.0.0-M4 | 开个分支试试水,但别合进 main |
| 想给 Spring AI 贡献代码 | 2.0.0-SNAPSHOT | 欢迎入坑,社区需要你 |
本项目的选择:1.1.4——稳定、功能全、Streamable HTTP 加持,没毛病。
从零搭建一个 MCP Server
第一步:创建项目
最低配 pom.xml,简洁得像一首俳句:
1 | <parent> |
你没看错,核心依赖只有一个。Spring AI BOM 会帮你搞定 MCP SDK、Jackson、Reactor 等一票传递依赖。这就是 Spring Boot “约定优于配置” 哲学的魅力——它替你做了 90% 的决定,而且大部分时候做得还不错。
Starter 选择指南
Spring AI 提供了多个 MCP Server Starter,别选错了:
| Starter | 传输方式 | 适用场景 |
|---|---|---|
spring-ai-starter-mcp-server |
stdio | 本地工具,客户端管理进程生命周期 |
spring-ai-starter-mcp-server-webmvc |
HTTP/SSE (同步) | Web 部署,本项目用的就是这个 |
spring-ai-starter-mcp-server-webflux |
HTTP/SSE (响应式) | 高并发场景,需要 WebFlux 技术栈 |
第二步:写启动类
1 |
|
对,就这么朴实无华。Spring Boot 的启动类从来不需要花里胡哨。
第三步:写工具类
这是整个项目最有趣的部分——定义 AI 可以调用的工具:
1 |
|
注意看,每个方法上面的 @Tool(description = "...") 就是告诉 AI:”嘿,我能干这个事,描述如下。” AI 在需要的时候就会来调用你。
第四步:注册工具(可选)
1 |
|
为什么说”可选”? 因为从 Spring AI 1.1.0 开始,注解扫描器默认开启,它会自动发现所有标注了
@Tool的 Spring Bean 方法。但手动注册的好处是:你可以精确控制哪些工具暴露出去。在生产环境中,”能干”和”该干”是两码事。
第五步:配置文件
1 | server: |
启动! ./mvnw spring-boot:run,然后你就拥有了一个运行在 http://localhost:8080/mcp 的 MCP 服务器。
就这?就这。五步搞定,比泡一碗方便面还简单。
核心注解详解
@Tool —— 工具的身份证
@Tool 注解标记一个方法为 MCP 工具,AI 可以通过 MCP 协议调用它。
1 |
|
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
description |
String | 是 | 工具功能描述,越清晰越好。这是 AI 理解你工具用途的唯一依据 |
name |
String | 否 | 工具名称,默认使用方法名。建议用 camelCase |
returnDirect |
boolean | 否 | 是否将结果直接返回给用户而非 AI。默认 false |
description 写法的艺术:
1 | // 反面教材 —— AI:这啥?能干啥?我要不要用?算了不用了 |
description 是你和 AI 之间的”契约”。写得越清楚,AI 越知道什么时候该用、什么时候不该用。把它当成 Javadoc 来写就对了——只不过你的读者从人类变成了 AI。
@ToolParam —— 参数的说明书
@ToolParam 为方法参数添加描述,帮助 AI 理解该传什么值。
1 | public String calculate( |
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
description |
String | 推荐 | 参数含义描述 |
required |
boolean | 否 | 是否必填,默认 true |
小贴士:
@ToolParam不是必须的——不加的话,AI 只能看到参数名和类型,靠猜。但你想让 AI 猜你的意思吗?
支持的参数类型
Spring AI 会自动将参数转换为 JSON Schema,以下类型开箱即用:
| Java 类型 | JSON Schema 类型 | 示例 |
|---|---|---|
String |
string | "hello" |
int / Integer / long / Long |
integer | 42 |
double / Double / float / Float |
number | 3.14 |
boolean / Boolean |
boolean | true |
List<T> |
array | [1, 2, 3] |
Map<String, T> |
object | {"key": "value"} |
| POJO | object (展开所有字段) | {"name": "test", "age": 18} |
enum |
string (带 enum 约束) | "MONDAY" |
返回值约定
方法可以返回任意类型,Spring AI 会自动序列化为 JSON 字符串发给 AI。但有几个最佳实践:
1 | // 推荐:返回 String,格式可控 |
配置参数大全
这是本文最”枯燥”但最有用的部分。泡杯茶,我们一个一个说。
最小化配置 —— 三行就能跑
先上结论,一个 MCP Server 真正需要配的只有三行:
1 | spring: |
没了?没了。其余全有默认值。
- 端口?默认
8080- 端点路径?默认
/mcp- Tool 能力?默认开启
- 注解扫描?默认开启
- 服务器类型?默认同步
Spring Boot 的哲学:能不让你写的,绝不让你多写一个字。
对应的最小 pom.xml 依赖也只有一个:
1 | <dependency> |
就这个组合,mvn spring-boot:run 一敲,http://localhost:8080/mcp 就活了。
如果你想更完整地了解”还能配什么”,请继续往下看。
完整配置树
1 | spring: |
逐项解析
name & version
1 | name: mcp-web-demo |
这两个是你 MCP 服务器的”身份证”。客户端连接时能看到这些信息,方便调试和管理。
建议 version 跟着你项目的实际版本走,别一直写 1.0.0 然后功能已经迭代到第 37 版了。(别问我怎么知道的。)
type —— 同步 vs 异步
1 | type: SYNC # 或 ASYNC |
| 类型 | 底层实现 | 适用场景 |
|---|---|---|
| SYNC | McpSyncServer |
大多数场景。工具执行快、逻辑简单 |
| ASYNC | McpAsyncServer |
工具执行耗时长(如调用外部 API、数据库大查询) |
选择困难症患者请看这里: 用
SYNC。如果你不确定要不要用ASYNC,那你就不需要用ASYNC。当你的工具开始因为超时被 AI 抱怨的时候,再切过去也来得及。
protocol —— 传输协议
1 | protocol: STREAMABLE # 或 SSE |
这是 1.1.0 最重要的新配置项。详见下一章 传输协议的前世今生。
capabilities —— 能力声明
1 | capabilities: |
MCP 协议定义了四种能力。对于大多数项目,你只用到 tool。但框架默认全开,因为开着又不收费。
如果你确定不用某个能力,关掉它可以减少握手时的信息交换量(虽然这点优化约等于没有):
1 | capabilities: |
变更通知
1 | tool-change-notification: true |
当服务端的 Tool/Resource/Prompt 发生变化时(比如热更新了一个新工具),主动通知客户端刷新。在开发阶段很有用,生产环境中如果你的工具列表不会动态变化,可以关掉。
streamable-http 配置块
1 | streamable-http: |
mcp-endpoint:MCP 服务的 URL 路径。改成 /api/mcp 也行,客户端连接时对应改就好。
disallow-delete:MCP 协议中,客户端可以发送 DELETE 请求来关闭会话。如果你出于安全考虑不想让客户端主动断开,设为 true。
keep-alive-interval:对于长时间运行的 SSE 连接,定期发送心跳包防止连接被代理或防火墙切断。支持 Duration 格式:30s、1m、PT30S 都行。
annotation-scanner
1 | annotation-scanner: |
这个配置决定了 Spring AI 是否自动扫描所有 Spring Bean 中的 @Tool 方法并注册为 MCP 工具。
设为 false 的话,你必须手动通过 ToolCallbackProvider Bean 注册工具。适合那些对安全性要求极高、想要精确控制暴露面的场景。
传输协议的前世今生
MCP 支持三种传输方式,每种都有自己的”人设”:
stdio —— 本地青梅竹马
1 | [AI 客户端] --stdin/stdout--> [你的 Java 进程] |
- 通过标准输入/输出通信
- 客户端负责启动和管理你的进程
- 优点:简单、零网络配置
- 缺点:只能本地用,无法远程共享
- 人话:AI 直接把你的程序跑起来,然后俩人通过管道悄悄话
1 | # Claude Code 配置 stdio 模式 |
SSE (Server-Sent Events) —— 曾经的浪漫
1 | [AI 客户端] --HTTP POST--> [服务器] --SSE stream--> [AI 客户端] |
- 客户端用 POST 发请求,服务器用 SSE 流式返回
- 优点:支持远程部署、支持流式响应
- 缺点:单向流、代理兼容性差、连接容易断
SSE 就像短信时代的异地恋——能通信但很不稳定,动不动就断线。
Spring AI 1.1.0 起,SSE 已被标记为 deprecated。 它还能用,但官方不推荐了。
Streamable HTTP —— 当代最优解
1 | [AI 客户端] <--HTTP--> [服务器] |
- 使用标准 HTTP 协议,支持请求/响应和流式两种模式
- 优点:兼容所有 HTTP 基础设施(代理、CDN、负载均衡、API 网关)
- 缺点:想了半天,没有
Streamable HTTP 就像 4G 网络时代的视频通话——稳定、双向、随时在线。
这就是为什么本项目选择了 protocol: STREAMABLE。
三种协议对比总结
| 特性 | stdio | SSE | Streamable HTTP |
|---|---|---|---|
| 部署方式 | 本地进程 | 远程服务 | 远程服务 |
| 网络要求 | 无 | 需要 HTTP | 需要 HTTP |
| 代理/CDN 兼容 | N/A | 差 | 优 |
| 双向通信 | 是 | 半双工 | 是 |
| 流式响应 | 是 | 是 | 是 |
| Spring AI 支持版本 | 1.0.0+ | 1.0.0+ (deprecated) | 1.1.0+ |
| 推荐度 | 本地开发用 | 请迁移 | 生产推荐 |
进阶玩法
1. 注入 Spring 生态的一切
@Tool 方法所在的类就是普通的 Spring Bean,这意味着你可以注入任何 Spring 管理的组件:
1 |
|
Spring 全家桶(JPA、MyBatis、Redis、Kafka、Elasticsearch……)都是你的弹药库。MCP 工具只是个入口,背后可以连接整个企业级后端。
2. 多工具类组织
当工具多了,别都塞在一个类里:
1 | // 按领域拆分 |
手动注册时在 Config 类中列举:
1 |
|
或者开启注解扫描器(默认开启),什么都不用配。框架自动帮你收集所有 @Tool 方法。
3. 异步工具处理
对于耗时操作,使用 ASYNC 模式:
1 | spring.ai.mcp.server.type: ASYNC |
1 |
|
4. 安全防护
MCP 服务器暴露在网络上时,安全性至关重要:
1 | // 通过 Spring Security 或自定义 Filter 实现认证 |
客户端配置(.mcp.json):
1 | { |
常见踩坑实录
坑 1:Java 版本不对
1 | error: switch expressions are not supported in -source 17 |
Spring AI 1.1.x 推荐 Java 21。如果你用了 switch 表达式等新语法,确保 pom.xml 里:
1 | <java.version>21</java.version> |
坑 2:用了 SSE Starter 却配了 STREAMABLE
1 | # pom.xml 里引的是 spring-ai-starter-mcp-server-webmvc |
规则: WebMvc/WebFlux Starter 支持 SSE 和 STREAMABLE。stdio Starter 只支持 stdio。别混搭。
坑 3:@Tool 方法没被发现
检查清单:
- 方法所在类是否是 Spring Bean(有
@Service、@Component等注解)? annotation-scanner.enabled是否被意外设为false?- 如果用手动注册,
MethodToolCallbackProvider.builder().toolObjects(...)里是否遗漏了你的 Bean?
坑 4:description 写得太模糊
1 | // AI 完全不知道什么时候该用这个工具 |
description 是 AI 识别和调用你工具的唯一线索。 想象你在写一份招聘启事——不写清楚岗位职责,谁知道要不要来应聘?
坑 5:返回值太大
如果你的工具返回了一个巨大的 JSON(比如几万条数据库记录),AI 的上下文窗口会被撑爆。
1 | // 反面教材 |
写在最后
Spring AI 让 MCP Server 的开发变得前所未有的简单:
- 一个 Starter 搞定所有依赖
- 两个注解(
@Tool+@ToolParam)定义工具 - 三行配置 启动服务
从某种意义上说,写一个 MCP Server 比写一个 REST Controller 还简单——你甚至不需要操心 URL 映射、请求序列化、响应格式这些破事。
AI 不再只是聊天的搭子,而是你应用的调用者。 而 Spring AI 让这个过程变得像写普通业务代码一样自然。
去吧,写你的第一个 MCP 工具,让 AI 替你打工。
本文档基于 Spring AI 1.1.4 + Spring Boot 3.4.4 编写,配套项目源码就在本仓库中。
如果这篇文档帮到了你,给个 Star,不帮到你……那就提个 Issue 来骂我吧。




