Spring AI企业级应用实战(6):MCP Client/Server接入、工具发现与生产治理
文章摘要
前五篇已经完成DeepSeek接入、统一ChatClient、流式输出、Chat Memory和Tool Calling。本篇把工具能力从本地Java方法扩展到标准MCP体系:使用Spring AI 2.0构建MCP Server,通过@McpTool暴露订单查询能力;在另一个Spring Boot应用中配置MCP Client,将远程工具自动接入ChatClient;最后补齐工具过滤、租户隔离、权限、超时、名称冲突、审计和协议迁移。完成后,业务Agent无需直接依赖订单系统SDK,即可通过标准协议调用企业工具。
一、本篇目标
最终架构:
用户请求
→ CustomerAgent
→ Spring AI ChatClient
→ ToolCallingAdvisor
→ MCP ToolCallback
→ Order MCP Server
→ Order Service
→ Database
工程拆成两个应用:
spring-ai-mcp-order-server
spring-ai-enterprise-agent
MCP Server负责
- 暴露工具;
- 业务参数校验;
- 资源归属校验;
- 返回结构化结果;
- 工具级审计。
Agent应用负责
- 连接MCP Server;
- 发现工具;
- 将工具交给模型;
- 控制模型调用循环;
- 管理用户对话和最终回答。
二、为什么不继续使用本地@Tool
本地Tool适合:
- 当前应用内部能力;
- 简单工具;
- 与Agent同生命周期;
- 不需要跨语言复用。
MCP适合:
- 工具属于独立业务系统;
- 多个Agent共享;
- Java、Python和桌面客户端都要使用;
- 需要独立扩容;
- 需要统一权限与审计;
- 工具团队和Agent团队独立发布。
订单系统不应该为了给Agent调用,就把SDK和数据库访问代码复制到每个Agent项目中。
三、创建MCP Server项目
依赖:
org.springframework.ai
spring-ai-bom
2.0.0
pom
import
org.springframework.ai
spring-ai-starter-mcp-server-webmvc
org.springframework.boot
spring-boot-starter-validation
org.springframework.boot
spring-boot-starter-actuator
四、配置服务端
application.yml:
server:
port: 8091
spring:
application:
name: order-mcp-server
ai:
mcp:
server:
name: order-mcp-server
version: 1.0.0
protocol: STATELESS
本例中的订单查询工具是独立请求—响应,不需要服务端主动Sampling和Elicitation,因此选择STATELESS。
如果后续工具需要双向交互,应评估切换:
protocol: STREAMABLE
五、定义领域返回对象
package com.zyentor.order.mcp.domain;
import java.time.Instant;
public record OrderStatusResult(
String orderId,
String status,
String logisticsStatus,
Instant updatedAt
) {
}
不要让工具直接返回数据库Entity。
原因:
- 字段过多;
- 可能泄露内部信息;
- Schema不稳定;
- 懒加载异常;
- 与表结构耦合。
六、定义工具参数
package com.zyentor.order.mcp.tool;
import jakarta.validation.constraints.NotBlank;
public record OrderStatusArgs(
@NotBlank
String orderId
) {
}
工具参数应尽量:
- 字段少;
- 类型明确;
- 有校验;
- 不包含tenantId和userId等可信上下文。
租户信息应来自认证和传输上下文。
七、实现订单服务
package com.zyentor.order.mcp.service;
import com.zyentor.order.mcp.domain.OrderStatusResult;
public interface OrderQueryService {
OrderStatusResult queryStatus(
String tenantId,
String orderId
);
}
实现层必须按租户查询:
WHERE tenant_id = ?
AND order_id = ?
不能先按订单号查出数据,再在Java中判断租户。
八、使用@McpTool暴露工具
package com.zyentor.order.mcp.tool;
import com.zyentor.order.mcp.domain.OrderStatusResult;
import com.zyentor.order.mcp.service.OrderQueryService;
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.stereotype.Component;
@Component
public class OrderMcpTools {
private final OrderQueryService orderQueryService;
private final McpIdentityResolver identityResolver;
public OrderMcpTools(
OrderQueryService orderQueryService,
McpIdentityResolver identityResolver
) {
this.orderQueryService = orderQueryService;
this.identityResolver = identityResolver;
}
@McpTool(
name = "order_query_status",
description = "当用户询问某个具体订单的当前状态、物流状态或是否完成签收时调用。必须提供orderId。不能用于查询商品库存或其他租户订单。"
)
public OrderStatusResult queryStatus(
OrderStatusArgs args
) {
McpIdentity identity =
identityResolver.current();
return orderQueryService.queryStatus(
identity.tenantId(),
args.orderId()
);
}
}
具体注解包名和上下文参数应以当前Spring AI 2.0.x文档及项目依赖为准。
九、为什么Description必须写清楚边界
模型主要通过:
工具名称
+Description
+JSON Schema
选择工具。
错误:
查询订单
推荐:
当用户询问某个具体订单的当前状态、物流状态或是否完成签收时调用。必须提供orderId。不能用于查询商品库存。
Description既帮助选择,也减少误调用。
十、身份上下文不能由模型提供
错误工具参数:
{
"tenantId": "T001",
"userId": "U1001",
"orderId": "A1001"
}
模型可以修改前两个字段。
正确结构:
tenantId、userId
→ Access Token和SecurityContext
orderId
→ 模型工具参数
定义:
public record McpIdentity(
String tenantId,
String userId,
Set scopes
) {
}
十一、服务端工具权限
public void requireScope(
McpIdentity identity,
String requiredScope
) {
if (!identity.scopes().contains(requiredScope)) {
throw new AccessDeniedException(
"缺少Scope:" + requiredScope
);
}
}
调用工具时:
requireScope(identity, "order:read");
即使客户端工具列表已经过滤,服务端仍必须再次授权。
十二、创建Agent客户端项目
依赖:
org.springframework.ai
spring-ai-starter-model-deepseek
org.springframework.ai
spring-ai-starter-mcp-client-webflux
org.springframework.boot
spring-boot-starter-webflux
十三、配置MCP Client
Spring AI MCP客户端支持多个命名连接。
配置结构应根据当前2.0.x官方文档填写,核心信息包括:
客户端启用
客户端类型SYNC或ASYNC
初始化开关
请求超时
服务端名称
Streamable或Stateless地址
认证
通用配置:
spring:
ai:
mcp:
client:
enabled: true
initialized: true
type: SYNC
request-timeout: 20s
toolcallback:
enabled: true
生产流式应用可使用:
spring-ai-starter-mcp-client-webflux
十四、自动工具适配
当:
toolcallback.enabled: true
MCP工具可以被适配为Spring AI ToolCallback,进入统一Tool Calling体系。
完整链路:
MCP tools/list
→ Tool定义
→ ToolCallback
→ ChatClient
→ ToolCallingAdvisor
→ MCP tools/call
因此,Spring AI本地Tool与MCP Tool可以共享模型调用机制。
十五、构建Agent ChatClient
@Configuration
public class AgentClientConfig {
@Bean
ChatClient orderAgentChatClient(
ChatClient.Builder builder
) {
return builder
.defaultSystem("""
你是企业订单助手。
规则:
1. 订单实时状态必须调用工具查询,不得猜测。
2. 用户没有提供订单号时,先要求补充。
3. 不得查询或透露其他租户的订单。
4. 工具失败时明确说明,不得编造结果。
""")
.build();
}
}
如果自动配置没有将MCP工具接入目标ChatClient,应显式检查或注入对应ToolCallback Provider。
十六、业务Service
@Service
public class OrderAgentService {
private final ChatClient chatClient;
public OrderAgentService(
@Qualifier("orderAgentChatClient")
ChatClient chatClient
) {
this.chatClient = chatClient;
}
public String chat(String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
}
测试:
查询订单A1001现在到哪里了?
模型应:
选择order_query_status
→ 生成orderId=A1001
→ MCP调用
→ 根据结果回答
十七、为什么工具已经发现,模型仍可能不调用
检查:
- 模型支持Tool Calling;
- 工具Description;
- Schema;
- ChatClient实际工具数量;
- Prompt是否要求实时数据必须查询;
- 工具名称是否冲突;
- 工具是否被过滤;
- 模型是否直接使用常识回答。
建立测试用例:
模型不可能从训练数据知道A1001实时状态
十八、多个MCP Server的名称冲突
假设:
订单Server:search
知识Server:search
CRM Server:search
必须使用前缀:
order_search
knowledge_search
crm_search
命名规则:
领域_动作_对象
例如:
order_query_status
wms_query_inventory
crm_search_customer
knowledge_search_policy
十九、工具过滤
不要把所有工具一次性交给所有Agent。
订单Agent只需要:
order_query_status
order_query_logistics
不需要:
finance_refund
user_delete
admin_update_role
过滤维度:
- Agent类型;
- 租户;
- 用户角色;
- 环境;
- 风险等级;
- 模型能力。
二十、超时、取消与重试
MCP客户端通用超时:
spring:
ai:
mcp:
client:
request-timeout: 20s
但不同工具需要不同策略。
查询订单:
5秒
可重试1次
取消订单:
15秒
默认不自动重试
除非有幂等键
用户取消流式回答时,还应传播取消信号到:
ChatClient
→ Tool Calling
→ MCP Client
→ 上游HTTP请求
二十一、高风险工具不能直接开放
本篇只实现只读查询工具。
如果增加:
order_cancel
order_refund
必须增加:
参数校验
资源归属
人工确认
幂等键
审批状态
执行审计
不能仅依赖模型问一句:
你确认吗?
审批必须由业务控制面记录和验证。
二十二、工具调用审计
服务端记录:
request_id
tenant_id
user_id
tool_name
object_id
argument_hash
result_status
duration_ms
protocol_version
client_name
Agent端记录:
conversation_id
model
工具选择
工具调用次数
最终回答
Token
成本
两端通过request_id关联。
二十三、MCP工具缓存
工具列表可以缓存,但需要:
ttlMs
cacheScope
listChanged
租户缓存键
工具新增、删除和权限变化后应触发失效。
执行时仍需重新鉴权,不能把列表缓存视为永久授权。
二十四、协议选择
本例选择:
STATELESS
因为订单查询是独立请求。
如果后续需要:
- 服务端请求用户补充参数;
- 服务端请求客户端模型执行Sampling;
- 双向进度交互;
应评估:
STREAMABLE
本地开发工具则可以使用STDIO。
二十五、MCP 2026-07-28兼容注意事项
Spring AI项目应确认:
Spring AI具体版本
MCP Java SDK具体版本
支持的协议版本
STATELESS是否只是传输能力
请求路由头是否经过网关
列表缓存是否正确实现
客户端和服务端互操作性
不要仅因为配置项存在,就宣布完整支持全部新规范扩展。
二十六、健康检查
应用启动后检查:
MCP Client是否初始化
Server协议版本
工具数量
必需工具是否存在
工具Schema是否合法
认证是否成功
如果核心工具缺失,可以让实例健康检查失败,避免接收生产流量。
二十七、测试
1. 工具发现测试
必须存在order_query_status
2. 正常调用
输入A1001
→ 返回正确状态
3. 缺少订单号
模型请求用户补充
不得调用空参数
4. 跨租户
T001用户查询T002订单
→ 拒绝
5. 上游超时
返回明确错误
不得编造订单状态
6. 工具被删除
缓存失效
Agent不再选择旧工具
二十八、生产架构升级
单一Server:
Agent
→ Order MCP
多业务系统后:
Agent
→ MCP Gateway
→ Order MCP
→ WMS MCP
→ CRM MCP
→ Knowledge MCP
网关负责:
- 统一发现;
- 名称映射;
- 权限;
- 审批;
- 限流;
- 审计;
- 降级。
二十九、完整调用链
HTTP用户请求
→ 认证上下文
→ OrderAgentService
→ ChatClient
→ ToolCallingAdvisor
→ MCP ToolCallback
→ MCP Client
→ Order MCP Server
→ 租户权限校验
→ OrderQueryService
→ 数据库
→ Tool Result
→ 模型生成最终回答
三十、下一步
下一篇将继续实现:
Spring AI企业级应用实战(7):RAG知识库、权限过滤与引用溯源。
将把企业文档检索接入当前统一ChatClient、Memory和MCP工具体系。
总结
Spring AI与MCP的组合解决了企业AI工具接入的两个问题:
Spring AI
→ 管理模型、Advisor和工具执行循环
MCP
→ 标准化远程工具、资源和Prompt
生产落地不能停留在“工具能调用”,还必须补齐:
协议选择
+工具发现
+名称治理
+租户权限
+超时重试
+高风险审批
+审计
+兼容测试
完成这些控制面后,MCP才能真正成为企业Agent的统一工具协议。
延伸阅读
如果你正在关注企业级 AI 应用、Spring AI、RAG、Agent 与 MCP 工程化落地,欢迎访问 智元界:
https://www.zyentor.com/
智元界将持续分享可运行的技术实战、架构设计、问题排查与企业应用案例。