Spring Boot服务如何改造成AI可调用的MCP工具

发布时间:2026/10/18 10:02:18
Spring Boot服务如何改造成AI可调用的MCP工具 1. 为什么要把Spring Boot服务改造成AI可调用的工具1.1 一个真实的需求场景手头有一套跑了两年多的Spring Boot订单系统接口稳定、日志齐全、权限体系也完善。某天团队想用AI助手做一个“智能客服”让AI能直接查订单状态、改收货地址、发起退款。第一反应是写个中间层把HTTP接口包装成AI能理解的格式但很快发现这条路走不通——每接一个新AI平台就要重写一遍适配层工具描述、参数Schema、调用协议全都不一样维护成本高得离谱。这个问题的本质是AI应用和业务服务之间缺少一个统一的“工具描述与调用协议”。传统做法是每个AI平台自己定义一套Function Calling格式业务方被迫为每个平台做适配。MCPModel Context Protocol要解决的就是这件事——它定义了一套标准协议让业务服务用统一的方式“声明”自己有哪些能力AI应用用统一的方式“发现”和“调用”这些能力。把Spring Boot服务变成MCP工具意味着你的订单查询、用户管理、支付接口可以被任何支持MCP的AI应用直接调用不需要为每个平台单独写适配代码。这对后端团队来说是把已有资产盘活的关键一步。1.2 MCP到底解决了什么问题用一句话概括MCP是AI应用和外部工具之间的“USB-C接口”。在MCP出现之前AI应用要调用外部能力通常有三种做法。第一种是硬编码把工具逻辑直接写在AI应用的代码里耦合度极高。第二种是自定义HTTP接口AI应用通过提示词告诉模型“你可以调用这个URL”但参数格式、错误处理、鉴权方式全靠约定换个模型就可能失效。第三种是各平台自己的插件体系比如某些AI平台有自己的工具注册机制但互不兼容。MCP的价值在于标准化。它规定了三件事工具如何描述名称、参数、返回值、工具如何被发现列表查询、工具如何被调用请求与响应格式。只要你的服务实现了MCP协议任何支持MCP的AI客户端都能直接接入不需要改一行AI侧的代码。对于Spring Boot开发者来说这意味着你不需要学习每个AI平台的插件开发规范只需要按照MCP的标准把服务暴露出来剩下的交给协议本身。1.3 适合哪些人参考这篇内容适合三类读者。第一类是后端开发者手上有现成的Spring Boot服务想快速接入AI能力但不想被某个AI平台绑定。第二类是AI应用开发者需要为自己的Agent寻找可靠的工具来源理解MCP服务端是怎么实现的有助于排查调用问题。第三类是技术负责人在评估“自研工具协议”还是“采用MCP标准”时需要了解落地成本和实际效果。前置知识方面你需要熟悉Spring Boot的基本开发Controller、Service、配置类了解JSON Schema的基本概念对AI应用的Function Calling机制有初步认知。不需要你懂大模型训练也不需要你深入理解MCP的底层传输细节跟着做就能跑通。2. MCP的核心概念与Spring Boot的映射关系2.1 MCP的三个核心角色MCP协议里定义了三个角色Host、Client、Server。Host是AI应用本身比如一个对话助手Client是Host内部负责与Server通信的模块Server就是提供工具的一方也就是我们要用Spring Boot实现的部分。这个划分很关键因为它决定了职责边界。Server只负责“声明能力”和“执行能力”不关心AI怎么决策、怎么组织对话。Host和Client负责发现工具、把工具描述传给模型、解析模型的调用意图、把调用请求转发给Server。这种分离让Server的实现可以非常纯粹——你只需要把业务逻辑包装成MCP规定的格式即可。在Spring Boot的语境下Server就是一个独立的Spring Boot应用或者是你现有应用中的一个模块。它对外暴露MCP协议规定的接口对内调用你已有的Service层。2.2 工具、资源与提示模板MCP Server可以暴露三种类型的能力Tools、Resources、Prompts。Tools是最常用的代表“可执行的操作”比如查询订单、创建用户、发送通知。每个Tool有名称、描述、输入参数的JSON Schema。AI应用会把Tool的描述传给模型模型决定是否调用、传什么参数。Resources代表“可读取的数据”比如文件内容、数据库记录、配置信息。Resources通常是只读的AI应用可以请求读取某个资源但不应该通过Resources修改状态。Prompts是预定义的提示模板Server可以提供一些常用的提示词片段供Host使用。实际项目中Prompts用得相对少Tools和Resources是重点。对于大多数Spring Boot服务来说你已有的Controller方法大致对应Tools已有的查询接口大致对应Resources。映射关系并不复杂关键是理解每个Tool的输入输出都要用JSON Schema描述清楚。2.3 传输层stdio与HTTPMCP支持多种传输方式最常用的是stdio和HTTP with SSE。stdio方式下MCP Server作为一个子进程启动通过标准输入输出与Client通信。这种方式适合本地工具部署简单不需要网络配置。但缺点是Server和Client必须在同一台机器上不适合分布式场景。HTTP with SSE方式下Server是一个HTTP服务Client通过HTTP请求发送调用通过Server-Sent Events接收流式响应。这种方式适合远程服务可以独立部署、独立扩缩容。对于Spring Boot服务来说HTTP方式是更自然的选择因为Spring Boot本身就是干这个的。选择哪种传输方式取决于你的部署形态。如果AI应用和Spring Boot服务在同一台机器上stdio更简单如果是微服务架构或者需要远程调用HTTP是唯一选择。后面的实操部分我会以HTTP方式为主因为它的适用场景更广。2.4 Spring Boot与MCP的对应关系表MCP概念Spring Boot对应物说明ToolController方法每个Tool对应一个可执行的操作Tool输入SchemaDTO类的字段定义用JSON Schema描述参数类型和约束Tool执行Service层方法调用实际的业务逻辑Resource查询接口/配置文件只读数据的暴露ServerSpring Boot应用承载所有Tool和ResourceClientAI应用内部模块不需要我们实现传输层HTTP端点Spring Boot的Web能力这张表的核心目的是帮你建立直觉你不需要从零学一套新东西MCP的概念在Spring Boot里都有对应物你要做的是把已有的东西“翻译”成MCP能理解的格式。3. 从零搭建一个MCP Server的完整实操3.1 项目初始化与依赖选择先创建一个标准的Spring Boot项目。我用的版本是Spring Boot 3.2.xJDK 17。依赖方面除了常规的spring-boot-starter-web还需要引入MCP的Java SDK。目前社区里有几个可选的实现我选的是官方维护的mcp-java-sdk版本用最新的稳定版。dependency groupIdio.modelcontextprotocol/groupId artifactIdmcp-server-spring-boot-starter/artifactId version0.10.0/version /dependency这个starter会自动配置MCP的HTTP端点、SSE通道、以及工具注册的基础设施。如果你不想用starter也可以手动引入核心包自己配置传输层但那样工作量会大很多除非你有特殊的定制需求否则建议直接用starter。注意MCP的Java SDK还在快速迭代中不同版本之间的API可能有变化。建议锁定一个版本不要频繁升级等稳定后再考虑迁移。项目结构上我建议单独建一个模块或者单独建一个应用不要和现有的业务应用混在一起。原因是MCP Server的职责是“暴露能力”它应该是一个薄层调用现有的Service而不是把业务逻辑重新写一遍。单独部署也方便你做权限控制和流量隔离。3.2 定义第一个Tool订单查询假设我们有一个订单服务已经有一个OrderService里面有个方法getOrderById(Long orderId)返回订单详情。现在要把它暴露成MCP Tool。第一步是定义Tool的输入参数。MCP要求用JSON Schema描述参数但在Java里我们通常用注解或者配置类来生成Schema。我选的方式是定义一个DTO用Jackson的注解描述字段SDK会自动生成Schema。public class GetOrderInput { JsonProperty(required true, value order_id) JsonPropertyDescription(订单的唯一标识纯数字) private Long orderId; // getter and setter }这里有个细节字段名用下划线风格order_id而不是驼峰因为JSON Schema的惯例是下划线而且很多AI模型对下划线命名的参数理解更好。JsonPropertyDescription的内容会直接出现在Schema的description字段里模型会根据这个描述来判断什么时候该调用这个Tool、该传什么参数。所以描述要写得具体不要写“订单ID”这种模糊的话要写“订单的唯一标识纯数字”。第二步是定义Tool的执行逻辑。SDK提供了Tool注解标注在方法上即可。Component public class OrderTools { private final OrderService orderService; public OrderTools(OrderService orderService) { this.orderService orderService; } Tool(name get_order, description 根据订单ID查询订单详情返回订单状态、金额、收货地址等信息) public OrderDetail getOrder(GetOrderInput input) { return orderService.getOrderById(input.getOrderId()); } }Tool注解的name是工具的唯一标识AI应用通过这个名字来调用。description是给模型看的要写清楚这个工具能做什么、返回什么。方法参数是输入DTO返回值是输出对象SDK会自动把返回值序列化成JSON。第三步是注册Tool。如果用starterComponent标注的类会被自动扫描Tool方法会被自动注册。不需要额外的配置。启动应用后访问MCP的tools/list端点应该能看到get_order这个工具以及它的输入Schema。这一步验证通过说明基础链路是通的。3.3 参数校验与错误处理Tool的参数校验不能只靠Schema。Schema只能描述类型和必填但业务规则比如订单ID必须大于0、状态必须是枚举值需要在代码里校验。Tool(name get_order, description ...) public OrderDetail getOrder(GetOrderInput input) { if (input.getOrderId() null || input.getOrderId() 0) { throw new McpToolException(订单ID必须为正整数); } OrderDetail detail orderService.getOrderById(input.getOrderId()); if (detail null) { throw new McpToolException(订单不存在); } return detail; }McpToolException是SDK提供的异常类抛出后会被转换成MCP协议规定的错误响应AI应用能收到结构化的错误信息而不是一个500错误页面。这一点很重要因为模型需要根据错误信息决定下一步怎么做——是重试、还是告诉用户“订单不存在”、还是换一个工具。错误信息要写得对模型友好。不要写“NullPointerException at line 42”要写“订单不存在请确认订单ID是否正确”。模型看到后者才能给出有意义的回复。实操心得我习惯在Tool方法里做三层校验——第一层是Schema层面的类型校验第二层是业务规则校验比如ID范围、状态合法性第三层是权限校验当前用户是否有权访问这个订单。前两层在Tool方法里做第三层建议放在Service层因为权限逻辑通常和业务逻辑耦合更紧。3.4 把Resource也暴露出来除了ToolsResources也值得暴露。比如“当前用户的订单列表”可以作为一个ResourceAI应用可以读取它但不应该通过它修改数据。Resource(uri order://current-user/orders, name 当前用户订单列表, description 返回当前登录用户的所有订单摘要) public ListOrderSummary getCurrentUserOrders() { return orderService.getOrdersByCurrentUser(); }Resource的URI是自定义的用order://这种scheme是为了和HTTP URL区分开。AI应用通过resources/read端点读取Resource内容返回的是JSON数据。Resources和Tools的区别在于语义Tools是“做一件事”Resources是“读一份数据”。模型在决策时如果只是需要信息应该优先读Resource如果需要执行操作才调用Tool。这个区分有助于模型做出更合理的决策。4. 让AI应用真正调用起来的联调过程4.1 用MCP Inspector做本地验证在接入真正的AI应用之前先用MCP Inspector做验证。Inspector是一个命令行工具可以连接MCP Server列出所有Tools和Resources手动调用并查看返回结果。npx modelcontextprotocol/inspector http://localhost:8080/mcp启动后Inspector会显示一个交互界面左侧是Tools列表右侧是调用面板。选中get_order输入{order_id: 123}点击调用应该能看到订单详情返回。如果报错Inspector会显示错误信息方便定位问题。这一步的价值在于它把“MCP协议层”和“AI应用层”分开了。如果Inspector能调通说明Server实现没问题问题出在AI应用侧如果Inspector调不通说明Server的Tool定义或传输层配置有问题。这种分层排查能省很多时间。4.2 接入AI应用以对话助手为例假设你有一个支持MCP的对话助手配置方式通常是在配置文件里加上MCP Server的地址。{ mcpServers: { order-service: { url: http://localhost:8080/mcp, transport: http } } }配置完成后助手启动时会自动连接MCP Server拉取Tools列表把每个Tool的描述注入到模型的系统提示里。当用户说“帮我查一下订单123的状态”模型会识别出需要调用get_order生成调用请求助手把请求转发给MCP Server拿到结果后再交给模型生成回复。这个过程中你不需要写任何AI侧的代码。模型怎么决策、怎么组织语言是AI应用的事你只需要保证Tool的描述准确、参数Schema清晰、返回结果结构化。4.3 联调中常见的坑第一个坑是Tool描述太模糊。比如描述写“查询订单”模型可能不知道什么时候该用这个Tool或者传错参数。描述要写清楚这个Tool做什么、输入是什么、输出是什么、有什么限制。我通常会把描述写成一段完整的话而不是几个关键词。第二个坑是参数命名不一致。Schema里写order_id但代码里用orderIdSDK如果没做自动转换就会导致参数绑定失败。建议统一用下划线风格或者在DTO上明确标注JsonProperty。第三个坑是返回结果太大。如果Tool返回一个包含几百个字段的对象模型处理起来会很吃力而且容易超出上下文限制。建议返回精简后的DTO只包含模型需要的信息。比如订单详情里模型可能只需要订单号、状态、金额、收货地址不需要创建时间、更新时间、操作人等内部字段。第四个坑是错误信息不结构化。如果Tool抛出未捕获的异常MCP协议会返回一个通用的错误模型无法理解具体原因。建议所有业务异常都转换成McpToolException并附带清晰的错误描述。避坑技巧在开发阶段把MCP Server的日志级别调到DEBUG可以看到每个请求的完整内容和响应。联调时先看日志再看Inspector最后才怀疑AI应用。大部分问题都出在Server侧。5. 生产环境部署与性能考量5.1 部署形态选择MCP Server的部署形态取决于你的AI应用在哪里。如果AI应用是本地工具比如开发者自己的对话助手Server可以部署在本地用stdio方式启动。如果AI应用是云端服务Server需要部署在云端用HTTP方式暴露。对于Spring Boot服务我推荐HTTP方式因为Spring Boot的Web能力成熟部署、监控、扩缩容都有现成方案。stdio方式虽然简单但进程管理、日志收集、故障恢复都要自己搞不适合生产环境。部署时要注意MCP Server应该和业务服务分开部署不要混在同一个进程里。原因是MCP Server的流量特征和业务服务不同——AI应用的调用可能突发性很强而且单个请求的耗时可能较长因为模型决策需要时间。分开部署可以独立扩缩容避免互相影响。5.2 超时与重试策略MCP协议本身没有规定超时时间但AI应用通常会有自己的超时设置。作为Server你需要确保Tool的执行时间在合理范围内。如果一个Tool需要调用多个下游服务总耗时可能超过AI应用的超时阈值导致调用失败。我的做法是单个Tool的执行时间控制在3秒以内。如果某个操作天然耗时较长比如批量导出不要把它做成同步Tool而是做成“提交任务”“查询任务状态”两个Tool。第一个Tool立即返回任务ID第二个Tool查询任务进度。这样每个Tool的响应都很快模型也能给用户及时的反馈。重试策略方面MCP协议支持Client侧重试但Server侧要做好幂等。如果一个Tool被重复调用比如网络抖动导致Client重试Server应该保证不会产生重复的业务影响。对于查询类Tool天然幂等对于写入类Tool需要在Service层做幂等控制比如用请求ID去重。5.3 权限与安全MCP Server暴露的是业务能力权限控制不能省。基本的做法是在HTTP层做认证比如用API Key或者OAuth。MCP协议本身不规定认证方式但HTTP传输层可以复用标准的认证机制。Configuration public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth - auth .requestMatchers(/mcp/**).authenticated() .anyRequest().permitAll() ) .httpBasic(Customizer.withDefaults()); return http.build(); } }除了认证还要做授权。不同的AI应用可能只能访问部分Tool。比如客服助手只能查订单不能改订单运营助手可以改订单但不能查用户隐私信息。这种细粒度的权限控制建议在Tool方法里做根据当前认证的身份判断是否有权调用。安全提醒MCP Server暴露的Tool可能包含敏感操作比如退款、删除数据。一定要确保只有经过认证和授权的AI应用才能调用这些Tool。不要因为“反正是内部服务”就省略认证。5.4 监控与日志生产环境必须要有监控。MCP Server的监控指标包括每个Tool的调用次数、成功率、平均耗时、错误分布。这些指标可以用Micrometer Prometheus Grafana的标准方案采集和展示。日志方面每个Tool调用都应该记录调用时间、调用方身份、Tool名称、输入参数脱敏后、返回结果摘要、耗时、是否成功。这些日志在排查问题时非常有用尤其是当模型决策异常时可以通过日志回溯模型到底调用了什么、传了什么参数。Around(annotation(tool)) public Object logToolCall(ProceedingJoinPoint joinPoint, Tool tool) throws Throwable { long start System.currentTimeMillis(); try { Object result joinPoint.proceed(); log.info(Tool {} called, args{}, duration{}ms, tool.name(), sanitize(joinPoint.getArgs()), System.currentTimeMillis() - start); return result; } catch (Exception e) { log.error(Tool {} failed, args{}, error{}, tool.name(), sanitize(joinPoint.getArgs()), e.getMessage()); throw e; } }这个切面可以统一处理日志避免在每个Tool方法里写重复的日志代码。注意输入参数要脱敏不要把密码、token之类的敏感信息记到日志里。6. 常见问题排查速查表问题现象可能原因排查方法解决方案Inspector连接失败传输层配置错误检查端口、路径、协议类型确认Server启动日志中的MCP端点地址Tools列表为空Tool未被扫描到检查Component和Tool注解确认包扫描路径包含Tool类调用返回参数绑定失败参数名不一致对比Schema和DTO字段名统一用JsonProperty标注模型不调用Tool描述太模糊查看Schema中的description重写描述写清楚用途和参数含义调用超时Tool执行太慢查看Tool耗时日志拆分长耗时操作或优化下游调用返回结果模型无法理解返回结构太复杂检查返回JSON的字段数精简DTO只返回必要字段重复调用导致数据异常缺少幂等控制检查Service层是否有去重逻辑引入请求ID做幂等处理认证失败认证配置不匹配检查Client和Server的认证方式统一认证协议确认密钥正确这张表覆盖了我实际项目中遇到的大部分问题。排查时建议按顺序来先确认连接是否正常再确认Tool是否注册再确认参数是否匹配最后才怀疑模型决策。大部分问题都在前三步。独家技巧如果模型不调用Tool除了检查描述还可以在系统提示里显式告诉模型“你可以使用get_order工具查询订单”。有时候模型需要一点提示才会想到用工具。另外Tool的name要简洁明了不要用get_order_by_id_v2这种名字模型对简洁的名字理解更好。7. 从单个Tool到工具集的演进思路7.1 工具分组与命名规范当Tool数量超过10个时命名规范就很重要了。我习惯用“领域_动作”的格式比如order_get、order_update、user_query、user_disable。这样模型能快速理解Tool的归属和用途。如果Tool特别多可以考虑分组暴露。比如订单相关的Tool放在一个MCP Server里用户相关的放在另一个。AI应用可以同时连接多个MCP Server按需调用。这种拆分方式也便于权限控制——客服助手只连接订单Server不连接用户Server。7.2 组合Tool与工作流有些业务操作需要多个步骤比如“退款”需要先查订单、再校验状态、再发起退款、再通知用户。如果把这些步骤都暴露成独立的Tool模型需要自己编排调用顺序容易出错。更好的做法是提供一个组合Tool内部完成所有步骤对外只暴露一个refund_order。模型只需要调用一次传订单ID和退款原因剩下的交给Server处理。这样既减少了模型的决策负担也保证了业务逻辑的一致性。当然组合Tool的粒度要把握好。太细会导致模型编排困难太粗会失去灵活性。我的经验是如果一个操作在业务上是一个原子操作就做成一个Tool。退款就是一个原子操作不应该拆成四个Tool。7.3 版本管理与兼容性Tool的Schema可能会变化比如新增参数、修改参数类型。这时候要考虑兼容性。如果直接改Schema已经接入的AI应用可能会调用失败。我的做法是新增参数用可选不删旧参数不改参数类型。如果必须做破坏性变更就新增一个Tool名字加版本后缀比如order_get_v2旧Tool保留一段时间等所有AI应用迁移后再下线。MCP协议本身没有版本协商机制所以兼容性要靠Server侧自己保证。这一点在多人协作的项目里尤其重要改Tool Schema之前一定要通知所有接入方。8. 我踩过的几个印象深刻的坑第一个坑是Tool返回值序列化失败。有个Tool返回了一个包含LocalDateTime的对象Jackson默认序列化成数组格式模型看不懂。后来加了JsonFormat注解指定日期格式为ISO 8601字符串问题才解决。教训是所有返回给模型的字段都要考虑序列化后的可读性。第二个坑是Tool描述里的示例参数被模型当成真实参数。我在描述里写“例如order_id123”结果模型真的传了123而不是用户实际要查的订单ID。后来把示例改成“订单ID纯数字”去掉了具体数值问题消失。教训是描述里不要放具体的示例值只描述格式和含义。第三个坑是并发调用导致的数据竞争。两个AI应用同时调用同一个写入Tool导致数据不一致。后来在Service层加了乐观锁并在Tool层面做了幂等控制问题解决。教训是MCP Server是无状态的但业务逻辑可能是有状态的并发控制不能省。第四个坑是日志里记录了敏感信息。有个Tool的输入参数包含用户手机号日志切面直接把参数打印出来了导致手机号泄露到日志系统。后来加了脱敏逻辑对手机号、身份证号、银行卡号等字段做掩码处理。教训是日志脱敏要从第一天就做不要等出事再补。这些坑的共同点是它们都不是MCP协议本身的问题而是把业务逻辑暴露给AI时产生的工程问题。MCP解决了协议标准化的问题但工程上的细节还是要靠开发者自己把控。9. 后续可以扩展的方向如果你已经跑通了单个Tool的接入下一步可以考虑几个方向。第一个方向是动态Tool注册。目前Tool是在代码里静态定义的如果业务方想在不重启服务的情况下新增Tool可以做一个管理界面把Tool的定义存到数据库运行时动态加载。这对多租户场景特别有用不同租户可以有不同的Tool集。第二个方向是Tool调用链追踪。当一个业务操作涉及多个Tool调用时追踪整个调用链有助于排查问题。可以在MCP协议之上加一层Trace ID贯穿所有Tool调用日志和监控都带上这个ID。第三个方向是Tool性能优化。对于高频调用的Tool可以加缓存、加连接池、加异步处理。MCP协议本身不限制这些优化Server侧可以自由发挥。第四个方向是多协议适配。虽然MCP是标准但现实中可能有些AI应用不支持MCP。可以在Server侧加一层适配同时暴露MCP和自定义HTTP接口让不同AI应用各取所需。适配层的成本不高但能扩大服务的适用范围。我个人在实际操作中的体会是MCP的价值不在于协议本身有多复杂而在于它让“业务服务”和“AI应用”之间的边界变得清晰。Server只管暴露能力Client只管调用能力中间的协议由MCP保证。这种清晰的边界让后端团队和AI团队可以并行开发互不阻塞。如果你手上有现成的Spring Boot服务花半天时间把它接入MCP后续的AI能力扩展会顺畅很多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询