Skip to content

a2ui://

Expose a Helix agent to a frontend via the Google A2UI (Agent-to-UI) protocol — a streaming JSONL protocol for delivering rich declarative UIs (cards, columns, rows, text, icons) progressively from an agent to a client renderer. The consumer stands up an HTTP endpoint that streams A2UI messages over SSE on GET and accepts client-to-server userAction events on POST.

Reach for a2ui:// when your agent should render structured UI elements (forms, cards, action buttons) rather than plain text. For free-text streaming to a frontend, agui:// is simpler.

Consumer-only — CreateProducer returns an error.

URI shape

a2ui://<host:port>/<path>?<params>
  • <host:port> is the listen address. If port is missing, 4200 is used.
  • <path> is the HTTP path the server serves (default /).

Parameters

Param Type Default Producer/Consumer/Both Description
(path) host string — Consumer Host:port to listen on.
(path) path string / Consumer HTTP path.
name string helix-a2ui Consumer Server identity (informational).
corsOrigin string * Consumer Access-Control-Allow-Origin value. Set to a specific origin in production.

HTTP surface

GET <path>?message=...&surface=...

Opens an SSE stream of A2UI JSONL messages.

  • message — required — the user prompt passed as the exchange body.
  • surface — optional — surface ID for component updates. Defaults to main.
  • X-Helix-Session header (or ?session= query) — optional — opt into session continuity. The server echoes the resolved session id back on the same header.

The server responds with:

Content-Type: text/event-stream
Cache-Control: no-cache
data: {"beginRendering":{...}}
data: {"updateComponents":{...}}
...

POST <path>

Accepts a JSON ClientMessage with a userAction event (button click, form submission). The consumer creates a new exchange with the event as the body and these headers:

Header Description
HelixA2UIEventType The event's type field.
HelixA2UISurfaceID Target surface ID.
HelixA2UIComponentID Component that fired the event.

Pipeline body becomes the JSON response. Request bodies are capped at 10 MB.

Per-call exchange shape (GET path)

Header Description
HelixStream "true" — downstream agent producers use streaming mode.
HelixA2UISurfaceID Surface ID requested by the client.

The pipeline can return one of four body shapes:

Body type Effect
[]a2ui.Message Sent verbatim as SSE data: lines.
*a2ui.Surface The surface's Messages() are sent.
*llm.StreamReader Progressive surface: beginRendering is sent first, then updateComponents is re-sent with the accumulating text each chunk (column → card → text-static).
chan string / <-chan string Same progressive flow, driven by a channel.
anything else (string body) Wrapped in a single column/card/text-static surface.

Errors yield an icon+text "Error" surface via writeSSEError.

Examples

Wire an agent through A2UI:

agents:
  - id: form-builder
    type: react
    model: { provider: openai, model: gpt-4o }

workflows:
  - id: a2ui
    trigger:
      uri: a2ui://0.0.0.0:4200/surface?corsOrigin=https://app.example.com
    steps:
      - target: agent://form-builder

Client calls:

GET /surface?message=Show%20me%20today%27s%20orders&surface=orders

…and renders the streamed JSONL messages with the A2UI JS/TS SDK.

Custom UI surface from a pipeline step (return a *a2ui.Surface):

workflows:
  - id: dashboard
    trigger: { uri: a2ui://0.0.0.0:4200/dashboard }
    steps:
      - transform:
          starlang: |
            %sl 2.0
            output application/x.a2ui-surface
            input payload
            ---
            # build a surface in Starlang (helper builtin)
            buildA2UISurface(payload)

User-action handler (POST):

workflows:
  - id: a2ui-actions
    trigger: { uri: a2ui://0.0.0.0:4201/actions }
    steps:
      - choice:
          - when: header("HelixA2UIEventType") == "submit"
            steps:
              - target: direct://submit-handler
          - otherwise:
              - log: { message: "ignoring unknown a2ui event" }

Behavior notes

  • Two HTTP methods on one path — GET opens the SSE stream; POST handles userAction. OPTIONS returns CORS preflight (204).
  • Server-sent events only — no chunked HTTP fallback; the server requires http.Flusher.
  • Session persistence — when the engine has a session store configured, state in helix.session.state round-trips between calls under the resolved session id.
  • Stream buffering — X-Accel-Buffering: no is set so reverse proxies (nginx) do not buffer SSE chunks.

Source

pkg/component/a2ui/server.go

See also

  • agui — simpler CopilotKit-flavored protocol (plain-text streaming, no rich UI).
  • agent — the underlying agent runtime.
  • mcp-server, a2a-server — sibling protocol servers.