A2A 与 MCP 双协议网关设计指南

A2A 与 MCP 双协议网关设计指南

📍 系列定位 :本篇是 A2A 系列的"番外篇",承接第 5 篇(A2A + MCP 双协议)和第 6 篇(企业级网关治理),专门回答一个落地时最容易纠结的架构问题:A2A 和 MCP 在同一个 project 里,网关到底该怎么管?

先给结论:

完全可以,而且很常见。但"网关怎么处理"取决于你把 MCP 和 A2A 放在什么位置------是"对外暴露的两个面",还是"一个对内、一个对外"。


一、先分清三种形态

形态 结构 网关需求
A. 并列暴露 一个 project,MCP Server module + A2A Server module,都对外 需要同时处理两种协议
B. 双端点 一个 module 同时暴露 MCP 和 A2A 端点 同一进程,网关按路径分流
C. 内外分工(最常见) Agent module 对外是 A2A Server,对内是 MCP Client 网关只管 A2A,MCP 走内网

大多数生产系统是 C:Agent 对外用 A2A 被委派,对内用 MCP 调工具。MCP Server 是内部基础设施,不对外暴露,所以不需要"对外网关同时处理两种协议"。

只有 A 和 B 才需要认真设计"双协议网关"。

💡 架构原则:先判断"MCP 是否需要被外部调用方直接访问",再决定网关复杂度。不要一上来就设计双协议网关,那是过度设计。


二、形态 A:多 module 并列

项目结构

text 复制代码
agent-platform/
├── pom.xml
├── mcp-common/                 # 公共:日志、鉴权工具、traceId
├── mcp-sales-server/           # MCP Server,暴露 sales_query
│   └── endpoint: /mcp/sales
├── mcp-hr-server/              # MCP Server,暴露 hr_query
│   └── endpoint: /mcp/hr
├── a2a-sales-agent/            # A2A Server,skill: sales_analysis
│   └── endpoint: /a2a/sales
├── a2a-orchestrator/           # A2A Server,编排其他 Agent
│   └── endpoint: /a2a/orchestrator
└── gateway/                    # 统一网关

对外只暴露一个域名:

text 复制代码
https://api.example.com/mcp/sales        → mcp-sales-server
https://api.example.com/mcp/hr           → mcp-hr-server
https://api.example.com/a2a/sales        → a2a-sales-agent
https://api.example.com/a2a/orchestrator → a2a-orchestrator

网关方案:统一网关,按路径分流

text 复制代码
Client
  │ HTTPS
  ▼
┌─────────────────────────────────────┐
│ 统一网关                             │
│  1. TLS 终止                         │
│  2. 按路径识别协议                    │
│     /mcp/** → MCP 处理链             │
│     /a2a/** → A2A 处理链             │
│  3. 统一鉴权(OAuth2 / Bearer)       │
│  4. 统一 traceId                     │
│  5. 分协议限流、审计                  │
└─────────────────────────────────────┘
  │                    │
  ▼                    ▼
MCP Servers        A2A Agents

关键:协议识别靠路径前缀。 这是最简单、最可靠的方式。不要靠解析 JSON-RPC body 来识别协议------那是兜底手段,不是首选。

网关 Filter 骨架

java 复制代码
// 基于 Spring Cloud Gateway (WebFlux) 的 GlobalFilter 骨架。
// 如果使用 Servlet 栈(Spring MVC),需替换为 HandlerInterceptor 或 Servlet Filter。
public class ProtocolRoutingFilter implements GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getPath().value();
        String traceId = UUID.randomUUID().toString();
        exchange.getAttributes().put("traceId", traceId);

        if (path.startsWith("/mcp/")) {
            exchange.getAttributes().put("protocol", "MCP");
            return handleMcp(exchange, chain);
        }
        if (path.startsWith("/a2a/")) {
            exchange.getAttributes().put("protocol", "A2A");
            return handleA2a(exchange, chain);
        }
        return reject(exchange, 404, "未知路径:" + path);
    }

    private Mono<Void> handleMcp(ServerWebExchange exchange, GatewayFilterChain chain) {
        // MCP 特有:SSE 长连接、Tool 级限流
        return auth(exchange)
            .then(rateLimit(exchange, "MCP"))
            .then(chain.filter(exchange))
            .doFinally(s -> audit(exchange, "MCP"));
    }

    private Mono<Void> handleA2a(ServerWebExchange exchange, GatewayFilterChain chain) {
        // A2A 特有:委派链防循环、Task 级审计、回调鉴权
        return auth(exchange)
            .then(antiLoop(exchange))       // X-A2A-Chain 检查
            .then(rateLimit(exchange, "A2A"))
            .then(chain.filter(exchange))
            .doFinally(s -> audit(exchange, "A2A"));
    }
}

两种协议的网关职责差异

职责 MCP 处理链 A2A 处理链
鉴权 OAuth2 scope 映射 Tool OAuth2 scope 映射 Skill
限流 按 Tool 计数 按 Agent + Skill 计数
会话 SSE 长连接、粘滞 SSE + Task 状态
防循环 不需要 必须 (X-A2A-Chain)
审计 Tool 名 + 参数摘要 委派链 + Task 状态变化
回调 无 推送通知回调鉴权
缓存 Tool 列表可缓存 Agent Card 可缓存

三、形态 B:一个 module 双端点

结构

text 复制代码
agent-service/
├── src/main/java/
│   ├── McpEndpoints.java        // /mcp 端点,暴露工具
│   ├── A2aEndpoints.java        // /a2a 端点,暴露 Agent
│   └── SharedBusinessService.java
└── application.yml
yaml 复制代码
server:
  port: 8080
  servlet:
    context-path: /agent

# 端点:
#   https://api.example.com/agent/mcp
#   https://api.example.com/agent/a2a

网关处理

和形态 A 一样,按路径分流:

text 复制代码
/agent/mcp/** → MCP 处理链
/agent/a2a/** → A2A 处理链

区别只是下游是同一个服务。网关不关心下游是不是同一个进程,只关心路径对应的协议。

⚠️ 注意 :同一个进程里,MCP 的 SSE 和 A2A 的 SSE 可能共享线程池和连接数。网关的限流要分协议独立计数,否则一个协议的流量会挤占另一个。


四、形态 C:内外分工(推荐)

结构

text 复制代码
sales-agent/
├── a2a/                         # 对外:A2A Server
│   ├── AgentCard.java
│   └── SalesA2AHandler.java
├── mcp/                         # 对内:MCP Client
│   └── McpClientHolder.java
└── service/
    └── SalesService.java
text 复制代码
外部 Agent ──A2A──> 网关 ──> sales-agent ──MCP──> 内网 MCP Server

网关只处理 A2A

  • 对外只暴露 /a2a/**。
  • MCP Client 调内网 MCP Server,不经过对外网关,走内网服务发现或直连。
  • 对外网关不需要懂 MCP 协议。

这是最省心的形态:

  • 网关职责单一,只处理 A2A。
  • MCP 在内网,安全边界由内网策略控制。
  • 两种协议的生命周期解耦。

🚨 内网不等于裸奔:内网 MCP Server 仍需校验调用方身份,防止内网其他被攻陷的服务越权调用工具。常见手段:

手段 说明 适合
mTLS 服务间双向证书认证,调用方必须是授权服务 高安全要求、服务网格环境
内部 JWT 内部 CA 签发短时 token,MCP Server 校验签名和 scope 通用,易与现有 OAuth2 体系对接
K8s ServiceAccount Token Pod 身份,结合 NetworkPolicy 使用 云原生环境
NetworkPolicy 网络层限制只有指定 namespace/pod 能访问 MCP Server 兜底层,不能替代应用层鉴权

原则:对外网关是"第一道门",内网鉴权是"第二道门"。两道门都要有,不能只靠一道。
💡 判断标准:MCP Server 是否需要被外部调用方直接访问?

  • 不需要 → 形态 C,网关只管 A2A。
  • 需要 → 形态 A/B,网关要处理双协议。

五、双协议网关的四个关键设计

1. 协议识别:路径优先,方法名兜底

java 复制代码
// 首选:路径前缀
if (path.startsWith("/mcp/")) protocol = "MCP";
if (path.startsWith("/a2a/")) protocol = "A2A";

// 兜底:解析 JSON-RPC method
// MCP: tools/list, tools/call, resources/read, prompts/get
// A2A: message/send, message/stream, tasks/get, tasks/cancel

不要只靠方法名,因为:

  • 解析 body 有性能开销
  • 流式请求 body 可能不完整
  • 方法名可能重叠(未来协议演进)

路径前缀是契约,方法名是辅助。

2. 鉴权:统一入口,分协议授权

text 复制代码
OAuth2 token
  ├── scope: mcp.sales.read   → 允许调 /mcp/sales 的只读 Tool
  ├── scope: a2a.sales.invoke → 允许委派 /a2a/sales
  └── scope: a2a.orchestrator → 允许调编排 Agent

网关统一校验 token,按 scope 决定:

  • 这个 token 能访问哪些 /mcp/*
  • 这个 token 能委派哪些 /a2a/*

不要让 MCP Server 和 A2A Agent 各自解析 token,否则鉴权逻辑分散,审计也难统一。

3. 审计:统一 traceId,分协议字段

json 复制代码
{
  "traceId": "trace-abc",
  "protocol": "A2A",
  "caller": "agent-b",
  "target": "sales-agent",
  "skill": "sales_analysis",
  "chain": ["orchestrator", "sales-agent"],
  "taskState": "completed",
  "cost": 1200
}
json 复制代码
{
  "traceId": "trace-def",
  "protocol": "MCP",
  "caller": "sales-agent",
  "target": "mcp-sales-server",
  "tool": "sales_query",
  "args": {"region": "华东", "week": "2026-W20"},
  "status": "ok",
  "cost": 35
}

同一个 traceId 贯穿 A2A 委派和内部 MCP 调用,才能看到完整链路。

4. 发现:统一目录或 Agent Card 代理

💡 问题:如果 MCP 和 A2A 都对外暴露,Client 怎么知道哪些路径是 MCP,哪些是 A2A?

三种做法:

做法 说明 适合
A2A Agent Card 扩展 在 Agent Card 的 extensions 字段声明关联的 MCP 端点 以 A2A 为主入口
网关统一 /registry 网关提供 /registry 接口,返回所有可用 Agent 和 MCP Server 列表 双协议平等,需要统一目录
静态文档 README / 开发者门户列出所有端点 小规模,端点少

Agent Card 扩展示例:

json 复制代码
{
  "name": "sales-agent",
  "url": "https://api.example.com/a2a/sales",
  "extensions": {
    "mcp_endpoints": [
      { "name": "sales_query", "url": "https://api.example.com/mcp/sales" }
    ]
  }
}

网关统一 /registry 示例:

json 复制代码
GET https://api.example.com/registry

{
  "a2a": [
    { "name": "sales-agent", "url": "/a2a/sales", "skills": ["sales_analysis"] },
    { "name": "orchestrator", "url": "/a2a/orchestrator", "skills": ["multi_agent"] }
  ],
  "mcp": [
    { "name": "sales-server", "url": "/mcp/sales", "tools": ["sales_query"] },
    { "name": "hr-server", "url": "/mcp/hr", "tools": ["hr_query"] }
  ]
}

⚠️ /registry 要鉴权:不是所有调用方都能看到全部端点,按 token scope 过滤。


六、推荐架构

小规模(≤3 个 MCP Server,≤3 个 A2A Agent)

text 复制代码
Client
  │
  ▼
Nginx(TLS + 路径路由 + 基础限流)
  ├── /mcp/** → MCP Servers
  └── /a2a/** → A2A Agents

Nginx 做粗粒度路由和 TLS,鉴权在应用层做。简单、够用。

中规模(多团队,需要统一鉴权和审计)

text 复制代码
Client
  │
  ▼
统一网关(Spring Cloud Gateway / APISIX)
  ├── 协议识别(路径)
  ├── OAuth2 鉴权(scope 分协议)
  ├── 分协议限流
  ├── 统一 traceId + 审计
  ├── A2A 防循环
  └── 路由
       ├── /mcp/** → MCP Servers
       └── /a2a/** → A2A Agents

大规模(企业级)

text 复制代码
Client
  │
  ▼
L7 负载均衡 / CDN
  │
  ▼
统一网关(控制面 + 数据面分离)
  ├── 控制面:权限矩阵、限流策略、灰度规则
  └── 数据面:协议识别、鉴权、限流、审计、防循环
       ├── /mcp/** → MCP 服务集群(Nacos 发现)
       └── /a2a/** → A2A Agent 集群(Registry 发现)

七、常见坑

坑 现象 解决
只按方法名识别协议 流式 body 不完整时识别失败 路径前缀优先
MCP 和 A2A 共享限流计数器 一个协议流量挤占另一个 分协议独立计数
MCP SSE 和 A2A SSE 共享连接池 长连接互相影响 分连接池或分实例
HTTP/2 下的 SSE 队头阻塞 MCP 和 A2A 的 SSE 流在同一个 HTTP/2 连接上多路复用,一个流阻塞会导致其他流(甚至普通 JSON-RPC 请求)被卡住 网关对 /mcp/** 和 /a2a/** 的 SSE 端点强制降级为 HTTP/1.1,或确保网关到下游使用独立的 HTTP/1.1 连接池
鉴权逻辑分散在各 module 审计难统一,权限难收敛 网关统一鉴权,下游只信任身份头
A2A 防循环只做在 Agent 层 恶意 Agent 绕过 网关层也要检查 X-A2A-Chain
MCP 工具对内网暴露 绕过 A2A 鉴权直接调工具 MCP 只在内网,不经过对外网关;内网也要鉴权(mTLS / 内部 JWT / K8s SA Token)
traceId 不贯穿 A2A 委派和 MCP 调用日志对不上 网关生成 traceId,透传到下游
双协议共享线程池 一个协议阻塞拖垮另一个 分线程池或分实例部署
/registry 未鉴权 端点信息泄露 按 token scope 过滤返回内容

关于 HTTP/2 SSE 队头阻塞的补充

🚨 这是双协议网关最隐蔽的 P0 级坑。

HTTP/2 多路复用让多个流共享一个 TCP 连接。SSE 是长连接,如果某个流的 TCP 包丢失,整个 HTTP/2 连接上的所有流都会被阻塞(TCP 层队头阻塞,HTTP/2 无法绕过)。

场景:

text 复制代码
Client ──HTTP/2──> Gateway ──HTTP/2──> MCP Server(SSE)
                        └──HTTP/2──> A2A Agent(SSE)

如果 MCP 的 SSE 流因为网络抖动丢包,A2A 的 SSE 流也会被卡住,甚至普通 JSON-RPC 请求也会超时。

解决:

方案 说明
SSE 端点强制 HTTP/1.1 网关对 /mcp/** 和 /a2a/** 的 SSE 路径,下游连接降级为 HTTP/1.1
独立连接池 网关到 MCP 和网关到 A2A 用不同的 HTTP/1.1 连接池
分实例部署 MCP 和 A2A 部署在不同进程/集群,物理隔离

Nginx 示例:

nginx 复制代码
location /mcp/ {
    proxy_pass http://mcp_backend;
    proxy_http_version 1.1;      # 强制 HTTP/1.1
    proxy_set_header Connection "";
    proxy_buffering off;
    proxy_read_timeout 3600s;
}

location /a2a/ {
    proxy_pass http://a2a_backend;
    proxy_http_version 1.1;      # 强制 HTTP/1.1
    proxy_set_header Connection "";
    proxy_buffering off;
    proxy_read_timeout 3600s;
}

八、一句话总结

A2A 和 MCP 可以放在同一个 project 的不同 module,甚至同一个 module。

网关处理的关键不是"能不能同时处理两种协议",而是"先判断 MCP 是否对外"------对外就双协议网关按路径分流,不对外就网关只管 A2A,MCP 走内网并做内网鉴权。

如果做双协议网关:路径识别协议、OAuth2 分 scope 鉴权、分协议限流、统一 traceId 审计、A2A 侧加防循环、SSE 端点防 HTTP/2 队头阻塞。


常见场景:

  1. "MCP 要对外给第三方开发者调用" → 需要 Nginx / APISIX 双协议配置骨架(含 SSE 代理、限流、鉴权)
  2. "MCP 只在内网给自家 Agent 用" → 需要 Spring Cloud Gateway A2A 专属配置 + 内网鉴权骨架(含 mTLS / 内部 JWT / K8s SA Token 方案对比)
  3. "已经有一个 API 网关,想复用" → 需要评估现有网关是否支持 SSE、JSON-RPC、防循环,以及改造点
相关推荐
hpoenixf7 小时前
当每个模块都能说“不”:我们为何要收回 Agent 系统里散落的否决权
agent
遇码8 小时前
流式调用中途断连别直接报错:瞬态故障识别、退避重试与中文根因诊断的搭建方法
大数据·人工智能·ai·rust·数据分析·大模型·agent
无障碍研究院9 小时前
2026TADC|圆桌对话:出海无障碍合规
安全·web安全
misty youth9 小时前
Agent-智能体工程方法论
人工智能·ai·llm·agent·智能体
持敬chijing9 小时前
白盒测试与代码审计-js登录逆向实战
javascript·网络·安全·web安全·网络安全·网络攻击模型
无障碍研究院10 小时前
2026TADC|圆桌对话:软/硬件无障碍的过去、现在、未来
安全
乔旭伟10 小时前
Linux 服务器安全加固实战:SSH、防火墙、补丁与入侵排查
linux·服务器·安全
跨境Jacky11 小时前
MCP 接入 Claude Code 实录: 配置调用排错
跨境电商·mcp·sorftime
裕晟资质规划11 小时前
三类涉密资质延续时限差异分析:军工60日与集成印制3个月的边界
java·服务器·前端·网络·安全