一、MCP 协议是什么
MCP(Model Context Protocol)是 Spring AI 提供的模型上下文协议,提供了一种标准化的方式来连接 AI 模型和外部工具。
简单理解:
- 之前我们直接在应用中使用
@Tool 定义工具
- MCP 协议可以把工具服务独立出来,作为一个独立的进程或服务
- AI 应用通过 MCP 协议调用这些独立的工具服务
MCP 的优势:
- 解耦:工具服务和 AI 应用解耦,可以独立开发和部署
- 标准化:提供了标准化的协议,便于集成
- 灵活:支持多种传输方式,适应不同场景
- 可扩展:可以轻松添加新的工具服务
使用场景:
- 工具服务需要独立部署
- 多个 AI 应用需要共享同一套工具
- 工具服务需要跨语言实现
- 需要进程隔离的场景
二、MCP 的三种传输方式
Spring AI 支持三种 MCP 传输方式:
1. Client + 公共 MCP-Server
- 连接到公共的 MCP 服务器
- 适合使用第三方提供的工具服务
2. STDIO(标准输入输出)
- 通过标准输入输出流进行通信
- 适合本地工具服务
- 进程间通信
3. SSE(Server-Sent Events)
- 通过 HTTP + SSE 进行通信
- 适合远程工具服务
- 网络通信
本文主要介绍 STDIO 和 SSE 两种方式。
三、MCP Client 配置
3.1 依赖配置
在 AI 应用(调用方)中添加 MCP Client 依赖:
1 2 3 4
| <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client-webflux</artifactId> </dependency>
|
注意:
- 这个依赖包含了 STDIO 和 SSE 两种客户端
- 需要 Spring WebFlux 支持(因为使用了响应式编程)
3.2 STDIO 配置
application.properties:
1 2 3 4 5 6
| spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp/mcp-servers.json spring.ai.mcp.client.timeout=60000
logging.level.io.modelcontextprotocol=DEBUG
|
mcp-servers.json:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "mcpServers": { "getweather": { "command": "java", "args": [ "-Dspring.ai.mcp.server.stdio=true", "-Dlogging.pattern.console=", "-jar", "D:\\git\\code\\study\\spring-ai-study\\05mcp-server-stdio\\target\\05mcp-server-stdio-1.0-SNAPSHOT.jar" ] } } }
|
配置说明:
servers-configuration:MCP Server 配置文件路径
timeout:超时时间(毫秒)
command:启动 MCP Server 的命令(这里是 java)
args:启动参数
-Dspring.ai.mcp.server.stdio=true:启用 STDIO 模式
-Dlogging.pattern.console=:禁用控制台日志(避免干扰通信)
-jar:指定 jar 包路径
注意事项:
- jar 包路径建议使用绝对路径
- Windows 路径使用
\\ 或 / 都可以
- 确保 jar 包已经打包好
3.3 SSE 配置
application.properties:
1 2 3 4 5 6 7
| spring.ai.mcp.client.sse.connections.userScores.url=http://localhost:8088 spring.ai.mcp.client.sse.connections.userScores.sse-endpoint=/sse spring.ai.mcp.client.timeout=60000
logging.level.io.modelcontextprotocol=DEBUG
|
配置说明:
connections.userScores:连接名称,可以自定义
url:MCP Server 的地址
sse-endpoint:SSE 端点路径(通常是 /sse)
多服务器配置:
1 2 3 4 5 6 7
| spring.ai.mcp.client.sse.connections.weather.url=http://localhost:8088 spring.ai.mcp.client.sse.connections.weather.sse-endpoint=/sse
spring.ai.mcp.client.sse.connections.userScores.url=http://localhost:8089 spring.ai.mcp.client.sse.connections.userScores.sse-endpoint=/sse
|
四、MCP Server - STDIO 方式
4.1 项目搭建
依赖配置:
1 2 3 4 5 6 7 8 9
| <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
|
注意:
spring-ai-starter-mcp-server:MCP Server 的核心依赖
spring-boot-starter-web:虽然 STDIO 不需要 Web,但某些场景可能需要
4.2 工具服务实现
WeatherService:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package org.study.ai;
import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Service; import org.springframework.web.client.RestClient;
@Service public class WeatherService {
private final RestClient restClient;
public WeatherService() { restClient = RestClient.builder().build(); }
@Tool(description = "获取指定经纬度的天气预报") public String getWeather(double latitude, double longitude) { return restClient.get() .uri("https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}¤t_weather=true", latitude, longitude) .retrieve() .body(String.class); } }
|
关键点:
- 使用
@Tool 注解定义工具方法
- 工具方法的实现和普通 Spring Bean 一样
- 可以调用外部 API、数据库等
4.3 启动配置
启动类:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @SpringBootApplication public class McpServerApplication { public static void main(String[] args) { SpringApplication.run(McpServerApplication.class, args); }
@Bean public ToolCallbackProvider toolCallbackProvider(WeatherService weatherService) { return MethodToolCallbackProvider.builder() .toolObjects(weatherService) .build(); } }
|
打包:
启动参数:
1 2 3
| java -Dspring.ai.mcp.server.stdio=true \ -Dlogging.pattern.console= \ -jar mcp-server-stdio.jar
|
关键参数:
-Dspring.ai.mcp.server.stdio=true:启用 STDIO 模式
-Dlogging.pattern.console=:禁用控制台日志(重要!否则会干扰通信)
4.4 工作原理
STDIO 工作流程:
- 启动进程:MCP Client 根据配置启动 MCP Server 进程
- 标准输入:MCP Client 通过标准输入(stdin)发送工具调用请求
- 标准输出:MCP Server 通过标准输出(stdout)返回结果
- 进程通信:通过进程间通信实现数据传输
通信格式:
- 使用 JSON-RPC 2.0 协议
- 请求和响应都是 JSON 格式
- 通过标准输入输出流传输
优点:
- 进程隔离,工具服务崩溃不影响主应用
- 部署简单,只需要一个 jar 包
- 适合本地工具服务
缺点:
五、MCP Server - SSE 方式
5.1 项目搭建
依赖配置:
1 2 3 4 5 6 7 8 9
| <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server-webflux</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
|
注意:
spring-ai-starter-mcp-server-webflux:SSE 方式的 MCP Server
- 需要 WebFlux 支持(响应式编程)
5.2 工具服务实现
UserToolService:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package org.study.ai;
import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Service;
import java.util.Map;
@Service public class UserToolService {
private Map<String, Double> userScore = Map.of( "xushu", 2.0, "xiaoming", 1.0 );
@Tool(description = "获取用户分数") public String getWeather(String userName) { if (userScore.containsKey(userName)) { return userScore.get(userName).toString(); } return "未找到用户分数"; } }
|
启动类:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @SpringBootApplication public class McpServerSSEApplication { public static void main(String[] args) { SpringApplication.run(McpServerSSEApplication.class, args); }
@Bean public ToolCallbackProvider toolCallbackProvider(UserToolService userToolService) { return MethodToolCallbackProvider.builder() .toolObjects(userToolService) .build(); } }
|
配置:
5.3 工作原理
SSE 工作流程:
- HTTP 连接:MCP Client 通过 HTTP 连接到 MCP Server
- 建立 SSE 流:建立 Server-Sent Events 流
- 双向通信:通过 SSE 流实现双向通信
- 实时传输:支持实时数据传输
通信格式:
- 使用 JSON-RPC 2.0 协议
- 通过 HTTP + SSE 传输
- 支持实时双向通信
SSE 端点:
- MCP Server 会自动暴露 SSE 端点
- 默认路径:
/sse
- 可以通过配置修改
优点:
- 可以远程部署
- 支持多个客户端连接
- 实时性好
- 调试方便(可以用浏览器测试)
缺点:
- 需要网络连接
- 需要 Web 服务器支持
- 配置相对复杂
六、STDIO vs SSE
| 特性 |
STDIO |
SSE |
| 部署方式 |
本地进程 |
远程服务 |
| 通信方式 |
标准输入输出 |
HTTP + SSE |
| 进程隔离 |
✅ 是 |
❌ 否 |
| 远程访问 |
❌ 否 |
✅ 是 |
| 实时性 |
高 |
高 |
| 调试难度 |
较难 |
较易 |
| 适用场景 |
本地工具服务 |
远程工具服务 |
选择建议:
- 本地工具服务:使用 STDIO
- 远程工具服务:使用 SSE
- 需要进程隔离:使用 STDIO
- 需要多个客户端共享:使用 SSE
七、实际案例
7.1 天气服务(STDIO)
场景: 提供一个天气查询工具,通过 STDIO 方式提供服务。
MCP Server 项目结构:
1 2 3 4 5 6
| 05mcp-server-stdio/ ├── src/main/java/ │ └── org/study/ai/ │ ├── McpServerApplication.java │ └── WeatherService.java └── pom.xml
|
WeatherService:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Service public class WeatherService { private final RestClient restClient;
public WeatherService() { restClient = RestClient.builder().build(); }
@Tool(description = "获取指定经纬度的天气预报") public String getWeather(double latitude, double longitude) { return restClient.get() .uri("https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}¤t_weather=true", latitude, longitude) .retrieve() .body(String.class); } }
|
MCP Client 配置(调用方):
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "mcpServers": { "getweather": { "command": "java", "args": [ "-Dspring.ai.mcp.server.stdio=true", "-Dlogging.pattern.console=", "-jar", "path/to/mcp-server-stdio.jar" ] } } }
|
使用:
7.2 用户分数服务(SSE)
场景: 提供一个用户分数查询工具,通过 SSE 方式提供服务。
MCP Server 项目结构:
1 2 3 4 5 6
| 06mcp-server-sse/ ├── src/main/java/ │ └── org/study/ai/ │ ├── McpServerSSEApplication.java │ └── UserToolService.java └── pom.xml
|
UserToolService:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Service public class UserToolService { private Map<String, Double> userScore = Map.of( "xushu", 2.0, "xiaoming", 1.0 );
@Tool(description = "获取用户分数") public String getWeather(String userName) { if (userScore.containsKey(userName)) { return userScore.get(userName).toString(); } return "未找到用户分数"; } }
|
MCP Client 配置(调用方):
1 2
| spring.ai.mcp.client.sse.connections.userScores.url=http://localhost:8088 spring.ai.mcp.client.sse.connections.userScores.sse-endpoint=/sse
|
使用:
八、常见问题
8.1 STDIO 方式启动失败
问题: MCP Server 启动失败,提示找不到类或配置错误。
原因:
- jar 包路径错误
- jar 包未正确打包
- 启动参数错误
解决:
- 检查 jar 包路径是否正确(使用绝对路径)
- 确认 jar 包已正确打包:
mvn clean package
- 检查启动参数是否正确
8.2 STDIO 方式没有输出
问题: MCP Client 调用工具但没有返回结果。
原因:
解决:
- 确保设置了
-Dlogging.pattern.console=
- 检查工具方法是否有异常
- 开启 DEBUG 日志查看通信内容
8.3 SSE 方式连接失败
问题: MCP Client 无法连接到 SSE 服务。
原因:
解决:
- 确认 MCP Server 已启动
- 检查 URL 和端口是否正确
- 使用浏览器访问
http://localhost:8088/sse 测试
8.4 工具调用超时
问题: 工具调用超时。
原因:
解决:
1 2
| spring.ai.mcp.client.timeout=120000
|
8.5 多个 MCP Server 配置
问题: 如何配置多个 MCP Server?
STDIO 方式:
1 2 3 4 5 6 7 8 9 10 11 12
| { "mcpServers": { "weather": { "command": "java", "args": ["-jar", "weather-server.jar"] }, "stock": { "command": "java", "args": ["-jar", "stock-server.jar"] } } }
|
SSE 方式:
1 2 3 4 5
| spring.ai.mcp.client.sse.connections.weather.url=http://localhost:8088 spring.ai.mcp.client.sse.connections.weather.sse-endpoint=/sse
spring.ai.mcp.client.sse.connections.stock.url=http://localhost:8089 spring.ai.mcp.client.sse.connections.stock.sse-endpoint=/sse
|
九、最佳实践
9.1 工具服务设计
单一职责
每个 MCP Server 只提供一类工具:
- 天气服务:只提供天气相关工具
- 用户服务:只提供用户相关工具
错误处理
1 2 3 4 5 6 7 8 9 10 11
| @Tool(description = "获取天气信息") public String getWeather(double latitude, double longitude) { try { return restClient.get() .uri("https://api.open-meteo.com/v1/forecast?...") .retrieve() .body(String.class); } catch (Exception e) { return "获取天气信息失败:" + e.getMessage(); } }
|
日志记录
1 2 3 4 5 6 7
| @Tool(description = "获取天气信息") public String getWeather(double latitude, double longitude) { log.info("查询天气,纬度:{},经度:{}", latitude, longitude); log.info("查询结果:{}", result); return result; }
|
9.2 部署建议
STDIO 方式:
- jar 包路径使用绝对路径
- 确保 jar 包有执行权限
- 考虑使用进程管理工具(如 systemd)
SSE 方式:
- 使用反向代理(如 Nginx)
- 配置健康检查
- 考虑负载均衡
9.3 性能优化
连接池
SSE 方式可以配置连接池:
1
| spring.ai.mcp.client.sse.connections.userScores.max-connections=10
|
超时设置
合理设置超时时间:
1
| spring.ai.mcp.client.timeout=60000
|
资源管理
及时释放资源,避免内存泄漏。
十、总结
- MCP 协议:提供了标准化的工具调用协议,实现工具服务和 AI 应用的解耦
- STDIO 方式:适合本地工具服务,通过标准输入输出通信
- SSE 方式:适合远程工具服务,通过 HTTP + SSE 通信
- 配置要点:STDIO 需要配置文件,SSE 需要 URL 配置
- 工具实现:使用
@Tool 注解定义工具,和普通 Spring Bean 一样
- 常见问题:启动失败、连接失败、超时等
- 最佳实践:单一职责、错误处理、日志记录、性能优化
下一篇文章会介绍 RAG(检索增强生成),这是 Spring AI 提供的另一个重要功能。