a2a-server://¶
Expose Helix agents (and invocable routes) as an A2A (Agent-to-Agent) server. The consumer stands up an HTTP listener that serves the A2A JSON-RPC protocol at /invoke and an agent card at /.well-known/agent-card.json. Peers — including other Helix nodes via a2a://, or any A2A-compliant client — can list skills and invoke them, with streaming over JSON-RPC events.
Consumer-only — CreateProducer returns an error. Configure as a from: trigger; the steps below the trigger become the request-handling pipeline for every incoming A2A message.
URI shape¶
If the path lacks :port, port 9000 is used.
Parameters¶
| Param | Type | Default | Producer/Consumer/Both | Description |
|---|---|---|---|---|
(path) addr |
string | — | Consumer | Listen address (e.g. 0.0.0.0:9000). |
name |
string | helix-a2a |
Consumer | Agent name (advertised in the agent card). |
description |
string | Helix AI Agent |
Consumer | Agent card description. |
version |
string | 1.0.0 |
Consumer | Version string. |
How skills are discovered¶
The agent card's skills are populated from:
- Registered agents — every agent registered with the
agentcomponent appears as a skill (tags: ["agent", <type>]). - Invocable routes — every workflow whose
from:URI isdirect://orseda://appears as a skill (tags: ["route"]). - Fallback — if neither list yields anything, a single
defaultskill matching the configured name/description is added.
The card also advertises:
URL: http://<addr>/invokePreferredTransport: JSONRPCDefaultInputModes: ["text"],DefaultOutputModes: ["text"]Capabilities.Streaming: true
Per-call exchange shape¶
Every A2A call creates a fresh Exchange(InOut):
| Header | Description |
|---|---|
HelixA2ATaskID |
Task ID assigned by the SDK. |
HelixA2AContextID |
Context ID (conversation thread). |
HelixStream |
Set to "true" — downstream agent/LLM producers use streaming mode automatically. |
The body is the text extracted from the inbound message's parts. workflowID is a2a-task, fromURI is a2a-server://task/<taskID>. Session state is keyed by the A2A ContextID (falling back to the TaskID).
After the pipeline runs, the result is sent back to the client in one of three shapes:
*llm.StreamReader— chunks becomeArtifactUpdateEvents on a single artifact; final chunk hasLastChunk: true, then a finalMessageis sent with the full accumulated text.<-chan string— same streaming flow, but driven by a channel.- anything else — final
Messagecontainingex.In().BodyAsString().
Errors and ex.IsFailed() produce a StatusUpdateEvent{State: TaskStateFailed, Message: <error>}.
Examples¶
Expose a single agent over A2A:
agents:
- id: helper
type: react
model: { provider: openai, model: gpt-4o }
system-prompt: "You are a helpful assistant."
workflows:
- id: a2a-server
trigger:
uri: a2a-server://0.0.0.0:9000?name=helper&description=General+helper
steps:
- target: agent://helper
The card is served at http://localhost:9000/.well-known/agent-card.json; JSON-RPC at http://localhost:9000/invoke.
Helix-on-Helix delegation:
# Node A — serves a translator
workflows:
- id: translator-a2a
trigger: { uri: a2a-server://0.0.0.0:9001?name=translator }
steps:
- target: agent://translator
# Node B — calls Node A
workflows:
- id: call-translator
trigger: { uri: direct://translate }
steps:
- target: a2a://node-a.internal:9001
Bridge to Google A2A clients (the card and JSON-RPC are the standard A2A SDK shapes — they interoperate without code on the client side).
Behavior notes¶
- One pipeline, every call — the same configured pipeline runs for every A2A call. Branch inside the pipeline (using headers or body content) if you need to dispatch.
- Streaming preference — because the consumer sets
HelixStream=true, anagent://step downstream will return a*llm.StreamReader, which the consumer translates into artifact events automatically. Pure non-streaming pipelines also work — the final body is sent as one message. - Session continuity — A2A's
ContextIDbecomes the session key.ex.Property("helix.session.state")persists between calls with that context. - Cancel — A2A's
Cancelis handled by emittingTaskStateCanceledfor the matching task. There's no in-flight processor interrupt today.
Source¶
pkg/component/a2a/server.go, pkg/component/a2a/tools.go
See also¶
- a2a — the client side.
- mcp-server — same idea, MCP protocol.
- agui, a2ui — frontend-focused alternatives.