前言
在上一篇Spring Boot整合DeepSeek+MCP实践详解文章中,我们已经集成了MCP Client
,并且通过MCP Client
成功地调用了本地的filesystem
、mysql
两个MCP Server
,接下来这篇文章我们需要自己通过SpringBoot
实现一个MCP Server
,并通过上一篇的MCP Client
接入自己实现的MCP Server
。这样的话就完成了使用java
实现MCP
的完整闭环了。
创建MCP Server
-
项目依赖
xml<dependencies> <!-- 对外提供restful api --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- mcp服务端依赖 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency> </dependencies> <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> </repositories>
主要就两个依赖:
spring-boot-starter-web
提供外部http
请求访问的能力,也是MCP SSE
通信方式必要的依赖;spring-ai-mcp-server-spring-boot-starter
提供MCP
协议具体的实现,是SpringBoot
实现MCP Server
最核心的依赖,目前1.0.0-M6
版本还不是最终release
版本; -
配置
application.yml
配置需要按照通信方式分两种,如果当前MCP Server
应用在整个服务架构中被设计成通过stdio
的方式与MCP Client
通信,那么使用下面这种配置:yamlspring: application: # 应用程序名称(因为我这个demo是查询天气的MCP Server) name: weather-mcp-server ai: mcp: server: # 开启stdio通信方式(关键配置) stdio: true # MCP Server命名(可根据自己的应用进行命名) name: "weather-mcp-server" # MCP Server版本号(可自定义) version: "0.0.1" main: # 关闭web服务(关键配置) web-application-type: none
上面配置主要就是要开启
stdio
通信方式,另外就是要关闭web
服务;如果打算采用
SSE
的通信方式与MCP Client
交互,那么需要调整配置:yamlspring: application: name: weather-mcp-server ai: mcp: server: name: "weather-mcp-server" version: "0.0.1" server: # 开放端口,用来和MCP Client建立连接 port: 8083
SSE
模式的配置有两个关键点,第一个是开放web
服务端口,用来与MCP Client
建立连接,第二个就是把stdio: true
删掉,因为默认就是开启SSE
通信方式的; -
业务代码
接下来我们开发一些简单业务代码,特别简单,代码如下:
kotlinimport lombok.extern.slf4j.Slf4j; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Service; /** * @ClassName WeatherServer * @Description * @Author weizou.zou * @Date 2025/4/10 19:01 * @Version $ */ @Slf4j @Service public class WeatherServer { @Tool(name = "getCurrentWeather", description = "查询指定城市当前的天气") public String getCurrentWeather(@ToolParam(description = "城市名称") String cityName) { log.info("当前城市:{}", cityName); return "天气晴"; } }
上面的业务代码主要就是做一个示例,并没有真正地去实现查询天气的逻辑,这里就是给大家打一个样。下面我们把最关键的点说一下:
@Tool注解
:使用这个注解标注就相当于对外暴露对应的能力,MCP Client
初始化的时候会收集好目标MCP Server
具备哪些能力,并把这些能力告诉LLM
大模型,作为大模型的工具库。比如这个MCP Server
就可以查询指定城市的天气;@ToolParam注解
:这个注解就是告诉MCP Client
调用这个功能需要传什么参数;
-
JavaBean配置
业务代码写完了,下面我们就需要把所有需要暴露的工具都打包好,一起提供给
MCP Client
,这一步需要通过下面的操作来做:kotlinimport com.example.weathermcpserver.mcpserver.WeatherServer; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @ClassName McpServerConfiguration * @Description * @Author weizou.zou * @Date 2025/4/10 18:56 * @Version $ */ @Configuration public class McpServerConfiguration { @Bean public ToolCallbackProvider toolCallbackProvider(WeatherServer weatherServer) { return MethodToolCallbackProvider.builder() .toolObjects(weatherServer) .build(); } }
上面的代码只演示了怎么把
WeatherServer
打包,假如有很多实现类,那么应该如何处理?给大家看一下
toolObjects
方法的实现就明白了:kotlinpublic Builder toolObjects(Object... toolObjects) { Assert.notNull(toolObjects, "toolObjects cannot be null"); this.toolObjects = Arrays.asList(toolObjects); return this; }
参数是一个数组,所以如果有很多实现类,也一样可以作为参数传递给
toolObjects
方法一起打包给MCP Client
;
MCP Client如何接入MCP Server
-
SSE
接入方式MCP Client
通过SSE
方式接入MCP Server
超级简单,只要做一步配置(基于上一篇文章):yamlspring: ai: mcp: client: sse: connections: # MCP Server名称,自己随便取名 weathermcpserver: # url根据MCP Server配置调整 url: "http://127.0.0.1:8083"
上面的
127.0.0.1
和8083
端口根据自己MCP Server
的具体配置做调整;是不是超级简单,哈哈! -
stdio
接入方式stdio
因为是基于标准输入输出通信的,所以意味着MCP Server
一定是和MCP Client
部署在一个机器上的,所以我们需要将MCP Server
进行打包;-
打包
MCP Server
: 可通过以下命令对MCP Server
打jar包:gomvn clean package
或者在
idea
中点击package
打jar包: -
把
jar
包复制到MCP Client
机器上MCP Server
打包后的目标文件在项目target
文件夹中:把上面的这个
.jar
后缀的文件复制到MCP Client
所在的机器; -
配置
MCP Client
yamlspring: ai: mcp: client: stdio: connections: weathermcpserver: command: "java" args: # MCP Server开启stdio - "-Dspring.ai.mcp.server.stdio=true" # MCP Server关闭web服务 - "-Dspring.main.web-application-type=none" # 日志配置 - "-Dlogging.pattern.console=" - "-jar" # jar包的文件路径 - "E:\my_projects\mcp-client\src\main\resources\weather-mcp-server.jar"
java
方式MCP
的stdio
方式就是通过java -jar
命令实现的,稍微比SSE
通信方式多几步。这两种方式各有各的优势,stdio
特别适合操作本地机器上的资源,比如文件管理,浏览器自动化操作等,SSE
适用分布式服务的调用。
-
问题点
-
SSE
通信方式返回报错当
MCP Server
方法被调用后,返回结果竟然报错了:vbscriptFailed to send message to session 012dcdf9-1654-4d08-873c-9d20ac112fbf: Cannot invoke "org.apache.catalina.connector.OutputBuffer.isBlocking()" because "this.ob" is null.
这个问题我帮兄弟们找到答案了,原因就是
Spring ai
的BUG
: github.com/spring-proj... github.com/modelcontex...对应的修复PR在这里: github.com/modelcontex...
需要说明的是,目前
1.0.0-M6
还未Release
,投入生产还需要等待。如果等不及的兄弟们也可以直接自己根据PR进行修复并替换掉对应的jar
包。
结语
MCP
技术对应应用层开发人员来说应该算是革命性的,从技术上来说,它不算很复杂,但是最核心的是它降低了应用层开发人员使用AI
的门槛,这一点将会使得AI
智能化在应用层迎来大爆发,真正地让普通大众也能普惠地使用AI
,非常看好MCP
未来的发展,建议开发同学也拥抱MCP
。