279 lines
6.8 KiB
Markdown
279 lines
6.8 KiB
Markdown
# Spring AI MCP WebFlux 服务器示例
|
||
|
||
## 项目简介
|
||
|
||
本项目是一个基于 Spring AI 框架的 Model Context Protocol (MCP) WebFlux 服务器示例。它展示了如何构建一个支持 WebFlux 和 STDIO 两种通信方式的 MCP 服务器,提供天气查询和空气质量信息等工具服务。
|
||
|
||
## 主要功能
|
||
|
||
- 支持 WebFlux 和 STDIO 两种通信方式
|
||
- 提供天气预报查询服务(基于 OpenMeteo API)
|
||
- 提供空气质量信息查询服务(模拟数据)
|
||
- 支持响应式编程模型
|
||
- 支持工具函数的动态注册和调用
|
||
|
||
## 技术栈
|
||
|
||
- Java 17+
|
||
- Spring Boot 3.x
|
||
- Spring WebFlux
|
||
- Spring AI MCP Server
|
||
- OpenMeteo API
|
||
- Maven
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
starter-webflux-server/
|
||
├── src/
|
||
│ ├── main/
|
||
│ │ ├── java/
|
||
│ │ │ └── org/springframework/ai/mcp/sample/server/
|
||
│ │ │ ├── McpServerApplication.java # 应用程序入口
|
||
│ │ │ └── OpenMeteoService.java # 天气服务实现
|
||
│ │ └── resources/
|
||
│ │ └── application.properties # 应用配置
|
||
│ └── test/
|
||
│ └── java/
|
||
│ └── org/springframework/ai/mcp/sample/client/
|
||
│ ├── ClientStdio.java # STDIO 客户端测试
|
||
│ ├── ClientSse.java # SSE 客户端测试
|
||
│ └── SampleClient.java # 通用客户端测试
|
||
└── pom.xml # Maven 配置
|
||
```
|
||
|
||
## 核心组件
|
||
|
||
### McpServerApplication
|
||
|
||
应用程序的主入口类,负责:
|
||
- 配置 Spring Boot 应用
|
||
- 注册天气工具服务
|
||
- 初始化 MCP 服务器
|
||
|
||
### OpenMeteoService
|
||
|
||
提供两个主要工具服务:
|
||
1. `getWeatherForecastByLocation`: 获取指定位置的天气预报
|
||
- 支持当前天气和未来 7 天预报
|
||
- 包含温度、湿度、风向、降水量等信息
|
||
- 使用 OpenMeteo 免费 API
|
||
|
||
2. `getAirQuality`: 获取指定位置的空气质量信息
|
||
- 提供欧洲 AQI 和美国 AQI 两种标准
|
||
- 包含 PM10、PM2.5、CO、NO2、SO2、O3 等污染物数据
|
||
- 目前使用模拟数据(可扩展为真实 API)
|
||
|
||
## 配置说明
|
||
|
||
### 服务器配置
|
||
|
||
在 `application.properties` 中:
|
||
|
||
```properties
|
||
# 服务器配置
|
||
spring.ai.mcp.server.name=my-weather-server
|
||
spring.ai.mcp.server.version=0.0.1
|
||
|
||
# 使用 STDIO 传输时的配置
|
||
spring.main.banner-mode=off
|
||
# logging.pattern.console=
|
||
```
|
||
|
||
### WebFlux 客户端配置
|
||
|
||
在客户端的 `application.properties` 中:
|
||
|
||
```properties
|
||
# 基本配置
|
||
server.port=8888
|
||
spring.application.name=mcp
|
||
spring.main.web-application-type=none
|
||
|
||
# API 密钥配置
|
||
spring.ai.dashscope.api-key=${AI_DASHSCOPE_API_KEY}
|
||
|
||
# SSE 连接配置
|
||
spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080
|
||
|
||
# 调试日志
|
||
logging.level.io.modelcontextprotocol.client=DEBUG
|
||
logging.level.io.modelcontextprotocol.spec=DEBUG
|
||
|
||
# 编码配置
|
||
server.servlet.encoding.charset=UTF-8
|
||
server.servlet.encoding.enabled=true
|
||
server.servlet.encoding.force=true
|
||
spring.mandatory-file-encoding=UTF-8
|
||
|
||
# 用户输入配置
|
||
ai.user.input=北京的天气如何?
|
||
```
|
||
|
||
## 使用方法
|
||
|
||
### 1. 编译项目
|
||
|
||
```bash
|
||
mvn clean package -DskipTests
|
||
```
|
||
|
||
### 2. 启动服务器
|
||
|
||
#### 作为 Web 服务器启动
|
||
|
||
```bash
|
||
mvn spring-boot:run
|
||
```
|
||
|
||
服务器将在 http://localhost:8080 启动。
|
||
|
||
#### 作为 STDIO 服务器启动
|
||
|
||
```bash
|
||
java -Dspring.ai.mcp.server.stdio=true \
|
||
-Dspring.main.web-application-type=none \
|
||
-Dlogging.pattern.console= \
|
||
-jar target/mcp-starter-webflux-server-0.0.1-SNAPSHOT.jar
|
||
```
|
||
|
||
### 3. 客户端示例
|
||
|
||
#### WebFlux 客户端
|
||
|
||
##### 代码示例
|
||
|
||
代码文件为 org.springframework.ai.mcp.sample.client.ClientSse,可以直接运行测试。
|
||
|
||
```java
|
||
// 配置 WebFlux 客户端
|
||
var transport = new WebFluxSseClientTransport(
|
||
WebClient.builder().baseUrl("http://localhost:8080")
|
||
);
|
||
|
||
// 创建聊天客户端
|
||
var chatClient = chatClientBuilder
|
||
.defaultTools(tools)
|
||
.build();
|
||
|
||
// 发送问题并获取回答
|
||
System.out.println("\n>>> QUESTION: " + userInput);
|
||
System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput).call().content());
|
||
```
|
||
|
||
##### mcp setting 配置示例
|
||
|
||
在 cursor、cline 等 MCP 客户端中,本示例可以使用如下配置:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"weather-local": {
|
||
"url": "http://localhost:8080/sse"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### STDIO 客户端
|
||
|
||
##### 代码示例
|
||
|
||
代码文件为 org.springframework.ai.mcp.sample.client.ClientStdio,可以直接运行测试。
|
||
|
||
```java
|
||
var stdioParams = ServerParameters.builder("java")
|
||
.args("-Dspring.ai.mcp.server.stdio=true",
|
||
"-Dspring.main.web-application-type=none",
|
||
"-Dlogging.pattern.console=",
|
||
"-jar",
|
||
"target/mcp-starter-webflux-server-0.0.1-SNAPSHOT.jar")
|
||
.build();
|
||
|
||
var transport = new StdioClientTransport(stdioParams);
|
||
new SampleClient(transport).run();
|
||
```
|
||
|
||
##### mcp setting 配置示例
|
||
|
||
在 cursor、cline 等 MCP 客户端中,本示例可以使用如下配置:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"weather-local": {
|
||
"command": "java",
|
||
"args": [
|
||
"-Dspring.ai.mcp.server.stdio=true",
|
||
"-Dspring.main.web-application-type=none",
|
||
"-Dlogging.pattern.console=",
|
||
"-jar",
|
||
"target/mcp-starter-webflux-server-0.0.1-SNAPSHOT.jar"
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 工具调用示例
|
||
|
||
### 获取天气预报
|
||
|
||
```java
|
||
CallToolResult weatherResult = client.callTool(
|
||
new CallToolRequest("getWeatherForecastByLocation",
|
||
Map.of("latitude", "39.9042", "longitude", "116.4074"))
|
||
);
|
||
```
|
||
|
||
### 获取空气质量信息
|
||
|
||
```java
|
||
CallToolResult airQualityResult = client.callTool(
|
||
new CallToolRequest("getAirQuality",
|
||
Map.of("latitude", "39.9042", "longitude", "116.4074"))
|
||
);
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
1. OpenMeteo API 是免费的,无需 API 密钥
|
||
2. 空气质量数据目前使用模拟数据,实际应用中应替换为真实 API
|
||
3. 使用 STDIO 传输时,必须禁用控制台日志和 banner
|
||
4. 默认作为 WebFlux 服务器运行,支持 HTTP 通信
|
||
5. 客户端需要正确配置 SSE 连接 URL
|
||
6. 确保环境变量 `DASH_SCOPE_API_KEY` 已正确设置
|
||
|
||
## 扩展开发
|
||
|
||
如需添加新的工具服务:
|
||
|
||
1. 创建新的服务类
|
||
2. 使用 `@Tool` 注解标记方法
|
||
3. 在 `McpServerApplication` 中注册服务
|
||
|
||
示例:
|
||
|
||
```java
|
||
@Service
|
||
public class MyNewService {
|
||
@Tool(description = "新工具描述")
|
||
public String myNewTool(String input) {
|
||
// 实现工具逻辑
|
||
return "处理结果: " + input;
|
||
}
|
||
}
|
||
|
||
// 在 McpServerApplication 中注册
|
||
@Bean
|
||
public ToolCallbackProvider myTools(MyNewService myNewService) {
|
||
return MethodToolCallbackProvider.builder()
|
||
.toolObjects(myNewService)
|
||
.build();
|
||
}
|
||
```
|
||
|
||
## 许可证
|
||
|
||
Apache License 2.0
|