简述
在开发一个结合了 Server-Sent Events (SSE) 和 Model Context Protocol (MCP) 的Java应用时,目标是构建一个能够实时与AI模型交互并接收更新的系统。这样的系统可以用于多种场景,比如实时数据分析、智能监控或个性化推荐等。下面是一个基于Spring Boot框架的示例,展示了如何使用Java来实现这种类型的集成。
当前 mcp 情况
| 默认方式 |
spring支持 |
| stdio |
webflux sse |
| sse |
webmvc sse4.2 |
|
studio |
使用 Cherry studio 作为 ai 引擎
配置 mcp 链接 选择 类型为 sse, 地址为spring boot 项目启动地址
问答情况
mcp 服务配置类
配置 McpServerConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| package com.mcp;
import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpServerTransportProvider; import org.springframework.ai.mcp.McpToolUtils; import org.springframework.ai.support.ToolCallbacks; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.server.RouterFunction;
@Configuration public class McpServerConfig { @Bean @ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "stdio") public StdioServerTransportProvider stdioServerTransportProvider() { return new StdioServerTransportProvider(); }
@Bean @ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "sse") public WebFluxSseServerTransportProvider sseServerTransportProvider() { return new WebFluxSseServerTransportProvider(new ObjectMapper(), "/mcp/message"); }
@Bean @ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "sse") public RouterFunction<?> mcpRouterFunction(WebFluxSseServerTransportProvider transportProvider) { return transportProvider.getRouterFunction(); }
@Bean public McpToolClient mcpToolClient() { return new McpToolClient(); } @Bean public McpSyncServer mcpServer(McpServerTransportProvider transportProvider, McpToolClient mcpToolClient) {
var capabilities = McpSchema.ServerCapabilities.builder() .tools(true) .logging() .build(); McpSyncServer server = McpServer.sync(transportProvider) .serverInfo("MCP Demo Weather Server", "1.0.0") .capabilities(capabilities) .tools(McpToolUtils.toSyncToolSpecifications(ToolCallbacks.from(mcpToolClient))) .build();
return server; }
}
|
@ConditionalOnProperty
| 元素 |
说明 |
@Bean |
告诉 Spring 容器:这是一个需要注册为 Bean 的方法。返回的对象会被加入 Spring 应用上下文中。 |
@ConditionalOnProperty(...) |
只有在满足指定的配置属性时才创建这个 Bean。 |
prefix = "transport" |
属性前缀,表示我们要检查的是以 transport. 开头的配置项。 |
name = "mode" |
要检查的属性名是 mode,即完整的属性路径是 transport.mode。 |
havingValue = "stdio" |
只有当 transport.mode=stdio 时才会加载这个 Bean。 |
| 方法体 |
如果条件成立,则返回一个新的 StdioServerTransportProvider 实例。 |
STDIO 传输配置
作用:创建一个基于标准输入输出(stdin/stdout)的传输层。
条件加载:仅当 application.properties 中配置了 transport.mode=stdio 时才会加载。
1 2 3 4 5
| @Bean @ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "stdio") public StdioServerTransportProvider stdioServerTransportProvider() { return new StdioServerTransportProvider(); }
|
SSE 传输配置
作用:创建基于 HTTP + SSE 的传输层。
路径:/mcp/message 是客户端连接的端点。
依赖:需要 spring-boot-starter-webflux 支持。
1 2 3 4 5
| @Bean @ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "sse") public WebFluxSseServerTransportProvider sseServerTransportProvider() { return new WebFluxSseServerTransportProvider(new ObjectMapper(), "/mcp/message"); }
|
路由器
作用:将 MCP 服务绑定到 WebFlux 路由中,启动 HTTP 服务器。
1 2 3 4 5
| @Bean @ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "sse") public RouterFunction<?> mcpRouterFunction(WebFluxSseServerTransportProvider transportProvider) { return transportProvider.getRouterFunction(); }
|
工具客户端 Bean
作用:注册一个 MCP 工具客户端,用于处理 MCP 客户端发起的工具调用请求。
1 2 3 4
| @Bean public McpToolClient mcpToolClient() { return new McpToolClient(); }
|
创建 MCP 同步服务器
参数说明:
- transportProvider:根据配置选择的传输方式(stdio 或 sse)
- weatherApiClient:提供工具调用逻辑的客户端
服务器功能说明:
- 设置服务器能力:
- 支持工具调用 .tools(true)
- 支持日志功能 .logging()
- 添加工具:
- 使用 ToolCallbacks.from(…) 提取 @Tool 注解的方法
- 使用 McpToolUtils.toSyncToolSpecifications(…) 转换为 MCP 工具规范
- 构建并返回同步的 MCP 服务器实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Bean public McpSyncServer mcpServer(McpServerTransportProvider transportProvider, McpToolClient mcpToolClient) {
var capabilities = McpSchema.ServerCapabilities.builder() .tools(true) .logging() .build(); McpSyncServer server = McpServer.sync(transportProvider) .serverInfo("MCP Demo Weather Server", "1.0.0") .capabilities(capabilities) .tools(McpToolUtils.toSyncToolSpecifications(ToolCallbacks.from(mcpToolClient))) .build();
return server; }
|
Client 类
@Tool(description = “Testing method, used to test whether the tool is called”)
表示这是一个工具方法。
描述信息将被用于生成工具文档或提供给模型理解用途。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.mcp;
import org.springframework.ai.tool.annotation.Tool;
import java.text.SimpleDateFormat; import java.util.Date;
public class McpToolClient {
@Tool(description = "Testing method, used to test whether the tool is called") public String helloworld(){ return "hello, world"; }
@Tool(description = "获取当前时间") public String nowTime(){ // 定义日期格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 获取当前时间 Date now = new Date(); // 转换为字符串 String nowStr = sdf.format(now); return "当前时间: " + nowStr; } }
|
yaml 文件配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| server: port: 8080
spring: main: banner-mode: OFF
transport: mode: sse
logging: file: name: mcp.weather.log
|
项目maven 信息导入配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.5</version> <relativePath /> </parent> <groupId>com.mcp</groupId> <artifactId>mcp-test</artifactId> <version>0.0.1-SNAPSHOT</version> <name>mcp-test</name> <description>Demo project for Spring Boot</description> <url/> <licenses> <license/> </licenses> <developers> <developer/> </developers> <scm> <connection/> <developerConnection/> <tag/> <url/> </scm> <properties> <java.version>17</java.version> <mcp.version>0.10.0</mcp.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-model</artifactId> </dependency>
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp</artifactId> </dependency>
<dependency> <groupId>io.modelcontextprotocol.sdk</groupId> <artifactId>mcp-spring-webflux</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>1.1.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency>
<dependency> <groupId>io.modelcontextprotocol.sdk</groupId> <artifactId>mcp-bom</artifactId> <version>${mcp.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <releases> <enabled>false</enabled> </releases> </repository> <repository> <id>central-portal-snapshots</id> <name>Central Portal Snapshots</name> <url>https://central.sonatype.com/repository/maven-snapshots/</url> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> </project>
|
项目地址
项目地址