大家好,我是一言,继续更新 Spring AI 2.0 系列。
前面写 Tool Calling 时,我们把 @Tool 方法直接注册给了当前应用的 ChatClient。
实际项目里,订单、库存和会员往往分属不同服务。客服 Agent、运营助手和其他 AI 客户端都想查订单,总不能每个项目都复制一份 Java 工具类。
MCP 可以把这个能力变成标准服务。客户端先发现工具,然后按照 JSON Schema 组装参数并调用。
Spring AI 2.0 里,现有 Spring Service 加上 @McpTool,再引入 MCP Server Starter,就可以走通这条链路。
先看运行结果#
我用 Java 21、Spring Boot 4.0.8 和 Spring AI 2.0.1 写了一个订单 MCP Server。
服务启动后,Spring AI 找到了一个工具:
Registered tools: 1
Tomcat started on port 8081MCP Client 调用 tools/list,拿到的核心内容是:
{
"name": "queryOrder",
"description": "Query shipping status, carrier and tracking number by order id",
"inputSchema": {
"type": "object",
"properties": {
"orderId": {
"type": "string",
"description": "Order id, for example A1024"
}
},
"required": ["orderId"]
}
}客户端继续执行:
queryOrder({"orderId":"A1024"})服务端返回:
{
"orderId": "A1024",
"status": "SHIPPED",
"carrier": "SF Express",
"trackingNo": "SF1234567890"
}这次验证覆盖了 MCP 初始化、工具发现和工具执行。后面的 ChatClient 代码也已通过编译。真实模型能否在复杂问题中选对工具,还会受模型能力和 description 质量影响。
先把 MCP Server 开起来#
服务端引入 WebMVC Starter:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>然后配置服务名、端口和传输协议:
server.port=8081
spring.ai.mcp.server.name=order-mcp-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=SYNC
spring.ai.mcp.server.protocol=STREAMABLESTREAMABLE 使用 Streamable HTTP,默认 MCP 端点路径是 /mcp。本文的初始化、工具查询和调用都通过 HTTP POST 完成。Spring AI 2.0 已将 SSE 标记为弃用,新的 HTTP 服务可以直接使用 Streamable HTTP。
给现有 Service 加一个注解#
订单查询的业务代码仍然放在 OrderService。MCP 这层只负责对外描述和转发:
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.ai.mcp.annotation.McpToolParam;
@Service
public class OrderMcpTools {
private final OrderService orderService;
public OrderMcpTools(OrderService orderService) {
this.orderService = orderService;
}
@McpTool(
name = "queryOrder",
description = "Query shipping status, carrier and tracking number by order id",
annotations = @McpTool.McpAnnotations(
readOnlyHint = true,
destructiveHint = false
)
)
public OrderSnapshot queryOrder(
@McpToolParam(
description = "Order id, for example A1024",
required = true
)
String orderId) {
return orderService.query(orderId);
}
}Starter 会扫描 Spring Bean 上的 @McpTool,把方法名、描述和参数转成 MCP Tool Definition。OrderSnapshot 会被序列化后放进工具结果。
这里还有个容易忽略的点:destructiveHint 默认是 true。queryOrder 只读不改数据,所以显式标记 readOnlyHint=true 和 destructiveHint=false。这些值会随 tools/list 返回给客户端,它们属于工具行为提示,权限检查仍然要在服务端执行。
required = true 也会进入 JSON Schema。我把 orderId 删掉后再调用,服务端没有进入 Java 方法,直接返回:
Tool (queryOrder) input validation failed:
JSON schema validation errors: 未找到所需属性“orderId”
isError=trueSpring AI 2.0 的 MCP Server 会默认按照 Schema 校验工具参数。金额范围、用户权限和订单状态仍然要由业务层检查。
Agent 应用怎么接#
另一个 Spring AI 应用引入 MCP Client Starter:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>配置订单服务地址:
spring.ai.mcp.client.type=SYNC
spring.ai.mcp.client.streamable-http.connections.order-server.url=http://localhost:8081客户端启动时会建立 MCP 连接,并把发现到的工具封装成 SyncMcpToolCallbackProvider。
这个 Provider 需要显式交给 ChatClient:
@Bean
ChatClient orderChatClient(
ChatClient.Builder builder,
SyncMcpToolCallbackProvider mcpTools) {
return builder
.defaultTools(mcpTools)
.build();
}之后的调用和本地 Tool Calling 一样:
String answer = chatClient.prompt()
.system("You are an order assistant. Call the order tool when order data is needed.")
.user("帮我查订单 A1024 到哪了")
.call()
.content();这条链路里,模型看到的仍然是工具名、description 和 JSON Schema。当它返回 queryOrder 调用请求时,Spring AI 会通过 MCP Client 请求订单服务,再把结果交回 ToolCallingAdvisor。

@Tool 和 @McpTool 怎么选#
| 常见用法 | @Tool |
@McpTool |
|---|---|---|
| 执行位置 | 当前 Java 应用 | MCP Server |
| 注册方式 | 生成 Spring AI ToolCallback |
生成 MCP Tool 并通过协议暴露 |
| 调用范围 | 当前 ChatClient |
已连接的 MCP Client |
| 适合场景 | 当前项目的小工具 | 订单、库存等可复用能力 |
客户端拿到 MCP Tool 后,Spring AI 会把它转成统一的 ToolCallback。ToolCallingAdvisor 处理工具循环时,本地工具和 MCP 工具可以放在同一次请求中。
Spring AI 2.0 这次改了什么#
Spring AI 1.1 已经提供过注解式 MCP 集成,当时的注解模块来自 Spring AI Community。
2.0 的几个变化和这篇代码直接相关:
- MCP 注解并入 Spring AI,包名统一到
org.springframework.ai.mcp.annotation.*; - MCP Java SDK 升级到 2.0.0,服务端默认开启工具输入 Schema 校验;
- WebMVC 和 WebFlux 传输模块进入 Spring AI 项目;
- SSE 已弃用,Streamable HTTP 成为新项目的主要选择。
从 1.x 升级时,旧的 org.springaicommunity.mcp.annotation.* import 要换成新包名,直接依赖的 org.springaicommunity:mcp-annotations 也要移除。
上线前先加鉴权#
Spring AI 的 HTTP MCP Starter 默认会暴露一个没有身份认证的 JSON-RPC 端点。只要能访问该地址,客户端就可以列出并调用已注册的工具。
本地开发可以先绑定在 localhost。部署到内网或公网前,要在 /mcp 前面加上 Spring Security、OAuth 2.0 或统一网关鉴权。
@McpTool 也要当成对外 API 看待。查询、更新、删除和付款类工具,需要分开做权限、幂等和审计。
最后#
这篇先跑通了一条最小链路:
Spring Service
↓ @McpTool
MCP Server
↓ tools/list + tools/call
MCP Client
↓ ToolCallbackProvider
ChatClient / Agent一个订单查询方法从应用内部走到 MCP 协议层以后,同一份能力就可以交给多个 Agent 客户端使用。
下一步可以继续看两个问题:多个 MCP Server 中的工具重名怎么处理,以及用户身份怎么从 Agent 传到 MCP Tool。
参考资料#
- Spring AI 2.0.1:Getting Started with MCP
- Spring AI 2.0.1:MCP Server Boot Starter
- Spring AI 2.0.1:MCP Server Annotations
- Spring AI 2.0.1:MCP Client Boot Starter
- Spring AI 2.0.1:Tool Calling 与 MCP
- Spring AI 2.0:Upgrade Notes
- Spring AI 2.0.0 GA 发布说明
