news 2026/10/23 23:32:26

Spring AI 2.0 MCP实战:如何把现有Spring Boot业务服务变成AI可调用工具?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AI 2.0 MCP实战:如何把现有Spring Boot业务服务变成AI可调用工具?

目录

前言

1. 先搞清楚:Tool Calling和MCP不是一回事

Tool Calling解决什么?

MCP解决什么?

2. 今天要做一个什么Demo?

3. 第一步:创建MCP Server

4. 第二步:把现有OrderService包装成MCP Tool

5. 再暴露一个库存Tool

6. 为什么我建议增加Adapter,而不是直接改Service?

7. Tool Description千万不要随便写

8. 第三步:创建MCP Client

9. 第四步:把MCP Tool交给ChatClient

10. 到这里,MCP到底帮我们做了什么?

直接Tool Calling

MCP

11. REST API已经有了,还有必要做MCP吗?

12. 一个Agent可以连接多个MCP Server吗?

13. MCP并不意味着“所有Service都暴露出去”

14. MCP、Tool Calling、Agent之间到底是什么关系?

Tool Calling

MCP

Agent

15. 总结


前言

很多 Java 开发者第一次接触 MCP,会觉得它是一个全新的 AI 技术体系。

但如果你已经做了很多年 Spring Boot,其实完全可以换一个角度理解。

假设公司现在已经有一套订单系统:

OrderController ↓ OrderService ↓ OrderRepository ↓ MySQL

以前调用它的是:

前端 第三方系统 其他微服务

现在我们想增加一个 AI 助手,让用户可以直接问:

帮我查询订单 A1001 现在是什么状态?

甚至:

查一下订单 A1001,如果还没发货,再帮我看看商品 SKU1001 还有多少库存。

问题来了:

大模型怎么调用我们已经存在的 Java 业务能力?

难道为了 AI 再重写一套订单服务?

当然没必要。

MCP 真正有价值的地方之一,就是可以把现有业务能力包装成一种 AI 客户端能够发现和调用的标准能力。

最终链路可以变成:

用户 ↓ AI应用 ↓ LLM ↓ 发现需要查询订单 ↓ MCP Client ↓ MCP Server ↓ OrderService ↓ 数据库

所以这一篇不先讲复杂的 MCP 协议细节。

我们直接解决一个问题:

如何使用 Spring AI 2.0,把现有 Spring Boot Service 变成 AI 可以调用的 MCP Tool?


1. 先搞清楚:Tool Calling和MCP不是一回事

在写代码之前,这两个概念一定要先分清。

很多刚开始学习的人会把:

Tool Calling MCP

混在一起。

实际上它们解决的问题并不完全相同。

Tool Calling解决什么?

Tool Calling 解决的是:

模型如何表达“我现在需要调用一个工具”。

例如用户问:

查询订单 A1001。

模型发现自己不知道实时订单状态,于是返回一个 Tool Call:

{ "name": "queryOrder", "arguments": { "orderNo": "A1001" } }

注意:

模型只是说:

我要调用queryOrder。

真正执行 Java 方法的,依然是我们的应用系统。

所以 Tool Calling 的基本流程是:

User ↓ LLM ↓ 决定调用queryOrder ↓ Application执行Tool ↓ 返回Tool Result ↓ LLM生成最终答案

MCP解决什么?

当系统越来越复杂以后,问题来了。

今天订单系统自己定义一套 Tool:

queryOrder

明天库存系统又定义:

queryInventory

后天 CRM、工单、知识库都要给 AI 用。

如果每个 AI 应用都自己重新接一遍,就会变成:

AI应用A → 订单接口 AI应用A → 库存接口 AI应用A → CRM AI应用B → 订单接口 AI应用B → 库存接口 AI应用B → CRM

耦合越来越重。

MCP——Model Context Protocol——解决的一个核心问题,就是:

用统一协议把 Tool、Resource、Prompt 等能力暴露给 AI Client。

所以可以简单理解成:

Tool Calling 解决: 模型什么时候调用工具 MCP 解决: 这些工具如何被标准化暴露、发现和调用

两者不是竞争关系。

而是经常组合使用。


2. 今天要做一个什么Demo?

我们模拟一个最常见的企业场景。

现有系统已经有:

订单服务 OrderService 库存服务 InventoryService

里面已经存在业务代码:

@Service public class OrderService { public OrderInfo queryOrder(String orderNo) { // 实际项目中这里查询数据库 return new OrderInfo( orderNo, "PAID", "SKU1001", 2 ); } }

库存服务:

@Service public class InventoryService { public InventoryInfo queryInventory(String skuCode) { return new InventoryInfo( skuCode, 128 ); } }

以前这些 Service 可能被 Controller 调用:

HTTP Request ↓ Controller ↓ Service

现在我们的目标是:

AI ↓ MCP ↓ Service

重点是:

原有业务逻辑不重写,只增加一层 MCP Adapter。

最终用户可以说:

查询订单 A1001 的状态,并告诉我这个订单对应商品还有多少库存。

然后模型自己完成:

queryOrder(A1001) ↓ 得到SKU1001 ↓ queryInventory(SKU1001) ↓ 生成自然语言回答

这就是一个最基础的 Agent + MCP 使用场景。


3. 第一步:创建MCP Server

首先创建一个新的 Spring Boot 服务。

如果我们使用传统 Spring MVC,可以加入:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId> </dependency>

Spring AI 2.0 已经支持:

STDIO SSE Streamable HTTP Stateless

对于一个独立部署的 Spring Boot MCP Server,我更建议直接使用:

Streamable HTTP

配置:

server: port: 8081 spring: ai: mcp: server: name: business-mcp-server version: 1.0.0 type: SYNC protocol: STREAMABLE streamable-http: mcp-endpoint: /mcp

这样一个基础 MCP Server 就已经准备好了。


4. 第二步:把现有OrderService包装成MCP Tool

这里我不建议直接把 AI 注解全部塞进原来的业务 Service。

例如原来:

@Service public class OrderService { public OrderInfo queryOrder(String orderNo) { // 业务逻辑 } }

最好继续保持它只负责业务。

然后单独增加:

OrderMcpTools

作为 MCP Adapter。

代码:

@Component public class OrderMcpTools { private final OrderService orderService; public OrderMcpTools(OrderService orderService) { this.orderService = orderService; } @McpTool( name = "query_order", description = "根据订单编号查询订单当前状态、商品SKU和购买数量", generateOutputSchema = true ) public OrderInfo queryOrder( @McpToolParam( description = "订单编号,例如A1001", required = true ) String orderNo ) { return orderService.queryOrder(orderNo); } }

Spring AI 2.0 的 MCP Annotation 会自动扫描这个 Bean,并把方法注册成 MCP Tool。

所以我们不需要自己写:

Tool JSON Schema Tool Registry Tool Definition

框架会根据:

方法 参数 注解

自动完成注册。


5. 再暴露一个库存Tool

库存服务同样处理:

@Component public class InventoryMcpTools { private final InventoryService inventoryService; public InventoryMcpTools( InventoryService inventoryService) { this.inventoryService = inventoryService; } @McpTool( name = "query_inventory", description = "根据商品SKU查询当前可用库存数量", generateOutputSchema = true ) public InventoryInfo queryInventory( @McpToolParam( description = "商品SKU编码,例如SKU1001", required = true ) String skuCode ) { return inventoryService.queryInventory(skuCode); } }

现在 MCP Server 对外已经提供两个 Tool:

query_order query_inventory

启动 Spring Boot 后,MCP Client 就可以发现这些能力。

于是原来的:

Spring Boot Service

已经变成:

Spring Boot Service ↓ MCP Adapter ↓ MCP Tool ↓ AI可发现能力

6. 为什么我建议增加Adapter,而不是直接改Service?

这是一个很小但很重要的设计。

当然,你完全可以这样写:

@Service public class OrderService { @McpTool(...) public OrderInfo queryOrder(...) { } }

Demo 没问题。

但是企业项目里我更倾向于:

OrderService ↓ OrderMcpTools

原因是:

业务能力和AI暴露边界不是一回事。

一个 Service 里可能有:

queryOrder createOrder cancelOrder refundOrder deleteOrder

但我们并不一定希望 AI 全部看到。

通过 MCP Adapter,可以明确控制:

哪些方法暴露 Tool叫什么 参数如何描述 返回什么结构 哪些字段允许给AI

这样业务层完全不用为了 AI 改造。

这和 Controller 很像:

Controller 是HTTP适配层 MCP Tools 是AI适配层

底层复用的依然是:

Service

对于 Java 开发者来说,这样理解其实就简单很多了。


7. Tool Description千万不要随便写

做到这里以后,经常会遇到一个问题:

Tool明明注册成功了,为什么模型就是不用?

一个非常常见的原因就是:

Description写得太差。

例如:

@McpTool( name = "query_order", description = "查询" )

对于模型来说:

查询什么?

什么时候调用?

返回什么?

它根本不知道。

更合理的描述应该是:

@McpTool( name = "query_order", description = "根据订单编号查询订单当前状态、商品SKU和购买数量" )

参数同样如此。

不要只写:

@McpToolParam(description = "id") String orderNo

而应该写:

@McpToolParam( description = "订单编号,例如A1001", required = true ) String orderNo

因为模型选择 Tool 的依据,本质上就是:

Tool Name Tool Description Input Schema 当前用户问题

所以:

Tool Description本身就是Prompt的一部分。

这也是 Tool Calling 开发中非常容易被低估的地方。


8. 第三步:创建MCP Client

现在服务端已经完成。

下一步创建 AI 应用。

加入:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency>

然后配置 MCP Server:

spring: ai: mcp: client: enabled: true name: business-ai-client version: 1.0.0 type: SYNC streamable-http: connections: business: url: http://localhost:8081 endpoint: /mcp

客户端启动以后,会连接:

business-mcp-server

然后获取它当前暴露的 Tool。

比如:

query_order query_inventory

Spring AI 会把这些 MCP Tools 转换成自己能够使用的:

ToolCallback

到这里 MCP 和 Spring AI Tool Calling 就真正接上了。


9. 第四步:把MCP Tool交给ChatClient

MCP Client 会提供一个 ToolCallbackProvider。

我们可以直接注入:

@Service public class AiAssistantService { private final ChatClient chatClient; public AiAssistantService( ChatClient.Builder builder, SyncMcpToolCallbackProvider mcpTools) { this.chatClient = builder .defaultTools(mcpTools) .build(); } public String chat(String question) { return chatClient.prompt() .user(question) .call() .content(); } }

现在我们调用:

aiAssistantService.chat( "查询订单A1001的状态," + "并告诉我对应商品还有多少库存" );

整个过程就可能变成:

1. 用户提问 查询订单A1001的状态, 并告诉我商品还有多少库存 ↓ 2. LLM分析 需要先知道订单信息 ↓ 3. Tool Call query_order( orderNo = "A1001" ) ↓ 4. MCP Client 调用远程MCP Server ↓ 5. OrderService 返回: { "orderNo": "A1001", "status": "PAID", "skuCode": "SKU1001", "quantity": 2 } ↓ 6. LLM继续判断 还需要查询SKU1001库存 ↓ 7. Tool Call query_inventory( skuCode = "SKU1001" ) ↓ 8. 返回 { "skuCode": "SKU1001", "availableStock": 128 } ↓ 9. LLM生成最终答案

最终用户看到:

订单 A1001 当前状态为已支付,购买商品 SKU1001 共 2 件。目前该商品可用库存为 128 件。

注意整个过程中,我们并没有在代码里写:

if (question.contains("库存")) { queryInventory(); }

到底需不需要调用 Tool、调用哪个 Tool,是模型根据问题和 Tool 定义决定的。


10. 到这里,MCP到底帮我们做了什么?

如果不用 MCP,我们当然也可以写:

@Tool public OrderInfo queryOrder(...) { }

然后直接:

chatClient.prompt() .tools(orderTools) .call();

完全可以。

所以很多人会问:

既然Spring AI Tool Calling已经能调用Java方法,为什么还需要MCP?

关键区别在于能力边界。

直接Tool Calling

通常更像:

AI Application ├── OrderTools ├── InventoryTools └── CustomerTools

工具和 AI 应用部署在一起,或者至少由 AI 应用自己维护。

适合:

单个项目 本地业务能力 简单AI应用

MCP

则可以变成:

┌─ Order MCP Server AI Application ───┼─ Inventory MCP Server ├─ CRM MCP Server └─ Knowledge MCP Server

工具提供方和 AI 应用可以完全分开。

一个 MCP Server 也可以被多个 AI Client 使用:

AI客服 ↘ Order MCP Server ↗ AI运营助手

所以我更倾向于这样理解:

Tool Calling解决“AI怎么调用能力”。

MCP解决“能力怎么标准化提供给AI”。


11. REST API已经有了,还有必要做MCP吗?

还有一个 Java 开发者很容易问的问题:

我们系统已经有 REST API 了,为什么不让 Agent 直接调用 REST?

REST 当然不会消失。

而且 MCP 并不是来替代 REST 的。

原来的业务结构完全可以继续存在:

┌→ REST Controller → 前端 │ OrderService ──┤ │ └→ MCP Tools → AI Client

真正复用的是:

业务 Service。

也就是说:

REST 面向传统系统集成 MCP 面向AI能力发现和调用

二者可以同时存在。

我反而不推荐:

MCP Tool ↓ HTTP调用自己Controller ↓ Service

如果就在同一个系统内部,完全可以直接调用 Service。

少绕一层 HTTP。


12. 一个Agent可以连接多个MCP Server吗?

当然可以。

Spring AI MCP Client 本身就支持配置多个连接。

例如:

spring: ai: mcp: client: streamable-http: connections: order: url: http://order-mcp:8081 endpoint: /mcp inventory: url: http://inventory-mcp:8082 endpoint: /mcp customer: url: http://customer-mcp:8083 endpoint: /mcp

最终 AI 可以获得:

订单Tool 库存Tool 客户Tool

用户说:

查询客户 C1001 最近一个订单,如果还没有发货,再检查一下商品库存。

Agent 就有可能完成:

queryCustomer ↓ queryLatestOrder ↓ queryInventory ↓ Final Answer

这时候 MCP 的价值就开始明显了。

我们不再是在:

给某一个ChatBot写几个Function。

而是在:

给AI系统建设可以重复使用的业务能力接口。


13. MCP并不意味着“所有Service都暴露出去”

写到这里也要提醒一个非常重要的问题。

千万不要看到 MCP 方便,就开始:

OrderService所有方法 全部MCP化 UserService所有方法 全部MCP化 AdminService所有方法 全部MCP化

MCP Server 应该暴露的是:

经过设计的AI业务能力。

而不是:

整个Service层。

例如订单系统可能只暴露:

query_order query_order_logistics

而不是直接暴露:

delete_order force_refund modify_payment_status update_database

Tool 越多,也不一定越好。

大量名称相近的 Tool 反而可能增加模型选择错误的概率。

所以设计 MCP Tool 时,我一般会优先考虑:

职责是否单一 名字是否清晰 Description是否明确 参数是否最小 返回是否结构化 是否真的需要AI调用

至于更进一步的:

权限 鉴权 审计 敏感Tool确认 超时 限流

属于 MCP 真正进入企业生产后的下一层问题。

这篇先不展开。


14. MCP、Tool Calling、Agent之间到底是什么关系?

最后再把三个经常混在一起的概念串一下。

Tool Calling

是一种能力:

模型可以请求调用工具。


MCP

是一套协议:

Tool、Resource、Prompt 等能力可以通过统一方式提供给 AI Client。


Agent

是一种任务执行模式:

根据当前目标不断决定下一步做什么。

它可能:

思考 ↓ 调用Tool ↓ 拿到结果 ↓ 继续判断 ↓ 再调用Tool ↓ 完成任务

所以三者可以组合成:

Agent 负责决策 ↓ Tool Calling 负责表达工具调用 ↓ MCP 负责连接标准化工具能力 ↓ Spring Boot Service 负责真正业务执行

这个关系理解以后,MCP 就没有那么玄学了。


15. 总结

如果你本身就是 Java / Spring Boot 开发者,我认为学习 MCP 最好的方式,并不是先研究一大堆协议字段。

而是先完成这样一个改造:

已有Spring Boot Service ↓ MCP Adapter ↓ MCP Server ↓ MCP Client ↓ Spring AI ChatClient ↓ LLM

整个过程中,原来的:

Controller Service Repository Database

都不用推倒重来。

我们只是给现有系统增加了一种新的能力出口:

以前业务能力主要给人和传统系统调用,现在也可以标准化地提供给AI。

如果只记住一句话,我觉得可以记这个:

Tool Calling让AI知道“我要调用一个工具”,MCP让这个工具可以用标准方式被AI发现和调用。

而对于 Java 开发者来说:

MCP Server并不是重新写一套AI业务系统,本质上是在现有Spring Boot业务能力之上增加一层AI Adapter。

先把这一层跑通,后面再继续讨论:

MCP权限怎么做? 多个MCP Server怎么管理? Tool是否需要鉴权? 敏感Tool怎么人工确认? MCP Gateway有没有必要?

就会容易很多。


🧩 AI项目实战

我也在持续维护一个面向企业真实业务场景的Java AI 开源项目:Spring AI Business Copilot。

项目基于Java + Spring AI构建,包含知识库、数据分析(NL2SQL)、客服、报表、招聘等典型企业 AI 场景,并持续实践RAG、Tool Calling、Workflow、Agent、Human-in-the-loop、Guardrails、调用审计与工程治理。

如果你正在学习 Java AI 应用开发,或者想看看一个 AI 项目如何从 Demo 逐步走向完整业务系统,可以作为学习和项目实践参考。

👉 GitHub:qcodingdev/spring-ai-business-copilot

如果项目对你有帮助,欢迎Star支持。


👨‍💻 关于作者|QCoding

专注AI应用开发与Java技术实践。

持续分享Spring AI、RAG、 Agentic、Agent Evaluation、企业AI架构、工程治理与AI转型实践

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 2:29:54

VS Code搭建前端开发环境:用TaoToken统一管理API Key与Base URL

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 10:56:30

两步解决vscode下载完Windsurf,Windsurf不显示的问题|TaoToken 配置排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 22:23:17

Claude 的 24小时无人执行落地方案:用 cc-switch 把 endpoint 改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 11:11:46

OSG架构设计与核心概念:场景树、NodeVisitor与渲染状态管理

我最早用OpenSceneGraph是在一个市级三维GIS项目里&#xff0c;十几万栋楼的数据加载进来之后帧率掉到个位数&#xff0c;UI卡成幻灯片。当时第一反应是换显卡、换模型格式、做纹理压缩&#xff0c;折腾一圈收效甚微。后来沉下心把OSG的架构设计重新啃了一遍才发现&#xff0c;…

作者头像 李华