随着人工智能技术的快速发展,如何将AI能力无缝集成到现有应用中成为开发者面临的重要挑战。Spring AI提供了强大的框架来简化AI集成过程,而Model Context Protocol (MCP)则为AI模型与外部工具交互提供了标准化协议。本文将深入探讨Spring AI与MCP的结合实践,帮助开发者构建功能强大的AI增强型应用。
Model Context Protocol (MCP)是一种标准化协议,使AI模型能够以结构化方式与外部工具和资源进行交互。它为大语言模型(LLM)提供了与外部世界交互的标准接口,支持多种传输机制,为不同环境提供灵活性。
MCP通过多层架构实现,包括客户端/服务端层、会话层和传输层,确保消息的可靠传递和处理。它支持同步和异步通信模式,适用于各种应用场景。
Spring AI是Spring生态系统的最新成员,专注于简化AI工程的应用框架。它将Spring生态系统的设计原则(如可移植性和模块化设计)应用到AI领域,促进使用POJO(普通Java对象)作为应用构建块。
Spring AI MCP则是Spring AI生态系统的一部分,它扩展了MCP Java SDK,提供Spring Boot集成,包括客户端和服务器启动器,大大简化了MCP应用的开发。
MCP支持两种主要的传输机制:SSE(Server-Sent Events)和stdio(标准输入/输出)。这两种机制各有优缺点,适用于不同的场景。
SSE是一种基于HTTP的单向通信技术,允许服务器向客户端推送数据。在MCP中,SSE传输主要用于分布式系统,支持远程访问和多客户端连接。
stdio是一种进程内通信机制,通过标准输入和输出流进行数据传输。在MCP中,stdio传输主要用于本地集成,提供低延迟和简单的设置。
| 特性 | SSE | stdio |
|------|-----|-------|
| 通信方式 | 基于HTTP的单向通信 | 进程内标准输入/输出流 |
| 网络依赖 | 需要网络连接 | 不依赖网络 |
| 延迟 | 较高(受网络影响) | 较低(进程内通信) |
| 客户端数量 | 支持多客户端 | 通常单一客户端 |
| 部署复杂性 | 需要配置HTTP端点 | 简单,无需特殊配置 |
| 安全性考虑 | 需要考虑HTTP安全性 | 主要关注进程安全性 |
| 集成方式 | 远程服务集成 | 本地工具集成 |
| 扩展性 | 高(可以横向扩展) | 低(受限于本地资源) |
| 适用环境 | 分布式系统、微服务 | 本地应用、命令行工具 |
在Spring AI MCP中,可以根据应用需求选择适合的传输机制。对于需要远程访问的服务,SSE是更好的选择;而对于本地集成和低延迟要求的场景,stdio则更为合适。
下面我们将通过实际代码示例,分别展示如何使用Spring AI MCP实现SSE和stdio两种传输方式。
首先,我们需要添加Spring AI MCP相关依赖到项目中:
org.springframework.boot
spring-boot-starter-parent
3.2.0
org.springframework.ai
spring-ai-starter-mcp-client
1.0.0
org.springframework.ai
spring-ai-starter-mcp-client-webflux
1.0.0
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-webflux
在application.yml
中配置SSE客户端:
spring:
ai:
mcp:
client:
enabled: true
name: mcp-sse-client
version: 1.0.0
request-timeout: 30s
type: SYNC # 同步客户端,也可以设为ASYNC使用异步客户端
sse:
connections:
server1:
url: http://localhost:8080/mcp/message
import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.List;
import java.util.Map;
@SpringBootApplication
public class SseClientApplication {
public static void main(String[] args) {
SpringApplication.run(SseClientApplication.class, args);
}
@Autowired
private List mcpSyncClients;
@Bean
public CommandLineRunner runner() {
return args -> {
// 假设我们只有一个客户端配置
McpSyncClient client = mcpSyncClients.get(0);
// 初始化客户端连接
client.initialize();
// 列出可用工具
var tools = client.listTools();
System.out.println("可用工具: " + tools.getTools());
// 调用一个名为"calculator"的工具
var result = client.callTool(
new CallToolRequest("calculator",
Map.of("operation", "add", "a", 2, "b", 3))
);
System.out.println("计算结果: " + result.getResult());
// 列出可用资源
var resources = client.listResources();
System.out.println("可用资源: " + resources.getResources());
// 关闭客户端
client.closeGracefully();
};
}
}
import org.springframework.ai.mcp.schema.Tool;
import org.springframework.ai.mcp.server.McpServer;
import org.springframework.ai.mcp.server.McpSyncServer;
import org.springframework.ai.mcp.server.feature.McpServerFeatures;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import java.util.Map;
@SpringBootApplication
@EnableWebMvc
public class SseServerApplication {
public static void main(String[] args) {
SpringApplication.run(SseServerApplication.class, args);
}
@Bean
public WebMvcSseServerTransport webMvcSseServerTransport(ObjectMapper mapper) {
return new WebMvcSseServerTransport(mapper, "/mcp/message");
}
@Bean
public RouterFunction mcpRouterFunction(WebMvcSseServerTransport transport) {
return transport.getRouterFunction();
}
@Bean
public McpSyncServer mcpSyncServer(WebMvcSseServerTransport transport) {
// 创建一个简单的计算器工具
var calculatorTool = new Tool(
"calculator",
"基础计算器",
Map.of(
"operation", "string",
"a", "number",
"b", "number"
)
);
// 工具处理函数
var calculatorHandler = (Map args) -> {
String operation = (String) args.get("operation");
Number a = (Number) args.get("a");
Number b = (Number) args.get("b");
double result = 0;
switch (operation) {
case "add":
result = a.doubleValue() + b.doubleValue();
break;
case "subtract":
result = a.doubleValue() - b.doubleValue();
break;
case "multiply":
result = a.doubleValue() * b.doubleValue();
break;
case "divide":
result = a.doubleValue() / b.doubleValue();
break;
}
return new CallToolResult(result, false);
};
// 创建和配置服务器
return McpServer.sync(transport)
.serverInfo("calculator-server", "1.0.0")
.capabilities(ServerCapabilities.builder()
.tools(true) // 启用工具支持
.logging() // 启用日志支持
.build())
.tools(new McpServerFeatures.SyncToolRegistration(calculatorTool, calculatorHandler))
.build();
}
}
在application.yml
中配置stdio客户端:
spring:
ai:
mcp:
client:
enabled: true
name: mcp-stdio-client
version: 1.0.0
request-timeout: 30s
type: SYNC
stdio:
connections:
calculatorServer:
command: java
args:
- -jar
- calculator-server.jar
env:
DEBUG: "true"
也可以使用Claude Desktop格式的JSON配置文件:
spring:
ai:
mcp:
client:
stdio:
servers-configuration: classpath:mcp-servers.json
mcp-servers.json
示例:
{
"mcpServers": {
"calculator": {
"command": "java",
"args": [
"-jar",
"calculator-server.jar"
],
"env": {
"DEBUG": "true"
}
}
}
}
import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.List;
import java.util.Map;
@SpringBootApplication
public class StdioClientApplication {
public static void main(String[] args) {
SpringApplication.run(StdioClientApplication.class, args);
}
@Autowired
private List mcpSyncClients;
@Bean
public CommandLineRunner runner() {
return args -> {
// 获取名为"calculatorServer"的客户端
McpSyncClient client = mcpSyncClients.stream()
.filter(c -> c.getName().equals("calculatorServer"))
.findFirst()
.orElseThrow(() -> new RuntimeException("未找到calculatorServer客户端"));
// 初始化客户端连接
client.initialize();
// 列出可用工具
var tools = client.listTools();
System.out.println("可用工具: " + tools.getTools());
// 调用计算器工具
var result = client.callTool(
new CallToolRequest("calculator",
Map.of("operation", "multiply", "a", 10, "b", 5))
);
System.out.println("计算结果: " + result.getResult()); // 应该输出50
// 关闭客户端
client.closeGracefully();
};
}
}
standalone-calculator-server.jar的主类:
import org.springframework.ai.mcp.schema.Tool;
import org.springframework.ai.mcp.server.McpServer;
import org.springframework.ai.mcp.server.McpSyncServer;
import org.springframework.ai.mcp.server.feature.McpServerFeatures;
import org.springframework.ai.mcp.transport.StdioServerTransport;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.Map;
@SpringBootApplication
public class StdioServerApplication {
public static void main(String[] args) {
SpringApplication.run(StdioServerApplication.class, args);
}
@Bean
public CommandLineRunner runServer() {
return args -> {
// 创建stdio传输
StdioServerTransport transport = new StdioServerTransport(new ObjectMapper());
// 创建计算器工具
var calculatorTool = new Tool(
"calculator",
"基础计算器",
Map.of(
"operation", "string",
"a", "number",
"b", "number"
)
);
// 工具处理函数
var calculatorHandler = (Map args) -> {
String operation = (String) args.get("operation");
Number a = (Number) args.get("a");
Number b = (Number) args.get("b");
double result = 0;
switch (operation) {
case "add":
result = a.doubleValue() + b.doubleValue();
break;
case "subtract":
result = a.doubleValue() - b.doubleValue();
break;
case "multiply":
result = a.doubleValue() * b.doubleValue();
break;
case "divide":
result = a.doubleValue() / b.doubleValue();
break;
}
return new CallToolResult(result, false);
};
// 创建和配置服务器
McpSyncServer server = McpServer.sync(transport)
.serverInfo("calculator-server", "1.0.0")
.capabilities(ServerCapabilities.builder()
.tools(true)
.logging()
.build())
.tools(new McpServerFeatures.SyncToolRegistration(calculatorTool, calculatorHandler))
.build();
// 服务器将通过stdio与客户端通信,直到进程终止
System.out.println("计算器服务器已启动,等待通过stdio接收命令...");
};
}
}
可以通过实现McpSyncClientCustomizer
或McpAsyncClientCustomizer
接口来自定义客户端行为:
import org.springframework.ai.mcp.client.customizer.McpSyncClientCustomizer;
import org.springframework.ai.mcp.client.McpClient;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.List;
@Component
public class CustomMcpClientConfig implements McpSyncClientCustomizer {
@Override
public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {
// 自定义请求超时时间
spec.requestTimeout(Duration.ofSeconds(30));
// 设置工具变更通知处理器
spec.toolsChangeConsumer((List tools) -> {
System.out.println("工具列表已更新: " + tools);
});
// 设置日志处理器
spec.loggingConsumer((McpSchema.LoggingMessageNotification log) -> {
System.out.println("[" + log.getLevel() + "] " + log.getData());
});
}
}
下面是一个使用WebFlux实现的异步MCP客户端和服务器的完整示例:
import org.springframework.ai.mcp.client.McpAsyncClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
@SpringBootApplication
public class AsyncWebFluxClientApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncWebFluxClientApplication.class, args);
}
@Autowired
private List mcpAsyncClients;
@Bean
public CommandLineRunner runner() {
return args -> {
// 获取第一个客户端
McpAsyncClient client = mcpAsyncClients.get(0);
// 使用响应式编程方式与MCP服务器交互
client.initialize()
.flatMap(initResult -> {
System.out.println("客户端初始化成功");
return client.listTools();
})
.flatMap(tools -> {
System.out.println("可用工具: " + tools.getTools());
// 调用计算器工具
return client.callTool(new CallToolRequest(
"calculator",
Map.of("operation", "add", "a", 5, "b", 3)
));
})
.flatMap(result -> {
System.out.println("计算结果: " + result.getResult());
return Mono.just("完成");
})
.doFinally(signalType -> {
// 关闭客户端
client.closeGracefully().subscribe();
})
.subscribe();
};
}
}
配置文件 application.yml
:
spring:
ai:
mcp:
client:
enabled: true
name: webflux-mcp-client
version: 1.0.0
request-timeout: 30s
type: ASYNC # 异步客户端
sse:
connections:
webfluxServer:
url: http://localhost:8080/mcp/message
import org.springframework.ai.mcp.schema.Tool;
import org.springframework.ai.mcp.server.McpAsyncServer;
import org.springframework.ai.mcp.server.McpServer;
import org.springframework.ai.mcp.server.feature.McpServerFeatures;
import org.springframework.ai.mcp.transport.webflux.WebFluxSseServerTransport;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.server.RouterFunction;
import reactor.core.publisher.Mono;
import java.util.Map;
@SpringBootApplication
public class AsyncWebFluxServerApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncWebFluxServerApplication.class, args);
}
@Bean
public WebFluxSseServerTransport webFluxSseServerTransport(ObjectMapper mapper) {
return new WebFluxSseServerTransport(mapper, "/mcp/message");
}
@Bean
public RouterFunction> mcpRouterFunction(WebFluxSseServerTransport transport) {
return transport.getRouterFunction();
}
@Bean
public McpAsyncServer mcpAsyncServer(WebFluxSseServerTransport transport) {
// 创建计算器工具
var calculatorTool = new Tool(
"calculator",
"基础计算器",
Map.of(
"operation", "string",
"a", "number",
"b", "number"
)
);
// 异步工具处理函数
var calculatorHandler = (Map args) -> {
String operation = (String) args.get("operation");
Number a = (Number) args.get("a");
Number b = (Number) args.get("b");
double result = 0;
switch (operation) {
case "add":
result = a.doubleValue() + b.doubleValue();
break;
case "subtract":
result = a.doubleValue() - b.doubleValue();
break;
case "multiply":
result = a.doubleValue() * b.doubleValue();
break;
case "divide":
result = a.doubleValue() / b.doubleValue();
break;
}
// 使用Mono包装结果
return Mono.just(new CallToolResult(result, false));
};
// 创建和配置异步服务器
return McpServer.async(transport)
.serverInfo("calculator-server", "1.0.0")
.capabilities(ServerCapabilities.builder()
.tools(true)
.logging()
.build())
.tools(new McpServerFeatures.AsyncToolRegistration(calculatorTool, calculatorHandler))
.build();
}
}
在使用Spring AI MCP进行开发时,以下是一些最佳实践和建议:
Spring AI MCP为开发者提供了一个强大的框架,用于构建与AI模型交互的应用。通过标准化的协议和灵活的传输机制,它简化了AI能力的集成过程。本文详细介绍了MCP的核心概念、Spring AI的主要特点,以及SSE和stdio两种传输机制的区别和实现。通过具体的代码示例,展示了如何使用Spring AI MCP构建同步和异步客户端/服务器应用。
随着AI技术的不断发展,MCP将在连接AI模型与外部世界方面发挥越来越重要的作用。Spring AI MCP作为Java生态系统中的重要工具,为开发者提供了便捷的方式来构建智能化应用,实现AI能力的无缝集成。
通过合理选择传输机制和遵循最佳实践,开发者可以充分发挥Spring AI MCP的潜力,构建功能强大、性能卓越的AI增强型应用。