You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过MCP服务器暴露现有Spring Boot REST接口并保留原有请求校验逻辑(基于Spring AI实现)

如何通过MCP服务器暴露现有Spring Boot REST接口并保留原有请求校验逻辑(基于Spring AI实现)

我完全懂你的痛点——手上跑着成熟的Spring Boot应用,一堆REST接口,想接入MCP但又不想一个个写@Tool重复造轮子,更不想多一层反向代理绕路,还得死死保住原来的过滤器、拦截器那套校验逻辑对吧?刚好我之前帮朋友处理过类似的场景,用Spring AI的MCP结合Spring Web的内部API就能搞定,不用额外加网络层,也不用改现有代码多少。

核心思路

咱们的核心目标是:让MCP接收的工具调用请求,直接复用现有Spring Web的完整处理链(过滤器→拦截器→控制器→异常处理器),而不是重新实现接口逻辑。具体来说,就是把MCP的工具调用参数转换成Spring Web的内部HTTP请求,让DispatcherServlet和Filter链原封不动地处理,这样所有原有校验逻辑都会自动执行,还不用走网络。

具体实现步骤

1. 引入Spring AI MCP依赖

首先确保你的Spring Boot项目里已经引入了Spring AI的MCP服务器starter,Maven依赖如下(版本要和你的Spring Boot版本兼容,比如Spring AI 1.0.x对应Spring Boot 3.2.x):

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-starter</artifactId>
    <!-- 替换成你用的稳定版本号 -->
    <version>1.0.0-M1</version>
</dependency>

2. 配置MCP服务器端口

为了避免和现有Spring Boot应用的HTTP端口冲突,在application.ymlapplication.properties里指定MCP服务器的端口:

spring:
  ai:
    mcp:
      server:
        port: 8081 # 选一个和你现有应用不冲突的端口

3. 实现通用MCP工具类

写一个通用的MCP工具,负责把MCP的工具调用请求转换成Spring Web的内部请求,调用现有Controller。这个工具会自动注册到MCP服务器,所有REST接口都能通过它调用:

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ai.tool.Tool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.FilterChainProxy;
import org.springframework.web.servlet.DispatcherServlet;

import java.io.IOException;
import java.util.Map;

@Component
public class GenericRestApiMcpTool implements Tool {

    private final DispatcherServlet dispatcherServlet;
    private final FilterChainProxy filterChainProxy;

    // 注入Spring的DispatcherServlet和Filter链代理(核心,确保走全流程)
    @Autowired
    public GenericRestApiMcpTool(DispatcherServlet dispatcherServlet, FilterChainProxy filterChainProxy) {
        this.dispatcherServlet = dispatcherServlet;
        this.filterChainProxy = filterChainProxy;
    }

    @Override
    public String getName() {
        // 这个是MCP调用时要指定的工具名称
        return "generic_rest_api_call";
    }

    @Override
    public String getDescription() {
        // 给模型看的工具描述,让模型知道怎么传参数
        return "调用现有Spring Boot应用中的REST接口,参数说明:\n" +
                "- method: HTTP请求方法(如GET/POST/PUT/DELETE)\n" +
                "- path: 接口完整路径(如/api/users/1,包含context-path的话要加上)\n" +
                "- params: URL查询参数(键值对格式)\n" +
                "- body: 请求体(JSON格式字符串,POST/PUT请求需要)";
    }

    @Override
    public Object call(Map<String, Object> parameters) {
        // 从MCP参数中提取HTTP请求信息
        String method = parameters.getOrDefault("method", "GET").toString().toUpperCase();
        String path = parameters.get("path").toString();
        Map<String, String> queryParams = (Map<String, String>) parameters.getOrDefault("params", Map.of());
        String requestBody = parameters.getOrDefault("body", "").toString();

        // 构造Spring Web的内部Mock请求
        MockHttpServletRequest mockRequest = new MockHttpServletRequest(method, path);
        // 设置URL查询参数
        queryParams.forEach(mockRequest::setParameter);
        // 设置请求体(如果是POST/PUT等方法)
        if (!requestBody.isEmpty()) {
            mockRequest.setContent(requestBody.getBytes());
            mockRequest.setContentType("application/json");
        }

        // 构造Mock响应对象,接收返回结果
        MockHttpServletResponse mockResponse = new MockHttpServletResponse();

        try {
            // 关键:先调用Filter链代理,再交给DispatcherServlet处理
            // 这样会完整执行所有自定义过滤器、Spring Security校验、HandlerInterceptor逻辑
            filterChainProxy.doFilter(mockRequest, mockResponse, new FilterChain() {
                @Override
                public void doFilter(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
                    dispatcherServlet.service(req, resp);
                }
            });

            // 把响应结果整理成模型能理解的格式返回
            return Map.of(
                    "status_code", mockResponse.getStatus(),
                    "response_headers", mockResponse.getHeaderNames().stream()
                            .collect(java.util.stream.Collectors.toMap(header -> header, mockResponse::getHeader)),
                    "response_body", mockResponse.getContentAsString()
            );
        } catch (ServletException | IOException e) {
            // 捕获处理过程中的异常,返回友好错误信息
            return Map.of(
                    "error", "接口调用失败",
                    "error_message", e.getMessage(),
                    "status_code", 500
            );
        }
    }
}

4. 启动测试

启动你的Spring Boot应用,MCP服务器会自动启动在你配置的端口(比如8081)。你可以用MCP客户端或者直接构造工具调用请求测试,比如:

{
  "tool_name": "generic_rest_api_call",
  "parameters": {
    "method": "GET",
    "path": "/api/users/1",
    "params": {"include_details": "true"},
    "body": ""
  }
}

这个请求会直接调用你现有的/api/users/1接口,所有过滤器、拦截器的校验逻辑都会原封不动执行,和正常HTTP请求完全一样。

关键细节与注意事项

  • 校验逻辑完全保留:因为咱们是直接调用Spring的Filter链和DispatcherServlet,所有自定义的FilterHandlerInterceptor、Spring Security权限校验、甚至Controller的@Valid参数校验都会自动执行,和正常HTTP请求没有任何区别。
  • 无额外网络开销:所有调用都是在同一个JVM内部完成的,没有反向代理的网络 hop,性能和直接调用Controller方法几乎一致。
  • 兼容现有配置:如果你的应用有server.servlet.context-path配置,记得在MCP调用的path参数里加上这个前缀(比如/my-app/api/users/1)。
  • 支持所有HTTP方法:不管是GET、POST、PUT还是DELETE,只要你的Controller支持,这个工具都能调用。
  • 异常处理一致:现有Controller的@ExceptionHandler逻辑会被触发,返回的错误响应和正常HTTP请求完全相同。

进阶:动态注册每个接口为独立MCP工具(可选)

如果你不想用通用工具,而是想让每个REST接口对应一个独立的MCP工具(比如模型调用时更直观),可以写一个ToolRegistrar,动态扫描所有Controller方法,自动注册对应的Tool。不过这个方案会比较复杂,需要处理路径参数、请求体映射等细节,一般来说通用工具已经足够满足大部分场景了。

要是你在实现过程中遇到问题,比如Filter链没触发或者参数映射不对,随时告诉我具体场景,我再给你调细节~

火山引擎 最新活动