如何通过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.yml或application.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,所有自定义的
Filter、HandlerInterceptor、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链没触发或者参数映射不对,随时告诉我具体场景,我再给你调细节~




