一条命令搞定!存量 Spring REST 服务秒变 MCP 服务

一条命令搞定!存量 Spring REST 服务秒变 MCP 服务

TL;DR

在 AI 技术浪潮中,MCP 为服务集成带来了创新性思路,LLM 与 MCP 的组合更是为存量 API 服务注入新活力。

本文先阐述基于 Spring AI MCP 开发 MCP 服务的详细步骤,随后引入 OpenRewrite 框架及其 spring-rest-to-mcp 工具,实现 Spring REST 服务到 MCP 服务的自动化转换。

最后,借助示例项目,全方位展示从环境搭建、代码转换到任务编排执行的完整流程,助力开发者快速打通存量 Spring REST 服务对接 MCP 协议的通道,大幅提升服务集成的灵活性与智能化水平 。

背景

在上一篇 超越 API:MCP 如何成为 AI 时代的 “万能适配器”? 文章发布后,我一直思考一件事。如果将以往的 API 集成看作系统的硬链接,那么 LLM + MCP 无疑是系统的软链接。MCP 的诞生,使我们能在运行时动态连接不同服务,摆脱设计阶段的束缚,实现更为灵活、智能的服务集成模式。

由此可见,LLM + MCP 的组合,对存量 API 服务而言是重大利好(当然也利好 LLM/GenAI 应用)。借助 MCP 的声明式服务描述,LLM 能自动获取并理解服务能力,实现服务的智能编排与调用。存量 API 服务只需实现 MCP 声明式服务描述,就能被 LLM 自动编排和调用。

那么,如何将存量 API 转换为 MCP 服务呢?有没有便捷的方法,比如一条命令就能完成?要是不行,两条命令也行。

接下来,先看看如何基于 3 月发布的 Spring AI 1.0.0-SNAPSHOT 开发 MCP 服务。熟悉 Spring AI MCP 的读者,可直接跳过此部分,前往 OpenRewrite 章节。

Spring AI MCP

MCP Java SDK 为 MCP 提供了 Java 语言实现,支持通过同步和异步通信模式,与 AI 模型和工具进行标准化交互。Spring AI MCP 则在 Spring Boot 框架下,对 MCP Java SDK 进行了扩展,提供了 客户端服务器 启动器。

Client Starters:

  • spring-ai-starter-mcp-client - 核心启动器提供 STDIO 和基于 HTTP 的 SSE 支持
  • spring-ai-starter-mcp-client-webflux  基于 WebFlux 的 SSE 传输实现

Server Starters:

  • spring-ai-starter-mcp-server - 具有 STDIO 传输支持的核心服务器
  • spring-ai-starter-mcp-server-webmvc - 基于 Spring MVC 的 SSE 传输实现
  • spring-ai-starter-mcp-server-webflux - 基于 WebFlux 的 SSE 传输实现

下面我们以 MVC-based SSE 类型的 MCP 服务为例,介绍如何开发一个简单的 MCP 服务。

代码实现

Spring AI 提供的注解极大简化了代码编写流程,下面具体介绍开发步骤。在 spring-ai-starter-mcp-server-webmvc 包中,提供了以下功能支持:

  • 服务端 Tool 发生变更时,向客户端发送变更通知。
  • 根据服务类型,自动为 Tool 切换同步或异步规范。
  • 通过 Spring Beans 机制,为 Tool 自动生成规范。

1. 引入依赖

由于 Spring AI 当前处于 SNAPSHOT 阶段,需从特定快照仓库获取依赖。

<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
    <repository>
        <name>Central Portal Snapshots</name>
        <id>central-portal-snapshots</id>
        <url>https://central.sonatype.com/repository/maven-snapshots/</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

依赖可以通过 spring-ai-bom 引入依赖。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>  
        <groupId>org.springframework.ai</groupId>  
        <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>  
    </dependency>
</<dependencies>

也可以直接引用 1.0.0-SNAPSHOT 版本的 starter

<dependencies>
    <dependency>  
        <groupId>org.springframework.ai</groupId>  
        <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>  
        <version>1.0.0-SNAPSHOT</version>
    </dependency>
</<dependencies>

2. 编写 Tool 类

写一个简单的服务类提供两个 Too:一个返回静态结果,另一个通过请求内容返回动态的结果。这里用到了两个注解 @Tool@ToolParam

@Service
public class HelloService {

    @Tool(description = "say hello")
    public String hello() {
        return "hello, devops";
    }

    @Tool(description = "say hello to someone")
    public String helloTo(@ToolParam(description = "name of the guy you want to say hello to") String name) {
        return "Hello, " + name;
    }
}

3. 注册 Tool

通过定义 ToolCallbackProvider Bean 来注册上面编写的 Tool 类。

@Bean
ToolCallbackProvider toolCallbackProvider(HelloController helloController) {
    return MethodToolCallbackProvider.builder()
            .toolObjects( helloController)
            .build();
}

4. 服务配置

application.yml 加入如下的配置,nameversionsse-message-endpoint 可以根据需要自定义;type 这里选择 SYNC(还支持异步服务 ASYNC)。

spring:
  ai:
    mcp:
    server:
        name: webmvc-mcp-server
        version: 1.0.0
        type: SYNC
        sse-message-endpoint: /mcp/messages

5. 测试

使用 MCP 官方的调试工具 Inspector 进行测试。服务类型选择 SSE,地址使用 http://localhost:8080/sse 。通过 List Tools 查看服务的 Tool 列表,可选择其中任意一个进行测试。

6. 思考

通过几个简单的注解和配置,就能实现一个 MCP 服务。在实际开发中,复杂部分主要在于具体业务逻辑,如调用其他服务、访问数据库、缓存或文件系统等,这与编写普通 Spring Boot 服务并无二致。

回顾定义的 Tool 类,它本质上是通过 @Service 注解定义的普通 Bean,再使用 Spring AI MCP 的注解进行标注,其余工作由框架完成,包括 Tool 参数规范等。

  • @Tool:定义 Tool,描述其功能。
  • @ToolParam:定义 Tool 参数,并对参数进行描述。

这种 Tool Bean 的定义方式,与定义 Controller 类颇为相似,主要区别在于方法和参数说明。

@RestController
public class HelloController {

    /**
     * say hello
     *
     * @return hardcoded hello world
     */
    @GetMapping("/hi")
    public String hello() {
        return "Hello, world";
    }

    /**
     * say hello to some guy
     *
     * @param name name of the guy you want to say hello
     * @return hello message
     */
    @GetMapping("/hi/{name}")
    public String helloTo(@PathVariable("name") String name) {
        return "Hello, " + name;
    }
}

由此不禁思考,能否将现有 API 服务转换为 MCP 服务,且无需手动编写 Tool 类,通过简单命令就能完成?答案是肯定的,借助 OpenRewrite 就能实现这一目标。

OpenRewrite

OpenRewrite 是由 Moderne 公司开源的自动化重构框架。它的目标是通过一系列可组合的“配方”(官方术语 Recipes,可以理解为重构规则,为了更贴近官方文档的术语,将其翻译为配方。)在无需手动干预的情况下对代码进行有条理的结构重写。简言之,它不是简单的全局替换字符串工具,而是能基于 无损语义树(LST) 进行语义级别的代码修改。

LST 的特点是无损地保留了原始源代码的所有细节信息,这不仅包括代码的语法和语义,还包含了空格、换行、注释、格式化风格等。

此前,我发布过几篇关于 OpenRewrite 的学习笔记,因时间原因未能持续更新。今天这篇也算对之前学习的总结,后续我会继续分享近期的学习心得和笔记。

这里不展开介绍 OpenRewrite,感兴趣的读者可参考我之前的学习笔记:

配方 spring-rest-to-mcp

接下来进入今天的重点部分,我们将使用 OpenRewrite 将现有的 Spring REST 服务自动转换成 MCP 服务。我们需要编写 Recipe 配方实现如下功能:

  • 将 Spring Web 注释转换为 Spring AI MCP @Tool 注解
  • 添加必要的 MCP 配置和组件
  • 更新 Maven 依赖项

这些配方自动从 Controller 的 javadoc 中提取方法描述和参数描述,并将其转换为 MCP 的 @Tool@ToolParam 注解。

使用 OpenRewrite 开发的工具我已经发布到了 GitHub 仓库 spring-rest-to-mcp,欢迎大家下载体验。

目前工具的运行需要 Java 17 和 Maven 3.6+ 的环境,带转换的 API 需要 SpringBoot 3.x 版本并使用 Maven 作为构建工具。

该工具实现的效果

转换前的 Spring Web Controller:

@RestController
public class UserController {
    /**
     * Get all users
     *
     * @return list of users
     */
    @GetMapping("/users")
    public List<User> getUsers() {
        //Implementation
    }

    /**
     * Add a new user
     *
     * @param user user to add
     * @return success message
     */
    @PostMapping("/users")
    public String addUser(User user) {
        //Implementation
    }
}

转换后的 MCP Tool(同时兼容 REST):

@RestController
public class UserController {
    /**
     * Get all users
     *
     * @return list of users
     */
    @GetMapping("/users")
    @Tool(description = "Get all users")
    public List<User> getUsers() {
        //Implementation
    }

    /**
     * Add a new user
     *
     * @param user user to add
     * @return success message
     */
    @PostMapping("/users")
    @Tool(description = "Add a new user")
    public String addUser(@ToolParam(description = "user to add") User user) {
        //Implementation
    }
}

接下来,通过实际场景进行演示。

演示

环境准备

1. 编译 spring-rest-to-mcp 工具

git clone https://github.com/yourusername/web-to-mcp.git
cd web-to-mcp
#可以添加 -DskipTests 跳过测试
mvn clean install

2. 示例项目

克隆示例项目:

git clone https://github.com/addozhang/spring-boot-3-rest-api-sample.git
cd spring-boot-3-rest-api-sample

查看示例项目结构:

  • 这是一个标准的 Spring Boot 3 应用程序,带有 REST Controller
  • 包含 HTTP 方法(GET、POST)的典型 REST 端点
  • 包含正确的 JavaDoc 注释,这些注释将被转换为 MCP 工具描述

3. 代码转换

首先,运行 Maven 命令更新 POM 文件,添加所需的依赖项和库:

mvn org.openrewrite.maven:rewrite-maven-plugin:6.4.0:run \
  -Drewrite.activeRecipes=RewriteWebToMCP \
  -Drewrite.recipeArtifactCoordinates=com.atbug.rewrite:web-to-mcp:1.0-SNAPSHOT \
  -Drewrite.exportDatatables=true

然后,再次运行相同的命令以执行实际的代码转换:

mvn org.openrewrite.maven:rewrite-maven-plugin:6.4.0:run \
  -Drewrite.activeRecipes=RewriteWebToMCP \
  -Drewrite.recipeArtifactCoordinates=com.atbug.rewrite:web-to-mcp:1.0-SNAPSHOT \
  -Drewrite.exportDatatables=true

验证更改(转换后的代码,我提交到了 另一个分支):

  • 检查的控制器类中是否添加了 @Tool@ToolParam 注解
  • 查找的主应用程序类中的新 ToolCallbackProvider Bean

启动服务(服务端口为 8080):

mvn spring-boot:run

测试

前文已介绍 MCP Inspector 工具,本次测试将其配置到 LLM 应用中。我使用的是 VSCode + Cline + DeepSeek API。

1. 配置 MCP 服务

在 Cline 中配置 MCP 服务:

{
  "mcpServers": {
    "spring-ai-mcp-sample": {
      "autoApprove": [],
      "disabled": false,
      "timeout": 60,
      "url": "http://localhost:8080/sse",
      "transportType": "sse"
    }
  }
}

配置后会自动获取服务的 Tool 列表:

2. 编排任务

编排一项涉及多阶段操作,并需要调用多个 Tool 的任务。

先帮我查看一下用户列表,检查是否包含名为 Carson 的用户。如果没有,就添加一个新用户:Carson carson@gmail.com;再查询下列表查看新用户是否添加成功。最后向 Carson 打个招呼。

3. 任务执行

通过上述配置和操作,任务成功执行。

(转载本站文章请注明作者和出处乱世浮生,请勿用于任何商业用途)

comments powered by Disqus