Interop
Expose agents through A2A, MCP, and AG-UI without changing their internal messages
Bridges connect external A2A, MCP, and AG-UI clients to agents that use AGDX. They map shared fields into AGDX messages. Protocol-specific data stays unchanged in the body.
A2A (a2a-bridge)
A2aBridge serves an internal agent through A2A JSON-RPC. Its v1.0 Agent Card is available at /.well-known/agent-card.json.
| A2A method | Mapping |
|---|---|
SendMessage | Publishes a typed AGDX command on a fresh task conversation - the task id is the conversation. Returns Submitted. |
SendStreamingMessage | Same publish as SendMessage - the stream is consumed log-natively, never re-emitted as SSE. |
GetTask | Reads the reply topic and maps the answering response/error envelope to the A2A task. Working until one lands. |
CancelTask | Publishes an AGDX error terminal (Cancelled). Returns Canceled. |
const bridge = new A2aBridge(
laser,
AgentId.new("a2a-gateway"),
AgentTopic.Commands,
AgentTopic.Responses
)
const card = bridge.card()let bridge = Arc::new(A2aBridge::new(
laser.clone(),
"a2a-gateway".parse()?,
AgentTopic::Commands,
AgentTopic::Responses,
));
let card = bridge.card();
let app = bridge.router();bridge = laser.a2a_bridge(
"a2a-gateway",
ls.Topics.COMMANDS,
ls.Topics.RESPONSES,
)
card = bridge.card()Rust's optional HTTP support provides router() for the JSON-RPC and Agent Card routes. TypeScript and Python expose bridge operations for the application's HTTP adapter.
With sign, A2aBridge::signed_card adds a detached JWS signature to the card. Clients can use it to authenticate the card before trusting it.
MCP (mcp-bridge)
McpBridge maps MCP JSON-RPC tool calls to AGDX commands. It waits for a reply with the matching correlation, the identifier that links a reply to its request.
| MCP method | Mapping |
|---|---|
initialize | Echoes the client's protocol version and advertises only the capabilities actually served. |
tools/list / tools/call | Tools configured via with_tool - a call publishes an AGDX command and renders the correlated reply as a tool result. |
resources/list / resources/read | Resources configured via with_resource, served from config. |
prompts/list / prompts/get | Prompts configured via with_prompt. |
const mcp = new McpBridge(
laser,
AgentId.new("mcp-gateway"),
AgentTopic.ToolCalls,
AgentTopic.ToolResults,
"my-server"
).withTool(
"ask",
"ask the assistant",
{ type: "object" }
)
const tools = mcp.listTools()let mcp = Arc::new(
McpBridge::new(
laser.clone(),
"mcp-gateway".parse()?,
AgentTopic::ToolCalls,
AgentTopic::ToolResults,
"my-server",
)
.with_tool(
"ask",
Some("ask the assistant".into()),
serde_json::json!({ "type": "object" }),
),
);
let tools = mcp.list_tools();
let app = mcp.router();mcp = laser.mcp_bridge(
"mcp-gateway",
ls.Topics.TOOL_CALLS,
ls.Topics.TOOL_RESULTS,
"my-server",
tools=[
{
"name": "ask",
"description": "ask the assistant",
"input_schema": {"type": "object"},
}
],
)
tools = mcp.list_tools()Rust's mcp-http feature supplies an Axum router(). Connect TypeScript or Python protocol operations to the application's HTTP framework.
AG-UI (agui)
AG-UI provides state and events for frontends. publish_state_snapshot and publish_state_delta record shared state and RFC 6902 patches. reconstruct_state applies a snapshot and later patches up to a historical offset.
agui_events converts a conversation into AG-UI events. Chat chunks become TEXT_MESSAGE_*, and reasoning becomes REASONING_MESSAGE_*. Tool arguments map to TOOL_CALL_START, ARGS, and END. Status maps to RUN_STARTED and RUN_FINISHED, while terminal errors map to RUN_ERROR.
await laser.publishStateSnapshot(
AgentTopic.Audit,
AgentId.new("ui"),
conversation,
{ count: 0 }
)
const events = await laser.aguiEvents(
conversation,
AgentTopic.LlmIo
)laser
.publish_state_snapshot(
AgentTopic::Audit,
"ui".parse()?,
conversation,
&serde_json::json!({ "count": 0 }),
)
.await?;
let events = laser
.agui_events(conversation, AgentTopic::LlmIo)
.await?;await laser.publish_state_snapshot(
ls.Topics.AUDIT,
"ui",
conversation_id,
{"count": 0},
)
events = await laser.agui_events(
conversation_id,
ls.Topics.LLM_IO,
)Human-in-the-loop
Agdx::request_input pauses an agent for a human decision. AgentCtx::respond_input supplies the decision. Both use existing command and response operations and need no bridge or new wire type.
const decision = await laser
.agdx(
AgentTopic.HumanInput,
AgentId.new("orchestrator"),
conversation
)
.requestInput(
AgentTopic.Responses,
utf8("approve a $500 credit?"),
300_000
)let decision = laser
.agdx(
AgentTopic::HumanInput,
"orchestrator".parse()?,
conversation.into(),
)
.request_input(
AgentTopic::Responses,
b"approve a $500 credit?".to_vec(),
Duration::from_secs(300),
)
.await?;decision = await laser.agdx(
ls.Topics.HUMAN_INPUT,
"orchestrator",
conversation_id,
).request_input(
ls.Topics.RESPONSES,
b"approve a $500 credit?",
timeout_secs=300,
)Authorization
Add authentication in the hosting HTTP framework. The JSON-RPC bridges do not authenticate HTTP requests.
with_default_stream in Rust, withDefaultStream in TypeScript, and with_stream in Python select a stream on the same connection. Use separate credentials when Iggy permissions must isolate access. See Governance for audience and step-up requirements.
Running it
Bridges work with Iggy and do not depend on a model provider. Use Laser Stack if the application also needs managed operations.