SpringBoot实现MCP Server实战详解

前言

在上一篇Spring Boot整合DeepSeek+MCP实践详解文章中,我们已经集成了MCP Client,并且通过MCP Client成功地调用了本地的filesystemmysql两个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通信,那么使用下面这种配置:

    yaml 复制代码
    spring:
      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交互,那么需要调整配置:

    yaml 复制代码
    spring:
      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通信方式的;

  • 业务代码

    接下来我们开发一些简单业务代码,特别简单,代码如下:

    kotlin 复制代码
    import 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,这一步需要通过下面的操作来做:

    kotlin 复制代码
    import 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方法的实现就明白了:

    kotlin 复制代码
    public 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超级简单,只要做一步配置(基于上一篇文章):

    yaml 复制代码
    spring:
      ai:
        mcp:
          client:
            sse:
              connections:
                # MCP Server名称,自己随便取名
                weathermcpserver:
                  # url根据MCP Server配置调整
                  url: "http://127.0.0.1:8083"

    上面的127.0.0.18083端口根据自己MCP Server的具体配置做调整;是不是超级简单,哈哈!

  • stdio接入方式

    stdio因为是基于标准输入输出通信的,所以意味着MCP Server一定是和MCP Client部署在一个机器上的,所以我们需要将MCP Server进行打包;

    • 打包MCP Server: 可通过以下命令对MCP Server打jar包:

      go 复制代码
      mvn clean package

      或者在idea中点击package打jar包:

    • jar包复制到MCP Client机器上 MCP Server打包后的目标文件在项目target文件夹中:

      把上面的这个.jar后缀的文件复制到MCP Client所在的机器;

    • 配置MCP Client

      yaml 复制代码
      spring:
        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方式MCPstdio方式就是通过java -jar命令实现的,稍微比SSE通信方式多几步。这两种方式各有各的优势,stdio特别适合操作本地机器上的资源,比如文件管理,浏览器自动化操作等,SSE适用分布式服务的调用。

问题点

  • SSE通信方式返回报错

    MCP Server方法被调用后,返回结果竟然报错了:

    vbscript 复制代码
    Failed to send message to session 012dcdf9-1654-4d08-873c-9d20ac112fbf: Cannot invoke "org.apache.catalina.connector.OutputBuffer.isBlocking()" because "this.ob" is null.

    这个问题我帮兄弟们找到答案了,原因就是Spring aiBUGgithub.com/spring-proj... github.com/modelcontex...

    对应的修复PR在这里: github.com/modelcontex...

    需要说明的是,目前1.0.0-M6还未Release,投入生产还需要等待。如果等不及的兄弟们也可以直接自己根据PR进行修复并替换掉对应的jar包。

结语

MCP技术对应应用层开发人员来说应该算是革命性的,从技术上来说,它不算很复杂,但是最核心的是它降低了应用层开发人员使用AI的门槛,这一点将会使得AI智能化在应用层迎来大爆发,真正地让普通大众也能普惠地使用AI,非常看好MCP未来的发展,建议开发同学也拥抱MCP

相关推荐
uzong3 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程4 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研4 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
一只爱撸猫的程序猿5 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋5 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack6 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt