☰
从0到1吃透MCP:把Spring Boot服务变成所有AI应用的通用工具
2026/10/22 6:24:40 网站建设 项目流程

1. 为什么所有AI应用都在抢同一个接口:MCP的价值与定位

最近大模型应用圈子有一个特别高频的词——MCP(Model Context Protocol,模型上下文协议)。如果你关注AI工具链,一定会发现类似“支持MCP”“接入MCP”这样的说明越来越常见。简单说,MCP是一种标准化协议,它规定了AI应用(比如各类智能助手、Agent)如何发现并调用外部工具和数据源。放在传统软件开发里类比,它像是给AI世界定了一个统一的“USB接口标准”,以前每个设备都要各自定制连接线,现在大家都在往同一个标准接口上靠。

对后端开发来说,这件事意味着一个重要的转向:一个普通的Spring Boot业务服务,如果按照MCP规范暴露能力,就能让多个AI应用直接调用,而不用为每个AI平台单独维护一套对接逻辑。你辛辛苦苦写的用户查询、订单处理、数据分析功能,不再是只能给人用、给自家前端用的“死代码”,而是可以被AI应用直接编排和调用的“活工具”。

这篇文章围绕“从0到1吃透MCP,把Spring Boot服务变成所有AI应用的工具”这个目标展开,我会从协议理解、依赖选型、工程搭建、工具实现、联调部署几个层面逐步拆解,整个流程我都实际跑过,也会把踩过的坑一并写出来。适合正在做AI应用集成的后端工程师、准备让老系统快速接入AI能力的开发团队,以及想搞懂MCP到底是什么的技术负责人阅读。读完你至少能回答三个问题:MCP服务端要暴露什么?Spring Boot怎么最省力地实现?AI客户端怎么连上来用?

2. 先搞懂MCP的底层逻辑:资源、工具、提示词与协议传输

2.1 一次完整的MCP调用发生的三件事

理解MCP不用被文档里一堆概念吓住。一次MCP调用,本质上是AI应用(Host)通过MCP客户端,连接到一个MCP服务端,然后做三件事:读资源、调工具、取提示词模板。

  • 资源(Resource):服务端暴露业务数据或文件内容,类似REST里的GET接口,AI可以读取但不能修改。
  • 工具(Tool):服务端暴露可执行函数,AI根据用户意图自动选择并传入参数调用,类似RPC或POST接口。
  • 提示词(Prompt):服务端预置的提示词模板,让AI按固定套路组织请求,特别适合“先想清楚再执行”的复杂场景。

这三类能力通过MCP协议定义好的消息格式进行传输,服务端不关心AI应用具体是哪一家,AI应用也不需要知道服务端背后的业务逻辑,只要双方都遵守MCP规范就能对接。

2.2 JSON-RPC的传输骨架与Spring Boot的天然契合点

MCP目前的底层传输基于JSON-RPC 2.0。客户端发请求,服务端回响应,也支持服务端主动推送通知。传输方式有两种主流选择:一种是基于标准输入输出的stdio,适合本地进程间通信;另一种是基于HTTP的Streamable HTTP传输,适合远程服务。Spring Boot服务天然是HTTP服务,所以实现MCP服务端时几乎都采用Streamable HTTP方式,对外暴露一个接口供AI客户端连接。

Spring Boot之所以和MCP搭配得这么顺,除了HTTP基础之外,还有几个实际原因:一是Spring的依赖注入让工具实现类可以复用已有的业务Service;二是参数校验、异常处理、日志追踪这些Web开发基建可以直接平移;三是MCP Java SDK对Spring Boot提供了专门的starter模块,减少了大量协议层面的样板代码。

举个例子说明架构关系:假设你有一个订单查询服务,传统方式是你给前端写一个/api/order/{id}接口,AI应用如果要调用它,你需要单独为AI写适配层,同时自己处理大模型怎么知道这个接口存在、怎么生成参数、怎么解析返回结果的问题。而采用MCP之后,你只要把订单查询逻辑写成一个MCP工具方法,AI客户端就能通过协议自动发现这个方法及其参数结构,由大模型决定何时调用以及如何传参。整个过程,AI应用除了把MCP客户端接好,不需要为你的订单服务写任何定制代码。

2.3 技术选型为什么首选MCP Java SDK

MCP官方提供了Java SDK,而且对Spring Boot的支持相当成熟。在开始写之前,我对比过几种方案:直接基于JSON-RPC手写协议、用Spring的WebSocket自定义一套、用官方Java SDK。最终选了官方SDK,理由很直接:协议兼容有保障,官方持续维护;工具方法注册和参数Schema生成是自动完成的;对Streamable HTTP传输的支持开箱即用。自己实现JSON-RPC服务端可行,但需要处理初始化握手、协议版本协商、能力声明、消息路由等细节,容易在边缘情况翻车。SDK把这些封装好了,省下大量时间。

从项目启动到让一个AI客户端调用你的第一个MCP工具,我实际统计过时间,如果依赖已经配好,核心代码大概控制在几十行到一百行左右。这个效率是实现业务价值的重点——时间不应该花在协议纠缠上,而应该花在把业务能力定义清楚。

3. 搭建工程:用Spring Boot Starter创建MCP服务端骨架

3.1 依赖引入与版本搭配建议

创建一个普通的Spring Boot项目,Java版本建议17及以上。核心依赖只需要一个官方Starter即可,名称是spring-boot-starter-mcp-server。我搭建时的关键依赖如下:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mcp-server</artifactId> </dependency>

其中spring-boot-starter-mcp-server会自动引入MCP Java SDK以及对应的Spring Boot自动配置。这个Starter专门为Spring Boot服务端设计的,原理是基于Spring的自动装配机制,把传输层、协议层和验证层都配置好,开发者只需要专注于编写工具方法。

3.2 关键配置项:一个端口与一个路径

配置文件里,核心配置并不复杂,下面这一组是我验证过的最小可用配置:

spring.main.web-application-type=servlet spring.mcp.server.name=order-service-mcp spring.mcp.server.version=1.0.0

如果你的服务里Web依赖和WebFlux依赖同时存在,需要显式声明使用Servlet还是Reactive,这个我后面讲坑的时候会专门提。默认情况下,MCP服务端的Streamable HTTP端点路径是/mcp,如果不想用默认路径,可以通过spring.mcp.server.endpoint-path自定义。AI客户端连接时,需要配置这个完整地址,形如http://localhost:8080/mcp。

提示:如果你有网关层或者其他统一入口,确保/mcp这个路径的POST请求不会被拦截。客户端发起连接时会有初始化请求,必须在没有额外鉴权的情况下先完成协议握手,这个我在后面的常见问题里详细展开。

3.3 最简单的验证:什么都不写也能跑

这里有个容易忽略的细节:只要引入了Starter并启动了Spring Boot项目,服务端其实已经暴露了MCP端点。即使一个自定义工具都没有写,AI客户端也能完成连接、拿到协议能力列表。这就好比饭店先把门面和厨房装修好了,菜单可以慢慢加。我第一次测试时,项目还没写任何工具方法,就用一个MCP客户端连上去看了一下能力列表,确认端点活着,才继续加工具。

所以搭建阶段的核心验证标准是:启动应用,访问/mcp端点,能应答即可。此时不需要写代码,也不需要复杂的配置。

4. 核心工具实现:把Service层方法变成AI能调用的Function

4.1 一个注解搞定工具注册

MCP Java SDK提供了一套非常简洁的注解体系,工具方法的注册主要靠@Tool注解。先看一个实际例子,比如一个订单查询服务,原本Service层可能有这样一个方法:

public OrderInfo getOrderById(String orderId) { // 查询数据库,返回订单信息 }

要把它变成MCP工具,做法是新建一个工具类,注入这个Service,然后在方法上标注@Tool和@ToolParam,下面给出完整示例:

@Component public class OrderMcpTool { private final OrderService orderService; public OrderMcpTool(OrderService orderService) { this.orderService = orderService; } @Tool(description = "根据订单ID查询订单详情,包括订单状态、金额、收货地址等信息") public OrderInfo getOrderById( @ToolParam(description = "订单编号,格式为ORD开头加数字,例如ORD2025001") String orderId) { return orderService.getOrderById(orderId); } }

只要工具类被Spring容器管理(有@Component或者通过配置类注册为Bean),SDK启动时会自动扫描带@Tool注解的方法,并基于方法名、参数名、参数类型和注解上的描述信息,自动生成开放API规范的Json Schema。AI客户端拿到这个Schema之后,才知道这个方法接收什么参数、参数是什么格式、方法有什么用。

4.2 复杂对象的返回与入参处理

有读者可能担心:“我的返回对象比较复杂,能不能直接返回?”答案是能。SDK会把方法返回值序列化成JSON文本传给客户端,所以返回对象只要能被Spring的ObjectMapper正确序列化就行。实际项目中常见的做法是返回一个统一结果对象,比如:

public class ApiResult<T> { private int code; private String message; private T data; }

这样设计有几个好处:第一,AI客户端能从code字段判断调用是否成功;第二,message字段可以作为错误信息直接给大模型,让它知道失败原因;第三,把业务错误包装成返回值而不是抛出异常,避免MCP协议层把它当成传输异常处理。大模型是文本推理模型,你给它的信息越结构化、越明确,它的后续决策就越准确。

入参也可以传复杂对象,@ToolParam可以直接作用于对象的字段,SDK会把整体参数Schema生成出来。前端AI应用会按Schema引导大模型填入这些字段,你可以给每个字段加描述,特别是字段取值范围和格式约束。比如价格字段,最好描述成“单位为分,整数”,避免AI传小数进来。

4.3 能力补充:资源与提示词模板的快速实现

除了工具之外,MCP还支持资源暴露和提示词模板。如果只想先跑通工具链路,这部分可以先不写。但实际项目中,同时把资源和提示词配好,AI应用的能力调度会更灵活。

资源暴露用@Resource注解,代码如下:

@Component public class OrderResourceProvider { @Resource(uri = "server://orders/{orderId}", name = "订单数据资源") public String getOrderResource(@ResourcePath("orderId") String orderId) { return "订单" + orderId + "的最新状态: 已发货"; } }

提示词模板用@Prompt注解,适合定义标准任务流程。比如“生成订单周报”的提示词模板,函数式定义如下:

@Component public class ReportPromptProvider { @Prompt(name = "generate_order_report", description = "生成订单周报的提示词模板") public String generateOrderReport(@PromptParam("summaryType") String summaryType) { return "请根据以下订单数据生成" + summaryType + "周报,包含订单量、销售额、客单价等维度"; } }

这三个维度一起提供了,AI客户端便可以在一次会话中动态选择用哪个工具查数据、读哪个资源获取上下文、按哪个模板生成结构化请求,整个使用体验才开始接近“一个完整AI工作流”而不是“单点功能调用”。

5. 部署与联调:同一个服务的多种接入路径

5.1 本地联调的正确姿势

本地联调时,我推荐先在Spring Boot应用启动后,用一个独立的MCP客户端测试工具,而不是直接上AI客户端配置。这样可以把问题隔离在“服务端是否正常”和“客户端如何配置”两个层面。直接上AI应用测试当然也可以,但一旦出错,你很难区分是协议问题、网络问题还是大模型本身不调用工具的问题。

我用过两种方式验证:一种是在本地代码里写一个简单的测试客户端程序,调用/mcp端点,初始化连接后获取工具列表,再执行一次工具调用;另一种是使用支持MCP的桌面AI客户端,在配置界面填入MCP服务地址,观察它是否能拉取到工具列表。两种方式配合,前者用来排查协议细节,后者用来验证真实使用体验。

5.2 让AI客户端识别你的服务

以支持MCP配置的AI客户端为例,通常需要在配置里填两样东西:服务名称和服务URL。URL就是http://你的服务地址/mcp。配置完成后,客户端会主动发起MCP初始化请求,拉取服务能力列表。你可以在服务端日志里看到对应的连接请求记录,比如Initialized或ListTools等关键字。看到这类日志,就说明握手已经成功,工具列表已经被客户端拉走。

配置完成之后,大模型是否主动调用服务端的工具,取决于两件事:第一,工具的description是否清晰;第二,用户提问是否和工具描述匹配。比如工具描述是“根据订单ID查询订单详情”,用户问“ORD2025001这个单子现在什么状态”,大模型就会在候选工具列表中匹配到这个工具并触发调用。

5.3 接入多个AI客户端的代价为零

一个MCP服务端可以被多个AI客户端同时连接,这一点是MCP架构最大的杠杆。同一个订单查询工具,在客户端A配置一次,在客户端B也配置一次,两边能获得完全一致的调用能力。你不需要为每个AI客户端单独开发对接模块,只需要在各自的配置页里填同一个MCP服务地址。也就是说,你的后端能力每多实现一个MCP工具,所有已接入的AI应用就同时多了一个可调用的工具。

这意味着开发策略可以很明确:优先把核心业务能力一个一个地变成MCP工具,工具的复杂度可以逐步增加,但每个工具一旦注册,所有支持MCP的客户端立即受益。这正是Spring Boot+MCP组合最值钱的地方——原来的Web API是给人用的,现在同一套业务逻辑又被变成了给AI用的工具层,而两者互不干扰。

6. 常见问题与排查技巧实录

6.1 启动报错:Servlet与Reactive冲突

这是Spring Boot项目里最常遇见的第一个坑。如果你的项目同时引入了spring-boot-starter-web和spring-boot-starter-webflux,启动时可能会看到类似“Spring MVC found on classpath, which is incompatible with Spring WebFlux”的报错。MCP的starter同时兼容Servlet和Reactive两种Web运行时,但你的项目里如果两者都出现,Spring无法自动判定用哪一个。

解决方案是在配置文件中显式指定:

spring.main.web-application-type=servlet

或者反过来指定为reactive,取决于你现有项目的Web栈。我实际遇到过一次原因是某个内部依赖传递引入了WebFlux,排查了很久才发现是传递依赖导致的。建议排查时直接看Maven依赖树,确认你的项目里到底有哪些Web框架的包。

6.2 AI客户端连不上:超时与鉴权问题

AI客户端连接MCP服务超时,通常有三个原因。第一个原因是网络不通,/mcp端点只能从内网访问,而客户端在公网。第二个原因是超时时间设置太短,MCP初始化过程要进行协议握手和工具列表拉取,如果你的工具数量很多或者服务端首次加载较慢,客户端默认的超时时间可能不够。第三个原因是网关或过滤器把请求拦截了。

鉴权问题要特别注意:MCP的Streamable HTTP传输,客户端发起连接时最初几次请求是协议层的初始化请求,此时客户端可能还没有携带业务认证信息。如果服务端在过滤器层强制要求所有请求都必须带Token,会导致初始化失败、工具列表拉取失败。实际项目中,我建议对/mcp路径的认证策略单独放开,或者在协议完成初始化后再通过自定义认证机制进行校验。安全性上,可以靠网络隔离、IP白名单和独立的认证机制来保障。

6.3 工具方法不显示:注解扫描的三大陷阱

工具方法没有被AI客户端识别,表现是服务端日志没有工具列表拉取记录,或者客户端工具列表是空的。我遇到过三种情况。第一种是工具类没有交给Spring管理,比如忘了加@Component。第二种是工具类所在的包不在Spring Boot启动类扫描范围内,Spring Boot默认只扫描启动类所在包及其子包,如果你的工具类放在别的包,需要手动加@ComponentScan。第三种是@Tool参数没有加@ToolParam描述,有些客户端实现对于缺少描述的工具会降低展示优先级,但一般不会完全不显示。最保险的排查方法是用MCP客户端直接拉取一次工具列表,返回结果里如果能看到方法名和参数结构,就说明服务端没问题,问题出在AI客户端配置上。

6.4 工具调用报错:大模型传参不听话

大模型根据用户问题自动决定参数值时,偶尔会传进不符合格式预期的参数。比如你定义了一个字段amount必须是整数,AI却传入了“一百”这种中文文本。这时服务端如果做了严格类型转换,就会报反序列化异常。解决方案有两个:一个是在参数描述里写明严格格式,例如“纯数字字符串,不要带任何中文”;另一个是在工具方法内部做防御性校验,参数不合法时不要抛异常,而是返回一个结构化的错误信息对象,让大模型自己“看到”错误并尝试修正。我强烈建议采用第二种方案,因为大模型具备上下文推理能力,你给它返回“参数格式错误:amount期望整数,实际收到: 一百”,它往往能自动纠错重试。用异常中断调用反而会让整个任务链路直接失败。

6.5 日志排查:MCP调用链路看哪里

在联调阶段,我习惯在服务端开启DEBUG级别的MCP相关日志,主要观察三个关键点:初始化握手是否成功、工具列表是否被拉取、工具方法是否被调用。这些信息可以快速判断问题到底出在协议层还是业务层。实际项目里,我还在工具方法入口统一加了@AuditLog之类的切面日志,记录每一次AI调用的入参和出参摘要。无论后续出问题,还是想分析AI都在用哪些能力,这些日志都能派上大用场。

7. 把Spring Boot老项目改造成MCP服务端的基本思路

7.1 不需要重写业务逻辑

不少团队担心接入MCP要改业务代码,这个担心是多余的。MCP工具层本质上是业务Service的一层薄薄的“AI适配器”,把已有的Service方法通过注解暴露出去即可。我们实际改造的一个老订单系统,核心代码只是新增了一个mcp包,里面放工具类、资源提供者、提示词模板,完全没有动原来的Controller和Service。

7.2 增量暴露比一步到位更稳

改造老项目时,我推荐增量策略,先挑两三个价值最高的查询类方法做成MCP工具,给AI客户端连上跑通,再逐步增加工具。不要试图一次性把所有业务方法都暴露成工具。暴露的工具越多,大模型在“选择哪个工具”时反而越容易困惑,工具筛选的准确率会下降。工具描述写得越精细、边界划得越清楚,AI的选择就越可靠。

7.3 为AI场景单独建模:工具层不是简单的方法映射

这里要给一个比较容易被忽略的建议:直接把Service方法暴露成工具是最快的路径,但未必是最好用的路径。AI调用工具和人调用接口的习惯不同,AI是“基于意图做选择”,它理解的是自然语言描述和你给的参数约束,因此工具层的输入输出最好针对AI场景重新设计。比如一个内部管理系统里“按用户ID查详情”的Service方法,直接暴露给AI时,参数名如果是userId加内部工号格式,AI很容易传错格式。此时工具层可以定义一个新的方法,描述为“按用户单号或手机号查询用户基本信息”,入参只保留一个keyword字段,然后在工具方法内部自己解析是工号还是手机号。这样对AI友好得多,大模型按语义匹配参数时也不需要理解你内部ID体系。

8. 个人经验总结:MCP落地的最佳路径与实验心得

从协议理解到实际跑通一个Spring Boot MCP服务端,我个人的体会是:MCP的入门门槛被SDK大大降低了,真正考验工程能力的地方不在协议本身,而在于如何设计出对AI友好、边界清晰、语义明确的工具层。

如果你准备在项目里落地MCP,我建议按照下面几条路径推进。第一,选一个内部价值明确、参数简单的查询方法作为第一个工具,先用最小路径跑通端到端调用,建立团队的信心。第二,单独准备一个MCP客户端测试环境,不要一上来就改生产环境的AI应用配置。第三,每增加一个工具,都要请业务方一起评审它的描述文本,确保描述与实际功能一致,描述含糊的工具很容易让大模型产生误调用。第四,日志和监控要第一时间跟上,AI工具的调用频率可能比人调用API更加密集和随机,没有完整的调用审计,上线后很难排查问题。

最后分享一个我一直在用的小技巧:工具实现尽量返回“结果+补充说明”的结构化文本,不要只返回一个孤立的数据。比如订单查询工具,除了返回订单基本信息之外,还可以补充一句“该订单已超过预计发货时间,建议联系客服核实物流状态”。这个额外的自然语言提示,能显著帮助大模型在复杂的多步骤任务中做出更合理的判断。MCP的工具不只是给人用、给AI用的机械API,它更像你和AI协作时递给对方的“带有上下文提示的便签”,写得越用心,协作效果越好。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询