Spring AI MCP Client:远程工具如何注册给大模型
MCP Server 负责对外暴露工具,但大模型想真正使用这些工具,还需要 MCP Client 来完成连接、发现和调用。
Spring AI MCP Client 的核心价值可以概括成一句话:
把远程 MCP 工具转换成 Spring AI 统一的 ToolCallback,让 Agent 无感使用本地工具和远程工具。
1. MCP Client 到底负责什么?
MCP Client 主要做三件事:
连接 MCP Server
↓
获取远程 Tool 列表
↓
转换成 Spring AI ToolCallback
↓
注册给 ChatClient / LLM
例如 Selenium MCP Server 提供:
openPage
click
input
screenshot
Spring AI Client 连接后,会把这些远程工具转换成大模型可以调用的 Tool。
最终模型看到的可能是:
searchJob
createTask
openPage
screenshot
模型并不知道:
searchJob
→ 本地 Java 方法
openPage
→ 远程 MCP Server
对模型来说,它们都是 Tool。
2. ToolCallback 是统一抽象
本地工具:
@Tool
String searchJob(...) {
...
}
最终会被转换成:
ToolCallback
远程 MCP 工具:
MCP Server
↓
McpAsyncClient
↓
AsyncMcpToolCallbackProvider
↓
ToolCallback
所以两条链最后汇合:
Local @Tool ───────┐
↓
ToolCallback
↑
Remote MCP ─ Adapter
这背后的设计思想非常重要:
业务层应该依赖统一能力接口,而不是工具的物理位置。
工具是在当前 JVM、本地子进程还是远程 HTTP 服务,对 Agent 都应该透明。
3. AsyncMcpToolCallbackProvider 是什么?
可以把它理解成一个 Adapter。
一边是 MCP:
McpAsyncClient
MCP Tool
MCP Protocol
另一边是 Spring AI:
ToolCallback
ChatClient
Function Calling
中间通过:
new AsyncMcpToolCallbackProvider(mcpClients);
完成转换。
再把转换后的工具注册给模型:
ToolCallingChatOptions.builder()
.toolCallbacks(
toolCallbackProvider.getToolCallbacks()
)
.build();
完整链路就是:
YAML / JSON
↓
MCP Client
↓
MCP Server
↓
发现 Tools
↓
Adapter
↓
ToolCallback
↓
LLM
4. MCP Client 挂了怎么办?
生产环境不能假设 MCP Server 永远在线。
如果 Selenium MCP 挂掉:
Agent
↓
MCP Tool
↓
连接失败
↓
整个流程失败
体验会很差。
一种更合理的设计是:
优先使用远程 MCP Tool
↓
不可用
↓
降级使用 Local Tool
例如通过可选注入:
@Autowired(required = false)
public void setMcpClients(List<McpAsyncClient> clients) {
...
}
如果 MCP Client 不存在,也允许应用正常启动。
随后:
@PostConstruct
public void init() {
if (toolCallbackProvider == null) {
// 注册本地工具
}
}
最终:
MCP 正常
→ Selenium MCP
MCP 异常
→ Local Crawler
上层 Agent 不需要修改任何代码。
这就是:
Graceful Degradation / 优雅降级。
5. AI Agent 同样需要容错
MCP 本质上还是一个外部依赖。
所以传统微服务里的思想同样适用:
Timeout
Retry
Fallback
Circuit Breaker
Monitoring
例如:
高级能力
Selenium MCP
↓
能操作浏览器、点击、登录
降级能力
HTTP Crawler
↓
只能抓网页内容
虽然能力下降,但系统还能提供基本服务。
因此:
Agent 工程不是只有 Prompt 和模型,同样需要传统后端的稳定性设计。
6. MCP 让工具变成插件
没有 MCP 时:
Agent
├── SearchTool
├── FileTool
└── BrowserTool
工具通常跟代码绑定。
有了 MCP:
Agent
↓
Tool Registry
├── Local Tools
├── Selenium MCP
├── Database MCP
└── Other MCP
于是新增能力不一定需要修改 Agent。
例如:
今天接入 Selenium MCP
明天接入数据库 MCP
后天接入其他业务 MCP
Agent 主体基本保持不变。
所以可以把 MCP 理解为:
Agent 的插件协议。
7. Agent 还能动态注册 MCP Server
更进一步,可以提供一个 Tool:
@Tool
String addMcpServer(...) {
...
}
它本身就是一个:
用来安装工具的工具。
流程:
Agent
↓
发现缺少能力
↓
调用 addMcpServer
↓
保存 MCP 配置
↓
加载新的 MCP Server
↓
获得新的 Tool
这意味着 Agent 的能力开始从:
开发阶段固定
逐渐变成:
运行配置决定
不过生产环境一定要配合:
权限控制
Server 白名单
URL 校验
认证
人工审批
风险隔离
不能允许模型随意接入任何服务。
8. 一个应用可以同时是 Server 和 Client
MCP Server 和 Client 不是固定身份。
一个应用可以同时扮演两个角色。
例如:
Claude / Codex
↓
MCP Client
↓
求职系统 MCP Server
这里求职系统负责:
对外提供岗位搜索
同时:
求职系统 Agent
↓
MCP Client
↓
Selenium MCP Server
这里求职系统又负责:
消费浏览器工具
整体:
外部 Agent
↓
MCP Server
↓
求职系统
↓
MCP Client
↓
外部 MCP Server
所以:
Server / Client 描述的是工具关系,而不是应用固定身份。
9. 和 Function Call 是什么关系?
这几个概念不要混。
Function Calling
→ LLM 决定调用哪个工具
ToolCallback
→ Spring AI 对工具的统一抽象
MCP Client
→ 把远程工具接进来
MCP Server
→ 把工具暴露出去
完整链路:
User
↓
Agent
↓
ChatClient
↓
LLM
↓
Function Call
↓
ToolCallback
├── Local Tool
└── MCP Tool
↓
MCP Client
↓
MCP Server
↓
Real Action
10. 最值得沉淀的几个知识点
MCP Client
→ 工具消费者
MCP Server
→ 工具提供者
ToolCallback
→ Spring AI 的统一工具接口
AsyncMcpToolCallbackProvider
→ MCP Tool 到 ToolCallback 的适配器
Fallback
→ MCP 不可用时降级到本地工具
Dynamic Registration
→ 动态扩展 Agent 能力
Server + Client
→ 同一应用可以同时提供和消费工具
最后总结
这篇真正值得记住的不是 YAML 怎么配置,而是这个架构思想:
远程工具通过 MCP Client 接入后,被统一转换成 ToolCallback,因此 Agent 不需要区分本地工具和远程工具。
再压缩成一句:
Function Call 决定“调用哪个工具”,ToolCallback 统一“工具长什么样”,MCP Client 解决“远程工具怎么接进来”。
把前面的知识继续串起来:
ChatClient
→ 组织一次 AI 调用
Advisor
→ 增强调用链
ReAct
→ 控制推理和工具循环
Function Call
→ 模型决定调用工具
ToolCallback
→ 统一工具抽象
MCP Client
→ 接入远程工具
MCP Server
→ 对外暴露工具