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

相关推荐
南雨北斗7 分钟前
3.laravel使用create-project 直接创建项目和使用全局安装程序创建项目的区别
后端
jhonroxton20 分钟前
使用golang快速构建你的MCP Server
llm·mcp
瀚海澜生24 分钟前
链表系列进阶攻略(三):拆解 LRU 与分割链表,突破链表解题瓶颈
后端·算法
bobz96533 分钟前
supervisord 的使用
后端
大道无形我有型33 分钟前
📖 Spring 事务机制超详细讲解(哥们专属)
后端
Re27535 分钟前
springboot源码分析--自动配置流程
spring boot·后端
Piper蛋窝38 分钟前
Go 1.2 相比 Go1.1 有哪些值得注意的改动?
后端·go
努力的搬砖人.41 分钟前
java爬虫案例
java·经验分享·后端
海风极客1 小时前
一文搞懂JSON和HJSON
前端·后端·面试
南雨北斗1 小时前
2.单独下载和配置PHP环境
后端