MCP + A2A:AI Agent协议之争,Java开发者怎么选?Spring AI实战接入
MCP + A2A:AI Agent协议之争,Java开发者怎么选?Spring AI实战接入
MCP已成事实标准,但安全漏洞仍时有发生;A2A让Agent之间能够协作,但生态刚起步。两个协议怎么配合?Java开发者该怎么选?用Spring AI实战来说明。
一、2026 Agent协议版图:MCP/A2A各自解决什么问题
1.1 协议分层
┌─────────────────────────────────────────────────┐
│ Agent应用层 │
│ (Claude Code、OpenClaw等) │
├─────────────────────────────────────────────────┤
│ A2A协议层 │
│ 发现、协商、任务分发 │
├─────────────────────────────────────────────────┤
│ MCP协议层 │
│ Tools、Resources、Prompts │
├─────────────────────────────────────────────────┤
│ 外部工具/API层 │
│ (数据库、微服务、第三方API) │
└─────────────────────────────────────────────────┘
MCP和A2A是分层协作关系:
- MCP解决:单个Agent如何调用外部工具
- A2A解决:多个Agent之间如何协作
1.2 MCP:工具调用的标准化
MCP(Model Context Protocol) 由Anthropic于2024年末发布,目的是解决AI工具调用的碎片化问题。
三层架构:
| 层级 | 角色 | 职责 |
|---|---|---|
| Host | 审主层 | AI应用(Claude Code、Spring AI应用) |
| Client | 客户层 | 连接器,与Server建立会话 |
| Server | 服务层 | 工具实现,暴露Tools/Resources/Prompts |
六大概念:
| 概念 | 作用 | 说明 |
|---|---|---|
| Tools | 工具调用 | 让AI执行具体函数(最常用) |
| Resources | 资源访问 | 给AI喂外部数据 |
| Prompts | 提示模板 | 预制提示模板 |
| Sampling | 采样控制 | 控制模型采样参数 |
| Roots | 根目录 | 文件系统访问范围 |
| Transports | 传输方式 | Stdio(本地)或SSE(远程) |
传输模式对比:
| 模式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Stdio | 本地进程 | 简单、安全 | 难共享 |
| SSE | 远程服务 | 可共享、灵活 | 依赖网络、安全配置复杂 |
1.3 A2A:Agent间协作的标准
A2A(Agent-to-Agent) 由Google于2025年4月9日发布,解决的是Agent之间的"通信壁垒"问题。
核心能力:
| 能力 | 说明 | 示例 |
|---|---|---|
| 发现 | Agent能找到其他Agent | 主控Agent发现"订票Agent" |
| 协商 | Agent能讨论任务分工 | “订票Agent"说"我只负责国内票” |
| 协作 | Agent能分工完成任务 | 主控Agent + 订票Agent + 酒店Agent |
| 数据交换 | Agent间安全传递数据 | 订票Agent把订单信息传给酒店Agent |
典型架构:
用户请求 → 主控Agent → A2A发现子Agent → 任务分发
↓
子Agent执行
↓
MCP调用工具(数据库/API)
↓
返回结果 → 主控Agent汇总
1.4 MCP vs A2A:不是竞争,是互补
| 维度 | MCP | A2A |
|---|---|---|
| 解决问题 | Agent调用外部工具 | Agent间协作 |
| 发布方 | Anthropic | |
| 成熟度 | 已成行业标准 | 刚起步(2025年发布) |
| Spring AI支持 | 完整支持 | 间接支持(需自行实现) |
| 典型场景 | 查数据库、调API | 多Agent分工 |
简单记:MCP负责"单Agent的工具扩展",A2A负责"多Agent的协作编排"。两者配合使用。
二、MCP深入:Spring AI MCP集成实战
2.1 Spring AI MCP依赖
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
2.2 MCP Server配置
方式1:Stdio本地模式(适合开发调试)
# application.yml
spring:
ai:
mcp:
client:
stdio:
servers-configuration: classpath:mcp-servers.json
// mcp-servers.json
{
"mcpServers": {
"local-tools": {
"command": "java",
"args": ["-jar", "mcp-server.jar"],
"env": {}
}
}
}
方式2:SSE远程模式(适合生产环境)
spring:
ai:
mcp:
client:
sse:
connections:
remote-tools:
uri: http://mcp-server.example.com:8080/mcp/sse
2.3 实战:Spring AI调用MCP工具
@RestController
@RequestMapping("/api/mcp")
public class McpController {
private final ChatClient chatClient;
public McpController(
ChatClient.Builder builder,
SyncMcpToolCallbackProvider toolCallbackProvider) {
this.chatClient = builder
.defaultOptions(OpenAiChatOptions.builder()
.model("deepseek-v4-pro")
.toolCallbacks(toolCallbackProvider.getToolCallbacks())
.build())
.build();
}
@PostMapping("/chat")
public String chat(@RequestBody Map<String, String> request) {
return chatClient.prompt()
.user(request.get("message"))
.call()
.content();
}
}
2.4 自定义MCP工具包装器
可以在工具调用前后加入日志和缓存逻辑:
public class CustomMcpToolWrapper implements ToolCallback {
private final ToolCallback delegate;
private final ChatService chatService;
public CustomMcpToolWrapper(ToolCallback delegate, ChatService chatService) {
this.delegate = delegate;
this.chatService = chatService;
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public String getDescription() {
return delegate.getDescription();
}
@Override
public String call(String functionArguments, ToolContext toolContext) {
// 记录每次工具调用的参数,方便排查问题
chatService.logToolCall(getName(), functionArguments);
String result = delegate.call(functionArguments, toolContext);
// 对高频查询结果做缓存,减少重复调用
chatService.cacheToolResult(getName(), result);
return result;
}
}
2.5 MCP安全注意事项
MCP Server多数封装了代码解释器能力,若缺乏防护,可能引发远程代码执行(RCE)。
基本防护措施:
@Configuration
public class McpSecurityConfig {
@Primary
@Bean
public McpToolCallbackProvider secureMcpProvider(SyncMcpToolCallbackProvider delegate) {
Set<String> allowedTools = Set.of("queryDatabase", "callApi", "sendEmail");
return new SecureMcpToolCallbackProvider(delegate, allowedTools);
}
}
public class SecureMcpToolCallbackProvider implements ToolCallbackProvider {
private final SyncMcpToolCallbackProvider delegate;
private final Set<String> allowedTools;
@Override
public List<ToolCallback> getToolCallbacks() {
return delegate.getToolCallbacks()
.stream()
.filter(tc -> allowedTools.contains(tc.getName()))
.toList();
}
}
三、A2A深入:Agent间协作的实现思路
3.1 A2A协议核心概念
Agent Card(Agent卡片):描述Agent的能力和联系方式,类似微服务的注册信息。
{
"agentId": "travel-agent",
"name": "旅行规划Agent",
"description": "负责规划行程、预订机票酒店",
"capabilities": ["flight-booking", "hotel-booking", "itinerary-planning"],
"endpoint": "http://travel-agent.example.com/a2a",
"auth": {
"type": "oauth2",
"scope": "booking"
}
}
Agent Directory(Agent目录):注册和发现Agent,类似服务注册中心。
@Service
public class AgentDirectoryService {
private final Map<String, AgentCard> agents = new ConcurrentHashMap<>();
/**
* 注册Agent到目录
*/
public void register(AgentCard card) {
agents.put(card.getAgentId(), card);
}
/**
* 按能力查找匹配的Agent
*/
public List<AgentCard> findByCapability(String capability) {
return agents.values()
.stream()
.filter(card -> card.getCapabilities().contains(capability))
.toList();
}
}
3.2 A2A DTO定义
record A2ARequest(String task) {}
record A2AResponse(String result, String status) {}
3.3 A2A协作流程实现
@Service
public class A2ACoordinator {
private final ChatClient chatClient;
private final AgentDirectoryService directory;
private final RestClient restClient;
/**
* 主控Agent:接收用户请求,分析后分发任务给子Agent
*/
public String coordinateTask(String userRequest) {
// 1. 分析用户请求,识别需要哪些能力的子Agent
List<String> requiredCapabilities = analyzeCapabilities(userRequest);
// 2. 从Agent目录发现对应的子Agent
Map<String, AgentCard> subAgents = new HashMap<>();
for (String cap : requiredCapabilities) {
List<AgentCard> candidates = directory.findByCapability(cap);
if (!candidates.isEmpty()) {
subAgents.put(cap, candidates.get(0));
}
}
// 3. 向每个子Agent发送任务请求
Map<String, String> results = new HashMap<>();
for (Map.Entry<String, AgentCard> entry : subAgents.entrySet()) {
String taskPrompt = buildTaskPrompt(entry.getKey(), userRequest);
String response = callSubAgent(entry.getValue(), taskPrompt);
results.put(entry.getKey(), response);
}
// 4. 汇总所有子Agent的结果,生成最终回复
return summarizeResults(userRequest, results);
}
/**
* 通过A2A协议调用子Agent
*/
private String callSubAgent(AgentCard agent, String taskPrompt) {
A2ARequest request = new A2ARequest(taskPrompt);
A2AResponse response = restClient.post()
.uri(agent.getEndpoint() + "/task")
.body(request)
.retrieve()
.body(A2AResponse.class);
return response.result();
}
}
3.4 子Agent实现(配合MCP)
子Agent对外通过A2A协议通信,对内通过MCP调用实际工具(比如查询航班API):
@RestController
@RequestMapping("/a2a")
public class FlightAgentController {
private final ChatClient chatClient;
private final SyncMcpToolCallbackProvider mcpTools;
@PostMapping("/task")
public A2AResponse handleTask(@RequestBody A2ARequest request) {
String result = chatClient.prompt()
.user(request.getTask())
.call()
.content();
return new A2AResponse(result, "completed");
}
}
四、实战:Spring AI同时接入MCP + A2A协作
4.1 场景说明
用户请求:“帮我规划下周的北京旅行,预算5000元”
需要多个Agent协作:
- 主控Agent:分析需求,分发任务
- 航班Agent:查询机票价格
- 酒店Agent:查询酒店价格
- 行程Agent:规划景点路线
每个子Agent内部通过MCP调用外部API。
4.2 完整架构
用户请求 → 主控Agent(Spring AI)
↓
A2A协议层(发现子Agent)
↓
┌─────────────┬─────────────┬─────────────┐
│ │ │ │
航班Agent 酒店Agent 行程Agent
│ │ │
MCP调用API MCP调用API MCP调用API
│ │ │
返回结果 返回结果 返回结果
│ │ │
└─────────────┴─────────────┘
↓
主控Agent汇总 → 用户
4.3 主控Agent代码
@Service
public class TravelCoordinatorService {
private final ChatClient chatClient;
private final AgentDirectoryService directory;
public TravelCoordinatorService(
ChatClient.Builder builder,
AgentDirectoryService directory) {
this.directory = directory;
this.chatClient = builder
.defaultSystem("""
你是一个旅行规划主控Agent。
当用户请求规划旅行时:
1. 分析需求(目的地、时间、预算)
2. 分发任务给对应的子Agent
3. 汇总结果返回给用户
""")
.build();
}
public String planTravel(String userRequest) {
List<String> capabilities = List.of("flight-booking", "hotel-booking", "itinerary");
Map<String, String> results = new HashMap<>();
for (String cap : capabilities) {
AgentCard agent = directory.findByCapability(cap).get(0);
String task = buildSubTask(cap, userRequest);
String result = callAgent(agent, task);
results.put(cap, result);
}
return chatClient.prompt()
.user("用户请求:" + userRequest + "\n子Agent结果:" + results)
.call()
.content();
}
}
4.4 子Agent代码(航班Agent)
@RestController
@RequestMapping("/a2a/flight")
public class FlightAgentController {
private final ChatClient chatClient;
private final SyncMcpToolCallbackProvider mcpTools;
public FlightAgentController(
ChatClient.Builder builder,
SyncMcpToolCallbackProvider mcpTools) {
this.mcpTools = mcpTools;
this.chatClient = builder
.defaultSystem("你是航班查询Agent,负责查询机票价格")
.defaultOptions(OpenAiChatOptions.builder()
.model("deepseek-v4-flash")
.toolCallbacks(mcpTools.getToolCallbacks())
.build())
.defaultTools(MethodToolCallback.builder().toolObject(this).build())
.build();
}
@Tool(description = "查询航班价格")
public FlightInfo queryFlights(
@ToolParam(description = "出发城市") String from,
@ToolParam(description = "目的地") String to,
@ToolParam(description = "日期") String date) {
return flightApiService.search(from, to, date);
}
@PostMapping("/task")
public A2AResponse handleTask(@RequestBody A2ARequest request) {
String result = chatClient.prompt()
.user(request.getTask())
.call()
.content();
return new A2AResponse(result, "completed");
}
}
五、协议选型指南:什么场景用什么协议
5.1 选型决策表
| 场景 | 需要MCP | 需要A2A | 说明 |
|---|---|---|---|
| 单Agent + 工具调用 | 是 | 否 | MCP足够 |
| 多Agent协作 | 可选 | 是 | 每个子Agent可能需要MCP |
| 企业系统集成 | 是 | 可选 | MCP连接现有系统 |
| 复杂业务流程编排 | 可选 | 是 | A2A负责流程编排 |
5.2 成本考量
| 维度 | MCP | A2A |
|---|---|---|
| 开发成本 | 中(配置Server) | 高(实现Agent通信) |
| 学习成本 | 低 | 高 |
| 维护成本 | 中 | 高 |
| Spring AI支持 | 完整 | 需自行实现 |
建议:
- 优先用MCP:工具调用场景更常见,Spring AI支持完善
- 谨慎用A2A:真正需要多Agent协作时再引入,目前生态不成熟
5.3 未来趋势
- MCP会成为基础设施,就像USB接口一样,所有Agent都支持
- A2A标准化进程会加速,由Google推动、Linux Foundation治理
- Spring AI对A2A的官方支持预计在2026下半年推出starter
六、踩坑总结
坑1:MCP的Stdio模式在Windows不稳定
Windows下Stdio模式的进程管理不如Linux稳定,容易出现进程卡死。
建议:生产环境改用SSE模式。
坑2:A2A没有官方Java SDK
A2A协议刚起步,没有成熟的Java SDK,需要自己实现Agent Directory和通信逻辑。
临时方案:用Spring Cloud的Service Discovery + RestClient模拟A2A的发现和调用。
坑3:多Agent协作的调试困难
Agent间通信的调试比单Agent复杂得多,建议:
- 为每个Agent配置独立的日志
- 建立统一的Agent Dashboard监控状态
- 使用Mock子Agent进行单元测试
七、延伸思考
1. MCP + A2A能否统一?
理论上可以设计一个统一协议同时支持工具调用和Agent协作,但目前没有这个趋势。Anthropic专注工具调用(MCP),Google专注Agent协作(A2A),两个大厂各有侧重。
2. 企业落地路径
阶段1:单Agent + MCP
用Spring AI MCP连接企业现有系统,快速落地,验证价值。
阶段2:多Agent + A2A
当业务复杂度提升后引入A2A,按领域划分Agent(客服Agent、财务Agent、运营Agent)。
阶段3:Agent生态
建立企业内部Agent Directory,支持Agent的注册、发现、协作。
MCP解决Agent如何调用工具,A2A解决Agent如何协作。两者分层配合,MCP在工具层,A2A在协作层。Spring AI已完整支持MCP,A2A目前需要自行实现。对Java开发者来说,优先用MCP,谨慎用A2A——除非你的场景真的需要多Agent协作。如果现在就想试水,可以先从MCP接入现有系统的工具开始,跑通后再考虑引入多Agent架构。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)