在 Spring AI 中使用 MCP Server
1.1 Spring AI MCP 的介绍
Spring AI MCP 为模型上下文协议提供 Java 和 Spring 框架集成。它使 Spring AI 应用程序能够通过标准化的接口与不同的数据源和工具进行交互,支持同步和异步通信模式。整体架构如下:
Spring AI MCP 采用模块化架构,包括以下组件:
Spring AI 应用程序:使用 Spring AI 框架构建想要通过 MCP 访问数据的生成式 AI 应用程序
Spring MCP 客户端:MCP 协议的 Spring AI 实现,与服务器保持 1:1 连接
通过 Spring AI MCP,可以快速搭建 MCP 客户端和服务端程序。
2.2 使用 Spring AI MCP 快速搭建 MCP Server
Spring AI 提供了两种机制快速搭建 MCP Server,通过这两种方式开发者可以快速向 AI 应用开放自身的能力,这两种机制如下:
基于 stdio 的进程间通信传输,以独立的进程运行在 AI 应用本地,适用于比较轻量级的工具。
基于 SSE(Server-Sent Events) 进行远程服务访问,需要将服务单独部署,客户端通过服务端的 URL 进行远程访问,适用于比较重量级的工具。
2.2.1 基于 stdio 的 MCP 服务端实现
基于 stdio 的 MCP 服务端通过标准输入输出流与客户端通信,适用于作为子进程被客户端启动和管理的场景。
添加依赖
首先,在项目中添加 Spring AI MCP Server Starter 依赖:
dart
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>
配置 MCP 服务端
在 application.yml 中配置 MCP 服务端,这次要实现的是一个天气服务:
dart
spring:
main:
web-application-type: none # 必须禁用web应用类型
banner-mode: off # 禁用banner
ai:
mcp:
server:
stdio: true # 启用stdio模式
name: my-weather-server # 服务器名称
version: 0.0.1 # 服务器版本
实现 MCP 工具
使用 @Tool 注解标记方法,使其可以被 MCP 客户端发现和调用,通过 @ToolParameter 注解工具的具体参数:
dart
@Service
public class OpenMeteoService {
private final WebClient webClient;
public OpenMeteoService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder
.baseUrl("https://api.open-meteo.com/v1")
.build();
}
@Tool(description = "根据经纬度获取天气预报")
public String getWeatherForecastByLocation(
@ToolParameter(description = "纬度,例如:39.9042") String latitude,
@ToolParameter(description = "经度,例如:116.4074") String longitude) {
try {
String response = webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/forecast")
.queryParam("latitude", latitude)
.queryParam("longitude", longitude)
.queryParam("current", "temperature_2m,wind_speed_10m")
.queryParam("timezone", "auto")
.build())
.retrieve()
.bodyToMono(String.class)
.block();
// 解析响应并返回格式化的天气信息
// 这里简化处理,实际应用中应该解析JSON
return "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的天气信息:\n" + response;
} catch (Exception e) {
return "获取天气信息失败:" + e.getMessage();
}
}
@Tool(description = "根据经纬度获取空气质量信息")
public String getAirQuality(
@ToolParameter(description = "纬度,例如:39.9042") String latitude,
@ToolParameter(description = "经度,例如:116.4074") String longitude) {
// 模拟数据,实际应用中应调用真实API
return "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的空气质量:\n" +
"- PM2.5: 15 μg/m³ (优)\n" +
"- PM10: 28 μg/m³ (良)\n" +
"- 空气质量指数(AQI): 42 (优)\n" +
"- 主要污染物: 无";
}
}
这里使用了 OpenMeteo, OpenMeteo 是一个开源的天气 API,为非商业用途提供免费访问,无需 API 密钥。
注册 MCP 工具
在应用程序入口类中注册工具:
dart
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(OpenMeteoService openMeteoService) {
return MethodToolCallbackProvider.builder()
.toolObjects(openMeteoService)
.build();
}
}
运行服务端
在控制台中执行如下命令,编译并打包应用:
Terminal window
dart
mvn clean package -DskipTests
基于 SSE 的 MCP 服务端实现
基于 SSE 的 MCP 服务端通过 HTTP 协议与客户端通信,适用于作为独立服务部署的场景,可以被多个客户端远程调用,具体做法与 stdio 非常类似。
添加依赖
首先,在您的项目中添加依赖
dart
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
</dependency>
配置 MCP 服务端
在application.yml中配置 MCP 服务端:
dart
server:
port: 8080 # 服务器端口配置
spring:
ai:
mcp:
server:
name: my-weather-server # MCP服务器名称
version: 0.0.1 # 服务器版本号
实现 MCP 工具
与基于 stdio 的实现完全相同:
dart
@Service
public class OpenMeteoService {
private final WebClient webClient;
public OpenMeteoService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder
.baseUrl("https://api.open-meteo.com/v1")
.build();
}
@Tool(description = "根据经纬度获取天气预报")
public String getWeatherForecastByLocation(
@ToolParameter(description = "纬度,例如:39.9042") String latitude,
@ToolParameter(description = "经度,例如:116.4074") String longitude) {
try {
String response = webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/forecast")
.queryParam("latitude", latitude)
.queryParam("longitude", longitude)
.queryParam("current", "temperature_2m,wind_speed_10m")
.queryParam("timezone", "auto")
.build())
.retrieve()
.bodyToMono(String.class)
.block();
// 解析响应并返回格式化的天气信息
return "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的天气信息:\n" + response;
} catch (Exception e) {
return "获取天气信息失败:" + e.getMessage();
}
}
@Tool(description = "根据经纬度获取空气质量信息")
public String getAirQuality(
@ToolParameter(description = "纬度,例如:39.9042") String latitude,
@ToolParameter(description = "经度,例如:116.4074") String longitude) {
// 模拟数据,实际应用中应调用真实API
return "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的空气质量:\n" +
"- PM2.5: 15 μg/m³ (优)\n" +
"- PM10: 28 μg/m³ (良)\n" +
"- 空气质量指数(AQI): 42 (优)\n" +
"- 主要污染物: 无";
}
}
注册 MCP 工具
在应用程序入口类中注册工具:
dart
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(OpenMeteoService openMeteoService) {
return MethodToolCallbackProvider.builder()
.toolObjects(openMeteoService)
.build();
}
@Bean
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
运行服务端
在控制台中输入命令,运行服务端:
Terminal window
dart
mvn spring-boot:run
服务端将在 http://localhost:8080 启动。
2.3 在 Claude 中测试 mcp 服务
在上一小节中我们编写完了 MCP 服务,这些服务到底是否能正常运行呢?在 Claude Desktop 中可以测试一下。
修改配置文件,添加 weather 的配置,一定要注意 jar 包的路径必须是全路径:
dart
{
"mcpServers": {
"github": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-github"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": your token
}
},
"weather": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-Dspring.main.web-application-type=none",
"-Dlogging.pattern.console=",
"-jar",
"<修改为stdio编译之后的jar包全路径>"
],
"env": {}
}
}
}
重启 Claude 之后看到,我们编写的两个 Tool 已经被加载进来了:
输入提示词,查询今天北京的空气质量:
Claude 触发了我们自己编写的天气服务,展示了完整的数据:
上面使用了 stdio 的方式在 Claude Desktop 中使用我们自己编写的 MCP 服务,但是很可惜 Claude Desktop 不支持直接通过 SSE 模式访问,必须使用 mcp-proxy 作为中介,所以这里我们不再演示 Claude Desktop 接入 SSE 模式的 MCP 服务。
资料推荐