ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot集成MCP协议:快速构建AI Agent工具箱

Spring Boot集成MCP协议:快速构建AI Agent工具箱

1. 项目概述:当Spring Boot遇见AI Agent工具箱

最近在折腾AI应用开发,特别是想把一些本地工具和API能力封装成AI Agent可以调用的“技能”。相信很多Java后端开发者都遇到过类似的场景:你手头有一堆用Spring Boot写的服务,比如用户管理、订单处理、数据报表生成,现在想把这些能力开放给大语言模型(LLM),让它能像调用函数一样来使用。传统的做法可能是为每个功能写一套REST API,然后让Agent通过复杂的提示词去理解和调用,过程繁琐且容易出错。

直到我发现了Model Context Protocol,也就是MCP。简单来说,MCP是一个标准协议,它定义了一套AI应用(如ChatGPT、Claude Desktop)与外部工具、数据源进行安全、结构化交互的规范。你可以把它想象成AI世界的“USB标准”——只要你的工具实现了MCP Server,就能被支持MCP的AI客户端(称为MCP Client)即插即用,无需为每个工具单独编写适配代码。

而Forge,作为一个流行的全栈Web框架,其MCP Server插件项目,正是为了解决“如何快速将Spring Boot应用转变为MCP Server”这个痛点。它不是一个运行时工具,而是一个代码生成和脚手架插件。其核心价值在于:通过极简的配置,自动为你生成一个符合MCP规范的Spring Boot Starter项目骨架,让你能专注于业务工具(Resources)和操作(Tools)的实现,而无需关心MCP协议底层的序列化、通信和生命周期管理等复杂细节。

这行“神奇的配置”可能长这样:在项目的forge.toml或构建脚本中声明一个依赖或插件。但它的背后,是一整套将Spring Boot的依赖注入、自动配置与MCP协议模型无缝衔接的巧妙设计。对于Java开发者而言,这意味着可以用自己最熟悉的Spring生态(如@Service,@RestController的理念)来开发AI Agent的工具,极大地降低了认知负担和开发门槛。接下来,我们就深入源码,看看这“一行配置”背后,究竟是如何把Spring Boot后台变成AI Agent工具箱的。

2. 核心架构与设计思想拆解

要理解Forge MCP Server插件,首先要抛开“插件就是一段可执行代码”的固有印象。在这个上下文中,它更像是一个项目模板生成器约定优于配置的实践者。其设计目标很明确:让开发者通过最少的配置,获得一个功能完整、结构清晰、可直接扩展的MCP Server项目。

2.1 MCP协议核心模型与Spring Boot的映射

MCP协议定义了几个核心概念,Forge插件的工作就是将这些概念“翻译”成Spring Boot开发者熟悉的模式。

  1. Server(服务器):即MCP Server本身,负责管理生命周期和分发请求。在插件生成的项目中,这对应着一个Spring Boot应用的主类。插件会配置好必要的网络层(通常是基于SSE或WebSocket),等待MCP Client的连接。
  2. Resources(资源):代表可读的数据源,例如数据库表、文件列表、系统状态信息。在Spring Boot语境下,一个Resource通常映射为一个@Component,其中包含一个方法,该方法返回的数据结构会被自动序列化为MCP协议定义的Resource格式(如textimage)。
  3. Tools(工具):代表可执行的操作,例如执行一个查询、调用一个API、运行一个脚本。这是AI Agent交互的核心。在生成的项目中,一个Tool会映射为一个@Component,其中包含一个用特定注解(可能是插件自定义的,如@McpTool)标记的方法。该方法的方法签名(参数名、类型)和JavaDoc注释会被用于自动生成Tool的Schema(JSON Schema),告知AI这个工具怎么用、需要什么参数。
  4. Prompts(提示词模板):可复用的提示词片段。虽然MCP协议支持,但在初期工具集成场景中使用频率相对Resources和Tools较低。插件可能为其预留了扩展点。

设计思想:插件采用了“注解驱动”和“自动扫描”的经典Spring哲学。开发者只需要按照约定(比如在某个包下创建类,使用特定的注解),插件在启动时就会自动扫描、注册这些组件为MCP的Resources或Tools。这避免了手动编写繁琐的注册代码,实现了声明式开发。

2.2 插件源码结构初探

假设我们克隆了forge-mcp-server插件的源码仓库,通常会看到类似如下的目录结构:

forge-mcp-server-plugin/ ├── pom.xml 或 build.gradle ├── src/main/java/com/example/forge/mcp/ │ ├── annotation/ # 核心注解定义 │ │ ├── McpResource.java │ │ └── McpTool.java │ ├── core/ │ │ ├── server/ # MCP服务器核心实现 │ │ │ ├── McpServerProperties.java # 配置类 │ │ │ └── SimpleMcpServer.java # 服务器启动与通信管理 │ │ └── model/ # MCP协议模型POJO(Resource, Tool, Prompt的定义) │ ├── spring/ │ │ ├── boot/ │ │ │ ├── autoconfigure/ │ │ │ │ └── McpServerAutoConfiguration.java # Spring Boot自动配置类 │ │ │ └── McpServerSpringBootStarter.java # Starter入口 │ │ └── context/ │ │ └── McpResourceToolRegistrar.java # 注解扫描与Bean注册器 │ └── template/ # 项目模板文件(Archetype) │ ├── pom.xml.vm │ ├── src/main/java/Application.java.vm │ └── src/main/resources/application.yml.vm └── README.md

这个结构清晰地揭示了插件的双重身份:

  • 作为库(Library):提供annotationcorespring包下的运行时类,供生成的项目代码依赖和调用。
  • 作为生成器(Generator)template目录包含了用于生成新项目的模板文件,这些是Velocity或Freemarker模板,在用户执行“一行配置”命令时被渲染并输出到目标目录。

2.3 “一行配置”背后的魔法

用户感知的“一行配置”,在Forge中可能是在项目根目录的forge.toml文件中添加:

[template] mcp-server = { version = "0.1.0" }

或者在命令行执行forge new mcp-server --name my-agent-tools

其内部执行流程如下:

  1. 命令解析:Forge CLI 解析到new mcp-server指令。
  2. 模板定位:CLI 根据mcp-server标识,在本地或远程仓库中找到forge-mcp-server-plugintemplate/目录。
  3. 变量渲染:CLI 读取模板文件(.vm后缀),并将用户输入的参数(如项目名my-agent-tools)作为上下文变量,渲染生成最终的文件内容。例如,Application.java.vm中的${{name}}会被替换为MyAgentTools
  4. 文件生成:将渲染后的内容写入到用户指定的新项目目录中,形成完整的、可立即导入IDE的Spring Boot项目。
  5. 依赖注入:生成项目的pom.xmlbuild.gradle中已经预置了对forge-mcp-server-spring-boot-starter的依赖。

至此,一个包含了MCP Server基础框架、示例配置、甚至可能有一两个示例Tool和Resource的Spring Boot项目就创建完成了。用户接下来的工作,就是从“脚手架搭建”转向“业务工具开发”。

注意:这里容易产生一个误解,即“插件在运行时起作用”。实际上,这个插件的主要作用在项目创建阶段。运行时起作用的是你项目中所引入的starter依赖包。理解这一点对后续的源码调试和自定义扩展至关重要。

3. 核心模块源码深度解析

让我们把焦点从项目生成转移到运行时,深入看看那些让Spring Boot Bean“变身”为MCP Tool和Resource的核心模块。

3.1 注解定义:声明即注册

@McpTool@McpResource是两个最关键的注解。它们的定义通常非常精简,主要作用是充当“标记”和“元数据载体”。

// 示例:McpTool注解定义 @Target(ElementType.METHOD) // 标注在方法上 @Retention(RetentionPolicy.RUNTIME) // 运行时保留 public @interface McpTool { /** * 工具的唯一名称,在MCP协议中标识此工具。 */ String name(); /** * 工具的描述,用于告知AI此工具的用途。良好的描述至关重要。 */ String description(); /** * 输入参数的JSON Schema定义。可以是一个字符串,也可以指向一个类。 * 如果为空,插件可能会尝试从方法参数自动推断。 */ String inputSchema() default ""; }

设计精妙之处:注解只定义了最核心的元数据(名称、描述)。复杂的参数Schema,既允许开发者通过inputSchema属性手动指定(对于复杂结构),也支持自动推断。自动推断会分析被注解方法的参数列表,利用Jackson等库的能力,将Java类型(如String,Integer,@RequestBody MyDto)转换为标准的JSON Schema。这极大地简化了开发。

3.2 自动配置与Bean后处理

这是连接Spring世界和MCP世界的桥梁。McpServerAutoConfiguration类是Spring Boot自动配置的核心。

@Configuration(proxyBeanMethods = false) @ConditionalOnClass(McpServer.class) // 当McpServer类在类路径中存在时,此配置才生效 @EnableConfigurationProperties(McpServerProperties.class) // 使配置属性生效 public class McpServerAutoConfiguration { @Bean @ConditionalOnMissingBean public McpServer mcpServer(McpServerProperties properties, List<Resource> resources, List<Tool> tools) { // 注入所有被扫描到的Resource和Tool Bean return new SimpleMcpServer(properties, resources, tools); } @Bean public static McpResourceToolRegistrar mcpResourceToolRegistrar() { // 这是一个BeanFactoryPostProcessor,在Bean定义加载后、实例化前执行 return new McpResourceToolRegistrar(); } }

关键角色McpResourceToolRegistrar实现了BeanFactoryPostProcessor接口。它会在Spring容器刷新早期执行,扫描所有Bean的定义。

public class McpResourceToolRegistrar implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { String[] beanNames = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); String beanClassName = beanDefinition.getBeanClassName(); // 使用ASM或反射加载类,避免过早实例化Bean Class<?> beanClass = ClassUtils.resolveClassName(beanClassName, ...); // 扫描类中所有带有 @McpTool 注解的方法 for (Method method : beanClass.getDeclaredMethods()) { McpTool toolAnnotation = method.getAnnotation(McpTool.class); if (toolAnnotation != null) { // 核心:为这个方法创建一个特殊的Tool Bean定义 BeanDefinition toolBeanDef = new RootBeanDefinition(ToolFactoryBean.class); toolBeanDef.getPropertyValues().add("targetBeanName", beanName); toolBeanDef.getPropertyValues().add("targetMethod", method); toolBeanDef.getPropertyValues().add("annotation", toolAnnotation); // 将这个新的Bean定义注册到容器,名称可能是基于方法名生成的 String toolBeanName = ...; ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(toolBeanName, toolBeanDef); } } // 类似地处理 @McpResource } } }

这个过程实现了“升华”:它将一个普通的Spring Bean中的某个方法,“包装”并注册成了一个独立的、符合MCP协议的Tool类型Bean。后续当McpServerBean被创建时,Spring会自动将所有类型为ToolResource的Bean注入进去。

3.3 协议适配层与服务器核心

SimpleMcpServer是协议处理的核心。它需要处理来自MCP Client(如Claude Desktop)的SSE连接,解析JSON-RPC格式的请求,路由到对应的Tool或Resource执行,再将结果封装回JSON-RPC响应。

public class SimpleMcpServer implements McpServer, ApplicationListener<ApplicationReadyEvent> { private final List<Tool> tools; private final ServerSentEventSseHandler sseHandler; private final ObjectMapper objectMapper; // Jackson用于序列化 @Override public void onApplicationEvent(ApplicationReadyEvent event) { // Spring Boot应用启动完成后,启动MCP Server的网络服务 startSseServer(); } private void startSseServer() { // 通常使用一个轻量级HTTP服务器(如Jetty或Netty)或复用Spring Web的端点 // 创建一个 `/sse` 端点,处理Client的连接 } // 处理来自Client的调用请求 private void handleCallToolRequest(JsonRpcRequest request) { String toolName = request.getParams().get("name").asText(); Tool targetTool = tools.stream().filter(t -> t.name().equals(toolName)).findFirst().orElseThrow(); // 1. 参数转换:将JSON-RPC请求中的参数,根据Tool的Schema反序列化为Java对象 Object[] args = convertParams(request.getParams(), targetTool.inputSchema()); // 2. 方法调用:通过反射调用底层Spring Bean的方法 // 这里需要获取ToolFactoryBean中保存的targetBean和targetMethod Object result = invokeTargetMethod(targetTool, args); // 3. 结果封装:将Java结果对象序列化为JSON-RPC响应 sendJsonRpcResponse(request.getId(), result); } }

难点与技巧

  • 参数转换:这是最复杂的部分之一。需要将动态的JSON参数,安全、准确地映射到Java方法的强类型参数上。通常会利用Jackson的JsonNodeJavaType系统,结合方法参数的泛型信息进行精细化的反序列化。
  • 异常处理:Tool执行可能抛出业务异常。MCP协议有标准的错误响应格式。服务器核心需要捕获所有异常,将其转换为协议错误,避免服务器崩溃,并给Client清晰的错误信息。
  • 异步支持:如果被注解的方法返回CompletableFutureMono/Flux(响应式),服务器核心需要能够处理异步结果,保持SSE连接直到异步操作完成。

4. 从零到一:使用插件创建并开发一个MCP Tool

理论分析之后,我们来一次实战。假设我们要开发一个“天气查询”Tool。

第一步:创建项目在命令行执行:forge new mcp-server --name weather-agent-tools。这会生成一个标准项目。

第二步:分析生成的项目结构

weather-agent-tools/ ├── pom.xml ├── src/main/java/com/example/weather/ │ ├── Application.java │ └── mcp/ │ ├── tool/ │ │ └── ExampleTool.java // 插件生成的示例 │ └── resource/ │ └── ExampleResource.java // 插件生成的示例 └── src/main/resources/ └── application.yml

Application.java是标准的Spring Boot启动类。application.yml里可能已经有了MCP Server的端口配置(例如mcp.server.port: 8081)。

第三步:实现WeatherTool我们删除示例,创建自己的工具。在src/main/java/com/example/weather/mcp/tool/下创建WeatherTool.java

package com.example.weather.mcp.tool; import org.springframework.stereotype.Component; import com.example.weather.mcp.annotation.McpTool; // 插件提供的注解 @Component // 首先它是一个Spring Bean public class WeatherTool { // 假设我们有一个简单的天气服务 private final SimpleWeatherService weatherService; public WeatherTool(SimpleWeatherService weatherService) { this.weatherService = weatherService; } @McpTool( name = "get_current_weather", description = "根据城市名称查询当前天气情况,包括温度、天气状况和湿度。" ) public WeatherResult getCurrentWeather(String cityName) { // 参数 cityName 会被自动映射 if (cityName == null || cityName.trim().isEmpty()) { throw new IllegalArgumentException("城市名称不能为空"); } // 调用业务服务 return weatherService.fetchCurrentWeather(cityName); } // 定义返回的数据结构 public static class WeatherResult { private String city; private double temperature; // 摄氏度 private String condition; // 如“晴朗”、“多云” private int humidity; // 湿度百分比 // ... getters and setters } }

第四步:运行与测试

  1. 启动Spring Boot应用:mvn spring-boot:run
  2. 应用启动后,MCP Server会在配置的端口(如8081)启动SSE服务。
  3. 使用一个MCP Client进行测试。例如,使用一个简单的Node.js测试脚本,或者配置Claude Desktop的claude_desktop_config.json来连接你的本地Server。

Claude Desktop 配置示例 (macOS):

{ "mcpServers": { "my-weather-tools": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-openweathermap" // 这里只是示例,实际需要指向你的Server ] } } }

对于本地开发的Spring Boot Server,你可能需要编写一个轻量级的桥接脚本,因为Claude Desktop默认期望通过命令行启动Server。一种常见做法是写一个Node.js脚本,通过HTTP连接到你的Spring Boot SSE端点。

5. 高级特性与自定义扩展指南

基础功能跑通后,你可能会需要更高级的特性。插件通常提供了扩展点。

5.1 处理复杂输入参数

上面的例子参数是简单的String。如果工具需要接收一个复杂的JSON对象呢?

方案一:使用Map或JsonNode(灵活但类型不安全)

@McpTool(name = "complex_tool", description = "...") public String handleComplex(@RequestBody JsonNode params) { String name = params.get("user").get("name").asText(); int age = params.get("user").get("age").asInt(); // ... }

方案二:定义明确的DTO类(推荐,类型安全,文档清晰)

public class QueryDto { private User user; private Filter filter; // ... getters/setters public static class User { private String name; private int age; } public static class Filter { private String type; private Date from; } } @McpTool(name = "complex_tool", description = "...") public String handleComplex(@RequestBody QueryDto query) { // 直接使用query.getUser().getName(),清晰安全 }

插件(通过底层的Jackson)会自动将传入的JSON反序列化为QueryDto对象。为了让AI更好地理解这个结构,你可以在@McpToolinputSchema属性中提供详细的JSON Schema字符串,或者依赖插件的自动推断(如果DTO结构清晰,Jackson的序列化信息通常足够生成一个不错的Schema)。

5.2 集成现有Spring Bean与事务管理

这是Spring Boot集成最大的优势之一。你的MCP Tool类本身就是一个@Component,可以像其他Spring Bean一样使用@Autowired注入任何服务层、数据访问层(如@Repository)的Bean。

@Component public class OrderTool { private final OrderService orderService; private final PaymentService paymentService; public OrderTool(OrderService orderService, PaymentService paymentService) { this.orderService = orderService; this.paymentService = paymentService; } @McpTool(name = "create_order", description = "创建新订单") @Transactional // 可以正常使用Spring的事务管理! public OrderResult createOrder(OrderCreateRequest request) { Order order = orderService.create(request); paymentService.processPayment(order); return convertToResult(order); } }

这意味着你的AI工具可以无缝融入现有的业务逻辑和事务边界,保障数据一致性。

5.3 自定义MCP Server配置与传输层

插件生成的application.yml中可能已经有了一些配置:

mcp: server: port: 8081 path: /sse # 是否启用,默认为true enabled: true

你可以通过实现或配置来改变传输层。默认可能是SSE,但MCP协议也支持Stdio(标准输入输出)和WebSocket。如果需要更改,你可能需要:

  1. 查看McpServerProperties配置类,看是否有相关配置项。
  2. 如果没有,你可能需要自己创建一个McpServerBean,覆盖自动配置提供的那个,使用不同的传输层实现。

自定义Server Bean示例

@Configuration public class MyMcpConfig { @Bean public McpServer myMcpServer(McpServerProperties properties, List<Tool> tools, List<Resource> resources) { // 使用WebSocket传输层,而不是默认的SSE Transport transport = new WebSocketTransport(properties.getPort()); return new SimpleMcpServer(properties, tools, resources, transport); } }

6. 常见问题、调试技巧与性能考量

在实际开发和运维中,你肯定会遇到各种问题。以下是一些常见场景和解决思路。

6.1 问题排查清单

问题现象可能原因排查步骤
Spring Boot应用启动正常,但AI Client无法连接。1. MCP Server未启动。
2. 端口被占用或防火墙阻止。
3. Client配置的command/args错误。
1. 检查日志,确认McpServerAutoConfiguration生效,SimpleMcpServerBean已创建。
2. 使用netstat -an | grep <端口号>查看端口监听状态。
3. 用curl测试SSE端点:curl -N http://localhost:8081/sse
Client连接成功,但看不到自定义的Tool。1. Tool类未被Spring扫描到。
2.@McpTool注解使用有误。
3. Tool的Bean注册失败。
1. 确保Tool类在@SpringBootApplication主类所在包或其子包下,或有@ComponentScan指定。
2. 检查注解是否加在方法上,namedescription是否为空。
3. 在McpResourceToolRegistrar中加日志,或调试查看Spring容器中类型为Tool的Bean列表。
调用Tool时参数解析失败。1. JSON参数与Java方法参数类型不匹配。
2. 复杂参数缺少无参构造器或Setter。
3. 参数中有枚举类型,传入了不匹配的值。
1. 在handleCallToolRequestconvertParams方法处打断点,查看原始JSON和转换目标。
2. 确保DTO类有默认构造器和getter/setter。
3. 枚举类型考虑使用@JsonCreator注解来定制反序列化。
Tool方法抛出异常,Client收到内部错误。1. Tool方法内未处理异常。
2. MCP Server的全局异常处理器未正确工作。
1. 在Tool方法内进行健壮的参数校验和业务异常捕获。
2. 检查SimpleMcpServerhandleCallToolRequest方法,确保所有Throwable都被捕获并转换为JsonRpcError响应。
性能问题:Tool调用响应慢。1. Tool方法本身是慢操作(如网络IO、复杂计算)。
2. 反射调用开销。
3. 序列化/反序列化开销大。
1. 考虑将Tool方法改为异步(返回CompletableFuture)。
2. 对于高频调用的Tool,可以考虑在McpResourceToolRegistrar中缓存Method对象。
3. 检查DTO对象是否过于复杂,简化数据结构。

6.2 调试技巧

  1. 开启详细日志:在application.yml中设置插件或相关包的日志级别为DEBUG
    logging: level: com.example.weather.mcp: DEBUG org.springframework.context: DEBUG # 查看Bean注册过程
  2. 单元测试隔离:为你的Tool类编写Spring Boot Test。这样可以在不启动完整MCP Server的情况下,验证Tool方法的业务逻辑和参数绑定是否正确。
    @SpringBootTest class WeatherToolTest { @Autowired WeatherTool weatherTool; @Test void testGetCurrentWeather() { WeatherTool.WeatherResult result = weatherTool.getCurrentWeather("Beijing"); assertNotNull(result); } }
  3. 模拟Client请求:使用Postman或编写简单的HTTP脚本直接向你的SSE端点发送JSON-RPC格式的请求,这样可以绕过Client,直接测试Server。

6.3 安全与生产就绪考量

  • 认证与授权:默认的MCP Server可能没有内置认证。在生产环境中,你需要确保只有受信的Client可以连接。可以在SSE端点前配置一个Spring Security过滤器,或者使用MCP协议未来可能支持的认证扩展。
  • 输入验证与净化:永远不要相信来自AI Client的输入。即使在Tool方法内部,也要对参数进行严格的验证、过滤和转义,防止注入攻击。
  • 资源限制:为MCP Server设置合理的超时时间、请求体大小限制和并发连接数,防止资源耗尽。
  • 监控与指标:将MCP Server的调用次数、成功率、延迟等指标集成到你的应用监控系统(如Micrometer + Prometheus)中。

Forge MCP Server插件提供了一条将Spring Boot生态能力快速赋予AI Agent的捷径。它通过巧妙的代码生成和运行时注解处理,把复杂的协议细节封装起来,让开发者回归到熟悉的Spring编程模型。虽然目前它可能更侧重于快速启动和原型开发,但在理解了其核心机制后,你完全可以根据业务需求对其进行定制和增强,构建出强大、稳定、可扩展的AI Agent工具箱。

返回列表