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 Google
成熟度 已成行业标准 刚起步(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协作:

  1. 主控Agent:分析需求,分发任务
  2. 航班Agent:查询机票价格
  3. 酒店Agent:查询酒店价格
  4. 行程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 未来趋势

  1. MCP会成为基础设施,就像USB接口一样,所有Agent都支持
  2. A2A标准化进程会加速,由Google推动、Linux Foundation治理
  3. 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架构。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐