Skip to content跳到正文
All works全部作品
Code代码

Universal Agent BridgeUniversal Agent Bridge

One control plane for every MCP server, A2A agent and custom runtime — a single protocol, DAG orchestration with streaming handoff, and governance in one place.给所有 MCP 服务、A2A agent 和自定义运行时做一个控制面——统一协议、带流式交接的 DAG 编排,治理集中在一处。

Stack技术栈
TypeScript · Node.js · JSON-RPC · MCP · A2A · Monorepo

Any team running agents is running more than one system: a few MCP tool servers, an A2A agent or two, maybe OpenClaw or Hermes, plus their own HTTP agents. Each speaks a different API for sessions, models, memory, artifacts and controls. There is no unified way to route across them, orchestrate a workflow that spans them, or govern them — auth, limits, audit, health — in one place.

This is that layer. Register each runtime as an adapter, clients speak one protocol, and the bridge handles routing, orchestration and the cross-cutting concerns. Instead of every client wiring to every runtime — N x M — everyone meets at the bridge: N + M.

Deliberately not a multi-agent system

This is multi-agent infrastructure, not a multi-agent system. The agents live behind adapters; the bridge unifies and governs them. Kubernetes is not a microservice — it is the platform that runs many. Same relationship here.

        Client / Dashboard / CLI
                   |
        +----------------------+
        | Universal Agent      |  sessions | routing | health | limits
        | Bridge Core          |  audit | resources | persistence | metrics
        +----------------------+
                   |
             Adapter Registry
                   |
   OpenClaw | Hermes | Pi | Host | A2A | MCP | HTTP JSON-RPC

The interesting problem: streaming across a protocol boundary

Single calls are easy. The hard part is a plan whose steps live on different runtimes and still need to hand work to each other while it is being produced.

A step marked stream: true runs through its adapter's streaming path; the bridge drives the stream, accumulates the text, and exposes it downstream as ${steps.<id>.stream.text}. A streamFrom dependency lets a step start the moment its source begins streaming instead of waiting for it to finish.

{
  "id": "heterogeneous_pipeline",
  "mode": "dag",
  "steps": [
    {
      "id": "generate",
      "runtime": "openclaw",
      "method": "chat.stream",
      "stream": true,
      "params": { "message": "In one sentence, what is a multi-agent bridge?" }
    },
    {
      "id": "relay",
      "runtime": "a2a",
      "dependsOn": ["generate"],
      "method": "a2a.message.send",
      "params": { "text": "${steps.generate.stream.text}" }
    }
  ]
}

That plan is verified end to end: a streamed OpenClaw turn feeds an A2A JSON-RPC agent, whose structured reply feeds an HTTP JSON-RPC agent — three different runtime protocols coordinated in one DAG.

Run it

The runtimes below are mocked, but the scheduling is the real thing. Flip the dependency mode and watch the second step: dependsOn holds it until the stream closes, streamFrom releases it at the first token so its work overlaps with producing the input.

Dependency

0.00s
Elapsed
3.89s
0.72s slower
generate
Idle
relay
Idle
summarize
Idle
generateIdle

openclaw

chat.stream

relayIdle

a2a

a2a.message.send

—

summarizeIdle

http-agent

system.ping

—

Three runtimes, three protocols, one DAG. streamFrom releases relay at the first token instead of the last, so the work that does not need the whole input overlaps with producing it.

The saving is exactly the overlap — no more, and it only exists for steps whose work does not need the complete input. That is the whole argument for streamFrom, and it is easier to see in two seconds than to read.

What the core carries

Sticky sessions that bind to a runtime on first call. Routing by runtime id, by capability, or by session. DAG plans with dependsOn, conditional skips, dataflow templating between steps, cancellation, timeout and resume. {{variable}} plan templates, so a workflow can be defined once and distributed as a parameterised recipe. Per-runtime circuit breaking with retries and failover, concurrency limits, an audit log, a normalised memory/artifact resource index, batched atomic persistence, and metrics with a dependency-free span-exporter hook.

Thirteen packages, 67 integration tests.

任何在跑 agent 的团队,跑的都不止一个系统:几台 MCP 工具服务、一两个 A2A agent、 可能还有 OpenClaw 或 Hermes,加上自己写的 HTTP agent。 它们在会话、模型、记忆、产物和控制上各讲各的 API。 没有统一的办法在它们之间路由、编排一个跨越它们的工作流, 也没办法把鉴权、限流、审计、健康检查这些治理放到一个地方。

这就是那一层。把每个运行时注册成一个适配器,客户端只讲一种协议, 桥来负责路由、编排和这些横切关注点。 不用每个客户端都接到每个运行时上——N x M——所有人在桥这里汇合:N + M。

刻意不是一个多 Agent 系统

这是多 agent 基础设施,不是一个多 agent 系统。 agent 们待在适配器后面;桥负责统一和治理它们。 Kubernetes 不是一个微服务——它是运行很多微服务的平台。这里是同样的关系。

        客户端 / 控制台 / CLI
                   |
        +----------------------+
        | Universal Agent      |  会话 | 路由 | 健康 | 限流
        | Bridge Core          |  审计 | 资源 | 持久化 | 指标
        +----------------------+
                   |
              适配器注册表
                   |
   OpenClaw | Hermes | Pi | Host | A2A | MCP | HTTP JSON-RPC

有意思的问题:跨协议边界的流式传输

单次调用很容易。难的是一个计划,它的各个步骤分布在不同的运行时上, 而且还要在内容正在产生的过程中互相交接。

标了 stream: true 的步骤会走它所在适配器的流式路径; 桥负责驱动这个流、累积文本,并以 ${steps.<id>.stream.text} 的形式暴露给下游。 streamFrom 依赖让一个步骤在它的数据源刚开始流式输出时就启动,而不是等它结束。

{
  "id": "heterogeneous_pipeline",
  "mode": "dag",
  "steps": [
    {
      "id": "generate",
      "runtime": "openclaw",
      "method": "chat.stream",
      "stream": true,
      "params": { "message": "In one sentence, what is a multi-agent bridge?" }
    },
    {
      "id": "relay",
      "runtime": "a2a",
      "dependsOn": ["generate"],
      "method": "a2a.message.send",
      "params": { "text": "${steps.generate.stream.text}" }
    }
  ]
}

这个计划是端到端验证过的:一个流式的 OpenClaw 轮次喂给一个 A2A JSON-RPC agent, 后者的结构化回复再喂给一个 HTTP JSON-RPC agent——三种不同的运行时协议在一个 DAG 里协调。

跑一下

下面的运行时是 mock 的,但调度是真的。 切换依赖模式,盯住第二个步骤:dependsOn 会一直把它按住直到流关闭, streamFrom 在第一个 token 就把它放出去,让它的工作和输入的生产重叠起来。

Dependency

0.00s
Elapsed
3.89s
0.72s slower
generate
Idle
relay
Idle
summarize
Idle
generateIdle

openclaw

chat.stream

relayIdle

a2a

a2a.message.send

—

summarizeIdle

http-agent

system.ping

—

Three runtimes, three protocols, one DAG. streamFrom releases relay at the first token instead of the last, so the work that does not need the whole input overlaps with producing it.

省下来的正好就是那段重叠——不多不少,而且只对那些不需要完整输入才能干活的步骤成立。 这就是 streamFrom 的全部理由,两秒钟看明白比读一遍容易。

核心里都有什么

首次调用即绑定运行时的粘性会话。按运行时 id、按能力、或按会话路由。 支持 dependsOn、条件跳过、步骤间数据流模板、取消、超时和恢复的 DAG 计划。 {{variable}} 计划模板,让一个工作流定义一次就能作为带参数的配方分发出去。 按运行时的熔断,带重试和故障转移、并发限制、审计日志、 归一化的记忆/产物资源索引、批量原子持久化, 以及带零依赖 span-exporter 钩子的指标。

十三个包,67 个集成测试。

Multi-AgentInfrastructureJSON-RPCMCPTypeScript多 Agent基础设施JSON-RPCMCPTypeScript