946/946 conformance cases passing A2UI v0.9 · v0.9.1 · v1.0

Agents describe it.
Phoenix renders it.

An Elixir implementation of A2UI, the open protocol for agent-driven interfaces: a pure protocol engine, a Phoenix LiveView renderer and an agent SDK. Agents send interfaces as data; your app draws them with components it trusts.

mix.exs {:a2ui_phoenix, "~> 0.1.0"}
booking agent → renderer
message.json createSurface · v1.0
{  "version": "v1.0",  "createSurface": {    "surfaceId": "booking",    "catalogId": "https://a2ui.org/…/basic/catalog.json",    "components": [      { "id": "root", "component": "Card", "child": "form" },      { "id": "form", "component": "Column",        "children": ["title", "name", "when", "book"] },      { "id": "title", "component": "Text",        "text": "## Trattoria Roma" },      { "id": "name", "component": "TextField", "label": "Your name",        "value": { "@path": "/booking/name" } },      { "id": "when", "component": "DateTimeInput", "label": "When",        "value": { "@path": "/booking/date" } },      { "id": "book", "component": "Button", "child": "go",        "action": { "event": { "name": "book", "context": {          "name": { "@path": "/booking/name" },          "date": { "@path": "/booking/date" } } } } },      { "id": "go", "component": "Text", "text": "Book a table" }    ],    "dataModel": {      "booking": { "name": "Ada", "date": "2026-10-17T19:30" }    }  }}
surface: booking rendered

Trattoria Roma

data model

/booking/name
"Ada"
/booking/date
"2026-10-17T19:30"
v1.0 7 components 4 bindings 1 action strict validation valid
  • A2UI v0.9 · v0.9.1 · v1.0
  • A2A JSON-RPC · streaming
  • MCP tools · resources · apps
  • AG-UI activity snapshots
  • SSE Last-Event-ID
  • WebSocket example agent
  • LiveView phoenix_live_view 1.2
  • JSON Schema draft 2020-12

Conformance

946/946

upstream cases that run, all passing

Protocol

v0.9 → v1.0

v0.9, v0.9.1 and v1.0 on one engine

JSON Schema

1302/1302

required tests of draft 2020-12

JMESPath

892/892

compliance tests

Catalog

18 + 14

basic components and functions

Coverage

97.8%

lines covered, behind a 95% gate

Video

A2UI for Elixir in a minute.

An agent sends an interface, Phoenix renders it, and the user's actions go back. Every surface in it is a recording of the examples in this repository.

Watch on YouTube

01Architecture

Three packages, one protocol

An agent built with a2ui_agent writes A2UI messages, any transport carries them, and a2ui_phoenix draws them in LiveView and sends the user's actions back. Both stand on a2ui_core, which owns the protocol.

your agent

a2ui_agent

  • Generator
  • RequestProcessor
  • DirectJson.Parser
  • Express
  • Builder

messages

  • A2A
  • MCP
  • AG-UI
  • SSE
  • WebSocket

actions · errors · function calls

your phoenix app

a2ui_phoenix

  • Session
  • Live
  • Surface
  • Components.Basic
  • MCPApp

a2ui_core

  • Processor
  • Message
  • Schema
  • Catalog
  • State.DataModel
  • Expression
  • Resolution.NodeResolver
  • RPC

pure Elixir · no processes · Jason its only dependency

a2ui_core

Protocol engine

A2UICore

Messages, schemas, catalogs, surface state, expressions, node resolution and function calls. Pure Elixir: no processes, Jason its only dependency.

  • Version adapters for v0.9, v0.9.1 and v1.0
  • A JSON Schema 2020-12 engine for messages and catalogs
  • A processor that returns effects as data
  • The resolved component tree a renderer draws
  • Function calls between renderer and agent

modules

  • Processor
  • Message
  • Schema
  • Catalog
  • State.DataModel
  • Expression
  • Resolution.NodeResolver
  • RPC
{:a2ui_core, "~> 0.1.0"}

a2ui_phoenix

LiveView renderer

A2UIPhoenix

Draws A2UI surfaces in Phoenix LiveView: the basic catalog as function components, two-way bindings, actions, MCP Apps, and a server-sent events plug.

  • A session that is plain data, owned by your LiveView
  • 18 components with ARIA attributes and a themable stylesheet
  • Your own components, by type
  • Agent function calls with busy states and timeouts
  • Sandboxed MCP Apps and their bridge

modules

  • Session
  • Live
  • Surface
  • Components.Basic
  • MCPApp
  • Plug.SSE
{:a2ui_phoenix, "~> 0.1.0"}

a2ui_agent

Agent SDK

A2UIAgent

Negotiates catalogs with each renderer, writes the model's prompt, and reads the model's output back as validated messages, whole or while it streams.

  • Catalog negotiation from the renderer's capabilities
  • Prompts in the direct JSON and Express formats
  • A streaming parser that emits messages as they complete
  • A component builder and macros
  • An A2A server and plug, with MCP and AG-UI helpers

modules

  • Generator
  • RequestProcessor
  • DirectJson.Parser
  • Express
  • Builder
  • A2A.Plug
  • MCP
  • AGUI
{:a2ui_agent, "~> 0.1.0"}

a2ui_conformance Runs the upstream YAML conformance suites, core, agent and extensions, against all three packages at a pinned commit of the specification. Not published; it keeps the three packages honest.

02Lifecycle

One turn, end to end

From the renderer's capabilities to the user's next action: what happens at each step, and the module that does it.

  1. 01

    Negotiate

    The renderer's capabilities choose the catalogs both sides hold.

    A2UIAgent.Generator
  2. 02

    Prompt

    The chosen catalogs' schemas go into the model's system prompt.

    A2UIAgent.RequestProcessor
  3. 03

    Stream

    The model's output is read chunk by chunk; each message leaves as it completes.

    A2UIAgent.DirectJson.Parser
  4. 04

    Validate

    Every message is checked against the protocol's schemas and the catalogs.

    A2UIAgent.PayloadValidator
  5. 05

    Render

    The processor applies each message, and LiveView draws the surfaces.

    A2UIPhoenix.Surface
  6. 06

    Respond

    Actions, errors and function calls go back to the agent.

    A2UIPhoenix.Live

03Features

Everything the protocol asks for

Every message, catalog, binding, function and transport of A2UI, from v0.9 to v1.0, and the checks between them, grouped by area.

Protocol and messages

06
  • Message adapters for v0.9, v0.9.1 and v1.0: decoded into version-neutral operations and encoded back
  • Every message checked against the specification's JSON schemas
  • createSurface, updateComponents, updateDataModel and deleteSurface, with v1.0 inline components and data
  • Renderer-to-agent messages: action (with userMessage), error, callAgentFunction, rendererFunctionResponse
  • Renderer capabilities and data models for the transport's metadata
  • v1.0 reserved @ keys, and @@ escaping for literal ones

messages× protocol versions

message from v0.9v0.9.1v1.0
createSurface agent
updateComponents agent
updateDataModel agent
deleteSurface agent
callRendererFunction agent — —
agentFunctionResponse agent — —
action renderer
error renderer
callAgentFunction renderer — —
rendererFunctionResponse renderer — —

core

Data binding and expressions

05
  • A data model addressed by JSON Pointer, with change notifications
  • Two-way bindings, list templates with relative paths, and @index
  • ${…} templates in formatString, with nested function calls
  • Numbers, currencies, TR35 dates and plurals, behind a pluggable formatter
  • Checks with required, regex, length, numeric, email, and, or and not; ValidationResult in v1.0

dynamic values→ resolved

{"@path": "/booking/name"} "Ada"
{"@call": "formatString", "args": {"value": "Hi ${/booking/name}"}} "Hi Ada"
"children": {"path": "/places", "componentId": "card"} a card per place

core

Catalogs and components

06
  • The basic catalog of each version: 18 components and 14 functions, implemented
  • Catalogs of your own: read, validated, and written back in their key order
  • catalogId per component and per function call: several catalogs on one surface (v1.0)
  • Composition constraints: allowedParents and allowedChildren
  • Inline catalogs, announced in the renderer's capabilities
  • Catalog names checked as UAX #31 identifiers

basic/catalog.jsonv1.0

  • catalogIdhttps://a2ui.org/…/basic/catalog.json
  • components18 Text Image Icon …
  • functions14 required regex …
  • instructionsrules and examples for the model

corephoenix

Rendering in LiveView

06
  • Surfaces as LiveComponents over a session that is plain data
  • The 18 basic components as function components, with ARIA attributes
  • Actions, two-way bindings and local UI state such as tabs and modals
  • Placeholders for children still streaming, and stable node identity
  • Your own components by type, and Markdown through the renderer you choose
  • v0.9 surface themes, and a stylesheet driven by CSS custom properties

NodeResolversurface booking

  • Card #root
  • Column #form
  • Text #title "Trattoria Roma"
  • TextField #name ⇄ /booking/name
  • DateTimeInput #when ⇄ /booking/date
  • Button #book → action book

phoenix

Functions and RPC

05
  • callRendererFunction answered with rendererFunctionResponse, under allowedCallers and requiresUserActivation
  • Calls a surface cannot run sent to the agent as callAgentFunction, with busy states and timeouts
  • Function-call actions, and openUrl restricted to http and https
  • RPC as a pure state machine: timeouts are events the host feeds in
  • Functions reserved for the agent, or needing a user gesture, refused inside the renderer

function callsv1.0

rendereragent
callAgentFunction
agentFunctionResponse
callRendererFunction
rendererFunctionResponse

corephoenix

Agent SDK

06
  • Catalog negotiation from the renderer's capabilities, inline catalogs included
  • Catalog transformers that prune components and functions
  • Prompts written byte for byte as the reference SDKs write them
  • A streaming parser that emits each message as it completes, and reads near-JSON
  • Express: a compact notation, its compiler and decompiler, and skills
  • A component builder in Elixir, and macros the model expands into catalog components

Parser.parse_chunk/3three chunks

  1. chunk 1 Here are two places. <a2ui-json>[{"version": "v1.0", "createSurface": {…}}, {"version": "v1.0", "updateCompo text createSurface
  2. chunk 2 nents": {"surfaceId": "list", "components": […]}} updateComponents
  3. chunk 3 , {"version": "v1.0", "updateDataModel": {…}}]</a2ui-json> Want to book? updateDataModel text

agent

Transports

05
  • A2A JSON-RPC: message/send and message/stream, extension negotiation, and a Plug
  • MCP: A2UI messages in tool results and resources
  • AG-UI: activity snapshots, events and user actions
  • Server-sent events with event ids and Last-Event-ID resumption
  • WebSocket, shown in the example agent

agentphoenix

MCP tools and apps

04
  • callMcpTool through your MCP client, with targetDataPath
  • McpApp: MCP Apps in a double-iframe sandbox under a Content Security Policy
  • The app bridge: data.paths, allowedTools, allowedFunctions, model context and messages
  • A complete JMESPath, with split, regexCapture, regexReplace and updateDataModel

corephoenix

Validation and resilience

05
  • Strict validation, and a relaxed mode for surfaces that are still streaming
  • Integrity checks: missing root, dangling references, cycles, orphans, depth limit
  • Errors as values, reported to the agent with a JSON Pointer to the field
  • Accessibility checks, and aria-busy on components waiting for the agent
  • JSON Schema draft 2020-12 with ECMA-262 regular expressions

agent sendsstrict

{"version": "v1.0", "updateComponents": {
  "surfaceId": "booking", "components": [
    {"id": "title", "component": "Text",
     "text": "Hi", "variant": "h2"}]}}

renderer reportsrejected

{"version": "v1.0", "error": {
  "code": "VALIDATION_FAILED",
  "surfaceId": "booking",
  "path": "/components/0/variant",
  "message": "Validation failed for component 'Text': components.title.variant: 'h2' is not one of [\"caption\", \"body\"]"}}

corephoenix

04Catalog

The basic catalog, complete

All 18 components and 14 functions of the basic catalog (v0.9.1 and v1.0), drawn by a2ui_phoenix and run by a2ui_core, with the MCP catalog and catalogs of your own beside them.

  • Text Content
  • Image Content
  • Icon Content
  • Video Content
  • AudioPlayer Content
  • Row Layout
  • Column Layout
  • List Layout
  • Card Layout
  • Tabs Layout
  • Modal Layout
  • Divider Layout
  • Button Input
  • TextField Input
  • CheckBox Input
  • ChoicePicker Input
  • Slider Input
  • DateTimeInput Input

14 functions

basic catalog
Checks
requiredregexlengthnumericemail
Formatting
formatStringformatNumberformatCurrencyformatDatepluralize
Logic
andornot
Actions
openUrl
System
@indexv1.0, in list templates

MCP catalog

v1.0
  • McpApp
  • callMcpTool
v0.9
  • callMcpTool
  • jmespath
  • split
  • regexCapture
  • regexReplace
  • updateDataModel

Your own catalogs

Validated like the basic one: components drawn by your function components, functions run by your code. catalogId picks a catalog per component and per call.

05Transports

Any wire, any model

A2UI does not care how messages travel. The packages carry them over the protocols agents already speak, and read them from what models write.

transport how A2UI travels where
A2A message/send answers with the finished task, message/stream with server-sent events as the model writes. The A2UI extension is negotiated per request; capabilities ride in the message metadata. A2UIAgent.A2A.Plug
MCP Layouts as application/a2ui+json resources, data in tool results, linked with _meta.ui. Actions and errors come back as calls of the a2ui_action and a2ui_error tools. A2UIAgent.MCP
AG-UI Each surface as ACTIVITY_SNAPSHOT events of the a2ui-surface activity; a user's action starts a run, under forwardedProps.a2uiAction. A2UIAgent.AGUI
SSE One message per event, with event ids, resumable with Last-Event-ID, for renderers outside LiveView. A2UIPhoenix.Plug.SSE
WebSocket JSON frames in both directions, as the example agent serves them. examples/restaurant_agent

Direct JSON

inference format

Message lists between <a2ui-json> tags, read leniently and chunk by chunk, so surfaces build while the model writes.

model output json
<a2ui-json>
[{"version": "v1.0", "createSurface": {
    "surfaceId": "form", "catalogId": "…"}},
 {"version": "v1.0", "updateComponents": {
    "surfaceId": "form", "components": [
      {"id": "root", "component": "Column",
       "children": ["name", "send"]},
      {"id": "name", "component": "TextField",
       "label": "Name", "value": {"@path": "/name"}},
      …]}}]
</a2ui-json>

Express

inference format

A compact notation between <a2ui> tags, compiled into validated messages and decompiled back for the model's history.

model output express
<a2ui>
surface("form")
root = Column([name, send])
name = TextField("Name", $/name)
send = Button(Text("Send"), action=Event("send", {name: $/name}))
</a2ui>

06Code

A few lines to get going

Render surfaces in a LiveView, serve an agent over A2A, or drive the protocol engine directly.

  1. 01Add the packages

    mix.exs elixir
    def deps do
      [
        # The renderer, with a2ui_core
        {:a2ui_phoenix, "~> 0.1.0"},
        # The agent SDK
        {:a2ui_agent, "~> 0.1.0"}
      ]
    end

    On Hex: a2ui_core, a2ui_phoenix and a2ui_agent.

  2. 02Add the assets

    Import the renderer's a2ui_phoenix.js into app.js and its a2ui_phoenix.css into app.css.

  3. 03Render, or serve

    Render surfaces in a LiveView, or serve an agent over A2A, as the samples beside this show.

  4. 04Read the guides

elixir
>= 1.17
jason
the core's only dependency
phoenix_live_view
~> 1.2, for the renderer
plug
optional, for the A2A plug

lib/my_app_web/live/agent_live.exRender in LiveView

defmodule MyAppWeb.AgentLive do
  use MyAppWeb, :live_view

  alias A2UIPhoenix.{Live, Session}

  def mount(_params, _session, socket) do
    # Every component checked against its catalog, as it streams in.
    validation = A2UICore.Validation.Config.relaxed()
    catalogs = [A2UICore.Catalog.Basic.catalog("v1.0")]
    session = Session.new(catalogs, validation: validation)
    {:ok, Live.init(socket, session, on_send: &send_to_agent/2)}
  end

  # Messages from the agent, however they arrive.
  def handle_info({:agent, payload}, socket) do
    {_result, socket} = Live.handle_message(socket, payload)
    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <.live_component
      :for={surface <- Session.surfaces(@a2ui)}
      module={A2UIPhoenix.Surface}
      id={"surface-" <> surface.id}
      session={@a2ui}
      surface_id={surface.id}
    />
    """
  end

  # Actions, errors and function calls, for the agent.
  defp send_to_agent(message, socket) do
    Phoenix.PubSub.broadcast(MyApp.PubSub, "a2ui:renderer", {:renderer, self(), message})
    socket
  end
end

lib/my_agent.exServe an agent

defmodule MyAgent do
  @behaviour A2UIAgent.A2A.Agent

  alias A2UIAgent.A2A.Request

  # No catalog negotiated: answer in text.
  @impl true
  def run(%Request{processor: nil, text: text}), do: {:ok, MyModel.reply(text)}

  # The renderer's capabilities chose the catalogs; the prompt holds their schemas.
  def run(%Request{processor: processor, text: text, events: events}) do
    {:ok, prompt} = A2UIAgent.RequestProcessor.prompt(processor)
    # A string, or the chunks as the model streams them.
    {:ok, MyModel.stream(prompt, text, events)}
  end

  @impl true
  def options do
    basic = A2UIAgent.CatalogConfig.new(A2UICore.Catalog.Basic.catalog("v1.0"))
    [generator: A2UIAgent.Generator.new([basic])]
  end

  @impl true
  def card do
    %{"name" => "My agent", "capabilities" => %{"extensions" => [A2UIAgent.A2A.agent_extension("1.0")]}}
  end
end

# In a Phoenix router or a Plug.Router:
forward "/a2a", A2UIAgent.A2A.Plug, agent: MyAgent

iexProcess messages

alias A2UICore.{Processor, Validation}

catalog = A2UICore.Catalog.Basic.catalog("v1.0")
processor = Processor.new([catalog], validation: Validation.Config.strict())

{:ok, processor, effects} =
  Processor.process(processor, [
    %{"version" => "v1.0",
      "createSurface" => %{"surfaceId" => "main", "catalogId" => catalog.catalog_id}},
    %{"version" => "v1.0",
      "updateComponents" => %{
        "surfaceId" => "main",
        "components" => [
          %{"id" => "root", "component" => "Column", "children" => ["greeting"]},
          %{"id" => "greeting", "component" => "Text", "text" => %{"@path" => "/name"}}
        ]}},
    %{"version" => "v1.0",
      "updateDataModel" => %{"surfaceId" => "main", "path" => "/name", "value" => "Ada"}}
  ])

effects
#=> [{:surface_created, "main"}, {:components_updated, "main", ["root", "greeting"]},
#    {:data_model_updated, "main", "/name"}]
elixir UTF-8 LF · spaces: 2

07Examples

Three apps to run and read

In the repository's examples/: an agent, a LiveView gallery and a terminal renderer that talk to each other, each with its README and tests. Clone a2ui-elixir/a2ui to run them. Below, three of their runs, recorded.

mix gallery.replays — the agents and A2UIPhoenix run for real; every surface below is the HTML the renderer drew at that step

examples/restaurant_agent

The restaurant agent over A2A and a renderer session: actions, agent and renderer functions, two-way bindings.

conversation rendered by A2UIPhoenix
you

Find Italian restaurants

agent

Here are 3 places. Pick one to book a table.

surfacerestaurants-m1

Italian restaurants near you

3 places nearby, nearest first.
Spaghetti al pomodoro with basil and parmesan

Trattoria Roma

Italian · $$ · 0.8 km 4.6 · 412 reviews
A margherita on a wooden board

Pizzeria Napoli

Italian · $ · 1.1 km 4.8 · 1033 reviews
Grilled river trout with lemon and herbs

Osteria del Ponte

Italian · $$$ · 2.4 km 4.2 · 198 reviews
messages agent 1 · renderer 0
  1. agent createSurface restaurants-m1 · 13 components
    {
      "version": "v1.0",
      "createSurface": {
        "surfaceId": "restaurants-m1",
        "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json",
        "components": [
          {
            "id": "root",
            "component": "Column",
            "children": [
              "title",
              "summary",
              "list"
            ]
          },
          {
            "id": "title",
            "component": "Text",
            "text": "## Italian restaurants near you"
          },
          {
            "id": "summary",
            "component": "Text",
            "text": {
              "@call": "pluralize",
              "args": {
                "one": "One place nearby.",
                "other": {
                  "@call": "formatString",
                  "args": {
                    "value": "${/count} places nearby, nearest first."
                  }
                },
                "value": {
                  "@path": "/count"
                }
              }
            },
            "variant": "caption"
          },
          {
            "id": "list",
            "component": "List",
            "children": {
              "componentId": "card",
              "path": "/restaurants"
            },
            "direction": "horizontal"
          },
          {
            "id": "card__image_1",
            "component": "Image",
            "description": {
              "@path": "dish"
            },
            "fit": "cover",
            "url": {
              "@path": "image"
            },
            "variant": "mediumFeature"
          },
          {
            "id": "card__text_1",
            "component": "Text",
            "text": {
              "@call": "formatString",
              "args": {
                "value": "#### ${name}"
              }
            },
            "variant": "body"
          },
          {
            "id": "card__text_2",
            "component": "Text",
            "text": {
              "@call": "formatString",
              "args": {
                "value": "${cuisine} · ${price} · ${formatNumber(value: ${distance}, decimals: 1)} km"
              }
            },
            "variant": "caption"
          },
          {
            "id": "card__column_2",
            "component": "Column",
            "align": "stretch",
            "children": [
              "card__image_1",
              "card__text_1",
              "card__text_2",
              "stars"
            ],
            "justify": "start"
          },
          {
            "id": "card__text_3",
            "component": "Text",
            "text": "Book a table",
            "variant": "body"
          },
          {
            "id": "card__button_1",
            "component": "Button",
            "action": {
              "event": {
                "context": {
                  "name": {
                    "@path": "name"
                  },
                  "restaurantId": {
                    "@path": "id"
                  }
                },
                "name": "book"
              }
            },
            "child": "card__text_3",
            "variant": "default"
          },
          {
            "id": "card__column_1",
            "component": "Column",
            "align": "stretch",
            "children": [
              "card__column_2",
              "card__button_1"
            ],
            "justify": "spaceBetween"
          },
          {
            "id": "card",
            "component": "Card",
            "child": "card__column_1"
          },
          {
            "id": "stars",
            "component": "Rating",
            "catalogId": "https://example.com/a2ui/catalogs/restaurant/v1/catalog.json",
            "label": {
              "@call": "formatString",
              "args": {
                "value": "${rating} · ${reviews} reviews"
              }
            },
            "value": {
              "@path": "rating"
            }
          }
        ],
        "dataModel": {
          "count": 3,
          "restaurants": [
            {
              "cuisine": "Italian",
              "dish": "Spaghetti al pomodoro with basil and parmesan",
              "distance": 0.8,
              "id": "trattoria-roma",
              "image": "http://127.0.0.1:4001/images/trattoria-roma.svg",
              "name": "Trattoria Roma",
              "price": "$$",
              "rating": 4.6,
              "reviews": 412,
              "stars": "★★★★★"
            },
            {
              "cuisine": "Italian",
              "dish": "A margherita on a wooden board",
              "distance": 1.1,
              "id": "pizzeria-napoli",
              "image": "http://127.0.0.1:4001/images/pizzeria-napoli.svg",
              "name": "Pizzeria Napoli",
              "price": "$",
              "rating": 4.8,
              "reviews": 1033,
              "stars": "★★★★★"
            },
            {
              "cuisine": "Italian",
              "dish": "Grilled river trout with lemon and herbs",
              "distance": 2.4,
              "id": "osteria-del-ponte",
              "image": "http://127.0.0.1:4001/images/osteria-del-ponte.svg",
              "name": "Osteria del Ponte",
              "price": "$$$",
              "rating": 4.2,
              "reviews": 198,
              "stars": "★★★★☆"
            }
          ]
        }
      }
    }

You ask for Italian restaurants. The agent answers with text and a surface: three cards, each with the restaurant catalog's Rating.

step 1 / 9

Restaurant agent

examples/restaurant_agent

Finds restaurants and books tables with A2UI surfaces, over A2A, MCP, AG-UI, server-sent events and WebSocket. Agent functions, renderer functions, Express and v0.9 included.

  • A2A
  • MCP
  • AG-UI
  • SSE
  • WebSocket
  • Express
  • v0.9
terminal shell
cd examples/restaurant_agent
mix deps.get
mix run --no-halt     # http://127.0.0.1:4001

Gallery

examples/gallery

A LiveView app with ten scenarios that cover the protocol, the 43 upstream example surfaces, a live chat with the agent, and LiveDashboard metrics.

  • 10 scenarios
  • 43 examples
  • chat over A2A
  • LiveDashboard
terminal shell
cd examples/gallery
mix deps.get
mix phx.server        # http://localhost:4000

Terminal client

examples/terminal_client

A renderer that draws surfaces as text, on a2ui_core alone, with a client for every transport the agent serves.

  • a2ui_core only
  • 6 transports
  • v1.0 and v0.9
terminal shell
cd examples/terminal_client
mix deps.get
mix chat "Find Italian restaurants"

examples/gallery — the scenarios, each a scripted agent with its controls and an inspector of every message

  1. 01

    Component gallery

    All 18 components, their variants and accessibility attributes

  2. 02

    Forms and validation

    Checks on every input, sendDataModel, a form swapped for a summary

  3. 03

    Data binding

    Templates, relative paths, @index, formatting, live updates

  4. 04

    Surfaces

    Surfaces opened and closed, components replaced, userMessage, openUrl

  5. 05

    Functions and RPC

    Calls both ways, with answers, errors, timeouts and refusals

  6. 06

    Custom catalogs

    Components and functions of the gallery's own catalog

  7. 07

    Streaming

    A surface built while the model's text arrives, and Express

  8. 08

    MCP tools and apps

    callMcpTool, A2UI in tool results, a sandboxed MCP App

  9. 09

    Errors and recovery

    Faulty messages: rejected, reported and recovered from

  10. 10

    Protocol v0.9

    The v0.9 wire format on the same renderer, with its theme

08Conformance

Checked against the specification, case by case

The upstream YAML suites run against all three packages at a pinned commit. Every case that applies runs and passes; the others are skipped for a stated reason.

every change passes

  1. format mix format --check-formatted
  2. compile mix compile --warnings-as-errors
  3. credo mix credo --strict
  4. dialyzer mix dialyzer
  5. coverage mix coveralls --umbrella, 95% minimum
  6. conformance The upstream suites, at a pinned commit
  7. browser The renderer's script and sandbox, under Node
  8. docs mix docs --warnings-as-errors; guides whose examples run

946/946

upstream cases that apply, all passing. The suites hold 1144 cases; the other 198 do not apply to these packages:

a2ui-project/a2ui@5d74ffb

  • 118 Protocol v0.8. The older wire format. The packages implement v0.9, v0.9.1 and v1.0. core · agent/direct_json · agent/legacy · extensions/a2a
  • 67 Superseded rules. Cases of the earlier agent interface whose rules the blueprint suites replaced. The Python SDK no longer runs them and the TypeScript SDK skips them too. agent/legacy
  • 11 Google's Python ADK. Adapters to the Python Agent Development Kit, which has no Elixir runtime. extensions/adk
  • 2 Elemental and Atom. Two other inference formats. The packages implement direct JSON and Express. agent/legacy
suite run passing skipped
core 533/572 skipped: 39 for protocol v0.8
agent (shared) 107/107
agent/direct_json 124/163 skipped: 39 for v0.8 catalogs
agent/express 115/115
agent/builder 11/11
agent/macros 18/18
agent/legacy 25/133 skipped: 67 replaced by the blueprint suites, 39 for v0.8, 2 in other formats
extensions/a2a 13/14 skipped: 1 v0.8 payload
extensions/adk 0/11 skipped: adapters for Google's Python ADK

Beyond the suites

  • The JSON Schema Test Suite, draft 2020-12: all 1302 required tests
  • The JMESPath compliance suite: all 892 tests
  • The 86 upstream example bundles: decoded, encoded back, validated and rendered in LiveView
  • Property tests for JSON Pointer and the data model

A pinned specification

Measured against a2ui-project/a2ui at 5d74ffb. Where the packages deliberately differ from the reference implementations, the difference is written down with its reason.

→Get started

Give your agents an interface.

Start with the examples, then add a renderer to your Phoenix app or put A2UI in your agent.

terminal shell
git clone https://github.com/a2ui-elixir/a2ui.git
cd a2ui/examples/gallery
mix deps.get
mix phx.server        # http://localhost:4000