# A2UI > A2UI (Agent-to-User Interface) is an open-source generative UI framework and streaming protocol. Agents compose declarative surfaces, registered catalog components, and reactive data models (via A2UI Express, Jev-style decision models, or server-side macros), while clients render them with native framework widgets across React, Lit, Angular, Flutter, SwiftUI, and Jetpack Compose. No untrusted code is executed on the client. Protocol versions: v1.0 (current), v0.9.1 (previous), v0.9 (previous), v0.8 (deprecated). Parallel Markdown editions of documentation pages are available by appending `.md` to the route path (for example, `https://a2ui.org/quickstart.md` or `https://a2ui.org/concepts/overview.md`), and the combined documentation corpus is served at [llms-full.txt](https://a2ui.org/llms-full.txt). ## Agent Harness Skills & Visual Tools - Agent skills CLI (Claude Code, Gemini CLI, Cursor, Codex, Windsurf): `npx skills add a2ui-project/a2ui` - [Get Help & Agent Skills](https://a2ui.org/get-help/) - [Built with A2UI: teams shipping A2UI and what they built](https://a2ui.org/built-with-a2ui/) - [A2UI Composer & Inspector](https://a2ui-editor.ag-ui.com) - [A2UI Playground](https://go.copilotkit.ai/a2ui-playground) ## When to Use A2UI - Dynamic multi-step user journeys where static tool-bound cards are too rigid, while arbitrary iframe code would break native brand consistency, accessibility, or security boundaries. - Cross-platform generative UI rendered natively on Web (React, Lit, Angular), mobile/desktop (Flutter, SwiftUI, Jetpack Compose, Lynx), or terminal (Go Bubble Tea, Ink) from a single agent stream. - Remote, multi-agent, or cross-trust-boundary integrations where agents must send declarative data rather than executable code. - Flutter projects: use [`a2ui_flutter`](https://pub.dev/packages/a2ui_flutter) with [`a2ui_core`](https://pub.dev/packages/a2ui_core) (and [`a2ui_agent`](https://pub.dev/packages/a2ui_agent) when calling models from Dart). Do not use the discontinued `flutter_genui` or `genui_a2ui` packages. ## Install Official SDKs - React: `npm install @a2ui/react @a2ui/web_core` - Lit: `npm install @a2ui/lit @a2ui/web_core` - Angular: `npm install @a2ui/angular @a2ui/web_core` - Flutter: `flutter pub add a2ui_flutter a2ui_core` - Swift (SPM): `https://github.com/a2ui-project/a2ui` (`A2UISwiftCore`, `A2UISwiftUI`, `BasicCatalogSwiftUI`) - Android Jetpack Compose (Google Maven): `androidx.compose.material3:material3-a2ui`, `androidx.a2ui.compose:compose-ui`, `androidx.a2ui.compose:compose-runtime`, and `androidx.a2ui:a2ui-model` - Agent SDKs: `pip install a2ui-agent-sdk` (Python, plus `a2ui-adk` and `a2ui-a2a`); `npm install @a2ui/agent` (TypeScript); `dart pub add a2ui_agent` (Dart) ## Getting Started & Concepts - [Quickstart](https://a2ui.org/quickstart/) ([Markdown](https://a2ui.org/quickstart.md)): run Travel A2UI, a Python agent server with React and Flutter clients from A2UI Labs, from one prompt to a coding agent - [Step-by-Step Walkthrough](https://a2ui.org/quickstart/step-by-step/) ([Markdown](https://a2ui.org/quickstart/step-by-step.md)): the same, by hand, then how A2UI works from what is on screen - Quickstart agent guides, for coding agents asked to give someone the quickstart: [web and mobile apps](https://a2ui.org/quickstart/agent.md) (the default), [web app only](https://a2ui.org/quickstart/agent/web.md), [mobile app only](https://a2ui.org/quickstart/agent/mobile.md) - [Overview: How A2UI Works](https://a2ui.org/concepts/overview/) ([Markdown](https://a2ui.org/concepts/overview.md)) - [Messages](https://a2ui.org/concepts/messages/) ([Markdown](https://a2ui.org/concepts/messages.md)) - [Catalogs](https://a2ui.org/concepts/catalogs/) ([Markdown](https://a2ui.org/concepts/catalogs.md)) - [Data Model and Data Binding](https://a2ui.org/concepts/data-binding/) ([Markdown](https://a2ui.org/concepts/data-binding.md)) - [Actions, Events and Functions](https://a2ui.org/concepts/actions-and-functions/) ([Markdown](https://a2ui.org/concepts/actions-and-functions.md)) - [Inference Formats](https://a2ui.org/concepts/inference-formats/) ([Markdown](https://a2ui.org/concepts/inference-formats.md)) - [SDKs (Core, Agent, Renderer, and Go/Community)](https://a2ui.org/concepts/sdks/) ([Markdown](https://a2ui.org/concepts/sdks.md)) - [Glossary](https://a2ui.org/concepts/glossary/) ([Markdown](https://a2ui.org/concepts/glossary.md)) ## Integration Guides - [How to Integrate A2UI into a Client](https://a2ui.org/guides/client-integration/) ([Markdown](https://a2ui.org/guides/client-integration.md)) - [How to Integrate A2UI into an Agent](https://a2ui.org/guides/agent-integration/) ([Markdown](https://a2ui.org/guides/agent-integration.md)) - [How to Define a Catalog](https://a2ui.org/guides/defining-a-catalog/) ([Markdown](https://a2ui.org/guides/defining-a-catalog.md)) - [Using Macros](https://a2ui.org/guides/macros/) ([Markdown](https://a2ui.org/guides/macros.md)) - [MCP Overview](https://a2ui.org/guides/mcp-overview/) ([Markdown](https://a2ui.org/guides/mcp-overview.md)) - [Call MCP Tools from A2UI](https://a2ui.org/guides/mcp-catalog/) ([Markdown](https://a2ui.org/guides/mcp-catalog.md)) - [Serve A2UI Layouts from an MCP Server](https://a2ui.org/guides/a2ui-over-mcp/) ([Markdown](https://a2ui.org/guides/a2ui-over-mcp.md)) - [Embed MCP Apps in A2UI Surfaces](https://a2ui.org/guides/mcp-apps-in-a2ui/) ([Markdown](https://a2ui.org/guides/mcp-apps-in-a2ui.md)) - [Render A2UI Inside an MCP App](https://a2ui.org/guides/a2ui-in-mcp-apps/) ([Markdown](https://a2ui.org/guides/a2ui-in-mcp-apps.md)) - [Authoring Universal Web Components](https://a2ui.org/guides/authoring-universal-components/) ([Markdown](https://a2ui.org/guides/authoring-universal-components.md)) ## Specifications & Schemas - [Specification v1.0 (current)](https://a2ui.org/specification/v1.0-a2ui/) - [Specification v0.9.1 (previous)](https://a2ui.org/specification/v0.9.1-a2ui/) - [Specification v0.9 (previous)](https://github.com/a2ui-project/a2ui/tree/main/specification/v0_9) - [v1.0 JSON Schemas](https://a2ui.org/specification/v1.0/schemas/) ([Raw catalog.json](https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json)) - [v1.0 Basic Catalog Implementation Guide](https://a2ui.org/specification/v1.0-basic-catalog-implementation-guide/) ([Markdown](https://a2ui.org/specification/v1.0-basic-catalog-implementation-guide.md)) - [v1.0 A2A Extension Specification](https://a2ui.org/specification/v1.0-a2ui-extension-specification/) ([Markdown](https://a2ui.org/specification/v1.0-a2ui-extension-specification.md)) - [v1.0 Evolution Guide](https://a2ui.org/specification/v1.0-evolution-guide/) ([Markdown](https://a2ui.org/specification/v1.0-evolution-guide.md)) ## Ecosystem & Optional - [Roadmap](https://a2ui.org/roadmap/) ([Markdown](https://a2ui.org/roadmap.md)) - [Community Projects (including Lynx, Svelte, Vue, Compose Multiplatform, and Go)](https://a2ui.org/ecosystem/renderers/) ([Markdown](https://a2ui.org/ecosystem/renderers.md)) - [Get Help & Agent Skills](https://a2ui.org/get-help/) - [Full Documentation Corpus (llms-full.txt)](https://a2ui.org/llms-full.txt) --- # Quickstart > Run a small A2UI app, a Python agent server with a React web client and a Flutter mobile client, from one prompt to your coding agent or in three commands. Canonical URL: https://a2ui.org/quickstart/ **A2UI** lets an AI agent answer with an interface instead of a paragraph: cards your users can tap, dates they can drag, a panel that keeps up with them. This quickstart runs **Travel A2UI**, a small trip planner built on the official A2UI SDKs. It is simple on purpose: **one Python server** holds the agent, and a **React** client (web) and a **Flutter** client (mobile) draw what it sends. Paste one of these prompts into your coding agent: - **Web + Mobile (Python + React + Flutter):** `Follow the steps at https://a2ui.org/quickstart/agent.md to set up the A2UI quickstart web and mobile apps and explain them to me.` - **Web only (Python + React):** `Follow the steps at https://a2ui.org/quickstart/agent/web.md to set up the A2UI quickstart web app and explain it to me.` - **Mobile only (Python + Flutter):** `Follow the steps at https://a2ui.org/quickstart/agent/mobile.md to set up the A2UI quickstart mobile app and explain it to me.` Or follow the [step-by-step walkthrough](/quickstart/step-by-step/). > **Note** Travel A2UI lives in A2UI Labs, at [samples/travel-a2ui](https://github.com/a2ui-project/labs/tree/main/samples/travel-a2ui). It is a sample for learning A2UI and is not meant for production use. ## How A2UI fits into this app A2UI is an SDK for your agent and a renderer for your app. Here is where each part lives in Travel A2UI: One server serves the API, the React app at `/` and the Flutter app at `/flutter/`. The clients know nothing about travel: each knows how to draw the catalog's components. You, the builder, decide what is in the catalog; the agent decides what to draw from it. [How A2UI works](/concepts/overview/) walks through the same picture for any A2UI app. ## What you will learn Once it runs, your agent (or the [step-by-step walkthrough](/quickstart/step-by-step/)) explains, with the app open: 1. **What on screen is A2UI** and what is ordinary app code: surfaces, the components in them, and the data they show. 2. **The catalog**: the components and client functions the agent may use, and why it is the guardrail. 3. **Agent skills**: how the model is taught to write A2UI for the catalog. 4. **What the model writes versus what the client receives**: A2UI Express, the A2UI messages the agent SDK turns it into, and the MessageProcessor that applies them. 5. **Renderers**: the two here, and the others A2UI has. ## Other fast paths To try A2UI without cloning anything: - **Agent skills**: run `npx skills add a2ui-project/a2ui` in your project to give your coding agent A2UI's protocol rules, SDK integration guides and catalog authoring workflows (see [Get Help](/get-help/)). - **[A2UI Composer](https://a2ui-editor.ag-ui.com)** ([source](https://github.com/a2ui-project/a2ui/tree/main/tools/composer)): a visual workspace for prompting an assistant, inspecting streamed messages and `dataModel` updates, and editing surfaces side by side. - **[A2UI Playground](https://go.copilotkit.ai/a2ui-playground)**: a browser sandbox for trying A2UI prompts and component catalogs with no local setup. --- # Step-by-Step Walkthrough > Run Travel A2UI yourself, with a React web client, a Flutter mobile client or both, then learn how A2UI works from what is on screen, with screenshots and real output. Canonical URL: https://a2ui.org/quickstart/step-by-step/ Run the app first, then read the second half with it open: every idea there is something you can see on screen. The app is [samples/travel-a2ui](https://github.com/a2ui-project/labs/tree/main/samples/travel-a2ui) in A2UI Labs: one Python server, a React client for the web and a Flutter client for mobile. ## Run it You need **Python 3.11+**, plus **Node 20+** for the web app and **Flutter 3.38+** for the mobile app. 1. Get the code: ```bash git clone https://github.com/a2ui-project/labs cd labs/samples/travel-a2ui ``` 2. Start the server with both clients: ```bash ./scripts/quickstart.sh both ``` Or `react` for the web app only, or `flutter` for the mobile app only. The script installs the server into `.venv`, builds the clients (Flutter takes about a minute) and starts the server. Its output, trimmed (your versions will differ): ```text → Checking what is installed Python 3.11.18 node v22.23.4 → The Python server installed into .venv → The React client ✓ built in 456ms → The Flutter client ✓ Built build/web → Starting on port 8080 React: http://127.0.0.1:8080 Flutter: http://127.0.0.1:8080/flutter/ No GEMINI_API_KEY set: paste a key into the app when it asks. ``` 3. Open the web app at [http://127.0.0.1:8080](http://127.0.0.1:8080) and the mobile app at [http://127.0.0.1:8080/flutter/](http://127.0.0.1:8080/flutter/). ### The web app ![The React client: flight cards in the conversation, the trip so far beside it](https://github.com/a2ui-project/a2ui/releases/download/docs-assets/quickstart--travel-a2ui--chat.a481b6c8.webp) You land on a recorded sample trip, no key needed. To talk to the agent, paste a free Gemini key from [AI Studio](https://aistudio.google.com/apikey) when the app asks (it stays in your browser), then try _"Six days in Madrid in April, two of us, around $2,500 all in"_. ### The mobile app The Flutter client in a phone layout, with the trip sheet open Paste your key, then try _"A weekend in Lisbon from JFK in May, two of us"_. Here Flutter is built for the web, in a phone layout; the same Dart code can target iOS and Android, which this sample does not set up. Ask both apps the same thing: the same messages, drawn by two renderers. ## How A2UI works Five short steps, in the order you meet them on screen: what on the screen is A2UI, what the agent is allowed to draw, how the model learns it, what travels to the client, and what draws it. For all of it in one picture, see [how A2UI fits into this app](/quickstart/#how-a2ui-fits-into-this-app). ### 1. What is A2UI, and what is app code Not everything on screen is A2UI. Blue is composed by the agent; grey is app code, written by you, the builder, and the same on every turn: ![The app with A2UI surfaces outlined in blue (a cost card in the conversation and the trip panel) and app code outlined in grey (the header, the message box and the reply text)](https://github.com/a2ui-project/a2ui/releases/download/docs-assets/quickstart--travel-a2ui--a2ui-vs-app.6cf941df.webp) - **A2UI, composed by the agent:** the cards in the conversation, the trip panel (the trip sheet in Flutter) and the web client's Home dashboard. - **App code:** the header and tabs, the message box, the chat bubbles (the model's words, shown as plain text), the key form, and the Catalog and Wire tabs. Each blue region is a **surface**: one area of UI the agent controls. A surface is made of **components** (a `Text`, a `Button`, a `FlightOption`), and the values they show come from the surface's **data model**. When the user taps a flight, the agent receives an **action** saying which one. A rule of thumb for your own app: if it should look the same on every turn, build it yourself as app code; if it depends on what the user just asked, let the agent compose it. ### 2. The catalog: what the agent may draw The **catalog** (`catalogs/a2ui-travel/catalog.json`) lists everything the agent may use, and it holds two kinds of thing: - **Components**, and the properties each takes. A2UI's **basic catalog** provides `Text`, `Row`, `Column`, `List`, `Button`, `TextField` and more; this app adds travel components such as `FlightOption`, `HotelCard`, `DateRangePicker` and `PriceSummary`. A component can hold others, so a card is a small arrangement of catalog components. - **Functions**, which run in the client. The basic ones format and check values (`formatCurrency`, `formatDate`, `pluralize`, `required`); this app adds `calcNights` and `equals`. A component can show a function's result, so the night count follows the dates the user picks, with no turn to the agent: ```text "${calcNights(start: ${/trip/startDate}, end: ${/trip/endDate})} nights" ``` The web client's **Catalog** tab draws every component with the real renderer: ![The Catalog tab: every component the agent may use, drawn by the real renderer](https://github.com/a2ui-project/a2ui/releases/download/docs-assets/quickstart--travel-a2ui--catalog.f3f5e3ff.webp) The catalog is also the **guardrail**. The server checks every message the model writes against it before sending anything, and each client implements every component and function in it, so the agent can only ask for what the client knows how to draw. The work splits between the model and you, the builder: | The model decides | You, the builder, decide | | -------------------------------- | -------------------------------------------------- | | Whether to show a card | Which components and functions exist (the catalog) | | Which components, and the layout | The data in them | | The wording | That every message is checked against the catalog | | What to ask next | What a press records | ### 3. Skills: teaching the model A2UI A **skill** is a Markdown file that teaches a model to write A2UI for a catalog. This app has two, in `skills/express-modular/`: `a2ui-core` teaches the notation, and `a2ui-travel` teaches this catalog. Here is an entry from the second: ```text • FlightOption(airline, departTime, arriveTime, origin, destination, price, action (static), duration?, stops?, flightNumber?, …) - Description: A single selectable flight itinerary leg. Use one per option when presenting a choice of flights. - airline: Operating carrier, e.g. 'Iberia' or 'Delta'. ``` Here the skills are generated from the catalog with the A2UI agent SDK's `SkillGenerator` (`scripts/build_skills.py`), so when a component changes, `npm run generate` updates what the model is taught to match. ### 4. What the model writes, and what the client receives The model writes **A2UI Express**, a short notation, as its skills taught it. `root` comes first, so the card can appear before the rest is written: ```text root = Column([head, list]) head = Text("### Flights to Madrid") list = List(_template($/flights, fare)) fare = FlightOption($airline, $departTime, $arriveTime, $origin, $destination, $price, Event("select_flight", {id: $id})) ``` On the server, the **A2UI agent SDK** reads the Express as the model streams it, turns it into **A2UI messages** and checks each against the catalog. The messages are JSON: `createSurface`, `updateComponents`, `updateDataModel` and `deleteSurface`. Here is the component list one of these blocks became (trimmed to ids and links): each component names its children by id, starting from `root`. ```json [ {"id": "root", "component": "Column", "children": ["head", "list"]}, {"id": "head", "component": "Text", "text": "### Flights to Madrid"}, {"id": "fare", "component": "FlightOption"}, { "id": "list", "component": "List", "children": {"path": "/flights", "componentId": "fare"} } ] ``` In the client, a **MessageProcessor** from the A2UI SDK applies the messages: it keeps each surface's components and data, fills in bound values, runs catalog functions, and sends presses back as actions. React uses `@a2ui/web_core`'s (`apps/web/src/surfaces.ts`); Flutter uses `a2ui_core`'s (`apps/flutter/lib/chat.dart`). The web client's **Wire** tab shows both forms of every surface side by side, what the model wrote and what the client received: ![The Wire tab: the Express the model wrote, beside the A2UI JSON the client received](https://github.com/a2ui-project/a2ui/releases/download/docs-assets/quickstart--travel-a2ui--wire.4515c08f.webp) Express is one way for a model to write A2UI, chosen because it is short. A2UI itself is the JSON. ### 5. Renderers A **renderer** implements the catalog's components with native widgets and uses a MessageProcessor to apply messages. It holds no app logic. - **In this app:** React (`@a2ui/react`, `@a2ui/web_core`) in `renderers/react/`, and Flutter (`a2ui_flutter`, `a2ui_core`) in `renderers/flutter/`. The apps around them are `apps/web/` and `apps/flutter/`. - **[Maintained by the A2UI project](/concepts/sdks/#renderer-sdk):** Lit (web components), Angular, React, Flutter, SwiftUI, Jetpack Compose and Markdown. - **[From the community](/ecosystem/renderers/):** UIKit, React Native, Vue, Lynx, a terminal renderer and more. The server never knows which client it is talking to. Adding a platform means writing a renderer for the same catalog, with no change to the agent. ## Deploy to Cloud Run Run the quickstart app in your own Google Cloud project: the server and both clients on one URL. You need a project with billing enabled. [![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://shell.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/a2ui-project/labs&cloudshell_git_branch=main&cloudshell_workspace=samples/travel-a2ui&cloudshell_tutorial=docs/cloud-shell.md&show=ide%2Cterminal) Cloud Shell opens with the sample and a short guide beside the terminal: sign in, choose a project, and run one script: ```bash ./scripts/deploy.sh ``` It turns on the services the deploy needs, lets the build use them, then builds and deploys (about ten minutes the first time) and prints the URL. No key is stored on the service: each visitor pastes their own. From your own clone, the same script works anywhere the gcloud CLI is signed in; see [deploying](https://github.com/a2ui-project/labs/blob/main/samples/travel-a2ui/docs/deploying.md) for what it runs. ## Keep going - [Build your own](https://github.com/a2ui-project/labs/tree/main/samples/travel-a2ui#build-one-of-your-own): what to change to make this app about something other than travel. - [A2UI renderers](/concepts/sdks/#renderer-sdk): the official Renderer SDKs and [community renderers](/ecosystem/renderers/). - [How A2UI works](/concepts/overview/): the concepts, in depth. - [A2UI v1.0 specification](/specification/v1.0-a2ui/). --- # Overview: How A2UI Works > How the A2UI agent SDK, transport, and client renderer fit together, traced through a single request. Canonical URL: https://a2ui.org/concepts/overview/ A2UI lets an AI agent describe an interactive UI as a stream of JSON messages that a client app renders with its own native widgets. Below you'll find the mechanics on each side of the connection, how they fit together, and one request traced from the user's prompt to a button tap. --- ## Generating UI On the server, you configure the [A2UI Agent SDK](/concepts/sdks/#agent-sdk) with a [component catalog](/concepts/catalogs/) (`catalog.json`) that describes the UI components and functions available to render on your client. From that catalog, the SDK's skill generator produces an A2UI skill containing prompt instructions and grammar rules. Your agent code and LLM runtime sit outside the SDK: they take the generated A2UI skill alongside your domain tools (such as a flight search API), call the model, and pass its UI output to the SDK's compiler. The compiler parses and validates the content against the catalog, then sends layout ([`updateComponents`](/concepts/messages/)) and data ([`updateDataModel`](/concepts/messages/)) messages over a transport such as AG-UI, A2A, MCP, or WebSockets. On the client, your app hosts an [A2UI Renderer](/concepts/sdks/#renderer-sdk), which draws the UI with native platform widgets inside a region of your screen called a surface. Controls bound to the surface's [data model](/concepts/data-binding/) update state locally as the user interacts with them, and user triggers such as button taps send [`action`](/concepts/actions-and-functions/) messages back to the agent. While this page focuses on generating UI at request time, A2UI content can also be authored in advance and sent as a static payload (for example, returned from an MCP server via an agent to a client), following a traditional server-driven UI pattern. _In the diagram above, purple marks your server-side agent code, blue marks your client app, and the teal dashed outlines mark the A2UI libraries on each side (the Agent SDK on the server and the Renderer on the client)._ --- ## Step-by-step generation The rest of this page traces a flight-booking request through each stage of the pipeline: defining the catalog, generating the skill, prompting the model, compiling and validating the output, rendering the surface on the client, and handling user actions. ### Define the catalog You define the components and functions your client can render in `catalog.json`. A travel app might pair the basic catalog with domain components such as `FlightCard`. Each component declares its properties with JSON Schema, so the Agent SDK can validate that the model only emits components and properties the client knows how to draw: #### v1.0 {/* prettier-ignore */} ```jsonc { "protocolVersion": "1.0", "catalogId": "https://example.com/catalogs/travel/v1/catalog.json", "components": { "FlightCard": { "type": "object", "description": "Displays a flight segment with fare and passenger count.", "properties": { "component": {"const": "FlightCard"}, "flightNumber": { "$ref": "common_types.json#/$defs/DynamicString", "description": "IATA flight designator, such as 'LH 454'." } // ... origin, destination, price, passengers }, "required": ["component", "flightNumber", "origin", "destination", "price", "passengers"] } // ... other components and functions } } ``` #### v0.9.1 {/* prettier-ignore */} ```jsonc { "catalogId": "https://example.com/catalogs/travel/v0_9/catalog.json", "components": { "FlightCard": { "type": "object", "allOf": [ {"$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ComponentCommon"}, { "type": "object", "description": "Displays a flight segment with fare and passenger count.", "properties": { "component": {"const": "FlightCard"}, "flightNumber": { "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicString", "description": "IATA flight designator, such as 'LH 454'." } // ... origin, destination, price, passengers }, "required": ["component", "flightNumber", "origin", "destination", "price", "passengers"] } ], "unevaluatedProperties": false } // ... other components and functions } } ``` ### Generate the skill From `catalog.json`, the Agent SDK's skill generator builds an A2UI skill containing grammar rules for the chosen [inference format](/concepts/inference-formats/) (such as Express) together with component and function signatures derived directly from your catalog: ```text --- name: a2ui description: Generates interactive user interface components for user requests. --- # A2UI Express DSL Output Contract You must output the user interface using A2UI Express. IMPORTANT: You MUST always surround the entire A2UI Express block with the literal sentinel tags `` and ``... ## Grammar Rules 1. Component constructors can be assigned to variables or nested inline inside parent component arguments: ... Keyword arguments (`param=value`) and positional arguments with `_` placeholders are supported. ... 6. Data bindings: prefix absolute paths in the data model with '$', e.g., $/user/firstName. ... ## Positional Component Signatures ... • FlightCard(flightNumber, origin, destination, price, passengers) - Description: Displays a flight segment with fare and passenger count. - flightNumber: IATA flight designator, such as 'LH 454'. ... ``` ### Prompt the model The user types a request in the client app: > _"Berlin to San Francisco today, and two tickets back on Oct 19."_ Your agent then: 1. Calls its domain tools, for example `searchFlights(from: "BER", to: "SFO", date: "2026-10-12")`, to get flights and prices. 2. Decides that a form with passenger counts is more useful here than a paragraph of text. 3. Loads the generated A2UI skill and prompts the model with the flight data. ### Compile and validate the output The model streams its answer. Instead of writing wire JSON directly, it can use a compact inference format such as Express: ```text $/outbound/pax = 1 $/outbound/price = 455 $/return/pax = 2 $/return/price = 970 root = Card(Column([ FlightCard(flightNumber="LH 454", origin="BER", destination="SFO", price=$/outbound/price, passengers=$/outbound/pax), FlightCard(flightNumber="UA 901", origin="SFO", destination="BER", price=$/return/price, passengers=$/return/pax), Button(Text("Confirm booking"), variant="primary", action=Event("confirm_booking", {outboundPax: $/outbound/pax, returnPax: $/return/pax})) ])) ``` The Agent SDK compiler processes the stream as it arrives: 1. Parses incoming tokens into an intermediate representation. 2. Validates that each component exists in the catalog and that its properties match the schema, reporting invalid output as soon as it appears. 3. Emits the wire messages: `createSurface` opens the surface on the client, `updateComponents` sends the validated component list, and `updateDataModel` sends the initial data values. #### Messages on the wire The tabs below show the `createSurface`, `updateComponents`, and `updateDataModel` messages for this example. Component IDs are chosen by the compiler; the ones here are for readability. #### v1.0 Surface creation message, defined by [`CreateSurfaceMessage`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/CreateSurfaceMessage) (shown separately from `updateComponents` and `updateDataModel` below to illustrate each message type; in v1.0, the compiler can also inline initial `components` and `dataModel` inside `createSurface`): ```json { "version": "1.0", "createSurface": { "surfaceId": "flight-booking-surface", "catalogId": "https://example.com/catalogs/travel/v1/catalog.json" } } ``` Layout message, defined by [`UpdateComponentsMessage`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateComponentsMessage): ```json { "version": "1.0", "updateComponents": { "surfaceId": "flight-booking-surface", "components": [ {"id": "root", "component": "Card", "child": "layout_col"}, { "id": "layout_col", "component": "Column", "children": ["flight_outbound", "flight_return", "submit_btn"] }, { "id": "flight_outbound", "component": "FlightCard", "flightNumber": "LH 454", "origin": "BER", "destination": "SFO", "price": {"@path": "/outbound/price"}, "passengers": {"@path": "/outbound/pax"} }, { "id": "flight_return", "component": "FlightCard", "flightNumber": "UA 901", "origin": "SFO", "destination": "BER", "price": {"@path": "/return/price"}, "passengers": {"@path": "/return/pax"} }, {"id": "submit_label", "component": "Text", "text": "Confirm booking"}, { "id": "submit_btn", "component": "Button", "child": "submit_label", "variant": "primary", "action": { "event": { "name": "confirm_booking", "context": { "outboundPax": {"@path": "/outbound/pax"}, "returnPax": {"@path": "/return/pax"} } } } } ] } } ``` Data message, defined by [`UpdateDataModelMessage`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateDataModelMessage): ```json { "version": "1.0", "updateDataModel": { "surfaceId": "flight-booking-surface", "path": "/", "value": { "outbound": {"pax": 1, "price": 455}, "return": {"pax": 2, "price": 970} } } } ``` #### v0.9.1 Surface creation message, defined by [`CreateSurfaceMessage`](/specification/v0.9.1/schemas/#server_to_client.json/$defs/CreateSurfaceMessage): ```json { "version": "v0.9", "createSurface": { "surfaceId": "flight-booking-surface", "catalogId": "https://example.com/catalogs/travel/v0_9/catalog.json" } } ``` Layout message, defined by [`UpdateComponentsMessage`](/specification/v0.9.1/schemas/#server_to_client.json/$defs/UpdateComponentsMessage): ```json { "version": "v0.9", "updateComponents": { "surfaceId": "flight-booking-surface", "components": [ {"id": "root", "component": "Card", "child": "layout_col"}, { "id": "layout_col", "component": "Column", "children": ["flight_outbound", "flight_return", "submit_btn"] }, { "id": "flight_outbound", "component": "FlightCard", "flightNumber": "LH 454", "origin": "BER", "destination": "SFO", "price": {"path": "/outbound/price"}, "passengers": {"path": "/outbound/pax"} }, { "id": "flight_return", "component": "FlightCard", "flightNumber": "UA 901", "origin": "SFO", "destination": "BER", "price": {"path": "/return/price"}, "passengers": {"path": "/return/pax"} }, {"id": "submit_label", "component": "Text", "text": "Confirm booking"}, { "id": "submit_btn", "component": "Button", "child": "submit_label", "variant": "primary", "action": { "event": { "name": "confirm_booking", "context": { "outboundPax": {"path": "/outbound/pax"}, "returnPax": {"path": "/return/pax"} } } } } ] } } ``` Data message, defined by [`UpdateDataModelMessage`](/specification/v0.9.1/schemas/#server_to_client.json/$defs/UpdateDataModelMessage): ```json { "version": "v0.9", "updateDataModel": { "surfaceId": "flight-booking-surface", "path": "/", "value": { "outbound": {"pax": 1, "price": 455}, "return": {"pax": 2, "price": 970} } } } ``` ### Render on the client The client receives `createSurface`, `updateComponents`, and `updateDataModel` over its transport (for example AG-UI, A2A, MCP, or a WebSocket), and the A2UI Renderer: 1. Creates the surface identified by `surfaceId` and stores the initial values in its data model. 2. Resolves the flat component list and data model into a hierarchical node layer starting at `root`, following each `child` and `children` ID and binding path references (such as `/outbound/pax`) to their values in the data model. 3. Builds the native widgets from that node tree and subscribes each widget to updates on its bound properties. At this point, the client's node layer joins the component hierarchy with the resolved values from the data model. `Card` (`root`) wraps `Column` (`layout_col`), which contains the two `FlightCard` nodes with their bound prices and passenger counts alongside `Button` (`submit_btn`) and its `Text` label: ```text Surface ("flight-booking-surface") └── Card ("root") └── Column ("layout_col") ├── FlightCard ("flight_outbound") │ flightNumber: "LH 454" │ origin: "BER" │ destination: "SFO" │ price: 455 ← /outbound/price │ passengers: 1 ← /outbound/pax ├── FlightCard ("flight_return") │ flightNumber: "UA 901" │ origin: "SFO" │ destination: "BER" │ price: 970 ← /return/price │ passengers: 2 ← /return/pax └── Button ("submit_btn") │ variant: "primary" │ action: event "confirm_booking" │ outboundPax: 1 ← /outbound/pax │ returnPax: 2 ← /return/pax └── Text ("submit_label") text: "Confirm booking" ``` ### Handle user actions Once the surface is rendered, the renderer handles local state edits on the client and sends explicit user triggers back to the agent: 1. When the user taps `+` on the outbound `FlightCard` to raise the passenger count to `2`, `FlightCard` writes `2` directly to `/outbound/pax` in the surface's data model. Every node bound to `/outbound/pax` updates in place, and nothing is sent to the agent. 2. When the user taps _Confirm booking_, the renderer triggers the `Button` node's action, resolves its `context` paths against the current data model, and sends an [`action`](/concepts/actions-and-functions/) message to the agent. The action message for this example, after the user raised the outbound count to `2`: #### v1.0 Defined by [`renderer_to_agent.json`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action): ```json { "version": "1.0", "action": { "name": "confirm_booking", "surfaceId": "flight-booking-surface", "sourceComponentId": "submit_btn", "timestamp": "2026-10-12T08:30:00Z", "context": {"outboundPax": 2, "returnPax": 2} } } ``` #### v0.9.1 Defined by [`client_to_server.json`](/specification/v0.9.1/schemas/#client_to_server.json/properties/action): ```json { "version": "v0.9", "action": { "name": "confirm_booking", "surfaceId": "flight-booking-surface", "sourceComponentId": "submit_btn", "timestamp": "2026-10-12T08:30:00Z", "context": {"outboundPax": 2, "returnPax": 2} } } ``` --- # Actions, Events and Functions > How component actions send events to the agent or trigger functions, and how A2UI functions work across actions, property bindings, validation checks, and agent requests. Canonical URL: https://a2ui.org/concepts/actions-and-functions/ An action defines what happens when a user interacts with a component by triggering either an [event](#events) or a [function](#functions). Events prompt the agent to respond with UI updates, while functions execute specific logic on the renderer or agent. Functions can also run outside user actions, such as property bindings, validation checks, and agent requests. ## Actions An **action** is an interaction handler property on an interactive UI component, such as `Button.action` on [`Button`](/specification/v1.0-basic-catalog-implementation-guide/#basic_catalog.json/components/Button), that runs when a user interacts with that component (for example, clicking or tapping a button). An action determines how user interaction is handled: - An event notifies the agent of user intent by sending an outbound [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) message. Use an event when the agent needs to decide what happens next and stream UI or data updates back to the client (such as confirming an order or booking a reservation). - A function invokes a registered catalog operation directly without starting a new agent conversation turn. The function can execute locally on the renderer (such as opening a link with [`openUrl`](/specification/v1.0-basic-catalog-implementation-guide/#basic_catalog.json/functions/openUrl)) or run remotely on the agent via [`callAgentFunction`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/callAgentFunction) to perform a discrete backend check (such as verifying inventory). See [`Action`](/specification/v1.0/schemas/#common_types.json/$defs/Action) in the protocol schema for the action structure. | Aspect | Event | Function | | :-------------- | :------------------------------------------------ | :------------------------------------------------------ | | Action property | `action.event` | `action.functionCall` | | Purpose | Prompts the agent to process intent and update UI | Triggers specific logic on the renderer or agent | | Triggered by | A user gesture on a component action | Actions, property bindings, validation checks, or agent | | Runs on | The agent, after an `action` message | The renderer, or the agent via `callAgentFunction` | | Agent turn | Starts a new agent conversation turn | Runs without starting an agent turn | | Result | The agent decides next steps and updates UI | A typed return value, or a local side effect | > **Note** In v0.9.1 ([`Action`](/specification/v0.9.1/schemas/#common_types.json/$defs/Action)), `action.event` does not support `userMessage`, and `action.functionCall` uses `"call"` instead of `"@call"` and executes only locally on the renderer. --- ## Events Events inform the agent that a user interacted with a component and prompt the agent to respond. When a user clicks or taps a component configured with an event (such as a "Book flight" button), the client sends an [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) message to the agent. The agent processes the interaction, makes any required decisions or backend updates, and streams back updated UI components or data. When dispatching an event, the client performs the following tasks: 1. Reads any context values attached to the event (such as the flight number or selected seat) and resolves data bindings against the current surface data model. 2. Evaluates an optional human-readable `userMessage` describing what the user did (for example, `"Confirmed flight reservation for Alex Rosario"`, formatted with [`formatString`](/specification/v1.0-basic-catalog-implementation-guide/#basic_catalog.json/functions/formatString)). 3. Sends an [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) message to the agent containing the event name, source component, timestamp, resolved context, and optional user message. If the surface is configured to share its data model, the client also attaches the full surface state to outbound messages so the agent has complete visibility into what the user sees. See [Data Model and Data Binding](/concepts/data-binding/#synchronizing-state-back-to-the-agent) for details. The following example shows a confirmation `Button` declaration and the outbound `action` message the renderer sends when the user clicks it. #### v1.0 `Button` component declaration: ```json { "id": "confirm_button", "component": "Button", "child": "confirm_label", "variant": "primary", "action": { "event": { "name": "confirmReservation", "userMessage": { "@call": "formatString", "args": { "value": "Confirmed flight reservation for ${/passenger/fullName}" } }, "context": { "flightNumber": "UA-402", "seat": {"@path": "/selection/seatNumber"} } } } } ``` Outbound [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) message sent on click: ```json { "version": "1.0", "action": { "name": "confirmReservation", "surfaceId": "booking_surface", "sourceComponentId": "confirm_button", "timestamp": "2026-11-02T03:45:00Z", "userMessage": "Confirmed flight reservation for Alex Rosario", "context": { "flightNumber": "UA-402", "seat": "14B" } } } ``` #### v0.9.1 `Button` component declaration: ```json { "id": "confirm_button", "component": "Button", "child": "confirm_label", "variant": "primary", "action": { "event": { "name": "confirmReservation", "context": { "flightNumber": "UA-402", "seat": {"path": "/selection/seatNumber"} } } } } ``` Outbound [`action`](/specification/v0.9.1/schemas/#client_to_server.json/properties/action) message sent on click: ```json { "version": "v0.9", "action": { "name": "confirmReservation", "surfaceId": "booking_surface", "sourceComponentId": "confirm_button", "timestamp": "2026-11-02T03:45:00Z", "context": { "flightNumber": "UA-402", "seat": "14B" } } } ``` > **Note** In v0.9.1, data bindings use `path` instead of `@path`, events do not support `userMessage` (all interaction details must be provided in `context`), and client-to-server action messages are defined in [`client_to_server.json`](/specification/v0.9.1/schemas/#client_to_server.json/properties/action). --- ## Functions Functions execute specific logic or calculations on either the client or the server. Unlike events, which start an agent turn to generate or update entire UI surfaces, functions are focused operations that compute a value, validate input, access local state, or trigger a local operation. Every available function is declared in a [catalog](/concepts/catalogs/), defining its name and expected parameters. ### Function types Functions run either locally on the client renderer or remotely on the agent. When evaluating a function call, the client checks whether the function is registered locally in its catalog. If a local implementation exists, the client runs it immediately without making a network request. If the function is not implemented locally, the client routes the call to the agent via [`callAgentFunction`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/callAgentFunction) and awaits the response. #### Renderer functions Renderer functions are implemented locally by the client renderer and registered with its catalog. Some types of renderer functions are: - Validation functions that check input values or form readiness against constraints, such as required fields or email formatting. - Data transformation functions that format numbers, dates, currency, or strings, or evaluate boolean logic before displaying values on screen. - Local data access functions that read client-side state or template context not stored in the surface data model, such as an item's position inside a list template or device screen dimensions. - Local action functions that perform client-side operations in response to user gestures, such as opening a URL in the browser. These examples illustrate common functions available in the [Basic Catalog](/specification/v1.0-basic-catalog-implementation-guide/#2-renderer-side-functions). See the [Basic Catalog functions specification](/specification/v1.0-basic-catalog-implementation-guide/#basic_catalog.json/functions) for the full set of standard renderer functions, or define custom functions in your own catalog. #### Agent functions Agent functions run on the server rather than in the client application. They let UI components trigger discrete backend operations (such as checking real-time inventory or validating a coupon code) without starting a full conversation turn. When a component calls an agent function, the following steps occur: 1. The client sends a [`callAgentFunction`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/callAgentFunction) request naming the function, its resolved `catalogId`, and its arguments. While waiting for the server, the client can display a pending or loading state. 2. The server executes the function logic and returns an [`agentFunctionResponse`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/AgentFunctionResponseMessage) containing either a `value` or an `error` object. 3. The client applies the returned `value` to the component property, validation check, or action. If the call returns an error, property bindings resolve to `null` and report an [`error`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/error) message (`code: "EXPRESSION_ERROR"` with the failed `functionCallId`), validation checks mark the component as invalid, and in-progress component actions stop and report `EXPRESSION_ERROR` with `functionCallId`. The following example shows an outbound agent function call to verify product stock: ```json { "version": "1.0", "callAgentFunction": { "surfaceId": "checkout_surface", "functionCallId": "call_inv_check_881", "callFunction": { "@call": "checkInventory", "catalogId": "https://example.com/catalogs/store/v1/catalog.json", "args": { "sku": "SKU-99201", "quantity": 2 } } } } ``` > **Note** Any function not implemented locally by the client renderer is automatically routed to the agent. If a renderer function name is misspelled (such as `"formatCurrncy"` instead of `"formatCurrency"`), the client attempts to call it as an agent function, and the agent responds with an unknown function error. ### Calling functions Functions can be invoked in four contexts: - Component actions, when a user clicks or taps a component configured to execute a function directly. - Property bindings, when a component property computes its display value from a function. - Validation checks, when an input field or button evaluates rules before allowing user progress. - Agent requests, when the agent asks the client to execute a local function on the device. #### Component actions A component action can execute a function directly instead of sending an event to the agent. For example, clicking this help button opens a support URL in the browser without contacting the agent: #### v1.0 ```json { "id": "help_link", "component": "Button", "child": "help_text", "variant": "borderless", "action": { "functionCall": { "@call": "openUrl", "args": { "url": "https://support.example.com/billing" } } } } ``` #### v0.9.1 ```json { "id": "help_link", "component": "Button", "child": "help_text", "variant": "borderless", "action": { "functionCall": { "call": "openUrl", "args": { "url": "https://support.example.com/billing" } } } } ``` #### Property bindings Component properties can compute their value from a function call. The client automatically re-evaluates the function whenever the underlying bound data changes. In this example, a text component formats an order subtotal into currency: #### v1.0 ```json { "id": "order_total_text", "component": "Text", "text": { "@call": "formatCurrency", "args": { "value": {"@path": "/order/subtotal"}, "currency": "USD" } } } ``` #### v0.9.1 ```json { "id": "order_total_text", "component": "Text", "text": { "call": "formatCurrency", "args": { "value": {"path": "/order/subtotal"}, "currency": "USD" } } } ``` #### Validation checks Interactive components like text fields and buttons can define validation rules. Each rule checks a condition (such as verifying required fields or checking email format) and provides an error message if the check fails. For buttons, failing any validation check automatically disables the button: #### v1.0 ```json { "id": "user_email_input", "component": "TextField", "label": "Email", "value": {"@path": "/formData/email"}, "@checks": [ { "condition": { "@call": "required", "args": {"value": {"@path": "/formData/email"}} }, "message": "Email address is required." }, { "condition": { "@call": "email", "args": {"value": {"@path": "/formData/email"}} }, "message": "Enter a valid email address." } ] } ``` #### v0.9.1 ```json { "id": "user_email_input", "component": "TextField", "label": "Email", "value": {"path": "/formData/email"}, "checks": [ { "condition": { "call": "required", "args": {"value": {"path": "/formData/email"}} }, "message": "Email address is required." }, { "condition": { "call": "email", "args": {"value": {"path": "/formData/email"}} }, "message": "Enter a valid email address." } ] } ``` #### Agent requests The agent can also request the client to execute a function locally via [`callRendererFunction`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/CallRendererFunctionMessage), for example to query device screen dimensions. The client runs the local function and always replies with a [`rendererFunctionResponse`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/rendererFunctionResponse) message containing the matching `functionCallId` and either `value` or `error` (even when the function's return type is `void`): ```json { "version": "1.0", "callRendererFunction": { "functionCallId": "hw_screen_001", "callFunction": { "catalogId": "https://example.com/catalogs/device.json", "@call": "getScreenResolution", "args": { "screenIndex": 0 } } } } ``` ### Who can call a function Because functions can trigger sensitive side effects (like opening a browser URL) or access device attributes, a catalog [`FunctionDefinition`](/specification/v1.0/schemas/#catalog_definition.json/$defs/FunctionDefinition) can control who is permitted to call it and whether it requires a direct user gesture: - The `allowedCallers` property restricts callers to `"rendererOnly"` (the default), `"agentOnly"`, or `"rendererOrAgent"`. For example, `"rendererOnly"` functions cannot be invoked remotely by the agent via `callRendererFunction`, while `"agentOnly"` functions can only be called by the agent and cannot be used in component properties or actions. - The `requiresUserActivation` property (which defaults to `false` and requires `allowedCallers: "rendererOnly"` when `true`) restricts execution to a direct user tap or click on a component action, such as `openUrl`. This prevents passive property bindings, validation checks, or incoming agent messages from triggering browser navigation without user intent. > **Note** In v0.9.1, function calls use `call` instead of `@call` and data bindings use `path` instead of `@path`. All functions execute locally on the client renderer; v0.9.1 does not support remote agent functions, agent requests to the renderer, or caller permission settings. --- # Catalogs > How component catalogs define the components, functions, and rules shared between agents and renderers. Canonical URL: https://a2ui.org/concepts/catalogs/ An A2UI catalog defines the UI components, the functions that populate data or handle user events, and the prompt instructions that an agent and a renderer agree on. Because both sides work from the same catalog contract, the agent only sends component types and properties that the renderer knows how to build natively, never raw HTML, CSS, or executable scripts. In the diagram below, the renderer advertises the catalogs it supports through `supportedCatalogIds`, and the agent selects a default catalog for a surface by setting `catalogId` in `createSurface`. The shared catalog document has three sections: components such as `FlightStatusCard` and `Column`, functions such as `formatCurrency` and `saveBoardingPass`, and Markdown `instructions` that the agent SDK adds to the LLM system prompt. ## Catalogs A catalog is a collection of components and functions that acts as a contract between an agent and a renderer. Both agents and renderers can support one or more catalogs at the same time: a renderer advertises its `supportedCatalogIds` when a session starts, and an agent can create surfaces against any of those catalogs or, in v1.0, combine components and functions from multiple catalogs on the same surface by setting `@catalogId` on individual components and `catalogId` on individual function calls. Every catalog has a `catalogId` string, conventionally formatted as a URI such as `https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json` or `https://example.com/catalogs/travel/v1/catalog.json`, along with optional `instructions`, `components`, and `functions`. Both sides of the connection read from that contract: 1. On the agent, the agent SDK (`@a2ui/agent`, `a2ui-agent-sdk`, `a2ui_agent`) reads the component schemas, function signatures, and `instructions` to build the LLM system prompt and validate outgoing messages. 2. On the renderer, the core and framework SDKs (`@a2ui/web_core`, `a2ui_core`, `A2UICore`, `androidx.a2ui`) pair each component schema with a native widget and each local function signature with code that runs on the client. ## Components A component is a UI component that is rendered natively on the client, such as a Flutter widget, a React, Lit, or Angular component, a SwiftUI view, or a Jetpack Compose composable, and composed with other components to form rich UIs. Instead of nesting JSON objects deeply, an A2UI surface stores component instances in a flat list where parent components reference child IDs, making layouts straightforward for an LLM to stream incrementally. Each entry in a catalog's `components` map is a JSON Schema that describes one component type: | Part | How it is declared | What it does | | :----------------- | :---------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ | | Component type | `"component": { "const": "FlightStatusCard" }` | Names the component type in the surface's flat component list. | | Dynamic properties | `$ref` to `DynamicString`, `DynamicNumber`, `DynamicBoolean`, and so on | Accepts a literal value, a data binding (`@path` in v1.0, `path` in v0.9.1), or a function call (`@call` / `call`). | | Children | `$ref` to `ComponentId` or `ChildList` | Points to child components by ID so the surface stays a flat list instead of a nested tree. | | Placement rules | `allowedParents`, `allowedChildren` (v1.0) | Limits where the component can appear in the layout. | | Validation | `$ref` to `Checkable` | Adds a `@checks` array (`checks` in v0.9.1) of `{condition, message}` rules to an input. | | Actions | `$ref` to `Action` | Says what happens when the user interacts with the component: send an event to the agent or call a function. | In v1.0, the optional envelope keys that any component instance can carry (`@catalogId`, `@accessibility`, `@metadata`, and `@checks` on `Checkable` components) take the `@` prefix, so a custom component can declare its own `catalogId`, `accessibility`, `metadata`, or `checks` property as an ordinary property. In v0.9.1, the `accessibility` and `checks` envelope keys are bare. Here is a custom `FlightStatusCard` component schema in a catalog: #### v1.0 ```json { "FlightStatusCard": { "type": "object", "description": "Shows a flight's number, fare, and action buttons.", "allowedParents": ["Column", "List"], "properties": { "component": {"const": "FlightStatusCard"}, "flightNumber": { "$ref": "common_types.json#/$defs/DynamicString", "description": "IATA flight designator, such as 'NH 105'." }, "fareAmount": { "$ref": "common_types.json#/$defs/DynamicNumber", "description": "Ticket fare in the booking currency." }, "actions": { "$ref": "common_types.json#/$defs/ChildList", "description": "Buttons shown in the card footer." } }, "required": ["component", "flightNumber"] } } ``` #### v0.9.1 ```json { "FlightStatusCard": { "type": "object", "description": "Shows a flight's number, fare, and action buttons.", "allOf": [ { "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ComponentCommon" }, { "type": "object", "properties": { "component": {"const": "FlightStatusCard"}, "flightNumber": { "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicString", "description": "IATA flight designator, such as 'NH 105'." }, "fareAmount": { "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicNumber", "description": "Ticket fare in the booking currency." }, "actions": { "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ChildList", "description": "Buttons shown in the card footer." } }, "required": ["component", "flightNumber"] } ], "unevaluatedProperties": false } } ``` ## Functions A function is named logic used to populate data in the UI, such as formatting a bound property or checking an input rule, or to handle user events when someone interacts with a component. A2UI supports both renderer-side and agent-side functions: 1. Renderer-side functions are declared in a catalog's `functions` map and run locally on the client. They handle formatting, input validation, boolean logic, and client side effects such as opening a URL. In v1.0, the agent can also invoke an authorized renderer-side function remotely by sending `callRendererFunction` and reading the returned `rendererFunctionResponse`. 2. Agent-side functions, introduced in v1.0, run on the server, such as checking live seat inventory or verifying a booking code. When a component evaluates a `@call` whose name is not registered in the renderer's local catalog, the renderer treats it as an agent-side function, dispatches `callAgentFunction`, and applies the returned `agentFunctionResponse`. In v0.9.1, function calls run only on the renderer, and components notify the agent of user interactions through `action` events. Each entry in a catalog's `functions` map declares a `returnType` (`"string"`, `"number"`, `"boolean"`, `"array"`, `"object"`, `"any"`, `"void"`, or, in v1.0, `"validationResult"`) and an `args` schema for its arguments: #### v1.0 ```json { "formatCurrency": { "type": "object", "description": "Formats a number as a currency string.", "returnType": "string", "properties": { "@call": {"const": "formatCurrency"}, "args": { "type": "object", "properties": { "value": { "$ref": "common_types.json#/$defs/DynamicNumber", "description": "The monetary amount." }, "currency": { "$ref": "common_types.json#/$defs/DynamicString", "description": "The ISO 4217 currency code (e.g., 'USD', 'EUR')." } }, "required": ["currency", "value"], "unevaluatedProperties": false } }, "required": ["@call", "args"] } } ``` #### v0.9.1 ```json { "formatCurrency": { "type": "object", "description": "Formats a number as a currency string.", "properties": { "call": {"const": "formatCurrency"}, "args": { "type": "object", "properties": { "value": { "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicNumber", "description": "The monetary amount." }, "currency": { "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicString", "description": "The ISO 4217 currency code (e.g., 'USD', 'EUR')." } }, "required": ["currency", "value"], "unevaluatedProperties": false }, "returnType": {"const": "string"} }, "required": ["call", "args"], "unevaluatedProperties": false } } ``` ## Using a catalog in an agent An agent loads a catalog from its JSON document and passes it to `A2uiGenerator` through a `CatalogConfig`. The generator uses the catalog to build the LLM system prompt and to validate the messages the LLM produces. #### TypeScript ```typescript import {readFileSync} from 'node:fs'; import {A2uiGenerator, CatalogConfig, ExpressFormatFactory} from '@a2ui/agent'; import {Catalog} from '@a2ui/web_core'; const travelCatalog = Catalog.fromJson(JSON.parse(readFileSync('./catalogs/travel.json', 'utf8'))); const generator = new A2uiGenerator( [new CatalogConfig(travelCatalog)], undefined, new ExpressFormatFactory(), ); ``` #### Python ```python import json from pathlib import Path from a2ui.core import Catalog from a2ui.inference_formats import ExpressFormatFactory from a2ui.processor import A2uiGenerator, CatalogConfig travel_catalog = Catalog.from_json( json.loads(Path("./catalogs/travel.json").read_text(encoding="utf-8")) ) generator = A2uiGenerator( catalogs=[CatalogConfig(catalog=travel_catalog)], inference_format_factory=ExpressFormatFactory(), ) ``` #### Dart ```dart import 'dart:convert'; import 'dart:io'; import 'package:a2ui_agent/a2ui_agent.dart'; import 'package:a2ui_core/a2ui_core.dart'; void main() { final travelCatalog = Catalog.fromJson( jsonDecode(File('./catalogs/travel.json').readAsStringSync()) as Map, ); final generator = A2uiGenerator( catalogs: [CatalogConfig(travelCatalog)], inferenceFormatFactory: const ExpressFormatFactory(), ); } ``` Swift (`A2UISwiftCore`, `A2UISwiftUI`) and Kotlin (`androidx.a2ui`, `androidx.a2ui.compose`) target client-side rendering; see [SDKs](/concepts/sdks/#agent-sdk) for server-side Agent SDK packages. ## Registering a catalog in a renderer A renderer builds a `Catalog` that pairs each component schema with a native widget (`ComponentImplementation`) and each local function signature with code that runs it (`FunctionImplementation`). It then passes the catalog to its message processor or framework provider. #### Flutter ```dart import 'package:a2ui_core/a2ui_core.dart'; import 'package:a2ui_flutter/a2ui_flutter.dart'; import 'package:flutter/material.dart'; import 'package:json_schema_builder/json_schema_builder.dart'; final flightStatusCard = ComponentImplementation( name: 'FlightStatusCard', allowedParents: ['Column', 'List'], schema: Schema.object( properties: { 'component': Schema.string(enumValues: ['FlightStatusCard']), 'flightNumber': CommonSchemasV1.dynamicString, 'fareAmount': CommonSchemasV1.dynamicNumber, }, required: ['component', 'flightNumber'], ), builder: (context, node, props, buildChild) { final flightNumber = props.string('flightNumber') ?? ''; final fare = props.number('fareAmount'); return Card( child: ListTile( leading: const Icon(Icons.flight_takeoff), title: Text('Flight $flightNumber'), trailing: fare != null ? Text('\$${fare.toStringAsFixed(2)}') : null, ), ); }, ); final travelCatalog = Catalog( id: 'https://example.com/catalogs/travel/v1/catalog.json', protocolVersion: A2uiProtocolVersion.v1_0, components: [flightStatusCard], functions: BasicCatalog.v1_0().functions.values.toList(), ); final processor = MessageProcessor(catalogs: [travelCatalog]); ``` #### React ```tsx import {z} from 'zod'; import {createComponentImplementation} from '@a2ui/react'; import {Catalog, type ComponentApi, MessageProcessor} from '@a2ui/web_core'; import {BASIC_FUNCTIONS, DynamicNumberSchema, DynamicStringSchema} from '@a2ui/web_core/v1_0'; const FlightStatusCardApi = { name: 'FlightStatusCard', allowedParents: ['Column', 'List'], schema: z.object({ flightNumber: DynamicStringSchema, fareAmount: DynamicNumberSchema.optional(), }), } satisfies ComponentApi; const FlightStatusCard = createComponentImplementation(FlightStatusCardApi, ({props}) => { const flightNumber = typeof props.flightNumber === 'string' ? props.flightNumber : ''; const fareAmount = typeof props.fareAmount === 'number' ? props.fareAmount : undefined; return (
Flight {flightNumber} {fareAmount !== undefined && ${fareAmount.toFixed(2)}}
); }); export const travelCatalog = new Catalog( 'https://example.com/catalogs/travel/v1/catalog.json', '1.0', [FlightStatusCard], BASIC_FUNCTIONS, ); const processor = new MessageProcessor([travelCatalog]); ``` #### Lit ```typescript import {html, nothing} from 'lit'; import {customElement} from 'lit/decorators.js'; import {z} from 'zod'; import {A2uiLitElement, type LitComponentApi} from '@a2ui/lit'; import {Catalog, type ComponentApi, MessageProcessor} from '@a2ui/web_core'; import {BASIC_FUNCTIONS, DynamicNumberSchema, DynamicStringSchema} from '@a2ui/web_core/v1_0'; const FlightStatusCardApi = { name: 'FlightStatusCard', allowedParents: ['Column', 'List'], schema: z.object({ flightNumber: DynamicStringSchema, fareAmount: DynamicNumberSchema.optional(), }), } satisfies ComponentApi; @customElement('flight-status-card') export class FlightStatusCardElement extends A2uiLitElement { static readonly tagName = 'flight-status-card'; protected override readonly api = FlightStatusCardApi; override render() { const rawFlight = this.controller?.props?.flightNumber; const rawFare = this.controller?.props?.fareAmount; const flightNumber = typeof rawFlight === 'string' ? rawFlight : ''; const fareAmount = typeof rawFare === 'number' ? rawFare : undefined; return html`
Flight ${flightNumber} ${fareAmount !== undefined ? html`$${fareAmount.toFixed(2)}` : nothing}
`; } } const FlightStatusCard = { ...FlightStatusCardApi, tagName: FlightStatusCardElement.tagName, element: FlightStatusCardElement, } satisfies LitComponentApi; export const travelCatalog = new Catalog( 'https://example.com/catalogs/travel/v1/catalog.json', '1.0', [FlightStatusCard], BASIC_FUNCTIONS, ); const processor = new MessageProcessor([travelCatalog]); ``` #### Angular ```typescript import {Component, computed} from '@angular/core'; import {z} from 'zod'; import { AngularCatalog, BASIC_FUNCTIONS, CatalogComponent, createComponentImplementation, provideA2UI, } from '@a2ui/angular'; import {type ComponentApi} from '@a2ui/web_core'; import {DynamicNumberSchema, DynamicStringSchema} from '@a2ui/web_core/v1_0'; const FlightStatusCardApi = { name: 'FlightStatusCard', allowedParents: ['Column', 'List'], schema: z.object({ flightNumber: DynamicStringSchema, fareAmount: DynamicNumberSchema.optional(), }), } satisfies ComponentApi; @Component({ selector: 'app-flight-status-card', template: `
Flight {{ flightNumber() }} @if (formattedFare(); as fare) { \${{ fare }} }
`, }) export class FlightStatusCardComponent extends CatalogComponent { readonly flightNumber = computed(() => { const flight = this.props().flightNumber?.value(); return typeof flight === 'string' ? flight : ''; }); readonly formattedFare = computed(() => { const fare = this.props().fareAmount?.value(); return typeof fare === 'number' ? fare.toFixed(2) : undefined; }); } export const travelCatalog = new AngularCatalog( 'https://example.com/catalogs/travel/v1/catalog.json', '1.0', [createComponentImplementation(FlightStatusCardApi, FlightStatusCardComponent)], BASIC_FUNCTIONS, ); export const appA2uiProviders = provideA2UI({catalogs: [travelCatalog]}); ``` #### SwiftUI ```swift import A2UICore import A2UISwiftUI import BasicCatalog import JSONSchema import SwiftUI let flightStatusCard = ComponentImplementation( name: "FlightStatusCard", schema: try! Schema( instance: """ { "type": "object", "properties": { "component": { "const": "FlightStatusCard" }, "flightNumber": { "$ref": "common_types.json#/$defs/DynamicString" }, "fareAmount": { "$ref": "common_types.json#/$defs/DynamicNumber" } }, "required": ["component", "flightNumber"] } """, remoteSchemas: A2UICommonSchema.allSchemas ), allowedParents: ["Column", "List"] ) { node in HStack { Image(systemName: "airplane.departure") Text("Flight \(node.string(for: "flightNumber") ?? "")") .font(.headline) Spacer() if let fare = node.double(for: "fareAmount") { Text(String(format: "$%.2f", fare)) } } .padding() } let travelCatalog = Catalog( id: "https://example.com/catalogs/travel/v1/catalog.json", protocolVersion: "1.0", components: [flightStatusCard], functions: V10BasicFunctions.allFunctions ) let processor = MessageProcessor(catalogs: [travelCatalog]) ``` #### Jetpack Compose ```kotlin import android.content.Context import java.util.Locale import androidx.a2ui.compose.runtime.A2uiComponentProperties import androidx.a2ui.compose.runtime.A2uiComponentScope import androidx.a2ui.compose.runtime.A2uiMessageProcessor import androidx.a2ui.compose.runtime.A2uiProperty import androidx.a2ui.compose.ui.A2uiCatalog import androidx.a2ui.compose.ui.A2uiComponent import androidx.a2ui.compose.ui.catalog.A2uiBasicCatalogV1 import androidx.a2ui.model.processor.A2uiMessageProcessor import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.Card import androidx.compose.material3.Text import androidx.compose.material3.a2ui.catalog.MaterialA2uiBasicCatalogV1Defaults import androidx.compose.material3.a2ui.catalog.materialA2uiBasicCatalogV1 import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp private val FlightNumberProp = A2uiProperty.dynamicString("flightNumber", required = true) private val FareAmountProp = A2uiProperty.dynamicNumber("fareAmount") val FlightStatusCard: A2uiComponent = object : A2uiComponent { override val name = "FlightStatusCard" override val description = "Shows a flight's number and fare." override val properties = listOf(FlightNumberProp, FareAmountProp) @Composable override fun A2uiComponentScope.isReady(properties: A2uiComponentProperties): Boolean = properties.bind(FlightNumberProp) != null @Composable override fun A2uiComponentScope.Content( properties: A2uiComponentProperties, modifier: Modifier, ) { val flightNumber = properties.bind(FlightNumberProp) ?: return val fare = properties.bind(FareAmountProp)?.toDouble() Card(modifier = modifier.fillMaxWidth()) { Row( modifier = Modifier.fillMaxWidth().padding(16.dp), horizontalArrangement = Arrangement.SpaceBetween, ) { Text(text = "Flight $flightNumber") if (fare != null) { Text(text = String.format(Locale.US, "$%.2f", fare)) } } } } } // androidx.a2ui implements v0.9.1, where each surface binds to a single catalogId. fun createTravelProcessor(context: Context): A2uiMessageProcessor { val basicCatalog = materialA2uiBasicCatalogV1( image = MaterialA2uiBasicCatalogV1Defaults.image { _, _, _, _, _ -> }, video = MaterialA2uiBasicCatalogV1Defaults.video { _, _, _ -> }, audioPlayer = MaterialA2uiBasicCatalogV1Defaults.audioPlayer { _, _, _, _ -> }, functions = A2uiBasicCatalogV1.Functions(context), ) val travelCatalog = A2uiCatalog( catalogId = "https://example.com/catalogs/travel/v1/catalog.json", components = basicCatalog.components + FlightStatusCard, functions = basicCatalog.functions, themeSchema = basicCatalog.themeSchema, ) // Launch processor.collectMessages() in a background coroutine scope // (such as viewModelScope) to process queued messages: val processor = A2uiMessageProcessor(catalogs = listOf(travelCatalog)) return processor } ``` ## The basic catalog A2UI publishes a basic catalog with 18 general-purpose components and 14 functions. The v1.0 schema lives at [`catalogs/basic/v1/catalog.json`](https://github.com/a2ui-project/a2ui/blob/main/catalogs/basic/v1/catalog.json), and the v0.9.1 schema lives at [`specification/v0_9_1/catalogs/basic/catalog.json`](https://github.com/a2ui-project/a2ui/blob/main/specification/v0_9_1/catalogs/basic/catalog.json). | Category | Entries | | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Layout | `Row`, `Column`, `List`, `Card`, `Tabs`, `Modal`, `Divider` | | Display and media | `Text`, `Image`, `Icon`, `Video`, `AudioPlayer` | | Inputs | `Button`, `TextField`, `CheckBox`, `ChoicePicker`, `Slider`, `DateTimeInput` | | Functions | `formatString`, `formatNumber`, `formatCurrency`, `formatDate`, `pluralize`, `required`, `regex`, `length`, `numeric`, `email`, `openUrl`, `and`, `or`, `not` | You do not have to use the basic catalog. You can adopt it as is, restyle its components to match your design system, combine it with domain-specific catalogs on the same surface, or replace it altogether. For an example of a domain catalog built on the basic catalog, see the experimental [Google Maps Platform Agentic UI toolkit](https://developers.google.com/maps/ai/agentic-ui-toolkit). Its catalog, `a2ui://maps-agentic-ui-catalog.json`, adds `GoogleMap` and `PlaceDetailsCompact` components to the basic catalog's components and functions. The toolkit ships renderers for web (Lit), Android, and iOS, so agents can show maps, routes, and place details next to standard layout widgets. ## Catalog negotiation and multi-catalog surfaces When a session starts, the renderer sends `supportedCatalogIds` to list the catalogs it supports. When creating a surface, the agent picks from that list (or sends no UI if none match). #### v1.0 Set `catalogId` in `createSurface` to choose the surface's default catalog, then add `@catalogId` on any component in `updateComponents` (or `catalogId` on any function call, `@call`) that comes from another supported catalog. In this example, `salesDashboard` uses the basic catalog by default, and `chart` comes from a custom charts catalog: ```json [ { "version": "1.0", "createSurface": { "surfaceId": "salesDashboard", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json" } }, { "version": "1.0", "updateComponents": { "surfaceId": "salesDashboard", "components": [ {"id": "root", "component": "Column", "children": ["title", "chart"]}, {"id": "title", "component": "Text", "text": "Quarterly sales"}, { "id": "chart", "component": "BarChart", "@catalogId": "https://example.com/catalogs/charts/v1/catalog.json", "data": {"@path": "/sales"} } ] } } ] ```
Edge cases when createSurface.catalogId is omitted `catalogId` in `createSurface` is optional. If a surface omits `createSurface.catalogId`, every component must set its own `@catalogId` and every function call its own `catalogId`. If an entry omits its catalog ID and the surface has no default `catalogId` either, the renderer rejects that component or function call rather than falling back to `supportedCatalogIds`. See [Mixable catalogs and component resolution logic](/specification/v1.0-a2ui/#mixable-catalogs-and-component-resolution-logic) for the full resolution rules.
#### v0.9.1 The agent selects a single `catalogId` in `createSurface`, locked for the lifetime of that surface: ```json { "version": "v0.9", "createSurface": { "surfaceId": "salesDashboard", "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } } ``` --- # Data Model and Data Binding > Learn how A2UI separates surface UI structure from reactive JSON state, enabling dynamic display bindings, two-way input bindings, dynamic list templates, and state synchronization back to the agent. Canonical URL: https://a2ui.org/concepts/data-binding/ A2UI separates UI component structure from runtime data by giving every surface an isolated JSON data model and binding components to paths within that model. Components reference locations inside the data model using [RFC 6901 JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901) (such as `/reservation/destination` or `/reservation/guestEmail`). When either the agent or the user updates a path in the data model, only the components bound to that path update. The following diagram shows how a surface data model and component bindings produce a rendered UI. On the left, the surface data model stores reservation state in JSON, and component definitions bind to paths such as `/reservation/destination` and `/reservation/guestEmail`. On the right, an application window renders the resulting interface: a heading displaying the destination, an editable text field bound two-way to the guest email, and a confirmation button. --- ## Populating the data model Each surface maintains its own JSON data model. In v1.0, you can provide an initial `dataModel` object inline when sending [`createSurface`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/CreateSurfaceMessage), or populate and update the data model at any time by sending [`updateDataModel`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateDataModelMessage) messages (in v0.9.1, data is always sent via [`updateDataModel`](/specification/v0.9.1/schemas/#server_to_client.json/$defs/UpdateDataModelMessage) after creating the surface). When the renderer receives an `updateDataModel` message, it applies upsert semantics at the target JSON Pointer `path`. If the path does not exist yet, the renderer creates any missing intermediate objects and inserts the value; if the path already exists, the renderer replaces the value at that path. Omitting `path` (or setting it to `"/"`) replaces the entire root data model for the surface. To delete a key at `path`, set `"value": null` in v1.0, or omit the `value` property in v0.9.1. #### v1.0 ```json { "version": "1.0", "updateDataModel": { "surfaceId": "booking", "path": "/reservation/destination", "value": "Kyoto, Japan" } } ``` #### v0.9.1 ```json { "version": "v0.9", "updateDataModel": { "surfaceId": "booking", "path": "/reservation/destination", "value": "Kyoto, Japan" } } ``` Because `updateDataModel` is separate from component layout messages ([`updateComponents`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateComponentsMessage)), the data model can be updated directly by the language model during generation, or from deterministic code on the agent side (such as a tool handler or backend service injecting database records or API responses directly into the surface without passing them through the model). --- ## Binding to the data model UI components can be bound to paths in the surface data model so they can display and update data. Component properties typed as dynamic values (such as [`DynamicString`](/specification/v1.0/schemas/#common_types.json/$defs/DynamicString), [`DynamicNumber`](/specification/v1.0/schemas/#common_types.json/$defs/DynamicNumber), [`DynamicBoolean`](/specification/v1.0/schemas/#common_types.json/$defs/DynamicBoolean), [`DynamicStringList`](/specification/v1.0/schemas/#common_types.json/$defs/DynamicStringList), or [`DynamicValue`](/specification/v1.0/schemas/#common_types.json/$defs/DynamicValue)) accept either a static literal value or a [`DataBinding`](/specification/v1.0/schemas/#common_types.json/$defs/DataBinding) object (`{"@path": "/path"}` in v1.0, or `{"path": "/path"}` in v0.9.1). Dynamic properties can also evaluate functions, which are covered in [Actions and Functions](/concepts/actions-and-functions/). ### Reading data as dynamic values To display a value from the data model, bind a component's dynamic property to a JSON Pointer path. Whenever the value at that path is populated or updated, the component updates automatically: #### v1.0 ```json { "id": "destinationText", "component": "Text", "text": {"@path": "/reservation/destination"} } ``` #### v0.9.1 ```json { "id": "destinationText", "component": "Text", "text": {"path": "/reservation/destination"} } ``` ### Two-way binding to dynamic values Interactive input components (such as `TextField`, `CheckBox`, or `Slider`) bind their `value` property to a path in the data model. This creates a two-way binding: the input displays the current value at that path, and as the user types or toggles the control, the renderer updates that path in the local data model immediately: #### v1.0 ```json { "id": "emailInput", "component": "TextField", "label": "Guest Email", "value": {"@path": "/reservation/guestEmail"} } ``` #### v0.9.1 ```json { "id": "emailInput", "component": "TextField", "label": "Guest Email", "value": {"path": "/reservation/guestEmail"} } ``` ### Referencing the data model in events Component events ([`action.event`](/specification/v1.0/schemas/#common_types.json/$defs/Action)) can include data model references inside their `context` object. When the user triggers the event (for example, by clicking a button to submit a form), the renderer resolves each path against the local data model and sends the current values in the outbound [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) message: #### v1.0 ```json { "id": "submitButton", "component": "Button", "child": "submitLabel", "action": { "event": { "name": "submit_reservation", "context": { "email": {"@path": "/reservation/guestEmail"} } } } } ``` #### v0.9.1 ```json { "id": "submitButton", "component": "Button", "child": "submitLabel", "action": { "event": { "name": "submit_reservation", "context": { "email": {"path": "/reservation/guestEmail"} } } } } ``` ### Displaying dynamic lists of data Container components whose `children` property uses [`ChildList`](/specification/v1.0/schemas/#common_types.json/$defs/ChildList) (such as `Column`, `Row`, or `List`) can either take a static array of child component IDs (`["item1", "item2"]`) or generate children dynamically from a list in the data model using a template object with `path` and `componentId` (`ChildList` uses the property name `"path"` in both v1.0 and v0.9.1). When a container binds to a list path (such as `"/guests"`), the renderer repeats the template component once for each item in the array and creates a local data scope for that item: 1. A **local data model path** omits the leading slash (such as `{"@path": "name"}` in v1.0, or `{"path": "name"}` in v0.9.1) and resolves relative to the current list item. For the first item in `"/guests"`, `"name"` resolves to `"/guests/0/name"`, and for the second item, it resolves to `"/guests/1/name"`. 2. A **root data model path** starts with a leading slash (such as `{"@path": "/reservation/destination"}`) and always resolves from the root of the surface data model, even inside a list template. 3. In v1.0, the **`@index` system function** (`{"@call": "@index"}`, with an optional `"args": {"offset": 1}`) returns the current item's 0-based index inside a list template. The following example populates a `/guests` list in the data model and renders each guest's `name` using a `Column` template: #### v1.0 ```json [ { "version": "1.0", "updateDataModel": { "surfaceId": "booking", "path": "/guests", "value": [{"name": "Alex Rivera"}, {"name": "Sam Taylor"}] } }, { "version": "1.0", "updateComponents": { "surfaceId": "booking", "components": [ { "id": "root", "component": "Column", "children": { "path": "/guests", "componentId": "guestRow" } }, { "id": "guestRow", "component": "Text", "text": {"@path": "name"} } ] } } ] ``` #### v0.9.1 ```json [ { "version": "v0.9", "updateDataModel": { "surfaceId": "booking", "path": "/guests", "value": [{"name": "Alex Rivera"}, {"name": "Sam Taylor"}] } }, { "version": "v0.9", "updateComponents": { "surfaceId": "booking", "components": [ { "id": "root", "component": "Column", "children": { "path": "/guests", "componentId": "guestRow" } }, { "id": "guestRow", "component": "Text", "text": {"path": "name"} } ] } } ] ``` The following diagram shows how the `/guests` array and the `guestRow` template expand into a rendered list UI. On the left, the `/guests` array holds two objects and `guestRow` binds its text to the local path `"name"`. On the right, an application window renders two list items resolved from `/guests/0/name` and `/guests/1/name`. --- ## Synchronizing state back to the agent When a user edits an input component, two-way bindings update the surface data model locally on the client without streaming every keystroke over the network. To synchronize data model state back to the agent, especially values updated by input components, you can use three mechanisms depending on how the data will be processed. ### Events Include data model references in `action.event.context` to send specific values (such as form fields) when the user triggers an [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) event. When the user clicks the `submitButton` component shown earlier, the renderer resolves each bound path against the local data model and sends the resolved `action` message to the agent. This is best when you want to send data model state to an agent to reason over and process: #### v1.0 ```json { "version": "1.0", "action": { "name": "submit_reservation", "surfaceId": "booking", "sourceComponentId": "submitButton", "timestamp": "2026-03-15T14:30:00Z", "context": { "email": "alex@example.com" } } } ``` #### v0.9.1 ```json { "version": "v0.9", "action": { "name": "submit_reservation", "surfaceId": "booking", "sourceComponentId": "submitButton", "timestamp": "2026-03-15T14:30:00Z", "context": { "email": "alex@example.com" } } } ``` ### Agent functions In v1.0, an interactive component such as a submit button can use `action.functionCall` to trigger an agent-side function (`callAgentFunction`) with arguments bound to paths in the data model. This is best when you want to send data model state directly to programmatic code on the agent side: #### v1.0 ```json { "id": "saveButton", "component": "Button", "child": "saveLabel", "action": { "functionCall": { "@call": "saveReservation", "args": { "email": {"@path": "/reservation/guestEmail"} } } } } ``` #### v0.9.1 Agent-side functions were introduced in v1.0. In v0.9.1, pass bound data model paths in `action.event.context` instead: ```json { "id": "saveButton", "component": "Button", "child": "saveLabel", "action": { "event": { "name": "save_reservation", "context": { "email": {"path": "/reservation/guestEmail"} } } } } ``` ### `sendDataModel: true` Set `"sendDataModel": true` in the [`createSurface`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/CreateSurfaceMessage) message to instruct the renderer to attach the full surface data model to the transport metadata of every outbound message (`a2uiRendererDataModel` in v1.0, or `a2uiClientDataModel` in v0.9.1, shaped as `{"version": "1.0", "surfaces": {"booking": {...}}}`). This is best when you want to synchronize complete surface state as context for the agent: #### v1.0 ```json { "version": "1.0", "createSurface": { "surfaceId": "booking", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "sendDataModel": true } } ``` #### v0.9.1 ```json { "version": "v0.9", "createSurface": { "surfaceId": "booking", "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json", "sendDataModel": true } } ``` --- ## Accessing the data model across SDKs Each Core SDK provides a `dataModel` interface on the surface model for reading values by JSON Pointer path: #### TypeScript ```typescript const surface = processor.model.getSurface('booking'); const email = surface?.dataModel.get('/reservation/guestEmail'); ``` #### Python ```python surface = processor.model.get_surface("booking") email = surface.data_model.get("/reservation/guestEmail") if surface else None ``` #### Dart ```dart final surface = processor.model.getSurface('booking'); final email = surface?.dataModel.get('/reservation/guestEmail'); ``` #### Swift ```swift let surface = processor.surfaceGroupModel["booking"] let email = surface?.dataModel.get("/reservation/guestEmail") ``` #### Kotlin ```kotlin import androidx.a2ui.engine.model.A2uiCoreSurfaceModel import androidx.a2ui.model.protocol.A2uiDataPath // Accessing the data model directly requires the androidx.a2ui:a2ui-engine // dependency (which provides A2uiCoreSurfaceModel and A2uiCoreDataModel). // Surface instances emitted by A2uiMessageProcessor implement A2uiCoreSurfaceModel: val surface = processor.activeSurfaces.value.firstOrNull { it.id == "booking" } as? A2uiCoreSurfaceModel val email = surface?.dataModel?.get(A2uiDataPath("/reservation/guestEmail")) ``` --- # Glossary > Alphabetical reference of A2UI protocol, SDK, and generative UI terminology. Canonical URL: https://a2ui.org/concepts/glossary/ A2UI (Agent to User Interface) uses specific terms for its wire protocol, client and server SDKs, and generative UI patterns, defined below in alphabetical order. [A](#a) · [B](#b) · [C](#c) · [D](#d) · [E](#e) · [F](#f) · [G](#g) · [I](#i) · [M](#m) · [N](#n) · [P](#p) · [R](#r) · [S](#s) ## A ### A2UI **Agent to User Interface**, an open-source generative UI framework (a platform-agnostic streaming protocol, native renderers, and agent SDKs) that lets [agents](#agent) generate stateful, native user interfaces across web, mobile, and desktop [renderers](#renderer) without sending executable code, HTML, or CSS over the wire. See [Overview: How A2UI Works](/concepts/overview/) and the [v1.0 Protocol Specification](/specification/v1.0-a2ui/). ### A2UI message A single JSON envelope exchanged between an [agent](#agent) and a [renderer](#renderer) over a streaming or request-response transport. Messages flow in two directions: [`agent_to_renderer`](#agent_to_renderer) envelopes create or delete [surfaces](#surface), update [components](#component) and [data models](#data-model), invoke renderer functions, and return agent function results; [`renderer_to_agent`](#renderer_to_agent) envelopes dispatch user [events](#event), invoke agent functions, return renderer function results, and report validation or runtime errors. Because A2UI supports progressive delivery over streaming transports, a message stream may be in progress, completed, or interrupted if transport delivery stops early. See [Messages](/concepts/messages/). ### A2UI tag A delimiter tag in model-to-agent text output, such as `...` (used by the Express [inference format](#inference-format)) or `...` (used by the Direct JSON format), that marks the start and end of a structured UI block. Because a single model response can interleave conversational prose with one or more UI blocks, the [Agent SDK](#agent-sdk) parser scans for A2UI tags to separate prose from raw UI blocks before compiling those blocks into [`agent_to_renderer`](#agent_to_renderer) messages. See [Inference Formats](/concepts/inference-formats/). ### A2uiGenerator The top-level coordinator class in an [Agent SDK](#agent-sdk) that holds the agent's supported `CatalogConfig` entries, optional prompt examples, and default [inference format](#inference-format) factory (`ExpressFormatFactory` by default). For each session or request, calling `createProcessor` (in TypeScript and Dart) or `create_processor` (in Python) with the renderer's [capabilities](#capabilities) negotiates the active [catalogs](#catalog) and returns an `A2uiRequestProcessor` that generates system prompt instructions and parses and validates model responses against `RendererState`. See [Agent SDK](/concepts/sdks/#agent-sdk) and [How to Integrate A2UI into an Agent](/guides/agent-integration/). ### Action An interaction handler property on a UI [component](#component) (such as `Button.action`), defined by [`common_types.json#/$defs/Action`](/specification/v1.0/schemas/#common_types.json/$defs/Action) in v1.0 and [`common_types.json#/$defs/Action`](/specification/v0.9.1/schemas/#common_types.json/$defs/Action) in v0.9.1. An `Action` is a `oneOf` union that specifies one of two behaviors when a user interacts with the component: 1. An [`event`](#event) (`{"event": {"name": "...", "context": {...}}}`), which causes the renderer to resolve any bound values and send an outbound [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) message in [`renderer_to_agent`](#renderer_to_agent) to notify the agent of user intent. 2. A [`functionCall`](#function-call) (`{"functionCall": {"@call": "...", "args": {...}}}` in v1.0, or `"call"` in v0.9.1), which invokes a [function](#function) without dispatching an `action` event message (in v1.0, running locally on the renderer or routing to the agent via [`callAgentFunction`](#callagentfunction) when unimplemented locally). See [Actions and Functions](/concepts/actions-and-functions/) and [Defining actions](/specification/v1.0-a2ui/#defining-actions) in the protocol specification. ### Agent The application or service that emits [`agent_to_renderer`](#agent_to_renderer) messages to build and update UI [surfaces](#surface) on a [renderer](#renderer), and processes inbound [`renderer_to_agent`](#renderer_to_agent) events and remote function calls. To decide what UI and data to emit, an agent typically calls a large language model (LLM), coordinates specialized subagents behind an orchestrator, or runs deterministic application logic (for example, in regulated workflows or automated tests). An agent can run on a server over transports such as AG-UI, Agent-to-Agent (A2A), Model Context Protocol (MCP), Server-Sent Events (SSE), or WebSockets, or inside the same client process as the renderer. Agents may be stateful (retaining negotiated [catalogs](#catalog) and `RendererState` across turns) or stateless (receiving renderer [capabilities](#capabilities) and surface [data model](#data-model) metadata on every request). See [Overview: How A2UI Works](/concepts/overview/) and [How to Integrate A2UI into an Agent](/guides/agent-integration/). ### Agent SDK A first-party agent-side library (published for TypeScript, Python, and Dart on top of each language's [Core SDK](#core-sdk)) that manages an [agent](#agent)'s interaction with language models and [renderers](#renderer). An Agent SDK loads and prunes [catalogs](#catalog) via [catalog transformers](#catalog-transformer), negotiates renderer [capabilities](#capabilities) through [`A2uiGenerator`](#a2uigenerator), generates model skills and system prompt snippets, compiles [inference format](#inference-format) output into wire JSON, and validates payloads against `RendererState` so errors can be fed back to the model for self-correction. See [Agent SDK](/concepts/sdks/#agent-sdk) for official package names and capabilities. ### agent_to_renderer The v1.0 protocol [envelope schema](#envelope-schema) ([`agent_to_renderer.json`](/specification/v1.0/schemas/#agent_to_renderer.json)) for messages streamed or returned from an [agent](#agent) to a [renderer](#renderer). In v0.9.1, this envelope schema is named [`server_to_client.json`](/specification/v0.9.1/schemas/#server_to_client.json). Every v1.0 `agent_to_renderer` message sets `"version": "1.0"` (v0.9.1 uses `"version": "v0.9"` or `"v0.9.1"`) alongside one of six message keys: 1. [`createSurface`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/CreateSurfaceMessage) initializes a new [surface](#surface) (and in v1.0 can embed initial `components` and `dataModel` inline). 2. [`updateComponents`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateComponentsMessage) adds or replaces [component](#component) definitions in a surface's flat component list. 3. [`updateDataModel`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateDataModelMessage) inserts, replaces, or deletes (`"value": null` in v1.0; omitted `value` in v0.9.1) data at a JSON Pointer path in a surface's [data model](#data-model). 4. [`deleteSurface`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/DeleteSurfaceMessage) removes a surface and its associated state. 5. [`callRendererFunction`](#callrendererfunction) (new in v1.0) invokes an authorized renderer-side [function](#function). 6. [`agentFunctionResponse`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/AgentFunctionResponseMessage) (new in v1.0) returns the `value` or `error` for a [`callAgentFunction`](#callagentfunction) request. See [Messages](/concepts/messages/) and [Envelope message structure](/specification/v1.0-a2ui/#envelope-message-structure) in the protocol specification. ### Agent turn The sequence of [`agent_to_renderer`](#agent_to_renderer) messages (and any accompanying conversational text) sent by an [agent](#agent) in response to a user prompt or [`Event`](#event) before the agent waits for the next user input. The end of an agent turn is signaled by the transport layer rather than by an A2UI message. See [Messages](/concepts/messages/) and [Transport decoupling](/specification/v1.0-a2ui/#transport-decoupling) in the protocol specification. ## B ### Basic catalog The optional, general-purpose reference [catalog](#catalog) maintained by the A2UI project (`https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json` in v1.0, [`basic_catalog.json`](/specification/v1.0-basic-catalog-implementation-guide/#basic_catalog.json); `https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json` in v0.9.1, [`basic_catalog.json`](/specification/v0.9.1-basic-catalog-implementation-guide/#basic_catalog.json)). The Basic catalog defines 18 standard [components](#component) (`Row`, `Column`, `List`, `Card`, `Tabs`, `Modal`, `Divider`, `Text`, `Image`, `Icon`, `Video`, `AudioPlayer`, `Button`, `TextField`, `CheckBox`, `ChoicePicker`, `Slider`, and `DateTimeInput`) and 14 [functions](#function) (`formatString`, `formatNumber`, `formatCurrency`, `formatDate`, `pluralize`, `required`, `regex`, `length`, `numeric`, `email`, `openUrl`, `and`, `or`, and `not`). Official [framework adapters](#framework-adapter) ship with a built-in implementation that applications can restyle, combine with custom domain catalogs, or replace altogether. See [Catalogs](/concepts/catalogs/#the-basic-catalog) and [Basic Component Catalog](/specification/v1.0-a2ui/#basic-component-catalog) in the protocol specification. ## C ### @call The reserved v1.0 protocol directive key in a [`FunctionCall`](/specification/v1.0/schemas/#common_types.json/$defs/FunctionCall) object that names the catalog or system [function](#function) to invoke, for example `{"@call": "formatCurrency", "args": {"value": {"@path": "/price"}, "currency": "USD"}}`. In v0.9.1, `FunctionCall` uses `"call"` without the `@` prefix ([`common_types.json#/$defs/FunctionCall`](/specification/v0.9.1/schemas/#common_types.json/$defs/FunctionCall)). Reserving the `@` prefix in v1.0 keeps plain JSON objects that have a `"call"` property from being treated as function calls; literal keys beginning with `@` inside `DynamicValue` objects are escaped by doubling the prefix (`"@@call"`). See [Function call](#function-call), [Actions and Functions](/concepts/actions-and-functions/), and [Functions](/specification/v1.0-a2ui/#functions) in the protocol specification. ### callAgentFunction A v1.0 [`renderer_to_agent`](#renderer_to_agent) message ([`renderer_to_agent.json#/properties/callAgentFunction`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/callAgentFunction)) sent by a [renderer](#renderer) to execute an agent-side [function](#function) remotely, such as checking live inventory or verifying an identifier. When the renderer resolves a [`FunctionCall`](#function-call)'s [catalog](#catalog) and finds no local implementation (provided the function is not an `@`-prefixed system function, is not declared `allowedCallers: "agentOnly"`, and passes any `requiresUserActivation` and declared argument schema checks), it sends `callAgentFunction` with `surfaceId`, a unique `functionCallId`, and `callFunction` (`@call`, the resolved `catalogId`, and evaluated `args`). The [agent](#agent) runs the function and replies with an [`agentFunctionResponse`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/AgentFunctionResponseMessage) carrying the same `functionCallId` and either `value` or `error`. Not present in v0.9.1, where functions run only on the renderer. See [Actions and Functions](/concepts/actions-and-functions/) and [`callAgentFunction`](/specification/v1.0-a2ui/#callagentfunction) in the protocol specification. ### callRendererFunction A v1.0 [`agent_to_renderer`](#agent_to_renderer) message ([`agent_to_renderer.json#/$defs/CallRendererFunctionMessage`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/CallRendererFunctionMessage)) sent by an [agent](#agent) to execute a [function](#function) registered on the [renderer](#renderer), such as reading client state or triggering an authorized client operation. The message carries a unique `functionCallId`, an optional `surfaceId` whose [data model](#data-model) provides the evaluation context (defaulting to an isolated empty root model `{}` when omitted), and `callFunction` with `@call`, a required `catalogId`, and optional `args`. Only functions declared with `allowedCallers` set to `"agentOnly"` or `"rendererOrAgent"` (and not requiring user activation) may be called by the agent. After executing or rejecting a well-formed call, the renderer returns a [`rendererFunctionResponse`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/rendererFunctionResponse) with the matching `functionCallId` and either `value` or `error` (malformed envelopes report a top-level [`error`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/error) message carrying `functionCallId`). Not present in v0.9.1. See [Actions and Functions](/concepts/actions-and-functions/) and [`callRendererFunction`](/specification/v1.0-a2ui/#callrendererfunction) in the protocol specification. ### Capabilities Metadata payloads exchanged via transport handshakes or per-message transport metadata (rather than as [A2UI messages](#a2ui-message)) that advertise which [catalogs](#catalog) each side supports: 1. Renderer capabilities ([`renderer_capabilities.json`](/specification/v1.0/schemas/#renderer_capabilities.json), sent under the `a2uiRendererCapabilities` metadata key in v1.0; [`client_capabilities.json`](/specification/v0.9.1/schemas/#client_capabilities.json) under `a2uiClientCapabilities` in v0.9.1) declare `supportedCatalogIds` and optional `inlineCatalogs` nested under the protocol version key (`"1.0"` in v1.0, `"v0.9"` in v0.9.1). 2. Agent capabilities ([`agent_capabilities.json`](/specification/v1.0/schemas/#agent_capabilities.json) in v1.0; [`server_capabilities.json`](/specification/v0.9.1/schemas/#server_capabilities.json) in v0.9.1) declare the agent's `supportedCatalogIds` and `acceptsInlineCatalogs` flag. See [Overview: How A2UI Works](/concepts/overview/) and [Capabilities and metadata](/specification/v1.0-a2ui/#capabilities-and-metadata) in the protocol specification. ### Catalog A versioned JSON Schema document ([`catalog_definition.json`](/specification/v1.0/schemas/#catalog_definition.json)) identified by a unique `catalogId` string (conventionally formatted as a URI) that defines the UI [components](#component), [functions](#function), and Markdown `instructions` shared between an [agent](#agent) and a [renderer](#renderer). A catalog is an explicit allowlist: the agent uses it to construct model prompts and validate generated messages, while the renderer binds each declared component to a native UI widget and each local function to client code. Catalogs can define low-level primitives (such as `Row`, `Text`, and `Button` in the [Basic catalog](#basic-catalog)) or domain-specific components (such as `FlightSelector` or `HotelCheckout`). In v1.0, a single [surface](#surface) can mix components and functions from multiple catalogs by setting `catalogId` on individual entries. See [Catalogs](/concepts/catalogs/), [How to Define a Catalog](/guides/defining-a-catalog/), and [The component catalog](/specification/v1.0-a2ui/#the-component-catalog) in the protocol specification. ### Catalog transformer An [Agent SDK](#agent-sdk) interface (`CatalogTransformer`) that filters or adapts a [catalog](#catalog) before prompt instructions are generated and validation schemas are compiled. Pruning unused components and functions lowers prompt token consumption and restricts what UI capabilities a model can emit for a specific task or user role. Built-in implementations in the TypeScript, Python, and Dart Agent SDKs include `ComponentPruningTransformer` (which keeps an allowlist of component names) and `FunctionPruningTransformer` (which keeps an allowlist of function names). For server-side composite component expansion, see [`MacroExpander`](#macroexpander). See [Agent SDK](/concepts/sdks/#agent-sdk). ### CheckRule A client-side validation rule ([`common_types.json#/$defs/CheckRule`](/specification/v1.0/schemas/#common_types.json/$defs/CheckRule) in v1.0; [`common_types.json#/$defs/CheckRule`](/specification/v0.9.1/schemas/#common_types.json/$defs/CheckRule) in v0.9.1) attached to input and interactive [components](#component) that include [`Checkable`](/specification/v1.0/schemas/#common_types.json/$defs/Checkable) (`@checks` in v1.0, `checks` in v0.9.1). In v1.0, each `CheckRule` pairs a `condition` (a [`DataBinding`](#data-binding) or [`FunctionCall`](#function-call) evaluating to a [`ValidationResult`](/specification/v1.0/schemas/#catalog_definition.json/$defs/ValidationResult) object `{valid, code, message, severity}` or a boolean) with an optional fallback `message`; in v0.9.1, `condition` is a `DynamicBoolean` and `message` is required. Checks that fail with `"error"` severity (the default, and what a `false` boolean coerces to) mark the component invalid and disable controls such as `Button`, while `"warning"` and `"info"` results display non-blocking feedback. See [Actions and Functions](/concepts/actions-and-functions/) and [Component Validation & Check Rules](/specification/v1.0-a2ui/#component-validation--check-rules) in the protocol specification. ### ChildList The schema type ([`common_types.json#/$defs/ChildList`](/specification/v1.0/schemas/#common_types.json/$defs/ChildList) in v1.0; [`common_types.json#/$defs/ChildList`](/specification/v0.9.1/schemas/#common_types.json/$defs/ChildList) in v0.9.1) used by container [components](#component) (such as `Column`, `Row`, and `List`) to hold multiple child components in a [surface](#surface)'s flat component list. A `ChildList` property accepts one of two forms: 1. A static array of `ComponentId` strings (for example, `["header", "body", "footer"]`). 2. A collection template object `{"path": "/items", "componentId": "itemCard"}` that instantiates `componentId` once for each element of the array at `path` in the [data model](#data-model), establishing a collection scope for relative [`@path`](#path) bindings and [`@index`](#index) calls. Template objects use the plain property name `"path"` (not `"@path"`) in both v1.0 and v0.9.1. See [Data Model and Data Binding](/concepts/data-binding/#displaying-dynamic-lists-of-data) and [Collection scopes (relative paths)](/specification/v1.0-a2ui/#collection-scopes-relative-paths) in the protocol specification. ### Component A declarative UI element defined in a [catalog](#catalog) (such as `Card`, `Button`, `TextField`, or a domain-specific `FlightStatusCard`) and rendered as a native widget by a client [framework adapter](#framework-adapter). Within a [surface](#surface), component instances are sent in `createSurface` or `updateComponents` as a flat adjacency list rather than a nested tree: each entry has a unique `id` (following Unicode UAX #31 identifier rules in v1.0), a `component` type name, optional `@catalogId`, `@accessibility` attributes, and `@metadata.extensions` (bare `accessibility` in v0.9.1), and references children by ID (`ComponentId` or [`ChildList`](#childlist)). Every rendered surface requires one component with `"id": "root"`, which mounts inside the reserved immutable `Surface` container ([`common_types.json#/$defs/Surface`](/specification/v1.0/schemas/#common_types.json/$defs/Surface)). See [Catalogs](/concepts/catalogs/) and [Component model](/specification/v1.0-a2ui/#component-model) in the protocol specification. ### Core SDK The UI-framework-agnostic foundational library in each language, shared by both [Agent SDKs](#agent-sdk) and client [framework adapters](#framework-adapter). The Core SDK implements protocol message models, `Catalog` schemas, reactive surface and [data model](#data-model) state containers (`SurfaceGroupModel`, `SurfaceModel`, `ComponentModel`, and `DataModel`, named `SurfaceViewModel` in Swift and `A2uiCoreSurfaceGroupModel`, `A2uiCoreSurfaceModel`, `A2uiCoreComponentRegistry`, and `A2uiCoreDataModel` in Kotlin, with `A2uiComponentModel` in `compose-runtime`), JSON Pointer resolution, [`@path`](#path) and [`@call`](#call) expression evaluation, [`MessageProcessor`](#messageprocessor), and payload validation. See [Core SDK](/concepts/sdks/#core-sdk) for official package names across languages. ## D ### Data binding Connecting a [component](#component)'s dynamic property (`DynamicString`, `DynamicNumber`, `DynamicBoolean`, `DynamicStringList`, or `DynamicValue` in [`common_types.json`](/specification/v1.0/schemas/#common_types.json)) to a path in the surface's [data model](#data-model) via a [`DataBinding`](/specification/v1.0/schemas/#common_types.json/$defs/DataBinding) object (`{"@path": "..."}` in v1.0, or `{"path": "..."}` in v0.9.1) instead of a literal value. Display components (such as `Text`) use one-way reactive bindings that update whenever the bound path changes. Input components (such as `TextField`, `CheckBox`, and `Slider`) establish local two-way bindings that read the current value at the bound path and write user edits back to the local data model immediately without per-keystroke network traffic. See [`@path`](#path), [Data Model and Data Binding](/concepts/data-binding/), and [Data model representation: binding, scope](/specification/v1.0-a2ui/#data-model-representation-binding-scope) in the protocol specification. ### Data model An isolated, reactive JSON state object maintained per [surface](#surface) on the [renderer](#renderer) and shared with the [agent](#agent) that owns the surface. The agent populates or mutates the data model inline in `createSurface` (v1.0) or incrementally via [`updateDataModel`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateDataModelMessage) using [RFC 6901 JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901). Local user edits remain on the renderer until sent back to the agent selectively in [`Event`](#event) `context` bindings, as resolved arguments to agent-routed function calls ([`callAgentFunction`](#callagentfunction) in v1.0), or in full via transport metadata (`a2uiRendererDataModel` matching [`renderer_data_model.json`](/specification/v1.0/schemas/#renderer_data_model.json) in v1.0; `a2uiClientDataModel` matching [`client_data_model.json`](/specification/v0.9.1/schemas/#client_data_model.json) in v0.9.1) when `createSurface` sets `"sendDataModel": true`. See [Data Model and Data Binding](/concepts/data-binding/) and [Data model updates: synchronization and convergence](/specification/v1.0-a2ui/#data-model-updates-synchronization-and-convergence) in the protocol specification. ## E ### Envelope schema A top-level JSON Schema that defines the outer wire structure (`"version"` plus a single message type key) for an [A2UI message](#a2ui-message) exchanged between an [agent](#agent) and a [renderer](#renderer), independent of which [catalog](#catalog) defines the UI [components](#component) and [functions](#function): 1. [`agent_to_renderer.json`](/specification/v1.0/schemas/#agent_to_renderer.json) ([`agent_to_renderer`](#agent_to_renderer)) validates v1.0 messages from agent to renderer (`"version": "1.0"`). 2. [`renderer_to_agent.json`](/specification/v1.0/schemas/#renderer_to_agent.json) ([`renderer_to_agent`](#renderer_to_agent)) validates v1.0 messages from renderer to agent (`"version": "1.0"`). 3. [`server_to_client.json`](/specification/v0.9.1/schemas/#server_to_client.json) validates v0.9.1 (and v0.8) messages from server (agent) to client (renderer), renamed to `agent_to_renderer.json` in v1.0. 4. [`client_to_server.json`](/specification/v0.9.1/schemas/#client_to_server.json) validates v0.9.1 (and v0.8) messages from client (renderer) to server (agent), renamed to `renderer_to_agent.json` in v1.0. Each envelope schema also has companion JSON array (`*_list.json`) and object-wrapped array (`*_list_wrapper.json`) schemas for batched message delivery. See [Messages](/concepts/messages/) and [Envelope message structure](/specification/v1.0-a2ui/#envelope-message-structure) in the protocol specification. ### Event The agent-bound variant of a component [`Action`](#action) (`action.event` in [`common_types.json#/$defs/Action`](/specification/v1.0/schemas/#common_types.json/$defs/Action)) that notifies the [agent](#agent) of user intent, such as clicking a submit button or choosing an item. An `event` object declares a required `name`, an optional `context` map of literal values or dynamic bindings, and (in v1.0) an optional `userMessage` (`DynamicString`). When triggered by the user (provided no component [`CheckRule`](#checkrule) fails with `"error"` severity), the [renderer](#renderer) resolves every binding in `context` and `userMessage` against the local [data model](#data-model) and emits an [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) message in [`renderer_to_agent`](#renderer_to_agent) containing `name`, `surfaceId`, `sourceComponentId`, ISO 8601 `timestamp`, resolved `context`, and (in v1.0) optional `userMessage` and `metadata.extensions`. See [Actions and Functions](/concepts/actions-and-functions/) and [`action`](/specification/v1.0-a2ui/#action) in the protocol specification. ## F ### Framework adapter A UI-framework-specific client library (also called a Renderer SDK), built on top of a [Core SDK](#core-sdk), that renders active surface state from [`MessageProcessor`](#messageprocessor) using native widgets in a target framework such as Flutter, React, Lit Web Components, Angular, SwiftUI, or Jetpack Compose. Framework adapters include a built-in implementation of the [Basic catalog](#basic-catalog), map A2UI `AccessibilityAttributes` (`label`, `description`, `live`, `hidden`) to platform accessibility APIs, and let teams register custom [catalog](#catalog) component views and local [functions](#function) while the adapter handles reactive state updates. See [Renderer SDK](/concepts/sdks/#renderer-sdk) for official package names and [How to Integrate A2UI into a Client](/guides/client-integration/). ### Function A named, typed operation declared in a [catalog](#catalog)'s `functions` map ([`catalog_definition.json#/$defs/FunctionDefinition`](/specification/v1.0/schemas/#catalog_definition.json/$defs/FunctionDefinition)) with an argument schema (`args`) and `returnType` (`"string"`, `"number"`, `"boolean"`, `"array"`, `"object"`, `"validationResult"`, `"any"`, or `"void"`). Functions execute logic without sending raw code between the [agent](#agent) and [renderer](#renderer). In v1.0, whether a function call runs locally or on the agent depends on whether the renderer registers a local implementation, while `allowedCallers` (`"rendererOnly"` by default, `"agentOnly"`, or `"rendererOrAgent"`) and `requiresUserActivation` govern which side and gesture context may invoke it: 1. Renderer functions have a local implementation on the renderer and execute with zero network round-trips for tasks such as string and number formatting, input validation, boolean combinators, or user-gesture actions like `openUrl` (`requiresUserActivation: true`, which requires `"rendererOnly"`). Functions declared with `allowedCallers` set to `"agentOnly"` or `"rendererOrAgent"` (without `requiresUserActivation`) can also be invoked remotely by the agent via [`callRendererFunction`](#callrendererfunction); `"agentOnly"` functions still run on the renderer and cannot be bound to component properties or actions. 2. Agent functions (new in v1.0) have no local implementation on the renderer and execute on the agent for server-side tasks such as checking inventory or verifying credentials; when the renderer resolves a [`FunctionCall`](#function-call)'s catalog and finds no local implementation (whether or not the catalog declares the function, and provided the call is not `@`-prefixed, declared `"agentOnly"`, or blocked by `requiresUserActivation`), the renderer dispatches [`callAgentFunction`](#callagentfunction) and awaits `agentFunctionResponse`. (In v0.9.1, functions execute only on the renderer.) An A2UI function is distinct from a language model tool: | Aspect | A2UI function | LLM tool | | :---------- | :----------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | | Executor | [Renderer](#renderer) (renderer functions) or [agent](#agent) (agent functions). | Model host or agent environment outside the UI. | | Timing | During UI rendering, on user interaction, or as an explicit remote call over the protocol. | During model generation before or while producing a response. | | Purpose | UI formatting, input validation, client-side effects, or UI-triggered server lookups. | Model reasoning, web search, database queries, or backend operations. | | Declaration | Declared in an A2UI [catalog](#catalog) with typed `args`, `returnType`, and caller rules. | Declared in a tool definition passed to the language model. | See [Actions and Functions](/concepts/actions-and-functions/) and [Functions in A2UI Content Execution](/specification/v1.0-a2ui/#functions-in-a2ui-content-execution) in the protocol specification. ### Function call An invocation of a catalog or system [function](#function) represented as a [`FunctionCall`](/specification/v1.0/schemas/#common_types.json/$defs/FunctionCall) object with [`@call`](#call) in v1.0 (`"call"` in v0.9.1), an optional `catalogId` override, and optional `args`. Function calls occur in four places across the protocol: 1. Dynamic component property bindings (`DynamicString`, `DynamicNumber`, `DynamicBoolean`, `DynamicStringList`, and `DynamicValue`), re-evaluated reactively when bound [data model](#data-model) paths change. 2. Validation rules ([`CheckRule`](#checkrule) `@checks[].condition` in v1.0, `checks[].condition` in v0.9.1), returning a `ValidationResult` or boolean to validate inputs and gate controls. 3. Component [`Action`](#action) handlers (`action.functionCall`), triggered by a user gesture. 4. Explicit remote procedure call envelopes ([`callRendererFunction`](#callrendererfunction) and [`callAgentFunction`](#callagentfunction) in v1.0). See [Actions and Functions](/concepts/actions-and-functions/). ## G ### Generative UI patterns Common application layouts for presenting agent-generated [surfaces](#surface) (none of which are required by the A2UI protocol): 1. Chat: Generated surfaces appear in chronological order inside a vertically scrollable conversation view alongside text messages. 2. Canvas: A shared workspace where the user and [agent](#agent) iteratively inspect and update one or more persistent surfaces across turns. 3. Dashboard: Generated surfaces are arranged by topic or role rather than time and remain pinned in fixed regions of the screen. 4. Wizard: Generated surfaces appear step by step to gather structured input for a multi-stage task. See [Overview: How A2UI Works](/concepts/overview/) and [How to Integrate A2UI into a Client](/guides/client-integration/). ## I ### @index The reserved v1.0 universal system [function](#function) ([`common_types.json#/$defs/IndexSystemFunction`](/specification/v1.0/schemas/#common_types.json/$defs/IndexSystemFunction)) that returns the 0-based index of the current item when rendering a [`ChildList`](#childlist) collection template, called as `{"@call": "@index"}` or `{"@call": "@index", "args": {"offset": 1}}` (or `${@index(offset: 1)}` inside `formatString`). In nested collection templates, `@index` resolves to the innermost enclosing template's index; calling `@index` outside a list template fails evaluation (reporting `EXPRESSION_ERROR` on dynamic bindings or `EXECUTION_ERROR` in [`callRendererFunction`](#callrendererfunction)). See [Data Model and Data Binding](/concepts/data-binding/#displaying-dynamic-lists-of-data) and [The `@index` function](/specification/v1.0-a2ui/#the-index-function) in the protocol specification. ### Inference format The structured notation in which a language model writes UI blocks inside [A2UI tags](#a2ui-tag) before the [Agent SDK](#agent-sdk) compiles them into [`agent_to_renderer`](#agent_to_renderer) wire JSON. Because inference formats are decoupled from the wire protocol, an agent can switch formats on [`A2uiGenerator`](#a2uigenerator) without modifying client renderers. Supported formats include Express (`ExpressFormatFactory`, the default compact positional DSL that cuts output tokens and decode latency), Direct JSON (`DirectJsonFormatFactory`, raw JSON message arrays with incremental stream healing), and experimental Python-only formats (Atom and Elemental). When reading a model response (`parseResponse` / `parse_response` or streaming `openStream` / `open_stream`), the Agent SDK parser runs three stages: 1. Tag unwrapping (`unwrap`): Scans model output for opening and closing A2UI tags to separate conversational prose from raw UI blocks. 2. Compilation (`compile`): Translates each raw block from format notation into `agent_to_renderer` messages without checking catalogs or `RendererState`. 3. Validation and state tracking: `parseResponse` / `parse_response` and `openStream` / `open_stream` validate compiled messages against the active [catalogs](#catalog) and update `RendererState`. See [Inference Formats](/concepts/inference-formats/). ## M ### MacroExpander An asymmetric catalog and message transformer in the Python [Agent SDK](#agent-sdk) (`a2ui.transformers.macros`) that lets developers define coarse-grained composite components on the server with the `@macro` decorator and lower them into standard [catalog](#catalog) primitives before wire transmission. `MacroExpander.transform_to_inference_catalog` augments a base catalog with component schemas synthesized from `@macro` function type annotations and docstrings for model prompting, and `MacroExpander.transform_to_transport` expands macro components emitted by the model into primitive `createSurface` and `updateComponents` subtrees so client [renderers](#renderer) need no custom widget code. See [Using Macros](/guides/macros/) and [Agent SDK](/concepts/sdks/#agent-sdk). ### MessageProcessor The stateful controller class in each language's [Core SDK](#core-sdk) (`A2uiMessageProcessor` in Kotlin) that processes incoming [`agent_to_renderer`](#agent_to_renderer) (and v0.9.1 `server_to_client`) message streams. `MessageProcessor` validates incoming payloads against registered [catalogs](#catalog), mutates reactive surface and [data model](#data-model) state containers (`SurfaceGroupModel`, `SurfaceModel`, `ComponentModel`, and `DataModel`, named `SurfaceViewModel` in Swift and `A2uiCoreSurfaceGroupModel`, `A2uiCoreSurfaceModel`, `A2uiCoreComponentRegistry`, and `A2uiCoreDataModel` in Kotlin, with `A2uiComponentModel` in `compose-runtime`), resolves JSON Pointers and [`@call`](#call) expressions, enforces function caller and user-activation rules, emits outbound [`renderer_to_agent`](#renderer_to_agent) messages (`action`, `callAgentFunction`, `rendererFunctionResponse`, and `error`), and builds renderer [capabilities](#capabilities) and `a2uiRendererDataModel` metadata snapshots. See [Core SDK](/concepts/sdks/#core-sdk) and [How to Integrate A2UI into a Client](/guides/client-integration/). ## N ### NoAI information Data categorized by an application as inaccessible to AI models and [agents](#agent), such as payment card details, credentials, or regulated personal records. Which fields count as NoAI information depends on the product and workflow (for example, medical history may be excluded from AI context in a billing app while required in a clinical diagnostic tool). In generative UI interfaces, keeping NoAI inputs outside agent-bound [`Event`](#event) `context` maps, outside arguments to function calls that can route to the agent via [`callAgentFunction`](#callagentfunction), and off `"sendDataModel": true` surfaces (while visually distinguishing AI-connected inputs from private inputs) lets end users see which data can reach a model. See [Synchronizing state back to the agent](/concepts/data-binding/#synchronizing-state-back-to-the-agent) in Data Model and Data Binding. ## P ### @path The reserved v1.0 protocol directive key in a [`DataBinding`](/specification/v1.0/schemas/#common_types.json/$defs/DataBinding) object that points to a value in a surface's [data model](#data-model) using an [RFC 6901 JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901), such as `{"@path": "/reservation/guestEmail"}`. A path starting with `/` resolves from the root of the surface data model, while a path without a leading slash (such as `{"@path": "guestName"}`) resolves relative to the current item in an enclosing [`ChildList`](#childlist) collection template. In v0.9.1, `DataBinding` uses `"path"` without the `@` prefix ([`common_types.json#/$defs/DataBinding`](/specification/v0.9.1/schemas/#common_types.json/$defs/DataBinding)). Even in v1.0, `updateDataModel.path` and `ChildList` template `path` use plain `"path"` because they are fixed schema fields rather than dynamic value unions. See [Data binding](#data-binding), [Data Model and Data Binding](/concepts/data-binding/), and [Path resolution & scope](/specification/v1.0-a2ui/#path-resolution--scope) in the protocol specification. ## R ### Renderer The client-side runtime inside a host application that advertises supported [catalogs](#catalog) via [capabilities](#capabilities), applies [`agent_to_renderer`](#agent_to_renderer) messages through [`MessageProcessor`](#messageprocessor), renders [surfaces](#surface) as native widgets via a [framework adapter](#framework-adapter), maintains per-surface [data models](#data-model), runs local [functions](#function), and sends [`renderer_to_agent`](#renderer_to_agent) messages back to the [agent](#agent). See [Renderer SDK](/concepts/sdks/#renderer-sdk) and [How to Integrate A2UI into a Client](/guides/client-integration/). ### renderer_to_agent The v1.0 protocol [envelope schema](#envelope-schema) ([`renderer_to_agent.json`](/specification/v1.0/schemas/#renderer_to_agent.json)) for messages sent from a [renderer](#renderer) to an [agent](#agent). In v0.9.1, this envelope schema is named [`client_to_server.json`](/specification/v0.9.1/schemas/#client_to_server.json). Every v1.0 `renderer_to_agent` message sets `"version": "1.0"` (v0.9.1 uses `"version": "v0.9"` or `"v0.9.1"`) alongside one of four message keys: 1. [`action`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/action) dispatches a user-triggered [`Event`](#event) with its resolved `context`. 2. [`callAgentFunction`](#callagentfunction) (new in v1.0) requests remote execution of an agent-side [function](#function). 3. [`rendererFunctionResponse`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/rendererFunctionResponse) (new in v1.0) returns the `value` or `error` for an agent-initiated [`callRendererFunction`](#callrendererfunction) request. 4. [`error`](/specification/v1.0/schemas/#renderer_to_agent.json/properties/error) reports validation errors (`VALIDATION_FAILED`, `UNALLOWED_PARENT`, `UNALLOWED_CHILD`) or runtime faults (`EXPRESSION_ERROR`, `EXECUTION_ERROR`, `INTEGRITY_ERROR`, `INVALID_FUNCTION_CALL`), allowing the agent to prompt the model for self-correction. See [Messages](/concepts/messages/) and [Renderer-to-agent event messages](/specification/v1.0-a2ui/#renderer-to-agent-event-messages) in the protocol specification. ## S ### Surface An isolated, non-nesting UI region created with a globally unique `surfaceId` via [`createSurface`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/CreateSurfaceMessage), populated with a flat adjacency list of [components](#component) rooted at `"id": "root"`, backed by its own [data model](#data-model), and removed via [`deleteSurface`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/DeleteSurfaceMessage). Creating a surface implicitly instantiates the reserved immutable `Surface` container ([`common_types.json#/$defs/Surface`](/specification/v1.0/schemas/#common_types.json/$defs/Surface)) with `"child": "root"`. Surfaces cannot nest inside one another, though a host application can display multiple surfaces at the same time (such as separate cards in a chat timeline or panels in a dashboard). See [Overview: How A2UI Works](/concepts/overview/), [Messages](/concepts/messages/), and [`createSurface`](/specification/v1.0-a2ui/#createsurface) in the protocol specification. --- # Inference Formats > How A2UI decouples the wire protocol from model generation with Express, Direct JSON, and custom inference formats. Canonical URL: https://a2ui.org/concepts/inference-formats/ An inference format defines the structured notation that a language model reads in its system prompt and writes in its response before the server compiles that output into standard A2UI [wire messages](/concepts/messages/). Because compilation happens on the server, every client [renderer](/concepts/sdks/#renderer-sdk) receives the same A2UI JSON over the wire regardless of how the model produced it. In the diagram below, `A2uiRequestProcessor` coordinates three server-side stages. First, `PromptGenerator` turns the negotiated [catalog](/concepts/catalogs/) schemas, `instructions`, and few-shot `examples` into a format-specific prompt snippet. Next, the model writes UI blocks inside sentinel tags (`...` for Express or `...` for Direct JSON) alongside conversational prose. Finally, `Parser` or `ResponseStream` heals streaming chunks, compiles each block into wire JSON, and validates every message against `RendererState`. ## Wire protocol vs. generation formats Standard JSON is well suited as a wire protocol between an agent server and a client renderer: it is self-describing, language-independent, and validated directly by JSON Schema. Asking a language model to emit raw wire JSON token by token, however, carries avoidable costs: 1. Repeated keys (`"id"`, `"component"`, `"@path"`) and hand-written component identifiers make raw wire JSON roughly three times longer than positional constructor calls. 2. Embedding full JSON Schemas in the system prompt consumes roughly twice as many input tokens as compact component signatures. 3. Deeply nested braces, quotes, and manual component IDs increase syntax and reference errors, especially on smaller, faster models. Inference formats separate what is best for the model to generate from what is best for the renderer to consume. The [Agent SDKs](/concepts/sdks/#agent-sdk) include two built-in formats and allow custom implementations: | Format | Sentinel tags | SDK support | How it represents UI | | :---------- | :--------------------------- | :--------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | | Express | `...` | TypeScript, Python, Dart (default) | Compact positional DSL with nested component calls and `$/path` data assignments, compiled to flat wire JSON. | | Direct JSON | `...` | TypeScript, Python, Dart | Standard A2UI wire JSON emitted directly by the model, with Markdown fence stripping and incremental string healing. | | Custom | Format-defined | TypeScript, Python, Dart | Any domain-specific grammar or model-tailored notation implemented against the SDK's `InferenceFormatFactory` contract. | Not every A2UI agent uses free-form model generation. Regulated or deterministic workflows can also construct wire messages directly in server code (driven by a classifier, state machine, or [macro](/guides/macros/)) and send the resulting JSON to the renderer without an LLM compilation step. ## Express vs. wire JSON In Express, the model calls component constructors with positional or keyword arguments, writes [data model](/concepts/data-binding/) values with `$/path = value`, and nests child components inline instead of inventing string IDs by hand. The example below uses `Card`, `Column`, `Text`, and `Button` from the Basic Catalog; Express derives signatures dynamically for any custom `catalog.json` you register (whitespace and indentation are optional): ```text title="Express block (indented for readability)" $/flight/number = "NH 105" $/flight/gate = "B12" root = Card( Column([ Text($/flight/number), Text($/flight/gate, "caption"), Button(Text("Board now"), action=Event("check_in", {flight: $/flight/number})) ]) ) ``` When the compiler reads that `` block, it flattens the nested tree and assigns deterministic IDs (`root`, `root_child`, `root_child_children_0`, and so on). In v1.0, initial data assignments fold into `createSurface.dataModel`; in v0.9.1, the compiler emits `createSurface`, `updateComponents`, and `updateDataModel`: #### v1.0 ```json [ { "version": "1.0", "createSurface": { "surfaceId": "main", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "components": [ { "id": "root", "component": "Card", "child": "root_child" }, { "id": "root_child", "component": "Column", "children": ["root_child_children_0", "root_child_children_1", "root_child_children_2"] }, { "id": "root_child_children_0", "component": "Text", "text": {"@path": "/flight/number"} }, { "id": "root_child_children_1", "component": "Text", "text": {"@path": "/flight/gate"}, "variant": "caption" }, { "id": "root_child_children_2", "component": "Button", "child": "root_child_children_2_child", "action": { "event": { "name": "check_in", "context": { "flight": {"@path": "/flight/number"} } } } }, { "id": "root_child_children_2_child", "component": "Text", "text": "Board now" } ], "dataModel": { "flight": { "number": "NH 105", "gate": "B12" } } } } ] ``` #### v0.9.1 ```json [ { "version": "v0.9", "createSurface": { "surfaceId": "main", "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } }, { "version": "v0.9", "updateComponents": { "surfaceId": "main", "components": [ { "id": "root", "component": "Card", "child": "root_child" }, { "id": "root_child", "component": "Column", "children": ["root_child_children_0", "root_child_children_1", "root_child_children_2"] }, { "id": "root_child_children_0", "component": "Text", "text": {"path": "/flight/number"} }, { "id": "root_child_children_1", "component": "Text", "text": {"path": "/flight/gate"}, "variant": "caption" }, { "id": "root_child_children_2", "component": "Button", "child": "root_child_children_2_child", "action": { "event": { "name": "check_in", "context": { "flight": {"path": "/flight/number"} } } } }, { "id": "root_child_children_2_child", "component": "Text", "text": "Board now" } ] } }, { "version": "v0.9", "updateDataModel": { "surfaceId": "main", "path": "/", "value": { "flight": { "number": "NH 105", "gate": "B12" } } } } ] ``` For the full grammar of Express, see the [A2UI Express specification proposal](https://github.com/a2ui-project/a2ui/blob/main/specification/proposals/express/a2ui_express.md). For the wire message schemas that all formats target, see [`createSurface`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/CreateSurfaceMessage), [`updateComponents`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateComponentsMessage), and [`updateDataModel`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateDataModelMessage) in the Schema Explorer. ## How inference formats stay swappable Swapping an inference format requires no changes to catalogs, few-shot examples, or client renderers because every format in the [Agent SDK](/concepts/sdks/#agent-sdk) shares three design properties: 1. Catalog-agnostic prompt synthesis: Rather than hardcoding component names, a format's `PromptGenerator` inspects the negotiated `Catalog` schemas at runtime to derive component signatures, function signatures, and dynamic binding rules. 2. Bidirectional translation (`compile` and `decompile`): Alongside compiling model output into wire messages, each format's `Parser` can decompile wire messages back into the format's text notation. Structured few-shot `examples`, fenced `json` message blocks inside a catalog's `instructions`, and prior conversation turns can therefore be authored once as wire messages and rendered automatically in whichever format is active. 3. Incremental stream compilation and validation: Both one-shot parsing (`parseResponse` / `parse_response`) and chunked streaming (`openStream` / `open_stream`) separate conversational text from UI sentinel blocks, heal in-progress string literals on configured `progressiveKeys` (such as `"text"`), emit incremental wire deltas as statements arrive, and validate every message against `RendererState` so malformed output can be returned to the model for self-correction. In the Agent SDKs (available for TypeScript, Python, and Dart; the Swift and Kotlin packages focus on client-side rendering), `A2uiGenerator` defaults to `ExpressFormatFactory` and accepts any `InferenceFormatFactory` globally or per request while keeping prompt generation and response parsing identical: #### TypeScript ```typescript import { A2uiGenerator, CatalogConfig, DirectJsonFormatFactory, ExpressFormatFactory, } from '@a2ui/agent'; // Uses ExpressFormatFactory by default; pass any InferenceFormatFactory to change it. const generator = new A2uiGenerator( [new CatalogConfig(catalog)], undefined, new ExpressFormatFactory(), ); // Prompt synthesis and parsing stay the same regardless of format, // and an individual request can override the format factory if needed. const processor = generator.createProcessor(rendererCapabilities, new DirectJsonFormatFactory()); const systemPrompt = processor.promptSnippet; const parts = processor.parseResponse(llmOutput); ``` #### Python ```python from a2ui.inference_formats import DirectJsonFormatFactory, ExpressFormatFactory from a2ui.processor import A2uiGenerator, CatalogConfig # Uses ExpressFormatFactory by default; pass any InferenceFormatFactory to change it. generator = A2uiGenerator( catalogs=[CatalogConfig(catalog=catalog)], inference_format_factory=ExpressFormatFactory(), ) # Prompt synthesis and parsing stay the same regardless of format, # and an individual request can override the format factory if needed. processor = generator.create_processor( renderer_capabilities, inference_format_factory=DirectJsonFormatFactory(), ) system_prompt = processor.prompt_snippet parts = processor.parse_response(llm_output) ``` #### Dart ```dart import 'package:a2ui_agent/a2ui_agent.dart'; void illustrateFormats( Catalog catalog, A2uiRendererCapabilities rendererCapabilities, String llmOutput, ) { // Uses ExpressFormatFactory by default; pass any InferenceFormatFactory to change it. final generator = A2uiGenerator( catalogs: [CatalogConfig(catalog)], inferenceFormatFactory: const ExpressFormatFactory(), ); // Prompt synthesis and parsing stay the same regardless of format, // and an individual request can override the format factory if needed. final processor = generator.createProcessor( rendererCapabilities, inferenceFormatFactory: const DirectJsonFormatFactory(), ); final systemPrompt = processor.promptSnippet; final parts = processor.parseResponse(llmOutput); } ``` Express covers v0.9, v0.9.1, and v1.0 generation with lower token cost, while Direct JSON remains useful when serving legacy v0.8 payloads in Python, debugging raw wire payloads, or calling a model fine-tuned directly on A2UI JSON schemas. Teams experimenting with alternative grammars (such as HTML-inspired or S-expression notations; see [`python/a2ui_agent/src/a2ui/inference_formats/experimental/`](https://github.com/a2ui-project/a2ui/tree/main/python/a2ui_agent/src/a2ui/inference_formats/experimental)) implement the same `InferenceFormatFactory`, `PromptGenerator`, and `Parser` interfaces. ## Token and latency benchmarks The A2UI evaluation suite (`eval/`) benchmarks each format on the 54-sample `core_v1_0` suite using `gemini-3.8-flash` (`temperature=0.0`). For earlier benchmark background, see [Fast, reliable A2UI, with swappable inference formats](https://developers.googleblog.com/fast-reliable-a2ui/). Whether thinking is disabled (`thinking_budget=0`) or left unbounded (default), Express reduces both prompt tokens and generated code tokens while matching Direct JSON on accuracy: | Format and thinking mode | Median input tokens | Median code tokens | Median thinking tokens | Median total output (`code + thinking`) | Pass@1 / Pass@2 | Judge quality | | :--------------------------------- | :------------------ | :----------------- | :--------------------- | :-------------------------------------- | :-------------------- | :------------ | | Direct JSON (`thinking_budget=0`) | 10,980 | 807 | 0 | 811 | 98.1% / 98.1% (53/54) | 97.2% | | Express (`thinking_budget=0`) | 6,000 (-45.4%) | 297 (-63.2%, 2.7x) | 0 | 299 (-63.1%) | 98.1% / 98.1% (53/54) | 97.2% | | Direct JSON (`unbounded` thinking) | 10,980 | 794 | 1,413 | 2,320 | 98.1% / 98.1% (53/54) | 98.1% | | Express (`unbounded` thinking) | 6,000 (-45.4%) | 260 (-67.3%, 3.1x) | 1,447 | 1,789 (-22.9%) | 98.1% / 98.1% (53/54) | 98.1% | --- # Messages > Asynchronous bidirectional JSON message types and transport metadata in A2UI. Canonical URL: https://a2ui.org/concepts/messages/ A2UI agents and client renderers communicate by exchanging asynchronous JSON messages in both directions to manage UI surfaces and handle user interactions. These message types can be sent over a variety of transports, including [Agent-to-Agent (A2A)](https://a2a-protocol.org) and [Model Context Protocol (MCP)](https://modelcontextprotocol.io). Alongside explicit A2UI messages, implementations can use [metadata](#metadata) to include renderer context in requests sent for other purposes. For example, when a user sends a text prompt, the renderer can attach its supported catalogs or synchronized surface state to the outgoing request. --- ## Agent-to-renderer messages Agent-to-renderer messages construct surfaces, update component hierarchies and reactive data models, clean up surfaces, invoke client-side functions, and return results for agent-side function calls. Each wire message in either direction is a JSON object with a `"version"` field (`"1.0"` in v1.0, or `"v0.9"` / `"v0.9.1"` in v0.9.1) and a single message key. ### createSurface The `createSurface` message allocates a new UI surface identified by `surfaceId`. The `surfaceId` must be globally unique within the rendering context (the `MessageProcessor` instance on the renderer), which is typically a conversation. Creating a surface implicitly instantiates the reserved [`Surface`](/specification/v1.0/schemas/#common_types.json/$defs/Surface) container component with `child: "root"`. In v1.0, `createSurface` can also include optional surface-level custom extensions under `metadata.extensions` ([`Extensions`](/specification/v1.0/schemas/#common_types.json/$defs/Extensions)). #### Properties | Property | Type | Required | Description | | :-------------- | :------ | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `surfaceId` | string | Yes | Unique identifier for the surface. | | `catalogId` | string | Optional (v1.0), Yes (v0.9.1) | Default catalog identifier for this surface (for example, `travel:flights`). In v1.0, individual components (`@catalogId`) and function calls (`catalogId`) can specify their own catalog when omitted here. | | `sendDataModel` | boolean | Optional | When `true`, the renderer attaches the full surface data model in transport metadata (`a2uiRendererDataModel` in v1.0 / `a2uiClientDataModel` in v0.9.1) on every message to the creating agent. | | `components` | array | Optional (v1.0) | Initial flat list of component definitions, allowing single-message surface creation. | | `dataModel` | object | Optional (v1.0) | Initial root data model JSON object for the surface. | | `metadata` | object | Optional (v1.0) | Surface-level extensions container with optional `extensions` ([`Extensions`](/specification/v1.0/schemas/#common_types.json/$defs/Extensions)). | > **Note** In v1.0, `catalogId` is optional, `components` and `dataModel` can be passed inline to initialize a surface in a single message, and the v0.9.1 `theme` property is removed in favor of host application styling. #### Example Creating a `"flight_search"` surface: #### v1.0 ```json { "version": "1.0", "createSurface": { "surfaceId": "flight_search", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "sendDataModel": false, "components": [ { "id": "root", "component": "Column", "children": ["title", "flight_card"] }, { "id": "title", "component": "Text", "text": "Available Flights" }, { "id": "flight_card", "component": "Card", "child": "flight_info" }, { "id": "flight_info", "component": "Text", "text": {"@path": "/flights/0/summary"} } ], "dataModel": { "flights": [ {"summary": "LH458 · BER -> SFO · $680", "price": 680} ] } } } ``` #### v0.9.1 ```json { "version": "v0.9", "createSurface": { "surfaceId": "flight_search", "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json", "sendDataModel": false } } ``` --- ### updateComponents The `updateComponents` message adds or replaces component definitions in an existing surface. Relationships between components are expressed by ID references in a flat adjacency list rather than nested JSON trees, and one component in the surface must have `id: "root"`. In v1.0, individual component definitions in `components` can also carry optional component-level extensions via `@metadata.extensions` ([`ComponentCommon`](/specification/v1.0/schemas/#common_types.json/$defs/ComponentCommon)). #### Properties | Property | Type | Required | Description | | :----------- | :----- | :------- | :---------------------------------------------------------------------------------------------------------------------------- | | `surfaceId` | string | Yes | Identifier of the surface to update. | | `components` | array | Yes | Non-empty flat array of component objects. Components whose `id` already exists in the surface replace the stored definition. | #### Example Populating a surface with a `Column`, header `Text`, and interactive `Button`: #### v1.0 ```json { "version": "1.0", "updateComponents": { "surfaceId": "flight_search", "components": [ { "id": "root", "component": "Column", "children": ["header", "btn_select_lh458"] }, { "id": "header", "component": "Text", "text": "Outbound Flights" }, { "id": "btn_select_label", "component": "Text", "text": "Select LH458 ($680)" }, { "id": "btn_select_lh458", "component": "Button", "child": "btn_select_label", "variant": "primary", "action": { "event": { "name": "select_flight", "userMessage": "Selected flight LH458", "context": { "flightId": "LH458", "price": {"@path": "/flights/0/price"} } } } } ] } } ``` #### v0.9.1 ```json { "version": "v0.9", "updateComponents": { "surfaceId": "flight_search", "components": [ { "id": "root", "component": "Column", "children": ["header", "btn_select_lh458"] }, { "id": "header", "component": "Text", "text": "Outbound Flights" }, { "id": "btn_select_label", "component": "Text", "text": "Select LH458 ($680)" }, { "id": "btn_select_lh458", "component": "Button", "child": "btn_select_label", "variant": "primary", "action": { "event": { "name": "select_flight", "context": { "flightId": "LH458", "price": {"path": "/flights/0/price"} } } } } ] } } ``` --- ### updateDataModel The `updateDataModel` message updates the surface data model at a targeted location using an [RFC 6901 JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901), allowing the agent to refresh bound values without resending component definitions. #### Properties | Property | Type | Required | Description | | :---------- | :----- | :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `surfaceId` | string | Yes | Identifier of the target surface. | | `path` | string | Optional | RFC 6901 JSON Pointer indicating where to apply the update (for example, `"/selectedFlight"`). When omitted or set to `"/"`, the update replaces the entire data model. | | `value` | any | Yes (v1.0), Optional (v0.9.1) | Value to store at `path`. | > **Note** In **v1.0**, `value` is required; set `"value": null` to delete the key at `path`. In **v0.9.1**, `value` is optional, and omitting `value` removes the key at `path`. #### Examples #### v1.0 Updating `/selectedFlight`: ```json { "version": "1.0", "updateDataModel": { "surfaceId": "flight_search", "path": "/selectedFlight", "value": { "flightId": "LH458", "status": "confirmed", "passengers": 2 } } } ``` Deleting `/selectedFlight` in v1.0 by setting `"value": null`: ```json { "version": "1.0", "updateDataModel": { "surfaceId": "flight_search", "path": "/selectedFlight", "value": null } } ``` #### v0.9.1 Updating `/selectedFlight`: ```json { "version": "v0.9", "updateDataModel": { "surfaceId": "flight_search", "path": "/selectedFlight", "value": { "flightId": "LH458", "status": "confirmed", "passengers": 2 } } } ``` Deleting `/selectedFlight` in v0.9.1 by omitting `value`: ```json { "version": "v0.9", "updateDataModel": { "surfaceId": "flight_search", "path": "/selectedFlight" } } ``` --- ### deleteSurface The `deleteSurface` message instructs the renderer to unmount the surface identified by `surfaceId` and release its component tree and data model state. #### Properties | Property | Type | Required | Description | | :---------- | :----- | :------- | :--------------------------------- | | `surfaceId` | string | Yes | Identifier of the surface to drop. | #### Example Deleting the `"flight_search"` surface: #### v1.0 ```json { "version": "1.0", "deleteSurface": { "surfaceId": "flight_search" } } ``` #### v0.9.1 ```json { "version": "v0.9", "deleteSurface": { "surfaceId": "flight_search" } } ``` --- ### callRendererFunction (v1.0 only) The `callRendererFunction` message is an agent-initiated remote procedure call (RPC) that directs the renderer to execute a catalog function registered on the client (for example, reading device timezone or screen dimensions). Only catalog functions declaring `allowedCallers` as `"agentOnly"` or `"rendererOrAgent"` may be called by the agent. > **Note** `callRendererFunction` is introduced in protocol v1.0. In v0.9.1, `server_to_client.json` does not define an agent-to-renderer function invocation message. #### Properties | Property | Type | Required | Description | | :--------------- | :----- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `functionCallId` | string | Yes | Unique correlation identifier for this call, copied verbatim into [`rendererFunctionResponse`](#rendererfunctionresponse-v10-only). | | `callFunction` | object | Yes | [`FunctionCall`](/specification/v1.0/schemas/#common_types.json/$defs/FunctionCall) object with required `@call`, required `catalogId`, and optional `args`. | | `surfaceId` | string | Optional | Surface ID whose data model provides the evaluation context. When omitted, the function executes in an isolated root context with `{}`. | #### Example Calling a client-side `getDeviceTimezone` function on the renderer: ```json { "version": "1.0", "callRendererFunction": { "surfaceId": "flight_search", "functionCallId": "call_tz_101", "callFunction": { "@call": "getDeviceTimezone", "catalogId": "device:sensors", "args": {} } } } ``` --- ### agentFunctionResponse (v1.0 only) The `agentFunctionResponse` message returns the execution result or structured error for a preceding [`callAgentFunction`](#callagentfunction-v10-only) message sent by the renderer. #### Properties | Property | Type | Required | Description | | :--------------- | :----- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------- | | `functionCallId` | string | Yes | Correlation identifier matching the initiating `callAgentFunction` message. | | `value` | any | Conditional | Return value produced on success. Exactly one of `value` or `error` must be present. | | `error` | object | Conditional | Error object with required `code` (`string`) and `message` (`string`) fields if execution failed. Mutually exclusive with `value`. | #### Example Returning baggage allowance data for an agent function call: ```json { "version": "1.0", "agentFunctionResponse": { "functionCallId": "call_baggage_202", "value": { "allowedBags": 2, "maxWeightKg": 23, "feePerExtraBag": 75 } } } ``` --- ## Renderer-to-agent messages Renderer-to-agent messages report user interactions, invoke remote functions on the agent, and return results from agent-initiated client function calls. ### action The `action` message notifies the agent that the user triggered an event action on an interactive component (such as clicking a `Button` configured with `action.event`). Before dispatching the message, the renderer resolves any data bindings inside `action.event.context` and `action.event.userMessage` against the local data model. In v1.0, `action` can also include optional renderer-side custom extensions under `metadata.extensions` ([`Extensions`](/specification/v1.0/schemas/#common_types.json/$defs/Extensions)). #### Properties | Property | Type | Required | Description | | :------------------ | :----- | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Event name declared on the component's `action.event.name`. | | `surfaceId` | string | Yes | Surface ID where the interaction originated. | | `sourceComponentId` | string | Yes | Component `id` that triggered the action. | | `timestamp` | string | Yes | ISO 8601 date-time string indicating when the event occurred. | | `context` | object | Yes | Resolved key-value dictionary from `action.event.context` with all data bindings evaluated to current values. | | `userMessage` | string | Optional (v1.0) | Human-readable string resolved from `action.event.userMessage`, suitable for adding to LLM conversation history. | | `metadata` | object | Optional (v1.0) | Action-level extensions container with optional `extensions` ([`Extensions`](/specification/v1.0/schemas/#common_types.json/$defs/Extensions)). | #### Example Reporting a `"select_flight"` button action with resolved context: #### v1.0 ```json { "version": "1.0", "action": { "name": "select_flight", "surfaceId": "flight_search", "sourceComponentId": "btn_select_lh458", "timestamp": "2026-11-02T10:15:30.000Z", "userMessage": "Selected flight LH458", "context": { "flightId": "LH458", "price": 680 } } } ``` #### v0.9.1 ```json { "version": "v0.9", "action": { "name": "select_flight", "surfaceId": "flight_search", "sourceComponentId": "btn_select_lh458", "timestamp": "2026-11-02T10:15:30.000Z", "context": { "flightId": "LH458", "price": 680 } } } ``` --- ### callAgentFunction (v1.0 only) The `callAgentFunction` message is a renderer-initiated RPC requesting that the agent execute a function remotely (for example, checking live baggage rules or verifying a backend identifier). #### Properties | Property | Type | Required | Description | | :--------------- | :----- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `surfaceId` | string | Yes | Surface ID where the call originated. | | `functionCallId` | string | Yes | Unique correlation identifier for this call, copied verbatim into [`agentFunctionResponse`](#agentfunctionresponse-v10-only). | | `callFunction` | object | Yes | [`FunctionCall`](/specification/v1.0/schemas/#common_types.json/$defs/FunctionCall) object with required `@call`, optional `catalogId`, and optional `args`. | #### Example Invoking `getBaggageAllowance` on the agent from the renderer: ```json { "version": "1.0", "callAgentFunction": { "surfaceId": "flight_search", "functionCallId": "call_baggage_202", "callFunction": { "@call": "getBaggageAllowance", "catalogId": "travel:flights", "args": { "flightId": "LH458", "cabin": "economy" } } } } ``` --- ### rendererFunctionResponse (v1.0 only) The `rendererFunctionResponse` message delivers the renderer's return value or structured execution error for a preceding [`callRendererFunction`](#callrendererfunction-v10-only) message. #### Properties | Property | Type | Required | Description | | :--------------- | :----- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------- | | `functionCallId` | string | Yes | Correlation identifier matching the initiating `callRendererFunction` message. | | `value` | any | Conditional | Return value produced on success. Exactly one of `value` or `error` must be present. | | `error` | object | Conditional | Error object with required `code` (`string`) and `message` (`string`) fields if execution failed. Mutually exclusive with `value`. | #### Example Returning the device timezone for a preceding `callRendererFunction` call: ```json { "version": "1.0", "rendererFunctionResponse": { "functionCallId": "call_tz_101", "value": { "timeZone": "Europe/Berlin", "utcOffsetMinutes": 60 } } } ``` --- ### Metadata Transport metadata lets the renderer attach A2UI context to outgoing requests sent for other purposes (such as user text prompts in A2A or request metadata in MCP), flowing from the renderer to the agent. | Metadata key (v1.0) | Metadata key (v0.9.1) | Purpose | | :------------------------- | :----------------------- | :-------------------------------------------------------------------------------------------- | | `a2uiRendererCapabilities` | `a2uiClientCapabilities` | Advertises supported protocol versions, `supportedCatalogIds`, and optional `inlineCatalogs`. | | `a2uiRendererDataModel` | `a2uiClientDataModel` | Sends full local data models for active surfaces created with `sendDataModel: true`. | Renderer capabilities (`a2uiRendererCapabilities` in v1.0 / `a2uiClientCapabilities` in v0.9.1) are an optional mechanism that loosely coupled systems can use to negotiate which protocol versions and catalogs can be used when they are not known in advance. Systems where the agent and client are owned by the same party may not need capability negotiation if the protocol version and catalogs are agreed in advance. Agents can also advertise their static catalog support ahead of time via [`agent_capabilities.json`](/specification/v1.0/schemas/#agent_capabilities.json) (v1.0) or [`server_capabilities.json`](/specification/v0.9.1/schemas/#server_capabilities.json) (v0.9.1), such as in an A2A Agent Card. When any active surface was created with `sendDataModel: true`, the renderer attaches `a2uiRendererDataModel` (or `a2uiClientDataModel` in v0.9.1) on outgoing messages to that agent so local input state stays synchronized without embedding full data models inside `action` payloads. #### Example Attaching capabilities and synchronized surface state to an outgoing transport request: #### v1.0 ```json { "metadata": { "a2uiRendererCapabilities": { "1.0": { "supportedCatalogIds": [ "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "travel:flights" ] } }, "a2uiRendererDataModel": { "version": "1.0", "surfaces": { "flight_search": { "selectedFlight": "LH458", "passengers": 2 } } } } } ``` #### v0.9.1 ```json { "metadata": { "a2uiClientCapabilities": { "v0.9": { "supportedCatalogIds": [ "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json", "travel:flights" ] } }, "a2uiClientDataModel": { "version": "v0.9", "surfaces": { "flight_search": { "selectedFlight": "LH458", "passengers": 2 } } } } } ``` --- # SDKs > Official A2UI Core, Agent, and Renderer SDKs across TypeScript, Python, Dart, Swift, and Kotlin. Canonical URL: https://a2ui.org/concepts/sdks/ Official A2UI SDKs are organized into three layers: - **Core SDK** for shared protocol models and surface state. - **Agent SDK** for catalog-driven prompt generation and model output compilation. - **Renderer SDK** for mounting surface state in a client UI framework. Both the Agent SDK and the Renderer SDK build on their language's Core SDK, so server-side validation and client-side message resolution share one implementation per language. Client-server applications typically pair two layers on each side of a network boundary, such as a Python agent server communicating with a SwiftUI or Jetpack Compose client. Codebases that handle both model inference and UI rendering in the same language, such as TypeScript web apps or Dart and Flutter apps, can use all three layers together in one project. ## Core SDK Every Agent SDK and Renderer SDK depends on its language's Core SDK, which implements the protocol data structures, reactive state containers, and expression evaluators common to agents and clients. ### Primary functions 1. Parses and serializes A2UI [wire messages](/concepts/messages/) across supported protocol versions (v1.0, v0.9.1, and v0.8 in TypeScript and Python). 2. Loads [Catalog](/concepts/catalogs/) JSON Schemas and validates component hierarchies, placement constraints, and message payloads. 3. Maintains per-surface component graphs and JSON [data model](/concepts/data-binding/) state while applying incremental updates by component ID and JSON Pointer path. 4. Evaluates data bindings, collection templates, string interpolation expressions, function invocations, and validation checks. ### Packages | Language | Published packages | Versions | Source | | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------------- | | TypeScript | [@a2ui/web_core](https://www.npmjs.com/package/@a2ui/web_core) | v0.8, v0.9.1, v1.0 | [typescript/core/web_core](https://github.com/a2ui-project/a2ui/tree/main/typescript/core/web_core) | | Python | [a2ui-core](https://pypi.org/project/a2ui-core/) | v0.8, v0.9.1, v1.0 | [python/a2ui_core](https://github.com/a2ui-project/a2ui/tree/main/python/a2ui_core) | | Dart | [a2ui_core](https://pub.dev/packages/a2ui_core) | v0.9.1, v1.0 | [dart/a2ui_core](https://github.com/a2ui-project/a2ui/tree/main/dart/a2ui_core) | | Swift | [A2UISwiftCore](https://github.com/a2ui-project/a2ui/blob/main/Package.swift)
[BasicCatalog](https://github.com/a2ui-project/a2ui/blob/main/Package.swift) | v0.9.1, v1.0 | [swift/core](https://github.com/a2ui-project/a2ui/tree/main/swift/core) | | Kotlin | [androidx.a2ui](https://developer.android.com/jetpack/androidx/releases/a2ui) | v0.9.1 | [AndroidX a2ui](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:a2ui/) | All v0.9.1 packages accept v0.9 wire envelopes and catalog IDs. Swift packages are distributed through Swift Package Manager from [`a2ui-project/a2ui`](https://github.com/a2ui-project/a2ui) (the `A2UISwiftCore` product exposes the `A2UICore` module), and the AndroidX group on Google Maven publishes the `a2ui-model` and `a2ui-engine` artifacts. Most applications install a Core SDK automatically through an [Agent SDK](#agent-sdk) or [Renderer SDK](#renderer-sdk), though you can depend on a Core SDK directly to build headless state processors or custom UI adapters. To implement a Core SDK in another language, read [`blueprints/modules`](https://github.com/a2ui-project/a2ui/tree/main/blueprints/modules) and validate against the YAML suites in [`conformance/core`](https://github.com/a2ui-project/a2ui/tree/main/conformance/core). ## Agent SDK An Agent SDK connects a language model or agent runtime to the A2UI protocol while leaving model selection, tool execution, and conversation state under your application's control. ### Primary functions 1. Negotiates active [Catalog](/concepts/catalogs/) definitions and protocol versions (v0.9.1 and v1.0) against the capabilities reported by the client renderer (see [Overview](/concepts/overview/)). 2. Generates skills, system prompt instructions, and examples directly from catalog schemas and prompt instructions, replacing hand-written prompts that become outdated when components change. 3. Compiles streaming or batch model output from [inference formats](/concepts/inference-formats/) (such as compact Express syntax, which cuts output tokens without client changes, or chunk-healed Direct JSON) into standard A2UI [wire messages](/concepts/messages/). 4. Validates outgoing messages statefully against active surface state on the server so broken component IDs, placement violations, or schema mismatches can be fed back to the model for self-correction before transmission. 5. Supports optional server-side [`@macro`](/guides/macros/) expansion in Python and integrates with agent frameworks and transports including [Google ADK](https://github.com/google/adk-python) and the [A2A protocol](https://a2a-protocol.org/). ### Packages | Language | Published packages | Versions | Source | | :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | TypeScript | [@a2ui/agent](https://www.npmjs.com/package/@a2ui/agent) | v0.9.1, v1.0 | [typescript/agents/a2ui_agent](https://github.com/a2ui-project/a2ui/tree/main/typescript/agents/a2ui_agent) | | Python | [a2ui-agent-sdk](https://pypi.org/project/a2ui-agent-sdk/)
[a2ui-adk](https://pypi.org/project/a2ui-adk/) (ADK)
[a2ui-a2a](https://pypi.org/project/a2ui-a2a/) (A2A) | v0.8, v0.9.1, v1.0 | [python/a2ui_agent](https://github.com/a2ui-project/a2ui/tree/main/python/a2ui_agent)
[python/a2ui_adk](https://github.com/a2ui-project/a2ui/tree/main/python/a2ui_adk)
[python/a2ui_a2a](https://github.com/a2ui-project/a2ui/tree/main/python/a2ui_a2a) | | Dart | [a2ui_agent](https://pub.dev/packages/a2ui_agent) | v0.9.1, v1.0 | [dart/a2ui_agent](https://github.com/a2ui-project/a2ui/tree/main/dart/a2ui_agent) | Swift and Kotlin target client-side rendering; for legacy v0.8 and v0.9 JVM servers, [`kotlin/agent_sdk_legacy`](https://github.com/a2ui-project/a2ui/tree/main/kotlin/agent_sdk_legacy) remains in the repository. ### Go and community SDKs For **Go** services and CLI clients, community-maintained libraries cover server-side validation, streaming parsers, and terminal rendering—including [`satorunooshie/a2ui-go`](https://github.com/satorunooshie/a2ui-go), [`tmc/a2ui`](https://pkg.go.dev/github.com/tmc/a2ui), [`charmbracelet/a2tea`](https://github.com/charmbracelet/a2tea), and [`muncus/a2term`](https://github.com/muncus/a2term) (listed with version details in [Community Projects](/ecosystem/renderers/)). To implement a conformance-verified Core or Agent SDK in Go, Rust, C#, or another language, follow the language-agnostic specifications in [`blueprints/modules`](https://github.com/a2ui-project/a2ui/tree/main/blueprints/modules), verify behavioral parity against the YAML suites in [`conformance/core`](https://github.com/a2ui-project/a2ui/tree/main/conformance/core) and [`conformance/agent`](https://github.com/a2ui-project/a2ui/tree/main/conformance/agent), and open a pull request to add your package to [Community Projects](/ecosystem/renderers/#how-to-submit-a-project). See [Using a catalog in an agent](/concepts/catalogs/#using-a-catalog-in-an-agent) and [Inference Formats](/concepts/inference-formats/#how-inference-formats-stay-swappable) for TypeScript, Python, and Dart examples of configuring `A2uiGenerator`, negotiating capabilities, and compiling model responses, or run a working agent in the [Quickstart](/quickstart/). ## Renderer SDK A Renderer SDK connects a Core SDK's resolved surface state to a client UI framework. Official Renderer SDKs include a built-in implementation of the [Basic Catalog](/concepts/catalogs/#the-basic-catalog) styled through the host framework's theming system, and let you register custom catalogs with per-component rendering logic and local functions (see [How to Define a Catalog](/guides/defining-a-catalog/)). ### Primary functions 1. Applies incremental [surface, component, and data model updates](/concepts/messages/) across supported protocol versions (v0.9.1, v1.0, and v0.8 on Web), updating only changed subtrees so unchanged siblings keep focus and local UI state. 2. Evaluates [reactive data bindings](/concepts/data-binding/), nested collection scopes, relative JSON Pointers, and local two-way input edits. 3. Executes [local catalog functions](/concepts/catalogs/#functions) and [reactive validation checks](/specification/v1.0-a2ui/#component-validation--check-rules), enforcing v1.0 caller and user-activation rules. 4. Builds capability and data-model sync metadata payloads for your transport layer, dispatches user action events and structured error messages, and handles v1.0 bidirectional function RPCs. 5. Maps protocol accessibility attributes to host accessibility APIs across Flutter, Web WAI-ARIA, SwiftUI, and Jetpack Compose. ### Packages | Framework | Published packages | Versions | Source | | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Flutter | [a2ui_flutter](https://pub.dev/packages/a2ui_flutter) | v0.9.1, v1.0 | [dart/a2ui_flutter](https://github.com/a2ui-project/a2ui/tree/main/dart/a2ui_flutter) | | React | [@a2ui/react](https://www.npmjs.com/package/@a2ui/react) | v0.8, v0.9.1, v1.0 | [typescript/renderers/react](https://github.com/a2ui-project/a2ui/tree/main/typescript/renderers/react) | | Lit | [@a2ui/lit](https://www.npmjs.com/package/@a2ui/lit) | v0.8, v0.9.1, v1.0 | [typescript/renderers/lit](https://github.com/a2ui-project/a2ui/tree/main/typescript/renderers/lit) | | Angular | [@a2ui/angular](https://www.npmjs.com/package/@a2ui/angular) | v0.8, v0.9.1, v1.0 | [typescript/renderers/angular](https://github.com/a2ui-project/a2ui/tree/main/typescript/renderers/angular) | | SwiftUI | [A2UISwiftUI](https://github.com/a2ui-project/a2ui/blob/main/Package.swift)
[BasicCatalogSwiftUI](https://github.com/a2ui-project/a2ui/blob/main/Package.swift) | v0.9.1, v1.0 | [swift/swiftui](https://github.com/a2ui-project/a2ui/tree/main/swift/swiftui) | | Jetpack Compose | [androidx.a2ui.compose](https://developer.android.com/jetpack/androidx/releases/a2ui-compose)
[material3-a2ui](https://developer.android.com/jetpack/androidx/releases/compose-material3) | v0.9.1 | [AndroidX a2ui](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:a2ui/) · [material3-a2ui](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3-a2ui/) · [Docs](https://developer.android.com/develop/ui/compose/agentic) | On Web, [`@a2ui/web_core`](https://www.npmjs.com/package/@a2ui/web_core) provides the shared Basic Catalog implementation used by React, Lit, and Angular, [`@a2ui/markdown-it`](https://www.npmjs.com/package/@a2ui/markdown-it) ([source](https://github.com/a2ui-project/a2ui/tree/main/typescript/renderers/markdown-it)) renders Markdown inside text components, and [`@a2ui/catalog-mcp`](https://www.npmjs.com/package/@a2ui/catalog-mcp) adds MCP app components (see [Call MCP Tools from A2UI](/guides/mcp-catalog/)). See [Registering a catalog in a renderer](/concepts/catalogs/#registering-a-catalog-in-a-renderer) and [How to Define a Catalog](/guides/defining-a-catalog/) for Flutter, Web, SwiftUI, and Jetpack Compose examples of setting up a message processor, registering component widgets and local functions, and mounting surfaces, or run a React or Flutter client in the [Quickstart](/quickstart/). Read [Authoring Universal Web Components](/guides/authoring-universal-components/) to share custom components across React, Lit, and Angular, or see the [Android Jetpack Compose guide](https://developer.android.com/develop/ui/compose/agentic) for Android integration. Browse [Community Projects](/ecosystem/renderers/) for community renderers, or consult [`blueprints/modules`](https://github.com/a2ui-project/a2ui/tree/main/blueprints/modules) and [`conformance`](https://github.com/a2ui-project/a2ui/tree/main/conformance) to build a custom framework adapter. --- # Render A2UI Inside an MCP App > Bundle an A2UI renderer inside an MCP App ui:// resource so existing MCP hosts can display interactive A2UI surfaces without native host code. Canonical URL: https://a2ui.org/guides/a2ui-in-mcp-apps/ An **[MCP App](https://modelcontextprotocol.io/extensions/apps/overview)** can bundle an embedded A2UI web renderer inside its `ui://` HTML resource (`text/html;profile=mcp-app`) so existing [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) hosts display interactive A2UI surfaces without adding A2UI renderer code to the host application. Instead of requiring every MCP client (such as Claude Desktop, the MCP Inspector, or an IDE) to install an A2UI SDK, your MCP server ships a self-contained HTML bundle that mounts `` inside the host's isolated iframe. When tools on the server return `application/a2ui+json` payloads, the embedded app feeds those messages into its own [`MessageProcessor`](/concepts/sdks/#core-sdk) and forwards user actions back through the MCP Apps `tools/call` channel. > **Note** See the [MCP Integration Overview](/guides/mcp-overview/) to compare all four MCP patterns. Use this pattern when the outer host supports MCP Apps (`ui://` HTML iframes) rather than A2UI natively. For the inverse architecture (embedding third-party MCP App widgets inside your own A2UI host), see [Embed MCP Apps in A2UI Surfaces](/guides/mcp-apps-in-a2ui/). If both the host and server support A2UI natively and you want to serve reusable A2UI layout templates over MCP without an iframe, see [Serve A2UI Layouts from an MCP Server](/guides/a2ui-over-mcp/) and [Call MCP Tools from A2UI](/guides/mcp-catalog/). Generative document editor demo showing selected text in an MCP App triggering contextual A2UI controls to revise content --- ## Quick start: run the community sample The [`samples/community/mcp/a2ui-in-mcpapps/`](https://github.com/a2ui-project/a2ui/blob/main/samples/community/mcp/a2ui-in-mcpapps/README.md) directory contains a runnable end-to-end sample with a Python MCP server (`server/server.py`), two single-file Angular micro-apps (`ui://editor/app` for a generative document editor and `ui://basic/app` for a counter), and a sample Angular MCP Apps host (`client/`) that runs a double-iframe sandbox on `http://localhost:4200`. > **Note** The community sample currently emits A2UI `v0.8` messages (`surfaceUpdate`, `dataModelUpdate`, and `beginRendering`) rendered by `@a2ui/angular` `0.10.x` inside the micro-apps. The implementation walkthrough below updates this pattern to `v1.0` and `v0.9.1` wire payloads with `@a2ui/lit` and `@a2ui/web_core`. ### Prerequisites Install these tools before building and running the sample from a clone of the A2UI repository: 1. Python 3.10 or later and [`uv`](https://docs.astral.sh/uv/) for the Python MCP server (`server/.python-version` targets Python 3.13 when managed by `uv`). 2. Node.js 22.12 or later with Corepack enabled (`corepack enable`) so `yarn` resolves the standalone Yarn workspace under `samples/community/`. 3. A `GOOGLE_API_KEY` from [Google AI Studio](https://aistudio.google.com/apikey) so `server/smart_editor_agent.py` can initialize its Gemini client at startup and generate contextual editor controls (if a Gemini call fails at runtime, the editor falls back to preset slider controls). ### Build the sandbox relay and micro-apps The server serves single-file HTML bundles from `server/apps/public/`, which are git-ignored in a fresh checkout. Install the `samples/community` workspace dependencies, build the host's outer sandbox relay, and build both micro-apps from the repository root: ```bash cd samples/community yarn install # Build the host's outer sandbox relay (client/public/sandbox_iframe/) yarn workspace a2ui-in-mcpapps-client run build:sandbox # Build and inline the Editor micro-app (server/apps/public/editor.html) yarn workspace basic-mcp-app-angular-editor run build:all # Build and inline the Basic Counter micro-app (server/apps/public/app.html) yarn workspace basic-mcp-app-angular run build:all ``` When iterating on a single micro-app, you can run `yarn build:all` directly inside `samples/community/mcp/a2ui-in-mcpapps/server/apps/editor` or `samples/community/mcp/a2ui-in-mcpapps/server/apps/src`. ### Start the MCP server and host client Open two terminals starting from the repository root. 1. In the first terminal, start the Python MCP server over Server-Sent Events (SSE) on `http://127.0.0.1:8000/sse` (pass `--transport stdio` instead when connecting a desktop MCP host over standard input/output): ```bash cd samples/community/mcp/a2ui-in-mcpapps/server export GOOGLE_API_KEY="your-api-key" # Required at startup by smart_editor_agent.py uv sync uv run python server.py --transport sse --port 8000 ``` 2. In the second terminal, start the sample Angular MCP Apps host on `http://localhost:4200`: ```bash cd samples/community/mcp/a2ui-in-mcpapps/client yarn start ``` ### Try both views in the browser Open `http://localhost:4200` to test how the sandboxed micro-apps render A2UI payloads returned by the MCP server: 1. Keep **Generative Editor App** selected and click **Load Selected App**. The host calls `get_editor_app` and loads `ui://editor/app` (`editor.html`). Select more than 10 characters of text inside any Editor.js paragraph; the app calls `smart_editor_get_controls`, renders generated A2UI controls (`Slider`, `CheckBox`, or `MultipleChoice`) in the right-hand panel, and calls `smart_editor_apply` when you click **Generate Revision** to rewrite the passage and swap in **Accept** and **Reject** A2UI buttons. 2. Switch the selector to **Basic Counter App** and click **Load Selected App**. The host calls `get_basic_app`, mounts `ui://basic/app` (`app.html`), and delivers the initial A2UI counter surface via `ui/notifications/tool-result`. Clicking **Increase counter** inside the rendered A2UI card calls `increase_counter` on the MCP server and updates `/counter` in place. --- ## Architecture and end-to-end flow In a native A2UI client integration, the outer host application owns `MessageProcessor` and renders A2UI components directly in its DOM. Embedding A2UI inside an MCP App moves the A2UI runtime across the iframe boundary so any standards-compliant [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) host can render A2UI surfaces unchanged: 1. The **MCP server** publishes one or more self-contained HTML bundles under `ui://` URIs (`mimeType: "text/html;profile=mcp-app"`), alongside entry tools and UI callback tools that return `EmbeddedResource` items carrying `application/a2ui+json` messages. 2. The **MCP host application** connects to the MCP server, inspects `_meta.ui.resourceUri` on the invoked tool, fetches the `ui://` HTML via `resources/read`, and mounts an outer sandbox proxy iframe (`sandbox.html`) that injects the HTML into an inner `srcdoc` iframe with `sandbox="allow-scripts"`. 3. The **embedded MCP App** runs inside that opaque-origin `srcdoc` frame. It bundles its own A2UI web renderer (`MessageProcessor`, component catalogs, and ``), renders `application/a2ui+json` payloads alongside any custom web views (such as the Editor.js canvas in `ui://editor/app`), and translates A2UI action events into JSON-RPC 2.0 `tools/call` requests relayed through the host. MCP Apps tool descriptors also scope which actor can invoke each tool via `_meta.ui.visibility` (as shown in [`server/server.py`](https://github.com/a2ui-project/a2ui/blob/main/samples/community/mcp/a2ui-in-mcpapps/server/server.py)): 1. Entry tools such as `get_editor_app` and `get_basic_app` declare `_meta.ui.resourceUri` and `visibility: ["model"]`, exposing a single launcher tool to the language model. 2. Fine-grained UI interaction tools such as `increase_counter`, `smart_editor_get_controls`, and `smart_editor_apply` declare `visibility: ["app"]`, allowing the sandboxed view to call them via `tools/call` without adding UI callback noise to the model's tool list. The sequence diagram below traces the full message lifecycle across the MCP server, the outer MCP host application, the sandbox proxy iframe, and the embedded MCP App running the A2UI renderer. During startup, the host lists tools to discover `_meta.ui.resourceUri` and `_meta.ui.visibility`, invokes the entry tool via `tools/call`, fetches the `ui://` HTML document via `resources/read`, and injects it through the sandbox proxy into the isolated `srcdoc` iframe. Once the embedded app completes the `ui/initialize` and `ui/notifications/initialized` handshake, the host forwards the entry tool's result via `ui/notifications/tool-result`, and the app feeds the embedded `application/a2ui+json` messages into `MessageProcessor` to mount the A2UI surface. When the user clicks a button or submits a form on that surface, `MessageProcessor` dispatches an action, the app sends a JSON-RPC `tools/call` request through the proxy to the host, and the server returns a new `application/a2ui+json` payload (such as `updateDataModel`) that updates the rendered surface in place. ```mermaid sequenceDiagram participant Server as MCP Server participant Host as MCP Host Application participant Proxy as Sandbox Proxy Iframe participant App as Embedded MCP App + A2UI Renderer Host->>Server: tools/list (discover _meta.ui.resourceUri & visibility) Server-->>Host: Tool definitions Host->>Server: tools/call (open_counter_app) Server-->>Host: CallToolResult (EmbeddedResource application/a2ui+json) Host->>Server: resources/read (ui://counter/app) Server-->>Host: TextResourceContents (HTML MCP App bundle) Host->>Proxy: Load sandbox proxy and send ui/notifications/sandbox-resource-ready Proxy->>App: Inject self-contained HTML into srcdoc (allow-scripts) App->>Host: ui/initialize (relayed via Proxy) Host-->>App: InitializeResult (hostCapabilities, hostContext) App->>Host: ui/notifications/initialized Host->>App: ui/notifications/tool-result (initial CallToolResult) Note over App: Extract application/a2ui+json and run MessageProcessor.processMessages Note over App: User clicks Button on a2ui-surface App->>Host: tools/call (increase_counter, arguments from action.context) Host->>Server: tools/call (increase_counter) Server-->>Host: CallToolResult (updateDataModel in application/a2ui+json) Host-->>App: CallToolResult response Note over App: Apply updateDataModel to MessageProcessor ``` --- ## Build an MCP App with an embedded A2UI renderer The three steps below build a minimal MCP App using `@modelcontextprotocol/ext-apps`, `@a2ui/lit`, `@a2ui/web_core`, and Vite. ### Step 1: Register the ui:// resource and MCP tools Start by defining the A2UI messages that your server tools return inside `EmbeddedResource` items (`mimeType: "application/a2ui+json"`). The example below uses the Basic Catalog, though A2UI catalogs are domain-specific and your embedded bundle can register any custom catalog (see [How to Define a Catalog](/guides/defining-a-catalog/)). In `v1.0`, protocol messages set `"version": "1.0"`, expressions use `@path` and `@call`, component IDs follow UAX #31 identifier syntax (such as `main_column`), and `Text` supports Markdown headings. In `v0.9.1`, messages set `"version": "v0.9"`, expressions use `path` and `call` (with `"returnType": "string"` on string-valued function calls), and `Text.variant` includes `"h3"`. Here is the initial surface payload returned by `open_counter_app`: #### v1.0 ```json [ { "version": "1.0", "createSurface": { "surfaceId": "counter_card", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json" } }, { "version": "1.0", "updateComponents": { "surfaceId": "counter_card", "components": [ { "id": "root", "component": "Card", "child": "main_column" }, { "id": "main_column", "component": "Column", "children": ["title_text", "count_text", "increment_button"] }, { "id": "title_text", "component": "Text", "text": "### Live counter" }, { "id": "count_text", "component": "Text", "text": { "@call": "formatString", "args": { "value": "Count: ${/count}" } } }, { "id": "increment_button_text", "component": "Text", "text": "Increase counter" }, { "id": "increment_button", "component": "Button", "child": "increment_button_text", "variant": "primary", "action": { "event": { "name": "increase_counter", "context": { "step": 1 } } } } ] } }, { "version": "1.0", "updateDataModel": { "surfaceId": "counter_card", "path": "/", "value": { "count": 0 } } } ] ``` #### v0.9.1 ```json [ { "version": "v0.9", "createSurface": { "surfaceId": "counter_card", "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } }, { "version": "v0.9", "updateComponents": { "surfaceId": "counter_card", "components": [ { "id": "root", "component": "Card", "child": "main_column" }, { "id": "main_column", "component": "Column", "children": ["title_text", "count_text", "increment_button"] }, { "id": "title_text", "component": "Text", "text": "Live counter", "variant": "h3" }, { "id": "count_text", "component": "Text", "text": { "call": "formatString", "args": { "value": "Count: ${/count}" }, "returnType": "string" } }, { "id": "increment_button_text", "component": "Text", "text": "Increase counter" }, { "id": "increment_button", "component": "Button", "child": "increment_button_text", "variant": "primary", "action": { "event": { "name": "increase_counter", "context": { "step": 1 } } } } ] } }, { "version": "v0.9", "updateDataModel": { "surfaceId": "counter_card", "path": "/", "value": { "count": 0 } } } ] ``` When the embedded app invokes `increase_counter`, the tool returns only an [`updateDataModel`](/specification/v1.0/schemas/#agent_to_renderer.json/$defs/UpdateDataModelMessage) message (`server_to_client.json` in `v0.9.1`) so the existing surface updates `/count` without rebuilding its component tree: #### v1.0 ```json [ { "version": "1.0", "updateDataModel": { "surfaceId": "counter_card", "path": "/count", "value": 1 } } ] ``` #### v0.9.1 ```json [ { "version": "v0.9", "updateDataModel": { "surfaceId": "counter_card", "path": "/count", "value": 1 } } ] ``` Register the `ui://counter/app` HTML resource (`text/html;profile=mcp-app`), the model-facing `open_counter_app` entry tool, and the app-only `increase_counter` callback tool on your MCP server: #### TypeScript ```typescript import fs from 'node:fs'; import { RESOURCE_MIME_TYPE, registerAppResource, registerAppTool, } from '@modelcontextprotocol/ext-apps/server'; import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; import {z} from 'zod'; const A2UI_MIME_TYPE = 'application/a2ui+json'; const APP_URI = 'ui://counter/app'; let count = 0; export function createCounterMcpServer(bundlePath: string): McpServer { const server = new McpServer({name: 'a2ui-counter-mcp-app', version: '1.0.0'}); registerAppResource( server, 'Counter MCP App', APP_URI, { description: 'Self-contained MCP App with an embedded A2UI renderer.', mimeType: RESOURCE_MIME_TYPE, }, async () => ({ contents: [ { uri: APP_URI, mimeType: RESOURCE_MIME_TYPE, text: fs.readFileSync(bundlePath, 'utf8'), }, ], }), ); registerAppTool( server, 'open_counter_app', { title: 'Open Counter App', description: 'Opens the counter view and returns its initial A2UI surface.', inputSchema: {}, _meta: { ui: { resourceUri: APP_URI, visibility: ['model'], }, }, }, async () => ({ content: [ {type: 'text', text: `Counter opened at ${count}.`}, { type: 'resource', resource: { uri: 'a2ui://counter-card', mimeType: A2UI_MIME_TYPE, text: JSON.stringify(buildInitialSurfacePayload(count)), }, }, ], }), ); server.registerTool( 'increase_counter', { title: 'Increase Counter', description: 'Increments the counter and returns an A2UI updateDataModel payload.', inputSchema: { step: z.number().int().default(1), }, _meta: { ui: { visibility: ['app'], }, }, }, async ({step}) => { count += step; const update = [ { version: '1.0', updateDataModel: { surfaceId: 'counter_card', path: '/count', value: count, }, }, ]; return { content: [ {type: 'text', text: `Count is now ${count}.`}, { type: 'resource', resource: { uri: 'a2ui://counter-card/data', mimeType: A2UI_MIME_TYPE, text: JSON.stringify(update), }, }, ], }; }, ); return server; } ``` #### Python ```python import json from pathlib import Path from typing import Any from mcp import types from mcp.server.lowlevel import Server from mcp.server.lowlevel.helper_types import ReadResourceContents A2UI_MIME_TYPE = "application/a2ui+json" MCP_APP_MIME_TYPE = "text/html;profile=mcp-app" APP_URI = "ui://counter/app" BUNDLE_PATH = Path(__file__).parent / "dist" / "index.html" app = Server("a2ui-counter-mcp-app") count = 0 # Requires mcp<2 (pip install "mcp>=1.20,<2") for the 1.x low-level Server decorators. @app.list_resources() async def list_resources() -> list[types.Resource]: return [ types.Resource( uri=APP_URI, name="Counter MCP App", mimeType=MCP_APP_MIME_TYPE, description="Self-contained MCP App with an embedded A2UI renderer.", ) ] @app.read_resource() async def read_resource(uri: str) -> list[ReadResourceContents]: if str(uri) != APP_URI: raise ValueError(f"Unknown resource: {uri}") return [ ReadResourceContents( content=BUNDLE_PATH.read_text(encoding="utf-8"), mime_type=MCP_APP_MIME_TYPE, ) ] @app.list_tools() async def list_tools() -> list[types.Tool]: return [ types.Tool( name="open_counter_app", title="Open Counter App", description="Opens the counter view and returns its initial A2UI surface.", inputSchema={"type": "object", "properties": {}}, _meta={"ui": {"resourceUri": APP_URI, "visibility": ["model"]}}, ), types.Tool( name="increase_counter", title="Increase Counter", description="Increments the counter and returns an A2UI updateDataModel payload.", inputSchema={ "type": "object", "properties": {"step": {"type": "integer", "default": 1}}, }, _meta={"ui": {"visibility": ["app"]}}, ), ] @app.call_tool() async def handle_call_tool( name: str, arguments: dict[str, Any] ) -> types.CallToolResult: global count if name == "open_counter_app": payload = build_initial_surface_payload(count) return types.CallToolResult( content=[ types.TextContent(type="text", text=f"Counter opened at {count}."), types.EmbeddedResource( type="resource", resource=types.TextResourceContents( uri="a2ui://counter-card", mimeType=A2UI_MIME_TYPE, text=json.dumps(payload), ), ), ] ) if name == "increase_counter": count += int(arguments.get("step", 1)) update = [ { "version": "1.0", "updateDataModel": { "surfaceId": "counter_card", "path": "/count", "value": count, }, } ] return types.CallToolResult( content=[ types.TextContent(type="text", text=f"Count is now {count}."), types.EmbeddedResource( type="resource", resource=types.TextResourceContents( uri="a2ui://counter-card/data", mimeType=A2UI_MIME_TYPE, text=json.dumps(update), ), ), ] ) raise ValueError(f"Unknown tool: {name}") ``` ### Step 2: Embed the A2UI renderer and forward actions in the MCP App Inside the MCP App project, install `@a2ui/lit`, `@a2ui/markdown-it`, `@a2ui/web_core`, `lit`, and `@modelcontextprotocol/ext-apps`: ```bash npm install @a2ui/lit @a2ui/markdown-it @a2ui/web_core lit @modelcontextprotocol/ext-apps ``` Create an `index.html` entry point with a mount element for your surface: ```html Counter MCP App
``` In `src/main.ts`, register `renderMarkdown` via `setMarkdownRenderer`, initialize `MessageProcessor` with `basicCatalog` (or your custom catalog), mount `` when a surface is created, and use `App` from `@modelcontextprotocol/ext-apps` to handle the `ui/initialize` handshake, incoming `ui/notifications/tool-result` payloads, automatic iframe resizing (`autoResize: true`), and outbound `tools/call` requests whenever a component dispatches an [action](/concepts/actions-and-functions/): #### v1.0 ```typescript import {html, render} from 'lit'; import '@a2ui/lit'; import {renderMarkdown} from '@a2ui/markdown-it'; import { basicCatalog, MessageProcessor, setMarkdownRenderer, type ProcessableMessagePayload, } from '@a2ui/web_core/v1_0'; import {App} from '@modelcontextprotocol/ext-apps'; import type {CallToolResult} from '@modelcontextprotocol/sdk/types.js'; setMarkdownRenderer(renderMarkdown); const A2UI_MIME_TYPE = 'application/a2ui+json'; const rootEl = document.getElementById('surface-root')!; const app = new App( {name: 'counter-mcp-app', version: '1.0.0'}, {availableDisplayModes: ['inline']}, {autoResize: true}, ); function applyToolResult(result: CallToolResult): void { for (const block of result.content ?? []) { if ( block.type === 'resource' && 'text' in block.resource && (block.resource.mimeType === A2UI_MIME_TYPE || block.resource.mimeType === 'application/json+a2ui') ) { const messages = JSON.parse(block.resource.text) as ProcessableMessagePayload; processor.processMessages(messages); } } } const processor = new MessageProcessor([basicCatalog], async action => { const result = await app.callServerTool({ name: action.name, arguments: action.context, }); applyToolResult(result); }); processor.onSurfaceCreated(surface => { render(html``, rootEl); }); app.ontoolresult = params => { applyToolResult(params); }; await app.connect(); ``` #### v0.9.1 ```typescript import {html, render} from 'lit'; import {basicCatalog} from '@a2ui/lit/v0_9'; import {renderMarkdown} from '@a2ui/markdown-it'; import { MessageProcessor, setMarkdownRenderer, type ProcessableMessagePayload, } from '@a2ui/web_core/v0_9'; import {App} from '@modelcontextprotocol/ext-apps'; import type {CallToolResult} from '@modelcontextprotocol/sdk/types.js'; setMarkdownRenderer(renderMarkdown); const A2UI_MIME_TYPE = 'application/a2ui+json'; const rootEl = document.getElementById('surface-root')!; const app = new App( {name: 'counter-mcp-app', version: '1.0.0'}, {availableDisplayModes: ['inline']}, {autoResize: true}, ); function applyToolResult(result: CallToolResult): void { for (const block of result.content ?? []) { if ( block.type === 'resource' && 'text' in block.resource && (block.resource.mimeType === A2UI_MIME_TYPE || block.resource.mimeType === 'application/json+a2ui') ) { const messages = JSON.parse(block.resource.text) as ProcessableMessagePayload; processor.processMessages(messages); } } } const processor = new MessageProcessor([basicCatalog], async action => { const result = await app.callServerTool({ name: action.name, arguments: action.context, }); applyToolResult(result); }); processor.onSurfaceCreated(surface => { render(html``, rootEl); }); app.ontoolresult = params => { applyToolResult(params); }; await app.connect(); ``` If you need to include full form state from the surface `DataModel` alongside explicit `action.context` entries (as the Editor sample does when submitting slider and checkbox adjustments to `smart_editor_apply`), read `processor.getSurface(action.surfaceId)?.dataModel.get('/')` inside the action callback and merge it into `arguments`. You can also speak the underlying JSON-RPC 2.0 `window.parent.postMessage` protocol directly without `@modelcontextprotocol/ext-apps`; see [`server/apps/src/src/main.ts`](https://github.com/a2ui-project/a2ui/blob/main/samples/community/mcp/a2ui-in-mcpapps/server/apps/src/src/main.ts) for a zero-wrapper reference. ### Step 3: Bundle into a single self-contained HTML file MCP Apps load inside a `srcdoc` iframe with `sandbox="allow-scripts"` and no base server URL, so external `" } ``` In this component, updates to `/scene3d` on the surface push into the widget, edits to `scene3d` inside the widget update sibling A2UI components, and tool calls outside `["capture_3d_snapshot"]` are blocked.
All McpApp component properties An embedded `McpApp` is isolated from your page DOM, cookies, and browser storage, and interacts with your surface only through explicit properties: | Property | Required | Purpose | | :----------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | `htmlContent` | Yes | HTML document of the app (values starting with `url_encoded:` are URL-decoded first). | | `title` | No | Accessible title for the iframe (defaults to `"MCP App"`). | | `data.paths` | No | Maps state keys inside the app (plus reserved `toolInput`, `toolResult`, and `modelContext` keys) to data model paths for two-way reactive sync. | | `allowedTools` | No | List of MCP tool names the app is allowed to call. Unlisted tool calls are rejected. | | `allowedFunctions` | No | Map of A2UI catalog function names to argument JSON Schemas that the app is allowed to invoke. | | `csp` | No | Content Security Policy domain allowlists (`connectDomains`, `resourceDomains`, `frameDomains`, `baseUriDomains`) from the resource `_meta.ui.csp`. | | `permissions` | No | Browser permissions (`camera`, `microphone`, `geolocation`, `clipboardWrite`) delegated from the resource `_meta.ui.permissions`. | When a tool result returned by `callMcpTool` contains an MCP App `ui://` resource, its `resourceUri`, `html`, `csp`, and `permissions` fields are also included in the `callMcpTool` payload so you can bind `McpApp` properties to them.
### Style the frame `McpApp` starts at `500px` tall and resizes automatically when the embedded widget reports its content size. Customize the container with CSS custom properties on `a2ui-mcp-app` or any parent element: ```css a2ui-mcp-app { --a2ui-sandboxed-frame-height: 400px; --a2ui-sandboxed-frame-border: 1px solid #e2e8f0; --a2ui-sandboxed-frame-border-radius: 12px; --a2ui-sandboxed-frame-background: #ffffff; } ```
Default values for CSS custom properties | CSS custom property | Default | Purpose | | :------------------------------------- | :------------------------------------------------------------------- | :------------------------------------- | | `--a2ui-sandboxed-frame-height` | `500px` | Initial height before auto-resize. | | `--a2ui-sandboxed-frame-border` | `var(--a2ui-border-width, 1px) solid var(--a2ui-color-border, #ccc)` | Border around the frame. | | `--a2ui-sandboxed-frame-border-radius` | `var(--a2ui-border-radius, 8px)` | Corner radius of the frame. | | `--a2ui-sandboxed-frame-background` | `#fff` | Background behind transparent content. |
--- ## Quick start: run the Kanban showcase The fastest way to see an MCP App working inside an A2UI v1.0 surface is to run the Kanban board showcase ([`samples/community/mcp/a2ui-kanban/`](https://github.com/a2ui-project/a2ui/blob/main/samples/community/mcp/a2ui-kanban/README.md)). It connects three local processes: an MCP server that provides a drag-and-drop board widget (`ui://kanban/board`), a Python agent, and a Lit chat app built with `basicCatalog` and `mcpCatalog`. ### Prerequisites Install these tools before running the sample from a clone of the A2UI repository: 1. Python 3.10 or later for the agent backend. 2. [`uv`](https://docs.astral.sh/uv/) to install Python dependencies and start the agent. 3. Node.js 22.12 or later with Corepack enabled (`corepack enable`) so `yarn` uses the repository's configured workspace manager. 4. A `GEMINI_API_KEY` from [Google AI Studio](https://aistudio.google.com/apikey), or `KANBAN_AGENT=scripted` to try the guided walkthrough without an API key. ### Start the three services Open three terminals starting from the root of the A2UI repository. #### Build the packages and start the MCP server In the first terminal, build `@a2ui/catalog-mcp` and `@a2ui/lit`, install the `samples/community` dependencies, and start the Kanban MCP server on `http://localhost:8788/mcp`: ```bash yarn install yarn workspace @a2ui/catalog-mcp run build yarn workspace @a2ui/lit run build cd samples/community yarn install cd mcp/a2ui-kanban/mcp-server yarn build yarn start ``` #### Start the Kanban agent In the second terminal, start the Python A2A agent on `http://localhost:10007`: ```bash cd samples/community/mcp/a2ui-kanban/agent export GEMINI_API_KEY="your-api-key" # or: export KANBAN_AGENT=scripted uv run . ``` #### Start the chat app In the third terminal, start the Vite dev server on `http://localhost:5173`: ```bash cd samples/community/mcp/a2ui-kanban/client yarn dev ``` ### Things to try in the browser Open `http://localhost:5173/` and try these interactions to see the widget, the A2UI surface, and the agent working together: 1. Click **"Show me the sprint board"** to render the sandboxed Kanban widget alongside native A2UI summary badges and a quick-add task bar. 2. Drag a card across columns inside the widget and watch the sibling A2UI column counts update right away without waiting for an agent turn. 3. Enter a title and assignee in the A2UI quick-add bar and click **"Add task"** to call the MCP server from the browser and update both the board widget and the badges at once. 4. Click a card to select it, then ask the agent in chat to split or move that card using the selection context shared by the widget. 5. Open the **Protocol Debug Inspector** drawer on the right side of the page to inspect the live A2UI messages, `postMessage` frames, and MCP tool calls behind each action.
Pre-1.0 sample (Calculator and Pong) This earlier sample predates A2UI v1.0 and `@a2ui/catalog-mcp` (using protocol v0.9) and defines its own sample-local custom component rather than importing `McpApp` from `@a2ui/catalog-mcp`. #### Calculator and Pong (Angular v0.9 + proxy agent + MCP server) This sample runs an SSE Calculator MCP server, a Python ADK proxy agent, and an Angular client hosting calculator and Pong widgets in separate sandboxed iframes: 1. Start the Calculator MCP server on `http://localhost:8000` (pass `--port 8001` if port `8000` is in use): ```bash cd samples/community/mcp/mcp-apps-calculator/ uv run . ``` 2. In a second terminal, start the MCP proxy agent on `http://localhost:10006`: ```bash cd samples/community/agent/adk/mcp_app_proxy/ export GEMINI_API_KEY="your-api-key" uv run . ``` 3. Build the monorepo renderer packages at the repository root, then install and start the Angular client on `http://localhost:4200`: ```bash yarn build:all cd samples/community/client/angular/ yarn install yarn start mcp_calculator ``` 4. Open `http://localhost:4200/?disable_security_self_test=true` in your browser. Because this pre-v1.0 sample serves its proxy iframe on the same `localhost` origin without `@a2ui/catalog-mcp`'s automatic origin check, pass `?disable_security_self_test=true` in the URL to skip the cross-origin self-test during local development:
Calculator App Pong App
Animated GIF of the calculator app performing multiplications. Animated GIF of the Pong app updating a sibling A2UI scoreboard.
Troubleshooting the samples If a sample fails to build or load, check these common issues: | Symptom | Fix | | :---------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | `GEMINI_API_KEY environment variable not set` | Export `GEMINI_API_KEY` in that terminal, add a `.env` file in the agent directory, or set `KANBAN_AGENT=scripted` for the Kanban agent. | | Kanban client fails to compile `@a2ui/catalog-mcp` or `@a2ui/lit` | Run `yarn install`, `yarn workspace @a2ui/catalog-mcp run build`, and `yarn workspace @a2ui/lit run build` at the repository root first. | | Port `8788` or `10007` is already in use on Kanban | Start the service on another port and pass `?mcp_url=http://localhost:/mcp&agent_url=http://localhost:` to `http://localhost:5173/`. | | Angular client shows a blank page or missing renderer imports | Run `yarn build:all` at the repository root before running `yarn start mcp_calculator` in `samples/community/client/angular/`. | | `ng serve` not found in the Angular sample | Run `yarn install` inside `samples/community/client/angular/` to install `@angular/cli`. | | `"URL with hostname not allowed"` in Angular | Open `http://localhost:4200/` rather than `0.0.0.0` (Angular's dev server restricts allowed hosts by default). | | Security self-test error in the pre-1.0 Angular sample | Append `?disable_security_self_test=true` to `http://localhost:4200/`. |
--- ## How McpApp works under the hood When you move beyond the samples and review `McpApp` for production, two design choices matter most: how untrusted HTML is isolated from your host app, and how state crosses that boundary safely. ### Double-iframe sandbox Putting third-party HTML in a single iframe with both `allow-scripts` and `allow-same-origin` is unsafe because scripts inside that frame share your app's origin and can read cookies, web storage, or `window.parent.document`. Instead, `McpApp` uses two nested iframes. An outer proxy frame loads `/a2ui-sandbox/sandbox.html`, checks message origins, and injects the untrusted HTML into an inner `srcdoc` iframe with `sandbox="allow-scripts"` only. Without `allow-same-origin`, the browser assigns the inner frame a unique opaque origin (`null`), blocking access to your app's DOM, cookies, and storage. In the diagram below, the host web application renders an A2UI surface containing `McpApp` (`a2ui-mcp-app`). That element exchanges origin-checked `postMessage` events with the outer `/a2ui-sandbox/sandbox.html` proxy iframe, which relays JSON-RPC 2.0 messages to and from the inner `srcdoc` iframe running on an opaque origin with `sandbox='allow-scripts'`. ```mermaid flowchart TD subgraph HostApp ["Host Web Application"] A["A2UI surface"] --> B["McpApp (a2ui-mcp-app)"] end subgraph OuterProxy ["Outer Sandbox Proxy"] B <-->|"Origin-checked postMessage"| C["iframe /a2ui-sandbox/sandbox.html"] end subgraph InnerFrame ["Embedded MCP App"] C <-->|"JSON-RPC 2.0 relay"| D["iframe srcdoc (opaque origin, sandbox='allow-scripts')"] end ```
Advanced: outer proxy checks, CSP allowlists, and authoring constraints Each `McpApp` instance enforces the following rules across its two frames: 1. The outer frame loads `sandbox.html` from `/a2ui-sandbox/` on your app's origin, or from a dedicated sandbox origin set via `configureMcpCatalog({sandbox: {baseUrl}})`. When hosted on a separate origin, `sandbox.html` runs a startup self-test verifying that it cannot reach `window.top`; on the same origin, `@a2ui/catalog-mcp` disables that check automatically. 2. The inner frame receives `sandbox="allow-scripts"` only. Omitting `allow-same-origin` isolates cookies and storage, omitting `allow-top-navigation` and `allow-top-navigation-by-user-activation` prevents frame-busting redirects (`window.top.location = "..."`), and omitting `allow-popups`, `allow-forms`, and `allow-modals` blocks unprompted windows, form submissions, and modal dialogs. 3. Before injecting `srcdoc`, the outer proxy applies any Content Security Policy domain allowlists in `csp` (`_meta.ui.csp`) and browser capability flags in `permissions` (`_meta.ui.permissions`, such as `camera`, `microphone`, `geolocation`, and `clipboard-write`). If you are authoring an MCP App to run inside this sandbox, keep three browser restrictions in mind: 1. `localStorage`, `sessionStorage`, `IndexedDB`, and cookies throw security errors inside an opaque `null` origin. Persist state on the MCP server via `tools/call` or synchronize it with the host surface via `data.paths`. 2. `window.parent.document` and `window.top` are blocked. Request iframe height updates with `ui/notifications/size-changed`, and open external `http:` or `https:` links in a new tab with `ui/open-link`. 3. External network requests, images, fonts, and subframes inside `srcdoc` are blocked unless the MCP resource lists their domains under `_meta.ui.csp` (`connectDomains`, `resourceDomains`, `frameDomains`, `baseUriDomains`). To serve `/a2ui-sandbox/` in your bundler and configure [`McpApp`](/guides/mcp-catalog/#mcp_catalog.json/components/McpApp) properties, see [Set up McpApp in your web app](#set-up-mcpapp-in-your-web-app). For the startup handshake (`ui/notifications/sandbox-proxy-ready`, `ui/notifications/sandbox-resource-ready`, and `ui/initialize`), see the [MCP App component specification](https://github.com/a2ui-project/a2ui/blob/main/catalogs/mcp/v1/mcp_app_specification.md).
### Synchronizing state and tool calls Once the sandbox loads, `McpApp` bridges four communication paths over JSON-RPC 2.0: reactive two-way data paths shared with sibling A2UI components, direct MCP tool calls from the browser, local A2UI catalog function calls, and follow-up messages or context updates sent to the agent. In the diagram below, the host web application holds `MessageProcessor` and the surface data model alongside `McpApp` and its sibling A2UI components. `McpApp` synchronizes bound `data.paths` keys and `allowedFunctions` locally with `MessageProcessor`, calls allowed MCP tools directly via `getMcpClientForTool`, and forwards `a2ui.mcpAppMessage` actions and `modelContext` state to the AI agent, which can invoke MCP tools and stream `updateDataModel` or `updateComponents` messages back to `MessageProcessor`. ```mermaid flowchart TD MCPServer["MCP Server (tools and ui:// HTML resources)"] Agent["AI Agent (streams A2UI v1.0 messages)"] subgraph HostApp ["Host Web Application"] direction TB Processor["MessageProcessor and Surface Data Model"] subgraph SurfaceTree ["A2UI Surface"] McpComponent["McpApp Component (double-iframe HTML widget)"] Siblings["Sibling A2UI Components (Text, Button, TextField)"] end McpComponent <-->|"1. Two-way data.paths and allowedFunctions"| Processor Processor <-->|"Reactive @path and @call bindings"| Siblings end McpComponent ==>|"2. Direct tools/call (getMcpClientForTool)"| MCPServer Siblings ==>|"callMcpTool"| MCPServer Processor ==>|"3. a2ui.mcpAppMessage and modelContext"| Agent Agent <==>|"MCP tool calls"| MCPServer Agent ==>|"4. updateDataModel or updateComponents"| Processor ```
Advanced: JSON-RPC 2.0 channels and how the Kanban showcase uses them `McpApp` communicates with the embedded widget using `@modelcontextprotocol/ext-apps/app-bridge` plus A2UI v1.0 extensions across four channels: 1. Two-way local data binding (`data.paths`). When a bound path in the surface data model changes (from a sibling input, a client function, or an `updateDataModel` message from the agent), `McpApp` sends `ui/notifications/data-model-update` to the widget (plus standard `ui/notifications/tool-input` and `ui/notifications/tool-result` notifications when `toolInput` or `toolResult` are bound). When the widget emits `ui/notifications/data-model-change`, `McpApp` writes the new value to the bound path so sibling A2UI components update immediately. 2. Direct MCP tool execution (`tools/call`). When the widget calls a tool listed in `allowedTools` and the host registered `getMcpClientForTool` via `configureMcpCatalog`, `McpApp` calls the MCP server directly and returns the `CallToolResult`. If no browser MCP client is configured, `McpApp` dispatches an A2UI action named after the tool so a backend proxy agent can run it instead. Unlisted tool names are rejected with a JSON-RPC error. 3. Local catalog function calls (`ui/requests/function-call`). When `allowedFunctions` maps A2UI catalog function names to JSON Schemas for their arguments, the widget can validate arguments and invoke those functions locally on the surface. 4. Agent context and follow-up turns (`ui/update-model-context` and `ui/message`). Calling `ui/update-model-context` writes the widget's active selection or filter state to the bound `modelContext` path in the surface data model so your host can attach it to the next user turn. Calling `ui/message` dispatches an `a2ui.mcpAppMessage` action so the host can forward a prompt or event from the widget to the agent. In the Kanban showcase above, every interaction maps to one of these channels: 1. Asking the agent to move or create tasks calls MCP tools on the server and streams `updateDataModel` to `/kanban/board`, which `McpApp` forwards via `ui/notifications/data-model-update`. 2. Selecting a card or filtering by assignee calls `ui/update-model-context`, writing `/kanban/modelContext` and `/kanban/selection` for the next chat turn. 3. Dragging a card sends `ui/notifications/data-model-change` for `board`, updating `/kanban/board` locally and recomputing the sibling `Text` column and WIP badges (bound via `@call: transformData`). 4. Dropping a card (`tools/call` plus `ui/message`) or clicking **"Ask agent to split"** (`ui/message`) dispatches `a2ui.mcpAppMessage` to the chat host. 5. Submitting the quick-add task bar chains `callMcpTool("create_task")`, `transformData`, and `updateDataModel` in the browser to update `/kanban/board` with zero agent turns.
--- ## Troubleshooting McpApp ### The McpApp frame stays blank (404 under /a2ui-sandbox/) Check that `node_modules/@a2ui/catalog-mcp/sandbox` is served at `/a2ui-sandbox/` on your app's origin (see [Serve the sandbox files](#serve-the-sandbox-files)), or pass `configureMcpCatalog({sandbox: {baseUrl}})` with the path where you host those files. ### Tool or function calls from an embedded McpApp are rejected Check the browser console for warnings. Every MCP tool the widget calls must be listed in `allowedTools`, and every A2UI function must be listed in `allowedFunctions` and registered in `mcpCatalog` or the surface's catalog. --- ## Next steps 1. Install `@a2ui/catalog-mcp` and invoke headless MCP tools in [Call MCP Tools from A2UI](/guides/mcp-catalog/). 2. Review the handshake lifecycle, JSON-RPC message schemas, and cycle-prevention rules in the [MCP App component specification](https://github.com/a2ui-project/a2ui/blob/main/catalogs/mcp/v1/mcp_app_specification.md). 3. Build an MCP server that returns reusable A2UI component layouts in [Serve A2UI Layouts from an MCP Server](/guides/a2ui-over-mcp/). 4. Bundle an A2UI renderer inside an MCP App in [Render A2UI Inside an MCP App](/guides/a2ui-in-mcp-apps/). --- # Call MCP Tools from A2UI > Connect A2UI v1.0 web surfaces to Model Context Protocol (MCP) tools and shape tool responses with @a2ui/catalog-mcp. Canonical URL: https://a2ui.org/guides/mcp-catalog/ The `@a2ui/catalog-mcp` package connects A2UI v1.0 or later web surfaces directly to **[Model Context Protocol (MCP)](https://modelcontextprotocol.io/)** servers so buttons, inputs, and surface actions can call MCP tools and write their JSON or text results straight into the surface [Data Model](/concepts/data-binding/) without waiting for another round-trip to the agent. > **Note** See the [MCP Integration Overview](/guides/mcp-overview/) to compare calling headless MCP tools with serving reusable `a2ui://` layouts ([Serve A2UI Layouts from an MCP Server](/guides/a2ui-over-mcp/)), embedding sandboxed `ui://` HTML apps ([Embed MCP Apps in A2UI Surfaces](/guides/mcp-apps-in-a2ui/)), or bundling A2UI inside an MCP App ([Render A2UI Inside an MCP App](/guides/a2ui-in-mcp-apps/)). --- ## Quick setup Setting up `@a2ui/catalog-mcp` takes three steps. ### Install the packages Install `@a2ui/catalog-mcp` alongside `@a2ui/web_core`, `lit`, and the MCP TypeScript SDK: ```bash npm install @a2ui/catalog-mcp @a2ui/web_core lit @modelcontextprotocol/sdk ``` `@a2ui/web_core` and `lit` are peer dependencies so your renderer and catalog share one copy of A2UI's core classes, and `@modelcontextprotocol/sdk` provides the MCP `Client` that connects to your server. ### Register the catalog in your app Connect an MCP `Client` to your server, pass `mcpCatalog` alongside `basicCatalog` (or your own component catalog) to `MessageProcessor`, and call `configureMcpCatalog`: #### Lit ```ts import {configureMcpCatalog, mcpCatalog} from '@a2ui/catalog-mcp'; import {basicCatalog, MessageProcessor} from '@a2ui/web_core/v1_0'; import {Client} from '@modelcontextprotocol/sdk/client/index.js'; import {StreamableHTTPClientTransport} from '@modelcontextprotocol/sdk/client/streamableHttp.js'; // 1. Connect to your MCP server. const mcpClient = new Client({name: 'my-web-app', version: '1.0.0'}); await mcpClient.connect(new StreamableHTTPClientTransport(new URL('http://localhost:8000/mcp'))); // 2. Register mcpCatalog alongside basicCatalog. const processor = new MessageProcessor([basicCatalog, mcpCatalog], action => { console.log('Action dispatched:', action); }); // 3. Tell the catalog how to reach your MCP client and message processor. configureMcpCatalog({ getMcpClientForTool: () => mcpClient, processor, }); ``` #### React ```ts import {configureMcpCatalog, mcpCatalog} from '@a2ui/catalog-mcp'; import {type ReactCatalogComponent} from '@a2ui/react'; import {basicCatalog, MessageProcessor} from '@a2ui/web_core/v1_0'; import {Client} from '@modelcontextprotocol/sdk/client/index.js'; import {StreamableHTTPClientTransport} from '@modelcontextprotocol/sdk/client/streamableHttp.js'; // 1. Connect to your MCP server. const mcpClient = new Client({name: 'my-web-app', version: '1.0.0'}); await mcpClient.connect(new StreamableHTTPClientTransport(new URL('http://localhost:8000/mcp'))); // 2. Register mcpCatalog alongside basicCatalog. const processor = new MessageProcessor( [basicCatalog, mcpCatalog], action => { console.log('Action dispatched:', action); }, ); // 3. Tell the catalog how to reach your MCP client and message processor. configureMcpCatalog({ getMcpClientForTool: () => mcpClient, processor, }); ``` #### Angular ```ts import {ApplicationConfig, inject, provideEnvironmentInitializer} from '@angular/core'; import {A2uiRendererService, AngularCatalog, BasicCatalog, provideA2UI} from '@a2ui/angular'; import {configureMcpCatalog, mcpCatalog} from '@a2ui/catalog-mcp'; import {Client} from '@modelcontextprotocol/sdk/client/index.js'; import {StreamableHTTPClientTransport} from '@modelcontextprotocol/sdk/client/streamableHttp.js'; const mcpClient = new Client({name: 'my-web-app', version: '1.0.0'}); await mcpClient.connect(new StreamableHTTPClientTransport(new URL('http://localhost:8000/mcp'))); const mcpAngularCatalog = new AngularCatalog( mcpCatalog.id, mcpCatalog.protocolVersion, [...mcpCatalog.components.values()], [...mcpCatalog.functions.values()], ); export const appConfig: ApplicationConfig = { providers: [ provideA2UI(() => ({ catalogs: [inject(BasicCatalog), mcpAngularCatalog], })), provideEnvironmentInitializer(() => { configureMcpCatalog({ getMcpClientForTool: () => mcpClient, processor: inject(A2uiRendererService).processor, }); }), ], }; ``` `getMcpClientForTool` receives the name of the tool being called and returns the MCP client that executes it. When your app connects to one MCP server, return that client for every call; for multiple servers, see [Advanced configuration and troubleshooting](#advanced-configuration-and-troubleshooting). ### Share the catalog schema with your agent Include the [MCP catalog schema](#mcp_catalog.json) in your agent setup so it knows how to emit MCP tool calls and `McpApp` components: 1. Load `@a2ui/catalog-mcp/catalog.json` (or [`catalogs/mcp/v1/catalog.json`](https://github.com/a2ui-project/a2ui/blob/main/catalogs/mcp/v1/catalog.json) on non-Node backends) alongside your component catalog when building your agent's prompt and schema validator. 2. Advertise the MCP catalog ID (`https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json`, exported as `mcpCatalog.id` and `MCP_CATALOG_ID`) in `supportedCatalogIds`. Calling `processor.getRendererCapabilities({versions: ['1.0']})` includes every registered catalog automatically. You are ready to call MCP tools from your UI. If you also embed `McpApp` HTML widgets, complete the extra sandbox file step in [Embed MCP Apps in A2UI Surfaces](/guides/mcp-apps-in-a2ui/#serve-the-sandbox-files). --- ## Calling MCP tools from your UI An A2UI component can invoke an MCP tool when a user clicks a button or types into a field. Because the browser calls your MCP server through [`callMcpTool`](#mcp_catalog.json/functions/callMcpTool), the surface updates right away without another agent turn. ### Store tool output at a data path The easiest way to call a tool is to pass `targetDataPath`. When the tool finishes, `callMcpTool` writes the response object to that path in the surface data model so your components can bind to it directly. For example, this button action from [`mcp-json-tool-result.json`](https://github.com/a2ui-project/a2ui/blob/main/catalogs/mcp/v1/examples/mcp-json-tool-result.json) calls `list_issues` with the value at `/repository` and writes the response to `/issueResponse`: ```json { "@call": "callMcpTool", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "name": "list_issues", "arguments": {"repository": {"@path": "/repository"}}, "targetDataPath": "/issueResponse" } } ``` Components on the same surface (`"version": "1.0"`) can then bind to `/issueResponse/structuredContent/issues` (when the server returns structured content), `/issueResponse/json` (when the tool returns JSON text), or `/issueResponse/text` (for plain text).
Response object fields, error handling, and rapid-input protection `callMcpTool` writes (and returns) an object with these fields: | Field | Contents | | :------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | | `structuredContent` | Structured data object returned by the MCP server, when the server includes one. | | `json` | Parsed JSON value when `text` is a JSON object or array (or an array of parsed JSON blocks when the tool returns several text blocks). | | `text` | All `text` content blocks from the tool result, joined with `\n`. | | `toolName` | Name of the tool that ran. | | `toolInput` | Resolved arguments sent to the tool. | | `toolResult` | Raw MCP `CallToolResult` from the server. | If a tool call fails or returns `isError: true`, `callMcpTool` throws an `A2uiExpressionError` and leaves the data model unchanged. When several calls write to the same `targetDataPath` concurrently (such as a search box calling a tool as the user types), only the latest call writes its result. Slower responses from earlier calls are ignored so stale results never overwrite newer ones. Give each independent UI feature its own `targetDataPath` so unrelated calls do not cancel each other's writes.
### Filter and reshape JSON results When an MCP tool returns more fields or rows than your UI needs, chain `callMcpTool` with `transformData` (a [JMESPath](https://jmespath.org/specification.html) query) and `updateDataModel`: ```json { "@call": "updateDataModel", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "updates": { "@call": "transformData", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "jmespath": "{\"/issues\": json[?state == 'open'].{number: to_string(number), title: title}}", "data": { "@call": "callMcpTool", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "name": "list_issues", "arguments": {"repository": {"@path": "/repository"}} } } } } } } ``` Reading from the innermost call outward: 1. `callMcpTool` runs `list_issues` and returns the response object containing `json`. 2. `transformData` keeps only open issues, selects `{number, title}`, and wraps the result in `{"/issues": [...]}`. 3. `updateDataModel` writes `/issues` into the surface data model, updating any components bound to `/issues`. Note that the rapid-input guard only applies to `targetDataPath` on `callMcpTool`, not to `updateDataModel`. For inputs that fire on every keystroke, bind directly to `targetDataPath`. ### Parse plain-text tool output Some MCP servers return plain text meant for a language model (such as `Title: ...\nURL: ...` blocks) rather than JSON. The catalog includes `split` and `regexCapture` so an A2UI payload can extract rows and fields before passing them to `transformData` and `updateDataModel`, without adding per-tool parsing code to your host app. Your host app still chooses which MCP servers to connect through `getMcpClientForTool` (see [Quick setup](#quick-setup)). These built-in functions are most useful when users or admins configure that server list at runtime, such as in a chat app or playground. If your app only connects to one or two MCP servers that you control, writing a custom function in your own catalog is usually simpler (see _Add your own data-parsing functions_ under [Advanced configuration and troubleshooting](#advanced-configuration-and-troubleshooting)). Here are the four data functions in `@a2ui/catalog-mcp`: | Function | Arguments | What it does | | :---------------- | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | | `transformData` | `jmespath`, `data` | Evaluates a standard [JMESPath](https://jmespath.org/specification.html) query against `data` (returning `null` for missing fields). | | `updateDataModel` | `updates` | Writes each `{path: value}` pair in `updates` to the surface data model (`/` keys are absolute; others resolve relative to context). | | `split` | `value`, `separator` | Splits a string (or each item in an array of strings) by `separator`, or into individual characters when `separator` is `""`. | | `regexCapture` | `value`, `pattern` | Returns capture groups from the first match of `pattern` (or `null` if unmatched) for a string or each string in an array. |
Example: parsing plain-text search results In [`mcp-text-tool-result.json`](https://github.com/a2ui-project/a2ui/blob/main/catalogs/mcp/v1/examples/mcp-text-tool-result.json), a search tool returns text records separated by blank lines (`\n\n`), where each record starts with `Title: ...` and `URL: ...`. This chain splits the text into records, captures the title and URL from each record, drops non-matching header blocks (`[?@ != null]`), and writes the resulting array to `/results`: ```json { "@call": "updateDataModel", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "updates": { "@call": "transformData", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "jmespath": "{\"/results\": [?@ != null].{title: [0], url: [1]}}", "data": { "@call": "regexCapture", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "pattern": "^Title: (.*)\nURL: (.*)", "value": { "@call": "split", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "separator": "\n\n", "value": { "@call": "transformData", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "jmespath": "text", "data": { "@call": "callMcpTool", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/mcp/catalog.json", "args": { "name": "tavily-search", "arguments": {"query": {"@path": "/query"}} } } } } } } } } } } } } ```
### Tools that return A2UI surfaces MCP servers built specifically for A2UI can attach `application/a2ui+json` UI resources to their tools or tool responses. When `callMcpTool` runs one of those tools, it fetches the UI resource and forwards the A2UI messages to the `processor` you passed to `configureMcpCatalog`. To build an MCP server that serves A2UI layouts, see [Serve A2UI Layouts from an MCP Server](/guides/a2ui-over-mcp/). --- ## Advanced configuration and troubleshooting Expand any section below for multi-server routing, partial catalog registration, custom parsers, regular expression rules, and troubleshooting fixes:
Route tool calls across multiple MCP servers (configureMcpCatalog options) A2UI payloads refer to tools by name only and never hardcode server URLs. If your host connects to multiple MCP servers, discover each server's tools with `listTools()` at startup and route each `toolName` to the matching client inside `getMcpClientForTool` (which may also return a `Promise`): ```ts configureMcpCatalog({ getMcpClientForTool: toolName => clientsByTool.get(toolName) ?? defaultClient, processor, }); ``` You can call `configureMcpCatalog` again at any time to update individual options: | Option | Type | Purpose | | :-------------------- | :--------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | | `getMcpClientForTool` | `(toolName: string) => McpToolClient \| Promise<...>` | Returns the MCP client (`Pick`) that handles `toolName`. | | `processor` | `MessageProcessor` | Receives A2UI messages extracted from tool results and `application/a2ui+json` UI resources. | | `defaultVersion` | `string` | Protocol version stamped onto decoded tool-result messages that omit `version` (defaults to `'1.0'`). | | `onMcpAppResource` | `(payload: McpAppResourcePayload, context: DataContext) => void` | Optional callback invoked when `callMcpTool` resolves a `ui://` (`text/html;profile=mcp-app`) resource. | | `sandbox` | `Partial` | Overrides where `/a2ui-sandbox/` is hosted (`baseUrl`, default `'/a2ui-sandbox/'`) or its startup check (`disableSecuritySelfTest`). | If you serve the sandbox directory at a custom path or on a dedicated sandbox origin, set `sandbox.baseUrl` before rendering the first surface: ```ts configureMcpCatalog({sandbox: {baseUrl: '/static/frames/'}}); ``` If `getMcpClientForTool` is not configured, `McpApp` dispatches allowed tool calls from the embedded widget as A2UI actions named after the tool, so a backend proxy agent can handle them through the `MessageProcessor` action callback.
Register only part of the catalog If you want to expose `McpApp` and `callMcpTool` without the four data functions (or pick specific functions), derive a restricted catalog with `mcpCatalog.withEntries` and the individual `*Implementation` exports (`CallMcpToolImplementation`, `TransformDataImplementation`, `UpdateDataModelImplementation`, `SplitImplementation`, and `RegexCaptureImplementation`): ```ts import {CallMcpToolImplementation, mcpCatalog} from '@a2ui/catalog-mcp'; const mcpAppOnlyCatalog = mcpCatalog.withEntries( [...mcpCatalog.components.values()], [CallMcpToolImplementation], ); ```
Add your own data-parsing functions Every function in `@a2ui/catalog-mcp` must be implemented across every A2UI renderer, so the built-in function list stays small. If you need a specialized parser such as CSV, define it with `createFunctionImplementation` in [your own catalog](/guides/defining-a-catalog/) and register that catalog alongside `mcpCatalog`: ```ts import {z} from 'zod'; import {mcpCatalog} from '@a2ui/catalog-mcp'; import { basicCatalog, Catalog, createFunctionImplementation, DynamicStringSchema, MessageProcessor, } from '@a2ui/web_core/v1_0'; const ParseCsvImplementation = createFunctionImplementation( { name: 'parseCsv', returnType: 'any', schema: z.object({value: DynamicStringSchema}), description: 'Splits CSV text into rows of cells.', }, ({value}) => String(value) .split('\n') .map(line => line.split(',')), ); const parsersCatalog = new Catalog( 'https://example.com/catalogs/parsers.json', '1.0', [], [ParseCsvImplementation], ); const processor = new MessageProcessor([basicCatalog, mcpCatalog, parsersCatalog], onAction); ``` Payloads can then call `parseCsv` with `"catalogId": "https://example.com/catalogs/parsers.json"` and compose it with `callMcpTool` and `transformData`.
Regular expression rules in regexCapture To keep payloads portable across web and native renderers, `regexCapture` accepts JavaScript regular expression syntax without lookahead or lookbehind assertions (`(?=`, `(?!`, `(?<=`, `(?`). It also rejects patterns vulnerable to catastrophic backtracking, using the same safety check as the basic catalog's `regex` function. Unsupported or unsafe patterns throw an `A2uiExpressionError`.
Troubleshooting duplicate Lit or @a2ui/web_core packages Loading two copies of `lit` or `@a2ui/web_core` breaks custom element registration and `Catalog` instance checks. Run `npm dedupe` (and check `npm ls lit @a2ui/web_core`), and in Vite add `resolve: {dedupe: ['lit', '@a2ui/web_core']}` to `vite.config.ts`.
--- ## Next steps 1. Configure `/a2ui-sandbox/`, run the Kanban board showcase, and explore double-iframe isolation in [Embed MCP Apps in A2UI Surfaces](/guides/mcp-apps-in-a2ui/). 2. Build an MCP server that returns reusable A2UI layout templates in [Serve A2UI Layouts from an MCP Server](/guides/a2ui-over-mcp/). 3. Bundle an A2UI renderer inside a standalone `ui://` widget for external MCP hosts in [Render A2UI Inside an MCP App](/guides/a2ui-in-mcp-apps/). 4. Browse the TypeScript API reference in the [`@a2ui/catalog-mcp` README](https://github.com/a2ui-project/a2ui/blob/main/typescript/catalogs/mcp/README.md) and the [MCP catalog JSON Schema](#mcp_catalog.json). --- ## Catalog schema ### mcp_catalog.json - Raw JSON Schema: [mcp_catalog.json](https://a2ui.org/specification/v1_0/json/mcp_catalog.json) --- # MCP Integration Overview > Choose the right pattern for connecting A2UI surfaces and Model Context Protocol (MCP) servers and apps. Canonical URL: https://a2ui.org/guides/mcp-overview/ A2UI and the **[Model Context Protocol (MCP)](https://modelcontextprotocol.io/)** integrate in four ways depending on whether your application or an external MCP client owns the outer host, and whether the MCP server returns structured JSON data, reusable A2UI layout templates, or sandboxed HTML apps. ## Choose an MCP integration pattern Use the table below to pick the guide that matches your host environment and MCP server output: | Your goal | Outer host | MCP server output | Guide | | :-------------------------------------------------------------- | :--------------------- | :---------------------------------------------- | :--------------------------------------------------------- | | Invoke headless MCP tools from A2UI inputs and buttons | Your A2UI web app | JSON or plain-text tool results | [Call MCP Tools from A2UI](/guides/mcp-catalog/) | | Return reusable A2UI layouts and per-call data from tools | Your A2UI web app | `a2ui://` templates (`application/a2ui+json`) | [Serve A2UI Layouts from an MCP Server](/guides/a2ui-over-mcp/) | | Embed third-party interactive HTML widgets on an A2UI surface | Your A2UI web app | `ui://` HTML apps (`text/html;profile=mcp-app`) | [Embed MCP Apps in A2UI Surfaces](/guides/mcp-apps-in-a2ui/) | | Display A2UI surfaces inside Claude Desktop, Cursor, or VS Code | External MCP Apps host | `ui://` HTML bundle containing an A2UI renderer | [Render A2UI Inside an MCP App](/guides/a2ui-in-mcp-apps/) | ## How the patterns fit together 1. When you own the web application, `@a2ui/catalog-mcp` connects your Lit, React, or Angular renderer directly to MCP `Client` sessions. Surfaces can call tools with `callMcpTool` and write returned JSON into the surface [Data Model](/concepts/data-binding/) ([Call MCP Tools from A2UI](/guides/mcp-catalog/)), or mount third-party `ui://` HTML widgets inside a double-iframe sandbox with `McpApp` ([Embed MCP Apps in A2UI Surfaces](/guides/mcp-apps-in-a2ui/)). 2. When an MCP server emits A2UI messages (`application/a2ui+json`), it can publish declarative component layouts as `a2ui://` resources and return compact `updateDataModel` updates from `tools/call` ([Serve A2UI Layouts from an MCP Server](/guides/a2ui-over-mcp/)). If the outer client supports MCP Apps (`ui://` HTML iframes) rather than A2UI natively, your server can package an A2UI web renderer inside the `ui://` HTML bundle itself ([Render A2UI Inside an MCP App](/guides/a2ui-in-mcp-apps/)). --- # v1.0 Protocol Specification > A2UI Protocol v1.0 specification included directly from specification/v1_0/docs/a2ui_protocol.md. Canonical URL: https://a2ui.org/specification/v1.0-a2ui/ > [!IMPORTANT] Current Specification > **A2UI v1.0** is the current specification. Compared to [v0.9.1](/specification/v1.0-a2ui/v0.9.1-a2ui.md), v1.0 renames the wire envelopes to `agent_to_renderer` and `renderer_to_agent`, prefixes dynamic bindings with `@` (`@path` and `@call`) and component envelope keys with `@` (`@catalogId`, `@accessibility`, `@metadata`, `@checks`), adds bidirectional function RPC messages (`callRendererFunction`, `callAgentFunction`, `rendererFunctionResponse`, `agentFunctionResponse`), and supports per-component `@catalogId` mixing and single-message `createSurface` payloads. See the [v1.0 Evolution Guide](/specification/v1.0-a2ui/v1.0-evolution-guide.md) and [v1.0 JSON Schemas](/specification/v1.0-a2ui/v1.0-schemas.mdx). A Specification for a JSON-Based, Streaming UI Protocol. **Version:** 1.0 **Status:** Current **Created:** Nov 20, 2025 **Last Updated:** Jun 8, 2026 A Specification for a JSON-Based, Streaming UI Protocol ## Introduction The A2UI Protocol is designed for dynamically rendering user interfaces from a stream of JSON objects sent from an agent. Its core philosophy emphasizes a clean separation of UI structure and application data, enabling progressive rendering as the renderer processes each message. Communication occurs via a stream of JSON objects. The renderer parses each object as a distinct message and incrementally builds or updates the UI. The agent-to-renderer protocol defines four message types: - `createSurface`: Signals the renderer to create a new surface and begin rendering it. - `updateComponents`: Provides a list of component definitions to be added to or updated in a specific surface. - `updateDataModel`: Provides new data to be inserted into or to replace a surface's data model. - `deleteSurface`: Explicitly removes a surface and its contents from the UI. End of agent turn is signaled by [transport layer](/docs/public/concepts/transports.md). ## Changes from previous versions The major differences between version 1.0 and 0.9 (including 0.9.1) are: - **Bidirectional Function Calls**: Supports explicit, typed function invocation messages (`callRendererFunction`, `callAgentFunction`, `rendererFunctionResponse`, and `agentFunctionResponse`) verified against runtime catalog definitions. - **Single-Message UI Instantiation**: Allows initial component trees and data models to be embedded directly within `createSurface`, enabling complete UI composition in a single payload. - **Decoupled Branding**: Removes rigid theme properties (removing hardcoded brand colors) to defer visual styling entirely to the target framework's native theme. A v1.0 `createSurface` that carries `theme` is invalid. - **Enhanced Catalog Schemas**: Refactors function definitions into object maps for direct O(1) lookups and supports standard JSON Schema metadata fields (`$schema`, `$id`) on inline catalogs. - **Strict Identifier & Context Standards**: Enforces Unicode (UAX #31) naming rules across all catalog entities and reserves the `@` namespace for universal system context evaluations (such as `@index`). See [the evolution guide](evolution_guide.md) for a detailed explanation of the differences between v0.9 and v1.0. ## Protocol overview & data flow The A2UI protocol uses a unidirectional stream of JSON messages from the agent to the renderer to describe and update the UI. The renderer consumes this stream, builds the UI, and renders it. User interactions are handled separately, typically by sending events to a different endpoint, which may in turn trigger new messages on the UI stream. Here is an example sequence of events (which don't have to be in exactly this order): 1. **Create Surface:** The agent sends a `createSurface` message to initialize the surface. 2. **Update Surface:** Once a surface has been created, the agent sends one or more `updateComponents` messages containing the definitions for all the components that will be part of the surface. 3. **Update Data Model:** Once a surface has been created, the agent can send `updateDataModel` messages at any time to populate or change the data that the UI components will display. 4. **Render:** The renderer renders the UI for the surface, using the component definitions to build the structure and the data model to populate the content. 5. **Dynamic updates:** As the user interacts with the application or as new information becomes available, the agent can send additional `updateComponents` and `updateDataModel` messages to dynamically change the UI. 6. **Delete Surface:** When a UI region is no longer needed, the agent sends a `deleteSurface` message to remove it. ```mermaid sequenceDiagram participant Agent participant Renderer Agent->>+Renderer: 1. createSurface(surfaceId: "main") Agent->>+Renderer: 2. updateComponents(surfaceId: "main", components: [...]) Agent->>+Renderer: 3. updateDataModel(surfaceId: "main", path: "/user", value: "Alice") User->>+Renderer: Interact with UI (e.g. click button) Renderer->>+Agent: action(name: "submit", context: {...}) Renderer-->>-Agent: (UI is displayed) Note over Renderer, Agent: Time passes, user interacts, or new data arrives... Agent->>+Renderer: 4. updateComponents or updateDataModel (Dynamic Update) Note right of Renderer: Renderer re-renders the UI to reflect changes Renderer-->>-Agent: (UI is updated) Agent->>+Renderer: 5. deleteSurface(surfaceId: "main") Note right of Renderer: Renderer removes the UI for the "main" surface Renderer-->>-Agent: (UI is gone) ``` ## Transport decoupling The A2UI protocol is designed to be transport-agnostic. It defines the JSON message structure and the semantic contract between the Agent and the Renderer, but it does not mandate a specific transport layer. ### The transport contract To support A2UI, a transport layer must fulfill the following contract: 1. **Reliable delivery**: Messages must be delivered in the order they were generated. A2UI relies on stateful updates (e.g., creating a surface before updating it), so out-of-order delivery can corrupt the UI state. 2. **Message framing**: The transport must clearly delimit individual JSON envelope messages (e.g., using newlines in JSONL, WebSocket frames, or SSE events). 3. **Metadata support**: The transport must provide a mechanism to associate metadata with messages. This is critical for: - **Data model synchronization**: The `sendDataModel` feature requires the renderer to send the current data model state as metadata alongside user actions. - **Capabilities exchange**: Renderer capabilities (supported catalogs, custom components) and Agent capabilities are exchanged via metadata or transport-specific handshakes (like Agent Cards in A2A or initialization in MCP). 4. **Bidirectional capability (optional)**: While the rendering stream is unidirectional (Agent -> Renderer), interactive applications require a return channel for `action` messages (Renderer -> Agent). ### Transport bindings While A2UI is transport agnostic, it is most commonly used with the following transports. #### AG-UI (Agent-to-User Interface) binding **[AG-UI](https://docs.ag-ui.com/introduction)** is the standard transport binding for Agent-to-User Interaction. It provides convenient integrations into many agent frameworks and frontends, offering low-latency and shared-state message passing between frontends and agentic backends. #### A2A (Agent-to-Agent) binding The **[A2A Extension](/specification/extensions/a2a/docs/a2ui_extension_specification.md)** maps A2UI over the **[A2A Protocol](https://a2a-protocol.org)**. It standardizes metadata placement, renderer-to-agent capability negotiation, and bidirectional data model synchronization for agent-to-agent interactions. #### MCP (Model Context Protocol) binding **[MCP](https://modelcontextprotocol.io/docs/getting-started/intro)** is a standard protocol for exposing data and tools to LLMs. A2UI can be carried over MCP tool calls, tool outputs, or resource subscriptions, allowing agents to dynamically render rich user interfaces for renderer-side applications. #### Other transports A2UI can also be carried over: - **[SSE](https://en.wikipedia.org/wiki/Server-sent_events) with [JSON RPC](https://www.jsonrpc.org/)**: Standard server-sent events for web integrations that support streaming, and JSON RPC for renderer-agent communication. - **[WebSockets](https://en.wikipedia.org/wiki/WebSocket)**: For bidirectional, real-time sessions. - **[REST](https://cloud.google.com/discover/what-is-rest-api?hl=en)**: For simple use case, REST APIs will work but lack streaming capabilities. ## The protocol schemas A2UI v1.0 is defined by three interacting JSON schemas. ### Common types The [`common_types.json`] schema defines reusable primitives used throughout the protocol. - **`DynamicString` / `DynamicNumber` / `DynamicBoolean` / `DynamicStringList`**: The core of the data binding system. Any property that can be bound to data is defined as a `Dynamic*` type. It accepts either a literal value, a `DataBinding` (`{"@path": "..."}` using [JSON Pointer]), or a `FunctionCall` (`{"@call": "...", "args": {...}}`). When a `DynamicBoolean` resolves to a `ValidationResult` object (an object with a boolean `valid` property), it is coerced to `result.valid`; strings, numbers, `null`, objects, and arrays are coerced as defined under [`DynamicBoolean` and `CheckRule` Coercion](#dynamicboolean-and-checkrule-coercion). - **`ChildList`**: Defines how containers hold children. It supports: - `array`: A static array of `ComponentId` component references. - `object`: A template for generating children from a list in the data model (requires a template `componentId` and a JSON Pointer `path` string; note that `path` here is a plain string property on the template object, not a `DataBinding` `{"@path": "..."}` object). - **`ComponentId`**: A reference to the unique ID of another component within the same surface. - **`AccessibilityAttributes`**: Standardized accessibility properties attached via `ComponentCommon.@accessibility` to any component, supporting `label` (`DynamicString`), `description` (`DynamicString`), `live` (`"off"` | `"polite"` | `"assertive"`), and `hidden` (`DynamicBoolean`). ### Agent to renderer message structure: the envelope The [`agent_to_renderer.json`] schema is the top-level entry point. Every message streamed by the agent must validate against this schema. It handles the message dispatching. ### The Basic Catalog The [`catalogs/basic/catalog.json`] schema contains the definitions for all specific UI components (e.g., `Text`, `Button`, `Row`) and functions (e.g., `required`, `email`). **Swappable Catalogs & Validation:** The [`agent_to_renderer.json`] envelope schema is designed to be catalog-agnostic. Within its `Component` definition (referenced by `ComponentsList`), it validates base properties against `common_types.json#/$defs/ComponentCommon` and references components using a placeholder filename: `catalog.json` (specifically `$ref: "catalog.json#/$defs/anyComponent"`). To validate A2UI messages: 1. **Basic Catalog**: Map `catalog.json` to `catalogs/basic/catalog.json`. 2. **Renderer Catalog**: Map `catalog.json` to your own catalog file (e.g., `my_company_catalog.json`). This indirection allows the same core envelope schema to be used with any compliant component catalog without modification. Defining your own catalog allows you to restrict the agent to using exactly the components and visual language that exist in your application. To use your own catalog, simply include it in the prompt in place of the basic catalog. It should have the same form as the basic catalog and use common elements in the [`common_types.json`] schema. ### Validator compliance when defining catalogs To ensure that automated validators can verify the integrity of your UI tree (checking that parents reference existing children), any catalog you define MUST adhere to the following strict typing rules: 1. **Single child references:** Any property that holds the ID of another component MUST use the `ComponentId` type defined in `common_types.json`. - Use: `"$ref": "common_types.json#/$defs/ComponentId"` - Do NOT use: `"type": "string"` 2. **List references:** Any property that holds a list of children or a template MUST use the `ChildList` type. - Use: `"$ref": "common_types.json#/$defs/ChildList"` Validators determine which fields represent structural links by looking for these specific schema references. If you use a raw string type for an ID, the validator will treat it as static text (like a URL or label) and will not check if the target component exists. ## Envelope message structure The envelope defines several message types, and every message streamed by the agent must be a JSON object containing exactly one of the following keys: `createSurface`, `updateComponents`, `updateDataModel`, `deleteSurface`, `callRendererFunction`, or `agentFunctionResponse`. The key indicates the type of message, and these are the messages that make up each message in the protocol stream. ### `createSurface` This message signals the renderer to create a new surface and begin rendering it. A surface must be created before any `updateComponents` or `updateDataModel` messages can be sent to it. While typically achieved by the agent sending a `createSurface` message, an agent may skip this if it knows of a preexisting surface that it has permission to modify. Once a surface is created, its `surfaceId` and default `catalogId` (if provided) are fixed; to reconfigure them, the surface must be deleted and recreated. It is a state integrity error (`code: "INTEGRITY_ERROR"`) to try to create a surface with a `surfaceId` that already exists without first deleting it via `deleteSurface`; `surfaceId` must be globally unique for the renderer's lifetime. Orchestrators with subagents are empowered to manage surface IDs as needed to prevent conflicts (e.g., prefixing the subagent's name to the `surfaceId` or requiring subagents to use UUIDs). The `createSurface` message implicitly instantiates the canonical `Surface` container component (`common_types.json#/$defs/Surface`). The `Surface` component always has `"child": "root"` and cannot be modified using `updateComponents`. To render the component tree, one of the components sent to the surface MUST have `"id": "root"`, which mounts as the child of `Surface`. **Properties:** - `surfaceId` (string, required): The unique identifier for the UI surface to be rendered. This must be globally unique for the renderer's lifetime. - `catalogId` (string, optional): A string that uniquely identifies the default catalog (components and functions) used for this surface. Note that `catalogId` is a string identifier, not a resolvable URI; while it is conventionally formatted as a URI (e.g., `https://mycompany.com/1.0/somecatalog`) to avoid naming collisions across organizations, it does not need to point to any deployed resource or downloadable file. Components and function calls on this surface that do not explicitly specify their own `@catalogId` (for components) or `catalogId` (for function calls) will use this surface-level default `catalogId`. - `sendDataModel` (boolean, optional): If true, the renderer will send the full data model of this surface in the metadata of every message sent to the agent (via the Transport's metadata mechanism). This ensures the surface owner receives the full current state of the UI alongside the user's action or query. Defaults to false. - `components` (array, optional): A list containing UI components for the surface, allowing the renderer to build and populate the UI tree immediately on surface creation. Conforms to the `ComponentsList` schema. - `dataModel` (object, optional): A plain JSON object representing the initial root state of the data model. - `metadata` (object, optional): Surface-level metadata containing an optional `extensions` map (`common_types.json#/$defs/Extensions`). When a single `createSurface` carries both `dataModel` and `components`, the renderer applies `dataModel` first, so components bind against the initial data on their first resolution. Separate messages keep arrival order (see [`callRendererFunction`](#callrendererfunction)). **Example:** ```json { "version": "1.0", "createSurface": { "surfaceId": "user_profile_card", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "sendDataModel": true, "components": [ { "id": "root", "component": "Column", "children": ["user_name"] }, { "id": "user_name", "component": "Text", "text": {"@path": "/name"} } ], "dataModel": { "name": "John Doe" } } } ``` ### `updateComponents` This message provides a flat list of UI components to add or update within a specific surface. Relationships between components are defined by ID references in an adjacency list. The component with `"id": "root"` mounts as the child of the surface's canonical `Surface` container. You cannot modify the `Surface` container itself using `updateComponents`. This message may only be sent to a surface that has already been created. Because components may reference children or data bindings that do not yet exist, renderers should handle missing references gracefully by rendering placeholders (progressive rendering). **Properties:** - `surfaceId` (string, required): The unique identifier for the UI surface to be updated. This must be globally unique for the renderer's lifetime. - `components` (array, required): A list of component objects. The components are provided as a flat list, and their relationships are defined by ID references in an adjacency list. **Example:** ```json { "version": "1.0", "updateComponents": { "surfaceId": "user_profile_card", "components": [ { "id": "root", "component": "Column", "children": ["user_name", "user_title"] }, { "id": "user_name", "component": "Text", "text": "John Doe" }, { "id": "user_title", "component": "Text", "text": "Software Engineer" } ] } } ``` ### `updateDataModel` This message is used to send or update the data that populates the UI components. It allows the agent to change the UI's content without resending the entire component structure. The `updateDataModel` message replaces the value at the specified `path` with the new content. If `path` is omitted (or is `/`), the entire data model for the surface is replaced. **Properties:** - `surfaceId` (string, required): The unique identifier for the UI surface this data model update applies to. This must be globally unique for the renderer's lifetime. - `path` (string, optional): A JSON Pointer to the location in the data model to update. Defaults to `/`. - `value` (any, required): The new value for the specified path. To delete the key/value at `path`, set `value` explicitly to `null`. **Example:** ```json { "version": "1.0", "updateDataModel": { "surfaceId": "user_profile_card", "path": "/user/name", "value": "Jane Doe" } } ``` ### `deleteSurface` This message instructs the renderer to remove a surface and all its associated components and data from the UI. **Properties:** - `surfaceId` (string, required): The unique identifier for the UI surface to be deleted. This must be globally unique for the renderer's lifetime. **Example:** ```json { "version": "1.0", "deleteSurface": { "surfaceId": "user_profile_card" } } ``` ### `callRendererFunction` This message is sent by the agent to execute a function registered on the renderer. Functions are catalog-defined abstractions that avoid sending raw executable code across the wire. Only functions which have `allowedCallers: "agentOnly"` or `allowedCallers: "rendererOrAgent"` in their catalog definition can be called by the agent. Renderer functions can only be called after a session has been initiated by the renderer. Functions are resolved by the specified `catalogId`. Upon completing execution of a `callRendererFunction` message, the renderer MUST always send a corresponding `rendererFunctionResponse` message (containing either `value` or `error`) back to the agent, even if the function's return type is `void`. **Properties:** - `callRendererFunction` (object, required): - `surfaceId` (string, optional): The unique identifier for the UI surface whose data model provides the evaluation context for this call. If omitted, the function executes in a surface-independent root context with an empty data model (`{}`). - `functionCallId` (string, required): A unique identifier for this invocation instance. The renderer MUST copy this ID verbatim into the subsequent `rendererFunctionResponse` message. - `callFunction` (object, required): The description of the function call. - `@call` (string, required): The registered name of the function to execute. - `catalogId` (string, required): The catalog ID defining the function to execute. `catalogId` MUST be specified on `callRendererFunction`. A call without it violates the schema, so the message is malformed, and should be rejected. Agents should validate outgoing messages against the schema so that a missing `catalogId` never reaches the renderer. If `catalogId` names a catalog the renderer has not registered, the renderer rejects the call with `code: "INVALID_FUNCTION_CALL"`. - `args` (object, optional): Arguments passed to the function, as defined by its schema in the catalog. **Evaluation Scope and Ordering:** - **Evaluation Scope**: - When `surfaceId` is specified, `callRendererFunction` executes against the root `DataContext` (`'/'`) of that surface's data model, allowing `DataBinding` (`{"@path": "..."}`) expressions inside `args` to read from the surface's data model. If no active surface with `surfaceId` exists, the renderer MUST reject the call and return a `rendererFunctionResponse` with `code: "INVALID_FUNCTION_CALL"`. - When `surfaceId` is omitted, `callRendererFunction` executes in a surface-independent root context with an isolated empty data model (`{}`). Renderers MUST NOT resolve it against any existing surface's data model; any `DataBinding` (`{"@path": "..."}`) inside `args` resolves against the empty root model (`null` / `undefined`). - In either context, calling `{"@call": "@index"}` outside a list template fails with `code: "EXECUTION_ERROR"`. - **Envelope Dispatch Ordering**: Message envelopes in a stream or batch are dispatched in arrival order. Synchronous surface mutations (`createSurface`, `updateComponents`, `updateDataModel`, `deleteSurface`) that precede a `callRendererFunction` or `agentFunctionResponse` envelope take effect before that RPC envelope is dispatched, while asynchronous `callRendererFunction` execution completes without blocking subsequent envelopes in the stream. **Security Boundaries and Verification:** Execution boundary verification (`"rendererOnly"`, `"agentOnly"`, or `"rendererOrAgent"`) and user activation checks (`requiresUserActivation`) are enforced at runtime by the renderer application: - When a renderer receives a `callRendererFunction` message, it determines the function's execution boundary (`allowedCallers`) and activation policy (`requiresUserActivation`) at runtime from the active catalog definition. - If the requested function is configured in the catalog as `"rendererOnly"`, requires an active user gesture (`requiresUserActivation: true`) when none is present, fails argument schema validation, or is not registered at all, the renderer MUST immediately reject the call and return a `rendererFunctionResponse` message containing an `error` object with `code: "INVALID_FUNCTION_CALL"`. - Note that `catalog_definition.json` requires functions declaring `requiresUserActivation: true` (such as `openUrl`) to set `allowedCallers: "rendererOnly"`, preventing agent-initiated remote invocation. Within the renderer, functions with `requiresUserActivation: true` MUST only execute when triggered by an active user gesture (such as a component `Action`). If referenced in a passive property binding or `CheckRule.condition`, the renderer MUST block execution and report a local evaluation error (`code: "EXPRESSION_ERROR"`). - Functions marked as `"agentOnly"` or `"rendererOrAgent"` are authorized for agent invocation via `callRendererFunction`. Functions configured as `"agentOnly"` CANNOT be bound to UI component properties or executed by renderer UI actions; they are restricted exclusively to agent-initiated `callRendererFunction` execution. **Example:** Agent sends this message to the renderer: ```json { "version": "1.0", "callRendererFunction": { "functionCallId": "get_device_resolution_123", "callFunction": { "@call": "getScreenResolution", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "args": { "screenIndex": 0 } } } } ``` If the function executes successfully, the renderer responds with a `rendererFunctionResponse`: ```json { "version": "1.0", "rendererFunctionResponse": { "functionCallId": "get_device_resolution_123", "value": [1920, 1080] } } ``` If the agent attempts to call a `rendererOnly` function (e.g., a local-only component validator), the renderer responds with a `rendererFunctionResponse` containing an `error` payload: ```json { "version": "1.0", "rendererFunctionResponse": { "functionCallId": "get_device_resolution_123", "error": { "code": "INVALID_FUNCTION_CALL", "message": "Function 'validateLocalInput' cannot be called by agent (allowedCallers is rendererOnly)." } } } ``` ### `agentFunctionResponse` This message is streamed by the agent to return the execution result or error of a renderer-initiated `callAgentFunction` request. **Properties:** - `agentFunctionResponse` (object, required): - `functionCallId` (string, required): The unique invocation ID matching the initiating `callAgentFunction` call. - `value` (any, optional): The returned execution value of the function call. - `error` (object, optional): An error object containing `code` (string) and `message` (string) describing execution failure. The payload MUST include either `value` or `error`. **Example (Success):** ```json { "version": "1.0", "agentFunctionResponse": { "functionCallId": "verify_provider_99", "value": { "valid": true, "name": "Acme Provider" } } } ``` **Example (Failure):** ```json { "version": "1.0", "agentFunctionResponse": { "functionCallId": "verify_provider_99", "error": { "code": "PROVIDER_NOT_FOUND", "message": "Provider ID PRV-102 was not found." } } } ``` ## Transport Interaction Patterns A2UI messages can be transported over both request-response channels (such as HTTP POST or polling) and bidirectional streaming channels (such as WebSockets, gRPC, or Server-Sent Events). ### 1. Request-Response Transport (e.g. HTTP POST) In request-response environments, the agent cannot initiate an unprompted connection to the client. Agent-initiated function calls (`callRendererFunction`) and renderer-initiated calls (`callAgentFunction`) operate within the request-response cycle: #### Agent-to-Renderer Function Call over HTTP ```mermaid sequenceDiagram autonumber participant Client as Renderer (Client) participant Agent as Agent (Server) Client->>Agent: HTTP POST (Action / Event) Agent-->>Client: 200 OK (callRendererFunction) Client->>Agent: HTTP POST (rendererFunctionResponse) Agent-->>Client: 200 OK (updateComponents) ``` 1. **Initiation:** The renderer sends a standard HTTP POST request (e.g., dispatching an event action or polling). 2. **Server Response:** The agent returns a `callRendererFunction` payload in the HTTP response. 3. **Execution & Delivery:** The renderer executes the function locally, then initiates a follow-up HTTP POST request delivering the `rendererFunctionResponse`. 4. **Completion:** The agent processes `rendererFunctionResponse` and returns updated UI components (`updateComponents`). --- ### 2. Bidirectional Streaming Transport (e.g. WebSockets / gRPC) In streaming environments, either party can send protocol messages asynchronously over the active downstream/upstream connection: #### Agent-to-Renderer Function Call over Stream ```mermaid sequenceDiagram autonumber participant Client as Renderer (Client) participant Agent as Agent (Server) Agent->>Client: Stream Message (callRendererFunction) Client->>Agent: Stream Message (rendererFunctionResponse) Agent->>Client: Stream Message (updateComponents) ``` #### Renderer-to-Agent Function Call over Stream ```mermaid sequenceDiagram autonumber participant Client as Renderer (Client) participant Agent as Agent (Server) Client->>Agent: Stream Message (callAgentFunction) Agent->>Client: Stream Message (agentFunctionResponse) ``` 1. **Asynchronous Dispatch:** Messages flow over the established stream without requiring HTTP request-response wrapping. 2. **Correlation:** Every function call includes a `functionCallId` which is copied verbatim into the returning `rendererFunctionResponse` or `agentFunctionResponse` (`FunctionResponse` payload) message to correlate requests and responses asynchronously. ## Example Stream The following example demonstrates a complete interaction to render a Contact Form, expressed as a JSONL stream. ```jsonl {"version": "1.0", "createSurface":{"surfaceId":"contact_form_1","catalogId":"https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json"}} {"version": "1.0", "updateComponents":{"surfaceId":"contact_form_1","components":[{"id":"root","component":"Card","child":"form_container"},{"id":"form_container","component":"Column","children":["header_row","name_row","email_group","phone_group","pref_group","divider_1","newsletter_checkbox","submit_button"],"justify":"start","align":"stretch"},{"id":"header_row","component":"Row","children":["header_icon","header_text"],"align":"center"},{"id":"header_icon","component":"Icon","name":"mail"},{"id":"header_text","component":"Text","text":"# Contact Us"},{"id":"name_row","component":"Row","children":["first_name_group","last_name_group"],"justify":"spaceBetween"},{"id":"first_name_group","component":"Column","children":["first_name_label","first_name_field"],"weight":1},{"id":"first_name_label","component":"Text","text":"First Name","variant":"caption"},{"id":"first_name_field","component":"TextField","label":"First Name","value":{"@path":"/contact/firstName"},"variant":"shortText"},{"id":"last_name_group","component":"Column","children":["last_name_label","last_name_field"],"weight":1},{"id":"last_name_label","component":"Text","text":"Last Name","variant":"caption"},{"id":"last_name_field","component":"TextField","label":"Last Name","value":{"@path":"/contact/lastName"},"variant":"shortText"},{"id":"email_group","component":"Column","children":["email_label","email_field"]},{"id":"email_label","component":"Text","text":"Email Address","variant":"caption"},{"id":"email_field","component":"TextField","label":"Email","value":{"@path":"/contact/email"},"variant":"shortText","@checks":[{"condition":{"@call":"required","args":{"value":{"@path":"/contact/email"}}},"message":"Email is required."},{"condition":{"@call":"email","args":{"value":{"@path":"/contact/email"}}},"message":"Please enter a valid email address."}]},{"id":"phone_group","component":"Column","children":["phone_label","phone_field"]},{"id":"phone_label","component":"Text","text":"Phone Number","variant":"caption"},{"id":"phone_field","component":"TextField","label":"Phone","value":{"@path":"/contact/phone"},"variant":"shortText","@checks":[{"condition":{"@call":"regex","args":{"value":{"@path":"/contact/phone"},"pattern":"^\\d{10}$"}},"message":"Phone number must be 10 digits."}]},{"id":"pref_group","component":"Column","children":["pref_label","pref_picker"]},{"id":"pref_label","component":"Text","text":"Preferred Contact Method","variant":"caption"},{"id":"pref_picker","component":"ChoicePicker","variant":"mutuallyExclusive","options":[{"label":"Email","value":"email"},{"label":"Phone","value":"phone"},{"label":"SMS","value":"sms"}],"value":{"@path":"/contact/preference"}},{"id":"divider_1","component":"Divider","axis":"horizontal"},{"id":"newsletter_checkbox","component":"CheckBox","label":"Subscribe to our newsletter","value":{"@path":"/contact/subscribe"}},{"id":"submit_button_label","component":"Text","text":"Send Message"},{"id":"submit_button","component":"Button","child":"submit_button_label","variant":"primary","action":{"event":{"name":"submitContactForm","context":{"formId":"contact_form_1","rendererTime":{"@call":"formatDate","args":{"value": "2026-02-02T15:17:00Z", "format": "E MMM d, YYYY h:mm a"}},"isNewsletterSubscribed":{"@path":"/contact/subscribe"}}}}}]}} {"version": "1.0", "updateDataModel":{"surfaceId":"contact_form_1","path":"/contact","value":{"firstName":"John","lastName":"Doe","email":"john.doe@example.com","phone":"1234567890","preference":["email"],"subscribe":true}}} {"version": "1.0", "deleteSurface":{"surfaceId":"contact_form_1"}} ``` ## Component model A2UI's component model is designed for flexibility, separating the protocol's structure from the set of available UI components. ### The component object Each object in the `components` array of an `updateComponents` message defines a single UI component. It has the following structure: - `id` (`ComponentId`, required): A unique string that identifies this specific component instance. This is used for parent-child references. It MUST follow the [Catalog Entity Naming Rules](#catalog-entity-naming-rules) (UAX #31): `user_card` and `userCard` are valid, `user-card` and `1st_item` are not. - `component` (string, required): Specifies the component's type (e.g., `"Text"`). - `@catalogId` (string, optional): A string that uniquely identifies the catalog for this component, overriding the surface's default `catalogId`. Useful when combining components from multiple catalogs in a single surface. - `@accessibility` (`AccessibilityAttributes`, optional): Accessibility attributes (`label`, `description`, `live`, `hidden`) for assistive technologies. - `@metadata` (object, optional): Component-level metadata containing an optional `extensions` map (`common_types.json#/$defs/Extensions`). - `@checks` (array of `CheckRule`, optional on `Checkable` components): Validation rules evaluated on the renderer. - **Component Properties**: Other properties relevant to the specific component type (e.g., `text`, `url`, `children`, or custom properties such as `metadata`, `catalogId`, `accessibility`, or `checks` whose names conform to UAX #31) are included directly in the component object. This structure is designed to be both flexible and strictly validated. Component-level envelope mixins (`@catalogId`, `@accessibility`, `@metadata`, and `@checks`) use the `@` sigil so they never collide with UAX #31 catalog property names (`id` and `component` remain unprefixed as the required structural identity and discriminator keys). #### Mixable catalogs and component resolution logic Renderers can support components and functions from multiple catalogs simultaneously within a single surface (mixable catalogs). When a renderer advertises `supportedCatalogIds` in its capabilities, components from any of those catalogs can be combined in the same UI tree. The set of available catalogs for a surface includes both `supportedCatalogIds` and the `catalogId` of any inline catalog declared in `inlineCatalogs` (when supported by the agent). All catalog IDs specified at the component (`@catalogId`) and function-call (`catalogId`) levels and at the surface-level (`catalogId`) must refer to catalogs which use the same A2UI specification version. When resolving a component (or function call), the renderer evaluates catalog identity using the following strict resolution order: 1. **Explicit Component/Function-Level Catalog ID**: The renderer checks if the component explicitly specifies `@catalogId` (or if the function call specifies `catalogId`). If provided, the component or function is resolved against that catalog. 2. **Surface Default `catalogId`**: If the component or function call does not specify its own catalog ID, the renderer checks if a default `catalogId` was specified on the surface in the `createSurface` message. If provided, the component or function is resolved against that surface default catalog. 3. **Resolution Error**: If neither an explicit component `@catalogId` (or function-call `catalogId`) nor a surface default `catalogId` is present, resolution fails immediately with an error and the component is not rendered (or the function call is rejected). > [!IMPORTANT] > There is **no fallback** to the list of catalogs declared in `rendererCapabilities` (even if the renderer only advertises a single supported catalog). Every component and function call must resolve through either its explicit `@catalogId` / `catalogId` or the surface default `catalogId`. Function calls interpolated inside [`formatString`](#the-formatstring-function) `${...}` expressions have no syntax for naming a `catalogId`. Non-system function calls inside `${...}` resolve against the catalog that the enclosing `formatString` call resolved to (even when the surface defines a different default `catalogId`), while `@`-prefixed system functions such as `@index` remain catalogless. ### Catalog-Agnostic Accessibility Requirements JSON Schema cannot tell which component properties hold visible text labels or accept user interaction, so catalog and renderer implementations enforce accessibility at runtime: **Catalog and renderer implementations MUST:** - **Plumb Accessibility Attributes**: Map all relevant A2UI `AccessibilityAttributes` (`label`, `description`, `live`, `hidden` under `@accessibility`) to the underlying UI framework's accessibility APIs (e.g., WAI-ARIA `aria-label`, `aria-describedby`, `aria-live`, and `aria-hidden` for web renderers; `Semantics` properties for Flutter; `AccessibilityNodeInfo` / `accessibilityLabel` for Android and iOS renderers). - **Infer Default Semantics**: Use component types and non-accessibility properties (such as visible titles or text labels) to configure accessibility defaults automatically. When explicit `AccessibilityAttributes` (e.g., `label` or `description`) are provided, they MUST override inferred visual defaults. For example, a button component with a title of `"Submit"` and an explicit `@accessibility.label` of `"Send Form"` must be announced by assistive technologies as `"Send Form"`. ### The component catalog The set of available UI components and functions is defined in a **Catalog**. The basic catalog is defined in [`catalogs/basic/catalog.json`]. While the Basic Catalog is useful for starting out, most production applications will define their own catalog to reflect their specific design system. The agent must generate messages that conform to the catalog understood by the renderer. #### Catalog structure Every catalog follows the standard `Catalog` object definition: - **catalogId** (string, required): A unique string identifier for this catalog. While conventionally formatted as a URI to avoid naming collisions across organizations, it is an arbitrary string ID and not a resolvable URI. Because A2UI catalogs are represented as JSON Schema documents, catalog definitions should include both `$id` (used by JSON Schema tooling) and `catalogId` (used by A2UI SDKs and catalog negotiation), setting both fields to the same URI. Renderer and agent developers must agree on shared catalogs with well-known IDs in order to build systems that are compatible with each other. - **instructions** (string, optional): Markdown-formatted design principles, rules, or developer guidelines specific to this catalog. These rules guide LLMs when generating UI layouts under this catalog. - **components** (object, optional): A map of supported UI components, where each key is the component type (e.g., `Text`) and its value is its JSON Schema definition. All keys MUST conform to the UAX #31 entity naming rules defined below. - **functions** (object, optional): A map of renderer-side validation or utility functions supported by the catalog, where each key is the function name and its value is its definition. All function names MUST conform to the UAX #31 entity naming rules defined below. The renderer determines a function's execution boundary (e.g., rendererOnly status) at runtime by reading its configuration from the active catalog definition. #### Catalog Entity Naming Rules To ensure complete cross-language compatibility across renderer SDKs, parsers, and code generators, all catalog entity identifiers—specifically **component names**, **function names**, and **argument/property names**—as well as **component instance IDs** (`ComponentId`) MUST adhere strictly to [Unicode Standard Annex #31 (UAX #31)](https://www.unicode.org/reports/tr31/) variable naming rules. 1. **Permitted Characters**: Identifiers must begin with a character in the Unicode property class `XID_Start` or an underscore (`_`, `U+005F`). Subsequent characters must belong to the Unicode property class `XID_Continue`. 2. **Prohibited Initial Characters**: Identifiers MUST NOT begin with a decimal digit (Unicode general category `Nd`). 3. **Prohibited Symbols and Whitespace**: Identifiers MUST NOT contain any whitespace or symbols matching the Unicode character property classes `Pattern_Syntax` or `Pattern_White_Space`, other than underscores. 4. **Reserved Component Names**: The protocol reserves the component type name `"Surface"` for the canonical surface container created by `createSurface`. Catalogs MUST NOT define a standard UI component named `"Surface"`. ##### Canonical Regular Expression ```regex ^[\p{XID_Start}_][\p{XID_Continue}]*$ ``` ##### Examples - **Valid**: `UserProfileCard`, `submit_form`, `item_id_1`, `_internal_state` - **Invalid**: - `User Card` (violates `Pattern_White_Space`) - `1stItem` (violates initial `Nd`) - `submit-form`, `user#name`, `calc$val` (violates `Pattern_Syntax`) #### Catalog Schema Rules and Conventions To ensure catalog schemas can be translated reliably into alternative, LLM-friendly DSL formats (e.g., HTML-like XML, functional, or compact inline formats), cleanly mapped to type-safe renderer SDK representations, automatically parsed, and bound seamlessly across platforms, all v1.0 component and function catalog definitions MUST conform to the following strict structural constraints and conventions: 1. **Strict Top-Level vs. `$defs` Boundary:** - **Top-Level components and functions:** All component and function schemas MUST be declared directly under the top-level keys `"components"` and `"functions"` respectively. - **External References inside `$defs`:** Any definition referenced externally (e.g., from the envelope schema `agent_to_renderer.json` or `common_types.json`) MUST reside inside the `"$defs"` object at the catalog root. This strictly includes: - `anyComponent`: Referenced as `catalog.json#/$defs/anyComponent`. - `anyFunction`: Referenced as `catalog.json#/$defs/anyFunction`. 2. **Catalog-Scoped Leaf `$defs`:** - Custom leaf definitions (such as design system tokens, value unions, or shared object option schemas) MAY be declared under `"$defs"` at the catalog root alongside `anyComponent` and `anyFunction`. - Component schemas MUST NOT be defined inside `"$defs"` (including inside `allOf` wrappers); all UI components must be declared at the top level under `"components"`. 3. **Restricted `$ref` Targets:** - Component properties and leaf definitions MAY reference catalog-scoped leaf definitions using `#/$defs/` (where `` is not `anyComponent` or `anyFunction`). - Component schemas inside `anyComponent` and function schemas inside `anyFunction` reference top-level components and functions (e.g., `#/components/Text`, `#/functions/required`). - External `$ref` targets MUST reference the standard types inside `common_types.json` using the relative target format (`common_types.json#/$defs/...`). Allowed `$ref` targets are limited to the following schemas: - `ComponentId` - `Child` - `ChildList` - `DynamicString` - `DynamicNumber` - `DynamicBoolean` - `DynamicStringList` - `DynamicValue` - `AccessibilityAttributes` - `CheckRule` - `Checkable` - `Action` - `DataBinding` - `FunctionCall` - Any other external `$ref` target, such as a reference into another catalog, makes the catalog invalid. Implementations MUST report a catalog error when the catalog is loaded, and MUST NOT fetch the target over the network. > [!NOTE] > **Catalog Evolution and Protocol Compatibility** > While there are breaking changes to the catalog API between v0.9 and v1.0, v1.0 catalog definitions reference standard types using relative paths (`common_types.json#/$defs/`). This design allows v1.0 catalogs to potentially work against future protocol versions without requiring changes to catalog type paths. 4. **Component Discriminator Rule:** - Every component schema defined inside the `components` map must have a required property named `component` whose value is a constant (`const`) matching the key under which it is defined. - Example: The component defined at `components.Text` must declare: ```json "properties": { "component": { "const": "Text" } } ``` This enables route-dispatch matching via the `discriminator` block inside `anyComponent` (designating `"propertyName": "component"`). 5. **Standard Component Structure & Scoped Combinators:** - Catalog components define their discriminator (`component: { const: "" }`) and local properties (e.g., its children, variant, specific layouts), and can optionally import common property sets (such as `Checkable`) via `$ref`. - Components MUST remain flat: declared as a single `type: "object"` with direct `properties`. Structural combinators (`oneOf`, `anyOf`, or non-trait `allOf`) MUST NOT be used at the component root. - Combinators (`oneOf`, `anyOf`) are scoped exclusively to leaf property types (such as design token definitions or value unions in `$defs` or property schemas). - Base component envelope properties (`id`, `@catalogId`, `@accessibility`, and `@metadata` via `ComponentCommon`) are composed at the envelope level in `agent_to_renderer.json` via `allOf` inside the `Component` definition (referenced by `ComponentsList`), and therefore MUST NOT be redundantly wrapped with `ComponentCommon` via `allOf` inside individual catalog component definitions. 6. **Strict Function Interface Pattern:** - Every function schema defined inside the `functions` map must validate a wire-level `FunctionCall` object. This requires: - A `properties` block with a `@call` property containing a constant of the function's name (e.g., `"@call": { "const": "email" }`). - An optional `args` property representing arguments (or absent if the function accepts no arguments). - Mandatory metadata fields outside the strict JSON validation properties to advertise interface details: - **`returnType`**: Must be a string enum indicating the return type (`string`, `number`, `boolean`, `array`, `object`, `validationResult`, `any`, or `void`). - **`allowedCallers`**: Must be a string enum indicating the authorized callers (`rendererOnly`, `agentOnly`, or `rendererOrAgent`). If omitted, it defaults to `rendererOnly`. - Base function envelope properties (`@call` and `catalogId` via `FunctionCommon`) are composed at the envelope level in `FunctionCall` (`common_types.json#/$defs/FunctionCall`) via `allOf`, and therefore MUST NOT be redundantly wrapped with `FunctionCommon` via `allOf` inside individual catalog function definitions. 7. **Strict Top-Level Schema Keys:** - To keep catalog schemas predictable and prevent custom extensions from polluting the global file space, a `catalog.json` file is restricted to the following root-level keys: - `$schema` - `$id` - `protocolVersion` (optional for backward compatibility; defaults to `"0.9"` if omitted, required for catalogs targeting `1.0` and beyond) - `title` - `description` - `catalogId` - `instructions` - `metadata` (optional; supports `metadata.extensions` for catalog-wide extension metadata) - `components` - `functions` - `$defs` - No other top-level keys are permitted. 8. **Deprecation Annotations:** - Components, functions, and individual properties may declare `deprecated: true` along with an optional human-readable `x-deprecated-reason` (string). ##### Example Schema Template Below is an annotated, fully compliant `catalog.json` schema template (written in JSONC format with comments) representing a visual, complete model of these rules in action: ```jsonc { // Strict Top-Level Schema Keys "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "protocolVersion": "1.0", "title": "A2UI Basic Catalog Template", "description": "An annotated example showcasing structural rules and conventions.", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "instructions": "Design instructions for LLMs when generating layouts under this catalog.", // Optional catalog-level metadata extensions "metadata": { "extensions": { "org_example_theme": { "primaryColor": "md.sys.color.primary", }, }, }, // Top-level components declared under top-level "components" map. "components": { "Text": { "type": "object", "properties": { // Required "component" property must be a constant matching the component key. "component": { "const": "Text", }, // Leaf properties can be standard JSON primitives or Dynamic wrappers "text": { "$ref": "common_types.json#/$defs/DynamicString", "description": "Text content to display.", }, // Leaf property referencing a catalog-scoped design token $def "color": { "$ref": "#/$defs/ColorToken", "description": "Text color token.", }, // Deprecated property. "rawContent": { "type": "string", "description": "Legacy unescaped text content.", "deprecated": true, "x-deprecated-reason": "Use 'text' property with Markdown formatting instead.", }, }, "required": ["component", "text"], }, }, // Top-level functions declared under top-level "functions" map. "functions": { "required": { "type": "object", "description": "Checks that the value is not null, undefined, or empty.", // Strict function metadata defined outside the properties block. "returnType": "validationResult", "allowedCallers": "rendererOnly", "properties": { // Function call schema requires constant with function's name. "@call": { "const": "required", }, "args": { "type": "object", "properties": { "value": { "description": "The value to check.", }, }, "required": ["value"], "additionalProperties": false, }, }, "required": ["@call", "args"], "unevaluatedProperties": false, }, }, // $defs contains external references (anyComponent, anyFunction) and optional leaf definitions. // Custom component definitions inside $defs are strictly prohibited. "$defs": { "anyComponent": { "oneOf": [ { // Local refs restricted to top-level components map. "$ref": "#/components/Text", }, ], "discriminator": { "propertyName": "component", }, }, "anyFunction": { "oneOf": [ { // Local refs restricted to top-level functions map. "$ref": "#/functions/required", }, ], }, // Catalog-scoped leaf definition (design token) "ColorToken": { "type": "string", "description": "Design token or hex color string.", "oneOf": [ { "type": "string", "pattern": "^md\\.sys\\.color\\.[a-z0-9-]+$", }, { "type": "string", "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$", }, ], }, }, } ``` #### Deprecating Catalog Properties - **`deprecated`** (boolean, optional): Standard JSON Schema annotation indicating that a component, function, or property is deprecated. - **`x-deprecated-reason`** (string, optional): Human-readable explanation of why the entity is deprecated and what to use instead. ### UI composition: the adjacency list model The A2UI protocol defines the UI as a flat list of components. The tree structure is built implicitly using ID references. This is known as an adjacency list model. Container components (like `Row`, `Column`, `List`, and `Card`) have properties that reference the `id` of their child component(s). The renderer is responsible for storing all components in a map (e.g., `Map`) and recreating the tree structure at render time. This model allows the agent to send component definitions in any order. Rendering can begin as soon as the `root` component is defined, with the renderer filling in or updating the rest of the tree progressively as additional definitions arrive. There must be exactly one component with the ID `root` in the component tree, acting as the root of the component tree. Until that component is defined, other component updates will have no visible effect, and they will be buffered until a root component is defined. Once a root component is defined, the renderer is responsible for rendering the tree in the best way possible based on the available data, skipping invalid references. ```mermaid flowchart TD subgraph "Agent Stream" A("updateComponents
components: [root, title, button]") end subgraph "Renderer-Side Buffer (Map)" C("root: {id: 'root', component: 'Column', children: ['title', 'button']}") D("title: {id: 'title', component: 'Text', text: 'Welcome'}") E("button: {id: 'button', component: 'Button', child: 'button_label'}") end subgraph "Rendered Widget Tree" F(Column) --> G(Text: 'Welcome') F --> H(Button) end A -- "Parsed and stored" --> C A -- "Parsed and stored" --> D A -- "Parsed and stored" --> E ``` #### Composition validation rules To validate component nesting hierarchies, A2UI component catalogs support composition constraints via `allowedParents` and `allowedChildren`. You define these constraints on component type definitions in the catalog JSON Schema, and the renderer evaluates them against the component tree at runtime. 1. **Composition Constraints**: - `allowedParents` (array of strings, optional): The list of parent component type names that can contain this component type. If omitted, all parent component types are allowed. - `allowedChildren` (array of strings, optional): The list of child component type names allowed inside this container or slot. If omitted, all child component types are allowed. 2. **`"Surface"` Container Component**: - The protocol reserves the component type name `"Surface"` for the canonical surface container. - The `createSurface` message implicitly creates this container (`common_types.json#/$defs/Surface`) with `"child": "root"`. You cannot modify `Surface` using `updateComponents`. - Parent-child validation applies uniformly across the entire component tree, with `Surface` acting as the top-level container parent. 3. **Catalog Schema Examples**: - **Top-Level Component**: To restrict a component so it can appear only as the top-level component (`"id": "root"`) of a surface: ```json { "AppLayout": { "type": "object", "allowedParents": ["Surface"], "properties": { "component": {"const": "AppLayout"} } } } ``` - **Top-Level or Container Union**: To allow a component as either the top-level component (`"id": "root"`) of a surface or a child of a specific container: ```json { "Card": { "type": "object", "allowedParents": ["Surface", "CanvasContainer"] } } ``` - **Container-Restricted Components**: To restrict a child component so it can appear only within a specific parent container: ```json { "Menu": { "type": "object", "allowedChildren": ["MenuItem"] }, "MenuItem": { "type": "object", "allowedParents": ["Menu"] } } ``` 4. **Validation Error Codes**: - If a component is placed under an unallowed parent, the renderer emits a validation error message with `code` set to `"UNALLOWED_PARENT"`. - If an unallowed child component is placed inside a container, the renderer emits a validation error message with `code` set to `"UNALLOWED_CHILD"`. ### Defining actions Interactive components (like `Button`) use an `action` property to define what happens when the user interacts with them. Actions can either trigger an event sent to the agent or execute a local renderer-side function. #### Agent actions To send an event to the agent, use the `event` property within the `action` object. It requires a `name` and supports an optional `userMessage` (`DynamicString`, resolved by the renderer into a human-readable string in the outbound `action` message) and an optional `context` object containing key-value parameters to dispatch to the agent. ```json { "id": "submit_button", "component": "Button", "child": "submit_button_label", "action": { "event": { "name": "submit_form", "userMessage": "Submitted order #123", "context": { "itemId": "123" } } } } ``` #### Local actions To execute a local function, use the `functionCall` property within the `action` object. This property references a standard `FunctionCall` object. Functions declared with `requiresUserActivation: true` in their catalog definition (such as `openUrl`) MUST only execute when triggered by an active user gesture (e.g., inside an `action` handler); referencing them in a passive property binding or validation `CheckRule.condition` is blocked by the renderer (`code: "EXPRESSION_ERROR"`). ```json { "id": "open_link_button", "component": "Button", "child": "open_link_button_label", "action": { "functionCall": { "@call": "openUrl", "args": { "url": {"@path": "/url"} } } } } ``` ## Data model representation: binding, scope This section describes how UI components **represent** and reference data from the Data Model. A2UI relies on a strictly defined relationship between the UI structure (Components) and the state (Data Model), defining the mechanics of path resolution, variable scope during iteration. ### Path resolution & scope Data bindings in A2UI are defined using **JSON Pointers** ([RFC 6901]). How a pointer is resolved depends on the current **Evaluation Scope**. > **Note on progressive rendering:** During the initial streaming phase, data paths may resolve to `undefined` if the `updateDataModel` message containing that data has not yet arrived. Renderers should handle `undefined` values gracefully (e.g., by treating them as empty strings or showing a loading indicator) to support progressive rendering. #### The root scope By default, all components operate in the **Root Scope**. - Paths starting with `/` (e.g., `/user/profile/name`) are **Absolute Paths**. They always resolve from the root of the Data Model, regardless of where the component is nested in the UI tree. #### Collection scopes (relative paths) When a container component (such as `Column`, `Row`, or `List`) utilizes the **Template** feature of `ChildList`, it creates a new **Child Scope** for each item in the bound array. - **Template definition:** When a container binds its children to a path (e.g., `path: "/users"`), the renderer iterates over the array found at that location. - **Scope instantiation:** For every item in the array, the renderer instantiates the template component. - **Relative resolution:** Inside these instantiated components, any path that **does not** start with a forward slash `/` is treated as a **Relative Path**. - A relative path `firstName` inside a template iterating over `/users` resolves to `/users/0/firstName` for the first item, `/users/1/firstName` for the second, etc. - **Mixing scopes:** Components inside a Child Scope can still access the Root Scope by using an Absolute Path. #### Example: scope resolution **Data model:** ```json { "company": "Acme Corp", "employees": [ {"name": "Alice", "role": "Engineer"}, {"name": "Bob", "role": "Designer"} ] } ``` **Component definition:** ```json { "id": "employee_list", "component": "List", "children": { "path": "/employees", "componentId": "employee_card_template" } }, { "id": "employee_card_template", "component": "Column", "children": ["name_text", "company_text"] }, { "id": "name_text", "component": "Text", "text": { "@path": "name" } // "name" is Relative. Resolves to /employees/N/name }, { "id": "company_text", "component": "Text", "text": { "@path": "/company" } // "/company" is Absolute. Resolves to "Acme Corp" globally. } ``` #### Type conversion When a non-string value is interpolated, the renderer converts it to a string: - **Numbers/Booleans**: Standard string representation. - **null/undefined**: An empty string `""`. - **Objects/Arrays**: Stringified as JSON to ensure consistency across different renderer implementations. ### Two-way binding & input components Interactive components that accept user input (`TextField`, `CheckBox`, `Slider`, `ChoicePicker`, `DateTimeInput`) establish a **Two-Way Binding** with the Data Model. #### The read/write contract Unlike static display components (like `Text`), input components modify the renderer-side data model immediately upon user interaction. 1. **Read (Model -> View):** When the component renders, it reads its value from the bound `path`. If the Data Model is updated via `updateDataModel`, the component re-renders to reflect the new value. 2. **Write (View -> Model):** When the user interacts with the component (e.g., types a character, toggles a box), the renderer **immediately** updates the value at the bound `path` in the local Data Model. If the path does not exist yet, the renderer creates it, so an input can be bound to a path the agent never initialized. #### Reactivity Because the local Data Model is the single source of truth, updates from input components are **reactive**. - If a `TextField` is bound to `/user/name`, and a separate `Text` label is also bound to `/user/name`, the label must update in real-time as the user types in the text field. #### Agent synchronization It is critical to note that Two-Way Binding is **local to the renderer**. - User inputs (keystrokes, toggles) do **not** automatically trigger network requests to the agent. - The updated state is sent to the agent only when a specific **User Action** is triggered (e.g., a `Button` click). - When an `action` is dispatched, the `context` property of the action can reference the modified data paths to send the user's input back to the agent. #### Example: form submission pattern 1. **Bind:** `TextField` is bound to `/formData/email`. 2. **Interact:** User types "jane@example.com". The local model at `/formData/email` is updated. 3. **Action:** A "Submit" button has the following action definition: ```json "action": { "event": { "name": "submit_form", "context": { "email": { "@path": "/formData/email" } } } } ``` 4. **Send:** When clicked, the renderer resolves `/formData/email` (getting "jane@example.com") and sends it in the `action` payload. ## Data model updates: synchronization and convergence While the sections above describe how components reference data, this section defines how the Data Model itself is **updated** and synchronized. To support reliable data synchronization between the Renderer and the Agent that created the surface, the A2UI protocol uses a simple synchronization mechanism controlled by the `sendDataModel` property in the `createSurface` message. ### Agent to renderer updates The agent sends `updateDataModel` messages to modify the renderer's data model. These updates follow strict upsert semantics: - If the path exists, the value is updated. - If the path does not exist, the value is created. - If the value is `null`, the key at that path is removed. The `updateDataModel` message replaces the value at the specified `path` with the new content. If `path` is omitted (or is `/`), the entire data model for the surface is replaced. **Properties:** - `surfaceId` (string, required): The ID of the surface to update. - `path` (string, optional): A JSON Pointer to the location in the data model to update. Defaults to `/`. - `value` (any, required): The new value for the specified path. To delete the key/value at `path`, set `value` explicitly to `null`. **Examples:** _Update a specific field:_ ```json { "version": "1.0", "updateDataModel": { "surfaceId": "surface_123", "path": "/user/firstName", "value": "Alice" } } ``` _Remove a field:_ ```json { "version": "1.0", "updateDataModel": { "surfaceId": "surface_123", "path": "/user/tempData", "value": null } } ``` _Replace the entire data model:_ ```json { "version": "1.0", "updateDataModel": { "surfaceId": "surface_123", "value": { "user": {"firstName": "Alice", "lastName": "Smith"}, "preferences": {"theme": "dark"} } } } ``` ### Renderer to agent updates When `sendDataModel` is set to `true` for a surface, the renderer automatically appends the **entire data model** of that surface to the metadata of every message (such as `action` or user query) sent to the agent that created the surface. The data model is included using the transport's metadata facility (the exact location and format are defined by the specific transport binding). The payload follows the schema in [`renderer_data_model.json`](/specification/json/renderer_data_model.json). - **Targeted Delivery**: The data model is sent exclusively to the agent that created the surface. Data cannot leak to other agents. - **Trigger:** Data is sent only when a renderer-to-agent message is triggered (e.g., by a user action like a button click). Passive data changes (like typing in a text field) do not trigger a network request on their own; they simply update the local state, which will be sent with the next action. - **Payload:** The data model is included in the transport metadata, tagged by its `surfaceId`. - **Convergence:** The agent treats the received data model as the current state of the renderer at the time of the action. ## Renderer-side logic & validation A2UI v1.0 generalizes renderer-side logic into **Functions**. These can be used for validation, data transformation, and dynamic property binding. ### Registered functions The renderer supports a set of named **Functions** (e.g., `required`, `regex`, `email`, `add`, `concat`) which are defined in the JSON schema (e.g. `catalogs/basic/catalog.json`) alongside the component definitions. The agent references these functions by name in `FunctionCall` objects. This avoids sending executable code. ### Component Validation & Check Rules Input components (like `TextField`, `ChoicePicker`) and interactive elements (like `Button`) can define a list of `@checks` (`CheckRule` objects). A `CheckRule` contains a `condition` (a `DataBinding` path or a `FunctionCall`) and an optional fallback `message` string. The `condition` evaluates to either a `ValidationResult` object (defined in [`catalog_definition.json#/$defs/ValidationResult`](/specification/json/catalog_definition.json)) or a primitive `boolean`. #### `ValidationResult` Structure Validation functions (declared with `"returnType": "validationResult"`) or data model bindings evaluate directly to a `ValidationResult` object: - **`valid`** (`boolean`, required): Whether the check passed. - **`code`** (`string`, optional): Machine-readable error code (e.g., `EXPIRED_CARD`, `OUT_OF_RANGE`). - **`message`** (`string`, optional): Human-readable error or warning message to display. - **`severity`** (`"error" | "warning" | "info"`, optional, default `"error"`). Because `ValidationResult` permits additional unconstrained properties, validation functions and specialized components can extend the object with custom domain-specific metadata (such as suggested fix values, field paths, or retry parameters). #### `DynamicBoolean` and `CheckRule` Coercion To allow validation functions (`required`, `email`, `regex`, `length`, `numeric`, which return `ValidationResult`) to compose with boolean properties and functions accepting `DynamicBoolean` arguments (such as `and`, `or`, and `not`, which return `boolean`), renderers apply three coercion rules: 1. **`ValidationResult` → `boolean` in `DynamicBoolean`**: - Schema validation of a `DynamicBoolean` occurs on the written wire payload (which accepts a literal `boolean`, any `DataBinding`, or any `FunctionCall`) before nested expressions are evaluated. - At runtime, whenever a `DynamicBoolean` (such as an argument to `and`, `or`, `not`, or a boolean component property) evaluates to a `ValidationResult` object (an object with a boolean `valid` property), the renderer extracts `result.valid` as its boolean value. - Functions with `DynamicBoolean` arguments (including `and`, `or`, and `not`) therefore receive and return plain booleans. When a boolean combinator is used as a `CheckRule.condition`, the enclosing `CheckRule.message` provides the user-facing error message when the combinator returns `false`. 2. **`boolean` → `ValidationResult` in `CheckRule.condition`**: - When a `CheckRule.condition` evaluates to `true`, the renderer coerces it to `{"valid": true}`. - When a `CheckRule.condition` evaluates to `false`, the renderer coerces it to `{"valid": false, "message": , "severity": "error"}`. - When a `CheckRule.condition` evaluates to a `ValidationResult` with `valid: false`, `CheckRule.message` is used as a fallback if `ValidationResult.message` is omitted, and `severity` defaults to `"error"` if omitted. 3. **Any value → `boolean` in `DynamicBoolean`**: - This coercion applies wherever a `DynamicBoolean` is resolved, including component properties typed `DynamicBoolean` and `DynamicBoolean` function arguments (`and`, `or`, `not`, and any catalog function whose argument schema references `DynamicBoolean`), before the function runs: | Resolved value | Coerced `boolean` | | :------------------------------------------------------------- | :---------------------------- | | `boolean` | Unchanged (`true` or `false`) | | `ValidationResult` (an object with a boolean `valid` property) | `result.valid` | | `string` `"true"` or `"false"` (in any letter case) | `true` or `false` | | Any other `string`, including `""` and `"hello"` | `false` | | `number` `0` | `false` | | Any other `number` | `true` | | `null` or absent | `false` | | Any other object or array | `false` | _Example Component Definition:_ ```json "@checks": [ { "condition": { "@call": "validateCreditCard", "args": { "cardNumber": { "@path": "/payment/cardNumber" } } } } ] ``` _Example Dynamic `ValidationResult` Returned by `validateCreditCard`:_ ```json { "valid": false, "code": "EXPIRED_CARD", "message": "The card expiration date (05/24) has passed.", "severity": "error" } ``` ### Example: button validation Buttons can also define `@checks`. If any check fails, the button is automatically disabled. This allows the button's state to depend on the validity of data in the model. ```json { "id": "submit_button", "component": "Button", "child": "submit_button_label", "action": { "event": { "name": "submit_form" } }, "@checks": [ { "condition": { "@call": "and", "args": { "values": [ { "@call": "required", "args": {"value": {"@path": "/formData/terms"}} }, { "@call": "or", "args": { "values": [ { "@call": "required", "args": {"value": {"@path": "/formData/email"}} }, { "@call": "required", "args": {"value": {"@path": "/formData/phone"}} } ] } } ] } }, "message": "You must accept terms AND provide either email or phone" } ] } ``` ## Basic Component Catalog The [`catalogs/basic/catalog.json`] provides the baseline set of components and functions. ### Components | Component | Description | | :---------------- | :------------------------------------------------------------------------------------------ | | **Text** | Displays text. Supports simple Markdown. | | **Image** | Displays an image from a URL. | | **Icon** | Displays a system-provided icon from a predefined list. | | **Video** | Displays a video from a URL. | | **AudioPlayer** | A player for audio content from a URL. | | **Row** | A horizontal layout container. | | **Column** | A vertical layout container. | | **List** | A scrollable list of components. | | **Card** | A container with card-like styling. | | **Tabs** | A set of tabs, each with a title and child component. | | **Divider** | A horizontal or vertical dividing line. | | **Modal** | A dialog that appears over the main content triggered by a button in the main content. | | **Button** | A clickable button that dispatches an action. Supports 'primary' and 'borderless' variants. | | **CheckBox** | A checkbox with a label and a boolean value. | | **TextField** | A field for user text input. | | **DateTimeInput** | An input for date and/or time. | | **ChoicePicker** | A component for selecting one or more options. | | **Slider** | A slider for selecting a numeric value within a range. | ### Functions > **Reserved Protocol Directives (`@` Prefix)**: The `@` prefix is strictly reserved for protocol-level directives and system context functions: > > - **`@path`**: Represents a dynamic data binding to a path in the data model (e.g. `{"@path": "/user/name"}`). > - **`@call`**: Represents a catalog or system function invocation (e.g. `{"@call": "formatString", "args": {...}}`). > - **`@index`**: Represents the universal system context function returning the 0-based iteration index during list template rendering (`{"@call": "@index"}`). Custom catalogs MUST NOT define functions prefixed with `@`. > > **Single-`@` Directive Rule**: Any key in a dynamic object matching `^@([^@]|$)` (a leading `@` that is not doubled, including a key consisting of `@` alone) that is not a recognized protocol directive is disallowed and rejected by renderers and validators. This reserves the single-`@` namespace for future protocol extensions without breaking backward compatibility. > > **Escaping via Prefix Doubling**: Plain objects that require literal property names starting with `@` must escape them by doubling the prefix: `"@@path"` evaluates to `"@path"`, `"@@type"` evaluates to `"@type"`, etc. Plain objects with `"path"` and `"call"` keys are literal objects and are never intercepted as dynamic bindings. | Function | Description | | :----------------- | :------------------------------------------------------------------------------ | | **@index** | Returns the 0-based index of the current item during template rendering. | | **required** | Checks that the value is not null, undefined, or empty. | | **regex** | Checks that the value contains a match for a pattern in the A2UI regex dialect. | | **length** | Checks string length constraints. | | **numeric** | Checks numeric range constraints. | | **email** | Checks that the value is a valid email address. | | **formatString** | Does string interpolation of data model values and registered functions. | | **formatNumber** | Formats a number with grouping and precision. | | **formatCurrency** | Formats a number as a currency string. | | **formatDate** | Formats a date/time using a pattern. | | **pluralize** | Selects a localized string based on a numeric count. | | **openUrl** | Opens a URL in a browser (requires user activation). | | **and** | Logical AND operation on a list of boolean values or validation results. | | **or** | Logical OR operation on a list of boolean values or validation results. | | **not** | Logical NOT operation on a boolean value or validation result. | ### The `formatString` function The `formatString` function supports embedding dynamic expressions directly within string properties. This allows for mixing static text with data model values and function results. #### `formatString` syntax Interpolated expressions are enclosed in `${...}`. To include a literal `${` in a string, it must be escaped as `\${`. #### `formatString` data model binding Values from the data model can be interpolated using their JSON Pointer path. - `${/user/profile/name}`: Absolute path. - `${firstName}`: Relative path (resolved against the current collection scope). **Example:** ```json { "id": "user_welcome", "component": "Text", "text": { "@call": "formatString", "args": { "value": "Hello, ${/user/firstName}! Welcome back to ${/appName}." } } } ``` #### `formatString` renderer-side functions Results of renderer-side functions can be interpolated. Function calls are identified by the presence of parentheses `()`. - `${now()}`: A function with no arguments. - `${formatDate(value:${/currentDate}, format:'yyyy-MM-dd')}`: A function with named arguments. Arguments can be **Literals** (quoted strings, numbers, or booleans), or **Nested Expressions**. Because `${...}` syntax cannot specify a `catalogId`, non-system function calls inside `${...}` (including nested calls inside their arguments) resolve against the catalog that the enclosing `formatString` call resolved to, even if the surface declares a different default `catalogId`. Reserved `@`-prefixed system functions (such as `${@index()}`) belong to no catalog and remain catalogless. #### `formatString` nested interpolation Expressions can be nested using additional `${...}` wrappers inside an outer expression to make bindings explicit or to chain function calls. - **Explicit Binding**: `${formatDate(value:${/currentDate}, format:'yyyy-MM-dd')}` - **Nested Functions**: `${upper(${now()})}` #### `formatString` type conversion When a non-string value is interpolated, the renderer converts it to a string: - **Numbers/Booleans**: Standard string representation. - **Null/Undefined**: An empty string `""`. - **Objects/Arrays**: Stringified as JSON to ensure consistency across different renderer implementations. ### The `regex` function The `regex` validation function evaluates a regular expression pattern in the **A2UI regex dialect** and applies a static safety check before matching. Any catalog function that accepts a regular expression pattern (`regex`, `regexCapture`, `split`) SHOULD use this dialect and safety check. #### `regex` dialect The dialect is a portable subset of ECMAScript `Pattern` syntax, evaluated in Unicode mode (`u`) with no other flags: 1. **Matching semantics**: Matching is case-sensitive. `.` matches any code point except U+000A, U+000D, U+2028, and U+2029. `^` and `$` match only at the start and end of the value (no multiline mode, and `$` does not match before a trailing newline). `\d`, `\w`, and `\b` are ASCII-only. `\s` is the ECMAScript WhiteSpace and LineTerminator set. The function uses search semantics: it returns valid when the pattern matches anywhere in the value, like `RegExp.prototype.test`. 2. **Allowed syntax**: Literals, `.`, character classes with ranges and negation, the class escapes `\d \D \w \W \s \S`, `\b \B`, control escapes `\t \n \v \f \r`, `\0` not followed by a digit, `\xHH`, `\uHHHH`, `\u{H...}`, identity escapes of syntax characters, capturing groups `( )`, non-capturing groups `(?: )`, lookahead `(?= )` and `(?! )`, alternation, and the quantifiers `* + ? {n} {n,} {n,m}` with optional lazy `?`. 3. **Not allowed**: Backreferences (`\1`-`\9`, `\k`), lookbehind (`(?<=`, `(?`), `\p{...}`/`\P{...}`, `\cX`, legacy octal escapes, and inline flags. A pattern that does not parse in this dialect, or uses a construct that is not allowed, fails with `code: "EXPRESSION_ERROR"`. #### `regex` safety check Before any matching, the renderer applies a static safety check and rejects the pattern with `code: "EXPRESSION_ERROR"` when: 1. It is longer than 256 code points. 2. A quantifier whose upper bound is above 1 applies to a subexpression that contains an unbounded quantifier (`*`, `+`, `{n,}`). 3. An unbounded quantifier applies to an alternation where two branches can start with the same character. 4. Two unbounded-quantified atoms with overlapping character sets are adjacent in one concatenation, where only zero-width assertions or items that can match the empty string lie between them. `?` and `{0,1}` count as upper bound 1. Counted bounds such as `{1,1000}` are bounded. A group whose only content is one quantified atom counts as that atom. Rule 3 compares the first-character sets of the branches after unwrapping groups. A value longer than 4,096 code points does not match; `regex` returns `{"valid": false}` and reports no error. #### `regex` examples - **Accepted**: `^[a-z]+@[a-z]+\.com$`, `^(?=.*\d).{8,}$`, `\w+\s+\w+`. - **Rejected**: `(a+)+$` (rule 2), `(a|ab)*c` (rule 3), `\d+\s*\d+` (rule 4), `(\w)\1` (backreference). ### The `@index` function The `@index` function returns the 0-based index of the current item when rendering a dynamic list from a template. It is a universal system function available across all catalogs. #### `@index` scope restriction The `@index` function MUST ONLY be available when evaluating template items within a list rendering context (Collection Scope). When an expression evaluator encounters `@index()`, it inspects the active Evaluation Context chain starting from the current scope and walking upward. If one or more Collection Scopes are present (such as in nested list templates), `@index` resolves to the **innermost** enclosing template's 0-based iteration index. If called outside of template iteration (e.g., directly in the Root Scope), the renderer MUST treat it as an error or evaluate it as invalid. #### `@index` arguments - `offset` (Optional, `number`): An offset added to the 0-based index. For example, `@index(offset: 1)` produces 1-based indexing (`1, 2, 3...`). Defaults to `0`. #### Example usage Displaying item positions inside a list row template: ```json { "id": "todo_index", "component": "Text", "text": { "@call": "formatString", "args": { "value": "#${@index(offset: 1)}" } } } ``` ## Usage pattern: the prompt-generate-validate loop The A2UI protocol is designed to be used in a three-step loop with a Large Language Model: 1. **Prompt**: Construct a prompt for the LLM that includes: - The desired UI to be generated. - The A2UI JSON schema, including the component catalog. - Examples of valid A2UI JSON. 2. **Generate**: Send the prompt to the LLM and receive the generated JSON output. 3. **Validate**: Validate the generated JSON against the A2UI schema. If the JSON is valid, it can be sent to the renderer for rendering. If it is invalid, the errors can be reported back to the LLM in a subsequent prompt, allowing it to self-correct. This loop allows for a high degree of flexibility and robustness, as the system can leverage the generative capabilities of the LLM while still enforcing the structural integrity of the UI protocol. ### Standard validation error format If validation fails, the renderer (or the system acting on behalf of the renderer) should send an `error` message back to the LLM. To ensure the LLM can understand and correct the error, use the following standard format within the `error` message payload: - `code` (string, required): Must be `"VALIDATION_FAILED"`, `"UNALLOWED_PARENT"`, or `"UNALLOWED_CHILD"`. Use `"UNALLOWED_PARENT"` if a component is placed under an unallowed parent, and `"UNALLOWED_CHILD"` if an unallowed child is placed inside a container. - `surfaceId` (string, required): The ID of the surface where the error occurred. - `path` (string, required): The JSON pointer to the field that failed validation (e.g. `/components/0/text`). - `message` (string, required): A short one-sentence description of why validation failed. **Example error message:** ```json { "error": { "code": "VALIDATION_FAILED", "surfaceId": "user_profile_card", "path": "/components/0/text", "message": "Expected stringOrPath, got integer" } } ``` ## Renderer-to-agent event messages The protocol defines messages that the renderer can send to the agent to report user interactions, execution results of agent-initiated function calls, or renderer-side runtime errors. Every renderer-to-agent message must validate against the [`renderer_to_agent.json`] schema and contain exactly one of the following top-level keys: `action`, `callAgentFunction`, `rendererFunctionResponse`, or `error`. ### `action` This message is sent when a user interacts with a component that has an agent action defined (such as a `Button`). **Properties:** - `name` (string, required): The name of the action. - `surfaceId` (string, required): The unique ID of the surface where the event originated. - `sourceComponentId` (string, required): The ID of the component that triggered the interaction. - `timestamp` (string, required): An ISO 8601 timestamp representing when the event occurred. - `userMessage` (string, optional): A human-readable message describing the action performed by the user, resolved from `action.event.userMessage`. - `context` (object, required): A JSON object containing the key-value pairs of the action's context parameters, after resolving all dynamic data bindings. - `metadata` (object, optional): Action-level metadata containing an optional `extensions` map (`common_types.json#/$defs/Extensions`). **Example:** ```json { "version": "1.0", "action": { "name": "submitForm", "surfaceId": "contact_form_1", "sourceComponentId": "submit_button", "timestamp": "2026-06-02T08:57:23Z", "userMessage": "Submitted contact form", "context": { "isSubscribed": true } } } ``` ### `callAgentFunction` This message is sent by the renderer to execute a function remotely on the agent (e.g. verifying a provider ID or checking inventory availability). **Function Location Resolution & Fallback Routing:** When the renderer evaluates a `FunctionCall` (from an action handler, validation check, or dynamic value), it determines the execution target using implicit fallback routing: 1. **Catalog Resolution:** The renderer resolves the catalog the call runs in, from its explicit `catalogId` or the surface default `catalogId` (see [Mixable catalogs and component resolution logic](#mixable-catalogs-and-component-resolution-logic)). A call whose catalog cannot be resolved is a catalog error, and is not routed to the agent. 2. **Local Lookup:** A reserved system function (`@`-prefixed, such as `@index`) is always evaluated locally and MUST NOT be routed to the agent. Otherwise, the renderer checks whether it has a local implementation of the function in that catalog. A function counts as registered locally only if the renderer can execute it. If it can, the renderer enforces the function's `allowedCallers` and `requiresUserActivation` boundaries and executes it locally. 3. **Fallback to Agent RPC:** If the renderer has no local implementation, the renderer assumes it is an agent-side function and dispatches a `callAgentFunction` message over the protocol. This covers a function that the catalog does not declare, and a function that the catalog declares without the renderer implementing it (a schema-only declaration). For a declared function, the renderer first applies the declaration's boundaries: if it sets `allowedCallers: "agentOnly"`, or sets `requiresUserActivation: true` and the evaluation is not user-activated (such as a data binding or a check), the renderer MUST NOT dispatch the call and reports an `EXPRESSION_ERROR` instead. The renderer resolves the arguments before dispatching, and validates the arguments of a declared function against its schema; arguments that fail validation are reported as an `EXPRESSION_ERROR` and are not sent. Routing is a renderer-initiated call, so it applies to a schema-only function whose `allowedCallers` is `"rendererOnly"`, the default: that value only forbids the agent from calling the function with `callRendererFunction`. - **Sharing and caching:** For reactive evaluations (data bindings and checks), a renderer MAY share one in-flight call between identical calls (the same resolved `catalogId`, function name and `args`) on a surface, and MAY reuse the settled answer of such a call on that surface. Imperative evaluations (actions) MUST send a new `callAgentFunction` each time. An agent function with side effects should therefore be called from an action. 4. **Agent Error Handling:** If the agent does not recognize the function name (or if parameter validation fails on the server), the agent MUST return an `agentFunctionResponse` message containing an `error` payload (`code: "UNKNOWN_FUNCTION"` or `"INVALID_FUNCTION_CALL"`). The renderer then handles the error state locally (e.g., via component error boundaries or fallbacks). Fallback routing only applies to function calls that the renderer accepts. A renderer that rejects payloads naming functions absent from its catalogs MUST declare the agent-side functions it expects, without implementing them, for those calls to reach the agent. **Properties:** - `callAgentFunction` (object, required): - `surfaceId` (string, required): The surface ID where the call originated. - `functionCallId` (string, required): A unique identifier for this invocation instance. The agent MUST copy this ID verbatim into the return `agentFunctionResponse`. - `callFunction` (object, required): The description of the function call (`@call`, `catalogId`, `args`). The renderer MUST set `catalogId` to the catalog the call resolved to, even when the originating `FunctionCall` omits it and relies on the surface default, so that the agent does not need to track each surface's default catalog. `args` carries the resolved argument values. For a function the catalog declares, `args` is the result of validating those values against the declared schema, with the schema's defaults applied. **Example:** ```json { "version": "1.0", "callAgentFunction": { "surfaceId": "contact_form_1", "functionCallId": "verify_provider_99", "callFunction": { "@call": "verifyProvider", "catalogId": "https://example.com/catalogs/healthcare.json", "args": { "providerId": "PRV-102" } } } } ``` ### `rendererFunctionResponse` This message is sent by the renderer to return the execution result or error of an agent-initiated `callRendererFunction` request. **Properties:** - `rendererFunctionResponse` (object, required): - `functionCallId` (string, required): The unique invocation ID matching the initiating `callRendererFunction` call. - `value` (any, optional): The returned execution value of the function call. - `error` (object, optional): An error object containing `code` (string) and `message` (string) describing execution failure. The payload MUST include either `value` or `error`. **Example (Success):** ```json { "version": "1.0", "rendererFunctionResponse": { "functionCallId": "get_device_resolution_123", "value": [1920, 1080] } } ``` **Example (Failure):** ```json { "version": "1.0", "rendererFunctionResponse": { "functionCallId": "get_device_resolution_123", "error": { "code": "EXECUTION_ERROR", "message": "Failed to query screen resolution." } } } ``` ### `error` This message is sent by the renderer to report surface-level validation/integrity errors or envelope-level function invocation faults to the agent. (Standard `callRendererFunction` execution and boundary failures are returned via `rendererFunctionResponse.error`, as described above.) **Properties:** - `code` (string, required): The machine-readable error code (e.g., `"VALIDATION_FAILED"`, `"INTEGRITY_ERROR"`, `"INVALID_FUNCTION_CALL"`). - `message` (string, required): A short, human-readable description of the error. - `surfaceId` (string, optional): The unique ID of the surface where the error occurred. This field is mutually exclusive with `functionCallId`. - `functionCallId` (string, optional): The unique ID of the function invocation that failed. This field is mutually exclusive with `surfaceId` and is used when reporting function invocation errors outside a `rendererFunctionResponse`. **Example (Function Invocation Error):** ```json { "version": "1.0", "error": { "code": "INVALID_FUNCTION_CALL", "message": "Malformed callRendererFunction payload.", "functionCallId": "delete_file_call_9" } } ``` ## Functions in A2UI Content Execution In addition to top-level protocol RPC messages (`callRendererFunction` and `callAgentFunction`), functions in A2UI can be embedded directly within UI component trees and content definitions. ### 1. Polymorphic Function Usage in UI Content `FunctionCall` objects can be used interchangeably across UI content bindings regardless of whether the target function executes locally on the renderer or remotely on the agent: - **Dynamic Value Bindings:** A `FunctionCall` can compute dynamic property values (e.g. formatting a timestamp or fetching calculated user statistics). - **Validation Rules (`Checkable`):** Components that support client validation (such as input fields) use `FunctionCall` objects inside check rules to perform validation logic. - **Action Handlers (`Action`):** Component interaction handlers can execute a `FunctionCall` directly on click or trigger. The component tree syntax is completely uniform. The renderer evaluates whether to execute the function locally or route a `callAgentFunction` RPC to the agent based on function target resolution rules. ### 2. Asynchronous Evaluation & Pending States When a UI component binding or validation rule depends on a function that routes remotely to the agent (or executes an asynchronous renderer function): 1. **Async Evaluation:** The renderer dispatches the function call (e.g. emitting `callAgentFunction`) and enters an asynchronous evaluation state. 2. **Pending UI State:** The renderer maintains a pending/loading state for the affected component binding (e.g., displaying a loading indicator or preserving existing component values) while awaiting `agentFunctionResponse`. 3. **Value Resolution:** Upon receiving `agentFunctionResponse`, the renderer updates the local dynamic value or validation state with the returned `value`. ### 3. Failure Propagation & Recovery Rules If a function call within a content pipeline fails (returns an `agentFunctionResponse` containing an `error` payload, throws a local execution error, times out, or triggers a transport error), the renderer applies the following recovery rules: - **Dynamic Value Binding Failure:** The binding resolves to `null` (there is no declared fallback value in v1.0). The renderer reports an `error` with `code: "EXPRESSION_ERROR"`, including the call's `functionCallId` when a routed agent function failed or the `surfaceId` for a local evaluation failure. The component and its siblings stay mounted and keep resolving their other bindings. - **Validation Rule Failure (`Checkable`):** The check evaluates to `{"valid": false, "message": , "severity": "error"}`, using the error's message when `CheckRule.message` is omitted. - **Action Pipeline Failure:** The renderer stops the remaining steps of that action, dispatches no further events from it, and reports an `error` (`code: "EXPRESSION_ERROR"` with the failed call's `functionCallId` when a routed agent function failed, or `code: "EXECUTION_ERROR"` with the `surfaceId` when a local renderer function ran and failed). Effects of steps that already ran are kept. The surface stays mounted. Whether the adapter also shows the failure to the user (inline message, toast) is the adapter's choice. --- ## Capabilities and metadata In A2UI v1.0, capabilities and other metadata travel in **transport metadata** or a **transport handshake**, with each transport binding defining where they are placed rather than encoding them as first-class A2UI messages. ### Agent capabilities An agent advertises its capabilities using the [`agent_capabilities.json`] schema. This indicates which catalogs it can generate UI for, and whether it accepts inline catalogs from the renderer. Each transport binding defines where this advertisement is carried. **Properties:** - `1.0` (object, required): The capability structure for version 1.0 of the A2UI protocol. - `supportedCatalogIds` (array of strings, optional): An array of strings identifying the Catalog Definition Schemas the agent can generate. - `acceptsInlineCatalogs` (boolean, optional, default `false`): Indicates if the agent can accept custom inline component/function catalogs in the renderer's capabilities metadata. ### Renderer capabilities The `a2uiRendererCapabilities` object in transport metadata follows the [`renderer_capabilities.json`] schema to describe the renderer's capabilities. Capabilities carried per message are scoped to that message's turn, and omitting them withdraws A2UI for that turn; capabilities carried at session setup are scoped to the session. Each transport binding states which applies (see [Capability scoping](/specification/extensions/a2a/docs/a2ui_extension_specification.md#capability-scoping) for the A2A rule). **Properties:** - `1.0` (object, required): The capability structure for version 1.0 of the A2UI protocol. - `supportedCatalogIds` (array of strings, required): The string identifiers of supported component and function catalogs. - `inlineCatalogs` (array, optional): An array of custom catalog definitions provided inline by the renderer. Functions defined within inline catalogs support declaring authorized callers (`allowedCallers: "rendererOnly" | "agentOnly" | "rendererOrAgent"`) to statically specify remote invocation safety. ### Renderer data model When `sendDataModel` is enabled for a surface, the renderer includes the `a2uiRendererDataModel` object in the transport metadata, following the [`renderer_data_model.json`] schema. **Properties:** - `version` (string, required): Must be the constant `"1.0"`. - `surfaces` (object, required): A map of surface IDs to their current local data models. ### Extensions In A2UI v1.0, strict schema validation (`additionalProperties: false`) protects components and wire messages from unrecognized fields. To enable non-visual metadata (such as access constraints, audit tags, and telemetry identifiers, etc.) without allowing arbitrary properties that would weaken schema validation, A2UI defines a centralized `Extensions` type in `common_types.json#/$defs/Extensions`. #### Architectural Principles 1. **Optional Schema Fields**: All `metadata.extensions` fields are strictly optional on the wire. When omitted, they incur zero token overhead. 2. **Parser Conformance Rule**: Conformant renderers MUST NOT reject payloads containing extension keys within an `extensions` object. Renderers MAY inspect and process extension keys they recognize, and MUST ignore unrecognized extension keys without error. 3. **Unicode Identifiers (UAX #31) and Prefix Reservation**: Extension names MUST be valid [Unicode UAX #31] identifiers (`^[\p{XID_Start}_][\p{XID_Continue}]*$`). To prevent key collisions: - Official A2UI extensions are strictly reserved under the prefix `a2ui_`. - Third-party extensions MUST be prefixed with a distinct organization or product identifier separated by an underscore (e.g. an extension from a company with the domain `company.com` might be named `com_company_extension`). - Use of names derived from reversed domain names is encouraged for public facing extensions. - Extension namespace uniqueness is self-managed by extension authors; no central registry is maintained. #### Wire Containers A2UI defines optional `metadata.extensions` containers across five scopes: - **Catalog Scope** (`metadata.extensions` in [`catalog_definition.json`]): Attach catalog-wide styling tokens, design system metadata, or configuration directly at the catalog root. - **Surface Scope** (`createSurface.metadata.extensions` in [`agent_to_renderer.json`]): Attach surface-level security metadata, access policies, or telemetry session identifiers. - **Component Scope** (`ComponentCommon.@metadata.extensions` in [`common_types.json`]): Attach component-instance styling overrides, telemetry markers, or custom validation rules. - **Catalog Component Definition Scope** (`ComponentDefinition.metadata.extensions` in [`catalog_definition.json`]): Attach static component metadata or default telemetry tagging directly to catalog component schemas. - **Action Egress Scope** (`action.metadata.extensions` in [`renderer_to_agent.json`]): Send client-side action attestations, audit signatures, or authorization tokens back to the agent when user actions are triggered. #### SDK Accessor Pattern Renderers and client SDKs expose surface and component metadata through a uniform set of properties, mutators, and change notifications. Names follow each language's conventions (for example `setMetadata` in TypeScript and `set_metadata` in Python), but the shape is the same everywhere: - `metadata`: Read-only snapshot of the current metadata object containing `extensions`, exposed on the surface state model and on each component state model. `extensions` is a convenience view of `metadata.extensions`. - `setMetadata(metadata)`: Validates every `extensions` key against UAX #31, stores a defensive copy, and notifies listeners when the value changes. Setting a structurally equal value is a no-op. - `setExtension(key, value)` and `removeExtension(key)`: Set or delete a single extension key. Removing the last key clears `metadata` entirely. - `onMetadataChanged`: An event source that fires whenever metadata changes, either from an incoming `updateComponents` message or from a local mutation. - Resolved component nodes expose instance metadata as a read-only reactive value (for example `ComponentNode.metadata`), separate from the schema-defined component properties, so a metadata-only update never re-renders views that read only properties. Catalog component definition metadata is available statically from the resolved component implementation (`node.impl.metadata`). - Outbound action dispatch accepts an optional `metadata` argument so renderers can attach `action.metadata.extensions` to the emitted action message. An `updateComponents` message replaces a component definition in full, including its `@metadata`. A component update that omits `@metadata` clears any previously stored component metadata, and renderers notify metadata subscribers of that change. [json pointer]: https://datatracker.ietf.org/doc/html/rfc6901 [rfc 6901]: https://datatracker.ietf.org/doc/html/rfc6901 [unicode uax #31]: https://www.unicode.org/reports/tr31/ [`agent_capabilities.json`]: ../json/agent_capabilities.json [`agent_to_renderer.json`]: ../json/agent_to_renderer.json [`catalog_definition.json`]: ../json/catalog_definition.json [`catalogs/basic/catalog.json`]: ../../../catalogs/basic/v1/catalog.json [`common_types.json`]: ../json/common_types.json [`renderer_capabilities.json`]: ../json/renderer_capabilities.json [`renderer_data_model.json`]: ../json/renderer_data_model.json [`renderer_to_agent.json`]: ../json/renderer_to_agent.json --- # v1.0 A2A Extension > A2UI v1.0 extension specification for the Agent-to-Agent (A2A) protocol. Canonical URL: https://a2ui.org/specification/v1.0-a2ui-extension-specification/ ## Overview This document is intended for developers implementing the A2UI A2A extension. The extension adds A2UI v1.0 support to A2A, a format for agents to send streaming, interactive user interfaces to renderers. Note that A2UI extension activation is optional as renderers and agents can negotiate A2UI support using A2A `message.metadata["a2uiRendererCapabilities"]` which is attached to every A2A message from the renderer and contains the supported protocol version and catalogs. Agents advertising A2UI support in their AgentCard is encouraged as renderers may rely on it to determine if they should send `message.metadata["a2uiRendererCapabilities"]`, however it is not explicitly required. ## Extension URI The URI of this extension is https://a2ui.org/a2a-extension/a2ui/v1.0 This URI is the canonical way to communicate protocol versioning between renderers and agents. The extension URI explicitly encodes the version (e.g., `v1.0`). A renderer requesting this specific URI indicates it supports the v1.0 schema format. ## Agent Card Agents are encouraged to advertise their A2UI capabilities in their AgentCard within the `AgentCapabilities.extensions` list. This advertisement is optional, but it informs the renderer whether to send `message.metadata["a2uiRendererCapabilities"]`. The `params` object defines the agent's specific UI support and MUST validate against [`agent_capabilities.json#/properties/1.0`](/json/agent_capabilities.json). The schema keys capabilities by version so a single object can describe multiple versions. Because A2A gives each version its own extension URI, `params` carries only the inner `1.0` object and never the `{"1.0": {...}}` wrapper. Example AgentCard payload: ```json { "name": "Dashboard Agent", "description": "Agent capable of generating dynamic UI dashboards.", "capabilities": { "extensions": [ { "uri": "https://a2ui.org/a2a-extension/a2ui/v1.0", "description": "Ability to render A2UI v1.0", "required": false, "params": { "supportedCatalogIds": [ "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "https://my-company.com/a2ui/v1.0/my_custom_catalog.json" ], "acceptsInlineCatalogs": true } } ] } } ``` The `params` object corresponds to the `1.0` object in the `agent_capabilities.json` schema: - `params.supportedCatalogIds` (optional): An array of strings, where each string is an ID identifying a Catalog Definition Schema that the agent can generate. This is not necessarily a resolvable URI. - `params.acceptsInlineCatalogs` (optional): A boolean indicating if the agent can accept an `inlineCatalogs` array in the renderer's `a2uiRendererCapabilities`. If omitted, this defaults to `false`. ## A2A Extension activation Activating the A2UI extension is optional. Renderers and agents can negotiate A2UI support using `message.metadata["a2uiRendererCapabilities"]` and A2A `DataPart.metadata["mimeType"] = "application/a2ui+json"`. Specifically: - If a renderer includes `message.metadata["a2uiRendererCapabilities"]`, the agent can use this object to determine the supported A2UI protocol version and catalogs. - If an agent returns A2A `DataPart.metadata["mimeType"] = "application/a2ui+json"`, the renderer knows the payload contains A2UI messages. While explicit activation is not required, renderers can still explicitly activate the extension using the transport-defined A2A extension activation mechanism. The [A2A Extensions Guide](https://a2a-protocol.org/latest/topics/extensions/) defines this process. Note: You should not use `accepted_output_modes: ['a2ui']` (which is not an A2UI standard) to trigger A2UI. ### JSON-RPC and HTTP transports To activate the A2UI A2A Extension, the `X-A2A-Extensions` HTTP header includes the extension URI. **Example HTTP `SendMessageRequest`:** ```http POST /v1/messages HTTP/1.1 Host: agent.example.com X-A2A-Extensions: https://a2ui.org/a2a-extension/a2ui/v1.0 Content-Type: application/json { "message": { "parts": [ { "text": "Hello, show me the dashboard" } ] } } ``` To see how the agent parses the extension URI, see [`extension.py`](/python/a2ui_a2a/src/a2ui/a2a/extension.py). ### GRPC transport To activate the A2UI A2A Extension, the renderer adds the extension URI to A2A `sendMessageParams.metadata["X-A2A-Extensions"]`. **Example gRPC `SendMessageRequest`:** ```json { "metadata": { "X-A2A-Extensions": "https://a2ui.org/a2a-extension/a2ui/v1.0" }, "message": { "parts": [ { "text": "Hello, show me the dashboard" } ] } } ``` ## A2A Renderer to Agent Metadata Renderers attach `a2uiRendererCapabilities` and `a2uiRendererDataModel` to A2A message metadata (`message.metadata`) to communicate their state and supported catalogs. ### `a2uiRendererCapabilities` The renderer sends `message.metadata["a2uiRendererCapabilities"]`, whose value is the whole version-keyed capabilities object and MUST validate against the [Renderer Capabilities Schema](/json/renderer_capabilities.json), to advertise which catalogs the renderer supports. Additionally, the renderer determines a function's execution boundary (e.g., `rendererOnly` status) at runtime by reading its configuration from the active catalog definition. **Example `SendMessageRequest` with Capabilities:** ```json { "message": { "parts": [ { "text": "Show me the dashboard." } ], "metadata": { "a2uiRendererCapabilities": { "1.0": { "supportedCatalogIds": [ "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "https://my-company.com/a2ui/v1.0/my_custom_catalog.json" ] } } } } } ``` ### Capability scoping In A2A, renderer capabilities ride on message metadata, so they are scoped to that message's turn. An agent MUST treat a message that carries no `a2uiRendererCapabilities` entry for its version as a turn in which the renderer offers no A2UI, and MUST NOT send `application/a2ui+json` DataParts in reply to it. Activating the extension through `X-A2A-Extensions` alone does not offer catalogs. An entry keyed for another version is ignored. Agent SDKs expose this lookup as `read_renderer_capabilities`, and `a2ui.a2a` wraps it for an A2A `Message`, reading `Message.metadata` first and then each `DataPart`'s metadata in order. ### `a2uiRendererDataModel` When a surface enables Data Model Sync, the renderer sends `message.metadata["a2uiRendererDataModel"]` = [Renderer Data Model Schema](/json/renderer_data_model.json) on every message. This model provides the agent with the latest UI state. For more details, see the [Actions Guide](/docs/public/concepts/actions.md). **Example `SendMessageRequest` with Data Model:** ```json { "message": { "parts": [ { "text": "Submit the form." } ], "metadata": { "a2uiRendererDataModel": { "version": "1.0", "surfaces": { "main_surface_id": { "user_id": "12345", "email": "user@example.com" } } } } } } ``` ## Data encoding Agents and renderers encode A2UI messages as an A2A `DataPart`. To identify a `DataPart` as containing A2UI data, it must have the following metadata: - `DataPart.metadata["mimeType"] = "application/a2ui+json"` The `data` field of the `DataPart` contains a list of A2UI JSON messages (e.g., `createSurface`, `updateComponents`, `action`). It MUST be an array of messages. ### Processing Rules The `data` field contains a list of messages. This list is NOT a transactional unit. Receivers (both Renderers and Agents) MUST process messages in the list sequentially. If a single message in the list fails to validate or apply (e.g., due to a schema violation or invalid reference), the receiver SHOULD report/log the error for that specific message and MUST continue processing the remaining messages in the list. Atomicity is guaranteed only at the individual message level. However, for a better user experience, a renderer SHOULD NOT repaint the UI until all messages in the list have been processed. This prevents intermediate states from flickering to the user. ### Agent-to-renderer messages When an agent sends a message to a renderer (or another agent acting as a renderer), the `data` payload must validate against the [Agent-to-Renderer Message List Schema](/json/agent_to_renderer_list.json). Example DataPart: ```json { "data": [ { "version": "1.0", "createSurface": { "surfaceId": "example_surface", "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json" } }, { "version": "1.0", "updateComponents": { "surfaceId": "example_surface", "components": [ { "id": "root", "component": "Text", "text": "Hello!" } ] } } ], "kind": "data", "metadata": { "mimeType": "application/a2ui+json" } } ``` ### Renderer-to-agent events When a renderer (or an agent forwarding an event) sends a message to an agent, it also uses a `DataPart` with the same `application/a2ui+json` MIME type. However, the `data` payload must validate against the [Renderer-to-Agent Message List Schema](/json/renderer_to_agent_list.json). Example `action` DataPart: ```json { "data": [ { "version": "1.0", "action": { "name": "submit_form", "surfaceId": "contact_form_1", "sourceComponentId": "submit_button", "timestamp": "2026-01-15T12:00:00Z", "context": { "email": "user@example.com" } } } ], "kind": "data", "metadata": { "mimeType": "application/a2ui+json" } } ``` --- # v1.0 Basic Catalog Guide > Implementation guide for components and local functions in the A2UI v1.0 Basic Catalog. Canonical URL: https://a2ui.org/specification/v1.0-basic-catalog-implementation-guide/ > [!NOTE] Reference Catalog > The Basic Catalog is an optional, styleable reference catalog included for convenience. Applications can style these components to match their own design system or define custom domain-specific catalogs; see [How to Define a Catalog](/specification/guides/defining-a-catalog.mdx) and the [`basic_catalog.json` schema](#basic_catalog.json). --8<-- "catalogs/basic/v1/basic_catalog_implementation_guide.md" ## Catalog schema ### basic_catalog.json - Raw JSON Schema: [basic_catalog.json](https://a2ui.org/specification/v1_0/json/basic_catalog.json) --- # v1.0 Evolution Guide > Detailed migration reference covering protocol changes from A2UI v0.9 / v0.9.1 to v1.0. Canonical URL: https://a2ui.org/specification/v1.0-evolution-guide/ This document serves as a comprehensive guide to the changes between A2UI version 0.9 (including 0.9.1) and version 1.0. It details the shifts in philosophy, architecture, and implementation, providing a reference for stakeholders and developers migrating between versions. ## 1. Executive summary Version 1.0 differs from 0.9 in the following ways: - Bidirectional RPC function calls are supported via explicit role-based messages: `callRendererFunction` for agent-initiated calls on the renderer (returning `rendererFunctionResponse`), and `callAgentFunction` for renderer-initiated calls on the agent (returning `agentFunctionResponse`). Execution results or errors are returned via shared `FunctionResponse` payloads. Runtime authorized callers (`allowedCallers`) and return types are defined in catalogs and verified at runtime. - Catalogs can now be mixed within a single UI surface. Advertised `supportedCatalogIds` are mixable, allowing UI trees to combine components and functions from multiple catalogs simultaneously. - Added `$defs/FunctionCommon` in `common_types.json` composed at the envelope level via `FunctionCall` for function-level catalog overrides, matching the `ComponentCommon` pattern. - Added an optional `@catalogId` property to `ComponentCommon` and `catalogId` property to `FunctionCall` to allow individual components and function calls to explicitly declare their source catalog. - Retained `catalogId` on `createSurface` as an optional parameter that defines the default catalog for that surface. - Added extensibility metadata support via `$defs/Extensions` in `common_types.json`, allowing `ComponentCommon` (via `@metadata`), `createSurface`, `ComponentDefinition`, and `action` to convey arbitrary extension key-value pairs (with Unicode UAX #31 keys and reserved `a2ui_` namespace). - Defined explicit component and function call resolution logic: the renderer checks the component-level `@catalogId` (or function call-level `catalogId`) first, then falls back to the surface default `catalogId`. If neither is defined, the renderer errors out and does not render the component (or rejects the function call). There is no fallback to catalogs declared in capabilities. Available catalogs for a surface include both `supportedCatalogIds` and any negotiated `inlineCatalogs`, and all mixed catalogs must use the same A2UI specification version. - The `theme` property in the catalog and surface creation message is removed, along with `primaryColor`, to separate layout from branding. - Components and initial data model states can be defined directly within the `createSurface` parameters. This allows for the creation of entire UIs in a single message, rather than a create followed by separate updates. - The `functions` field in catalog meta-schemas (`catalog_definition.json`) is now formalized as a JSON object map of function name to its definition, matching the map structure used in catalog files. - Standard JSON Schema metadata fields (`$schema`, `$id`, `title`, and `description`) are supported in catalogs, preventing validation failures on inline catalogs with strict property checks. - Identifier naming rules across all catalog entities (component names, function names, and argument keys) must conform to Unicode Standard Annex #31 (UAX #31). - Reserved `@` prefix across protocol keys (`@path` for data bindings, `@call` for function calls, `@index` for template iteration, and `@metadata`, `@catalogId`, `@accessibility`, `@checks` for flat component envelope mixin keys). Plain objects containing `"path"` or `"call"` keys are treated as literal dictionary values and are no longer intercepted as dynamic directives, and custom catalog components may freely declare `"metadata"`, `"catalogId"`, `"accessibility"`, or `"checks"` properties without colliding with the component envelope. Unknown single-`@` keys (`^@([^@]|$)`) in dynamic objects are disallowed, and literal `@` keys in plain objects are escaped via prefix doubling (`"@@path"` → `"@path"`). > [!WARNING] > Do not migrate v0.9 payloads with a blanket find-and-replace of `"path":` → `"@path":` or `"call":` → `"@call":`. Only `DataBinding` and `FunctionCall` positions take the prefix. And at the component root, prefix the component envelope mixin keys (`"@metadata"`, `"@catalogId"`, `"@accessibility"`, `"@checks"`), leaving `"id"`, `"component"`, and `FunctionCall.catalogId` unprefixed. These keep their unprefixed form in v1.0: > > - `ChildList` template objects: `{"componentId": "itemTemplate", "path": "/items"}`. > - The `updateDataModel` message envelope's `path` field, which is a message parameter rather than a data binding. > - Any `"call"` that is ordinary data rather than a directive, such as the `call` entry in the basic catalog's `Icon` name enum. > - `FunctionCall.catalogId`, `createSurface.catalogId`, `createSurface.metadata`, `action.metadata`, and `ComponentDefinition.metadata`, which live inside dedicated message or directive envelopes rather than flat component property maps. > > Rewrite only the objects a renderer evaluates as a `DynamicValue`. In those objects, every key that needs a literal leading `@` must be escaped by doubling it, whatever follows the prefix: `"@@type"` for a literal `"@type"`, `"@@"` for a literal `"@"`. An unescaped leading `@` is reserved for protocol directives and is rejected. - Standardized the names of core architectural components, renaming "client" to _renderer_ and "server" to _agent_ (e.g., `server_to_client` schemas are renamed to `agent_to_renderer`), because A2UI is sometimes generated on clients, and rendering sometimes happens on servers, making those terms ambiguous. - Catalogs can now define composition constraints (`allowedParents` and `allowedChildren`) on component definitions, using `"Surface"` as the canonical root component type. Because JSON Schema cannot natively restrict child component types across a flat adjacency list of ID references, these rules allow catalogs to declare valid parent-child relationships without altering the wire format. - `CheckRule` in `common_types.json` supports dynamic structured validation result objects (`ValidationResult`) returned directly by function evaluations or data bindings (containing `valid`, `code`, `message`, and `severity`) as well as booleans (coerced to `ValidationResult`), and `message` on `CheckRule` is made optional as a fallback error message. Conversely, `DynamicBoolean` coerces a `ValidationResult` object to its `valid` boolean property. - Enhanced `AccessibilityAttributes` in `common_types.json` by adding WAI-ARIA `live` region support (`"off"`, `"polite"`, `"assertive"`) and `hidden` (`DynamicBoolean`) alongside the existing `label` (`DynamicString`) and `description` (`DynamicString`) properties, while setting `"additionalProperties": false`. Established normative specification prose requiring catalog and renderer implementations to plumb accessibility attributes and infer default screen reader semantics from visible text properties. ## 2. Changes ### 2.1. Catalog definition schema - Removed the `$defs/theme` schema and the `primaryColor` property from the Catalog schema. - Formalized the `functions` property in `catalog_definition.json` as a map object, keyed by function name. - Added `allowedCallers` (enum: `rendererOnly`, `agentOnly`, `rendererOrAgent`, default: `rendererOnly`) to `FunctionDefinition` to restrict which roles can invoke a function. - Added `requiresUserActivation` (boolean, default: `false`) to `FunctionDefinition` to declare if a function requires user gesture/activation context to execute, conditionally restricting `allowedCallers` to `rendererOnly`. - Added `$defs/ValidationResult` schema (`valid`, `code`, `message`, `severity`) as the standard schema definition for validation function return payloads. - Added an optional `instructions` field to the `Catalog` schema to embed design guidelines and component usage rules directly in the catalog, replacing the external `rules.txt` file. - Supported standard JSON Schema metadata fields (`$schema`, `$id`, `title`, and `description`) in the Catalog object definition. Since the Catalog schema restricts properties with `additionalProperties: false`, this ensures inline catalogs containing standard schema metadata do not fail schema validation. - Added a `protocolVersion` field (e.g., `"protocolVersion": "1.0"`) to catalog definition metadata (`catalog_definition.json`). If omitted, `protocolVersion` defaults to `"0.9"` for backward compatibility; catalog definitions targeting `1.0` and beyond MUST specify `"protocolVersion"`. - Enforced Unicode Standard Annex #31 (UAX #31) identifier naming constraints (`XID_Start`, `XID_Continue`) across component names, function names, and argument keys. - Added optional `allowedParents` and `allowedChildren` properties to catalog component definitions (`catalog_definition.json`) to define parent-child composition constraints. Because JSON Schema cannot natively restrict child component types across a flat adjacency list of ID references, these rules allow catalogs to declare valid component relationships. - Added `"propertyNames": { "not": { "const": "Surface" } }` to the `components` map in `catalog_definition.json` to enforce reserving `"Surface"` at the schema validation level. - Added the canonical `"Surface"` container component type in `common_types.json` to represent the top-level container of a surface for `"allowedParents": ["Surface"]` rules. The protocol reserves the `"Surface"` component name. The `createSurface` message implicitly creates `Surface` with `"child": "root"`, and you cannot modify `Surface` using `updateComponents`. These schema additions are catalog-level metadata and do not alter the wire format of component instances in `createSurface` or `updateComponents`. - Added static `metadata` (containing `extensions`) property to `ComponentDefinition` inside `catalog_definition.json`. - Introduced `x-deprecated-reason` as a human-readable explanation for a deprecated component, function, or property in addition to the standard JSON Schema `deprecated: true`. - A catalog whose `$ref` targets anything other than its own components and functions or `common_types.json` is a catalog error, reported when the catalog is loaded. Implementations never fetch a `$ref` target over the network. ### 2.2. Standard catalogs (basic) - Added `posterUrl` property to the `Video` component in `catalogs/basic/catalog.json`, allowing a preview image to be displayed before the video plays. - Added `placeholder` prop to the `TextField` component schema, and removed `TextField.validationRegexp` in favor of `@checks` using the `regex` validation function. - Made `value` required on the `TextField` component, as it is on the other input components. Without a binding, the text a user types never reaches the data model. - Added a `steps` property to the `Slider` component schema to snap values to discrete intervals. - Updated `openUrl` function definition in `catalogs/basic/catalog.json` to set `"requiresUserActivation": true`. - Refactored component definitions in `catalogs/basic/catalog.json` from `allOf: [ComponentCommon, ...]` and `unevaluatedProperties: false` to direct explicit property definitions (including explicit `component` const and `weight` props) or `$defs/Checkable`. - Added an optional `instructions` field to the `Catalog` schema (`catalogs/basic/catalog.json`) to embed Markdown guidelines/rules directly, replacing the external `rules.txt` file. - Updated return types on standard validation check functions (`required`, `regex`, `length`, `numeric`, `email`) in `catalogs/basic/catalog.json` from `"boolean"` to `"validationResult"`. When passed to `DynamicBoolean` parameters (such as in `and`, `or`, `not`), `ValidationResult` objects are coerced to their `valid` boolean. - Updated external references to standard types in `catalogs/basic/catalog.json` to use relative paths (`common_types.json#/$defs/...`) instead of version-qualified URLs. - Removed `$defs/theme` from the basic catalog. ### 2.3. Agent-to-renderer messages - Replaced `callFunction` with `callRendererFunction` message structure (`CallRendererFunctionMessage`) to support agent-to-renderer function execution requests. - Added `agentFunctionResponse` message structure (`AgentFunctionResponseMessage`) to `agent_to_renderer.json` so agents can return execution results or error payloads for renderer-initiated function calls (referencing `common_types.json#/$defs/FunctionResponse`). - Updated the `createSurface` message (`CreateSurfaceMessage`) to remove the `theme` field, allowed passing initial `components` and `dataModel` directly inside the payload, made `catalogId` an optional parameter that acts as the surface's default catalog, and added optional surface-level `metadata` (containing `extensions`). - Added an optional `@catalogId` property to `ComponentCommon` and `catalogId` property to `FunctionCall` in `common_types.json` to enable mixing catalogs and explicitly designating the catalog on individual components or function calls. - Added an optional `userMessage` (`DynamicString`) property to `Action.event` in `common_types.json`, resolved by the renderer into the outbound `action` message. - Added `$defs/Extensions` to `common_types.json` and added optional `@metadata` (containing `extensions`, `$ref: "#/$defs/Extensions"`) to `ComponentCommon`, while renaming `ComponentCommon.accessibility` to `@accessibility` and `Checkable.checks` to `@checks`. - Added `$defs/Child` (`"$ref": "#/$defs/ComponentId"`), `$defs/FunctionCommon`, and `$defs/IndexSystemFunction` to `common_types.json`. - Added the `Component` definition in `agent_to_renderer.json` (referenced by `ComponentsList`) to compose `ComponentCommon` (`$ref: "common_types.json#/$defs/ComponentCommon"`) and updated `FunctionCall` in `common_types.json` to compose `FunctionCommon` (`$ref: "#/$defs/FunctionCommon"`), so base component and function call properties are validated at the envelope level regardless of catalog structure. - Updated all protocol version references and envelopes from `v0.9` or `v0.9.1` to `1.0`. ### 2.4. Renderer-to-agent events - Added `callAgentFunction` message structure to support renderer-initiated remote function execution requests sent to the agent. - Added `rendererFunctionResponse` renderer-to-agent message structure to return function execution results (`value`) or failure payloads (`error`), referencing `common_types.json#/$defs/FunctionResponse`. - Added optional `userMessage` (string, resolved from `Action.event.userMessage`) and `metadata` (containing `extensions`, `$ref: "common_types.json#/$defs/Extensions"`) to the renderer-to-agent `action` message in `renderer_to_agent.json`. - Updated renderer `error` messages to support `functionCallId` when reporting function execution failures, enforcing mutual exclusivity with `surfaceId`. - Added `"UNALLOWED_PARENT"` and `"UNALLOWED_CHILD"` error code values to `renderer_to_agent.json` for reporting validation errors when a component is placed under an unallowed parent or an unallowed child is placed inside a container. - Updated all protocol version references from `v0.9` or `v0.9.1` to `1.0`. ### 2.5. Agent card and transport metadata - Standardized the official MIME type to `application/a2ui+json` to conform to IANA media type guidelines. - Updated capabilities namespace in transport metadata and A2A metadata parameters from `v0.9`/`v0.9.1` to `1.0`. - Clarified that `supportedCatalogIds` in `rendererCapabilities` and `agentCapabilities` are mixable within a single UI surface. - Simplified `renderer_capabilities.json` by removing local inline catalog `$defs` (`Catalog`, `FunctionDefinition`) and replacing `inlineCatalogs` items with a direct `$ref` to `https://a2ui.org/specification/v1_0/catalog_definition.json`. - Relocated extension specification from `v0_9_1/docs/a2ui_extension_specification.md` to `v1_0/extensions/a2a/docs/a2ui_extension_specification.md`. ### 2.6. Data encoding - Standardized data deletion behavior in `updateDataModel` by making the `value` property required. Setting a path's value to `null` deletes the key at that path. Omitting the `value` property is now a schema validation error. - Removed `allowedCallers` and `returnType` properties and validation constraints from `FunctionCall` and dynamic value schemas in `common_types.json`, deferring boundary checking and return type validation entirely to runtime execution. - Added built-in `@index` function (with optional `offset` parameter) under `FunctionCall` to retrieve the iteration index during list template rendering. Reserved the `@` prefix for core system context evaluations. - Updated `CheckRule` in `common_types.json` to support dynamic `ValidationResult` objects returned directly by function evaluations or data model bindings, adding the `$defs/ValidationResult` schema (`valid`, `code`, `message`, `severity`) and making `CheckRule.message` optional as a fallback message. ### 2.7. Processing rules - Defined strict component and function catalog resolution logic: 1. Check the component's explicit `@catalogId` (or function call's explicit `catalogId`). 2. If not present, check the surface's default `catalogId` provided in `createSurface`. 3. If neither exists, report an error and do not render the component (or fail the function call). There is no fallback to catalogs advertised in capabilities. Available catalogs include `supportedCatalogIds` and negotiated `inlineCatalogs`, and all mixed catalogs must use the same A2UI specification version. - Explicitly specified that `surfaceId` must be globally unique per renderer session. Creating a surface with an ID that already exists (without first deleting it) is an error. - Enforced runtime lookup of function execution boundaries and return types. If a renderer receives a remote call to a function configured as `rendererOnly` or if the function is unregistered, it rejects the call and returns an error with the code `INVALID_FUNCTION_CALL`. - Enforced catalog entity naming compliance with Unicode Standard Annex #31 (UAX #31). - Restricted `@index` evaluation scope strictly to template instantiation loops (Collection Scope). Calling `@index` outside of template iteration results in an evaluation error. - Defined `DynamicBoolean` coercion for strings, numbers, `null`, objects and arrays, applied to properties and function arguments alike. - Defined the `regex` dialect and a static safety check; unsafe or unsupported patterns fail with `EXPRESSION_ERROR`. - A `createSurface` carrying `theme` fails strict validation; relaxed renderers drop it. ### 2.8. Terminology standardization - Renamed **client** to **renderer** and **server** to **agent** globally across all protocol schemas, capabilities, and message list definitions. - Renamed all client-to-server and server-to-client JSON schema files to use renderer/agent filenames: - `server_to_client.json` -> `agent_to_renderer.json` - `client_to_server.json` -> `renderer_to_agent.json` - `client_capabilities.json` -> `renderer_capabilities.json` - `server_capabilities.json` -> `agent_capabilities.json` - `client_data_model.json` -> `renderer_data_model.json` - Renamed batch message list and list wrapper schema files: - `server_to_client_list.json` -> `agent_to_renderer_list.json` - `server_to_client_list_wrapper.json` -> `agent_to_renderer_list_wrapper.json` - `client_to_server_list.json` -> `renderer_to_agent_list.json` - `client_to_server_list_wrapper.json` -> `renderer_to_agent_list_wrapper.json` ### 2.9. Component envelope mixin keys The optional properties that any component instance can carry share the flat component object with the properties that a catalog declares for that component. In v0.9.1 these were `accessibility` (from `ComponentCommon`) and `checks` (from `Checkable`), and early v1.0 drafts added bare `catalogId` and `metadata`. Those names were therefore unavailable to catalog authors: a custom component could not declare its own `metadata` or `checks` property without colliding with the envelope. For example, a table component that declares `metadata` for pagination information had that property stripped and failed validation ([#4012](https://github.com/a2ui-project/a2ui/issues/4012)). v1.0 moves these keys into the reserved `@` namespace: | v0.9.1 | v1.0 | Defined in | | --------------- | ---------------- | ----------------- | | `accessibility` | `@accessibility` | `ComponentCommon` | | `checks` | `@checks` | `Checkable` | | (not available) | `@catalogId` | `ComponentCommon` | | (not available) | `@metadata` | `ComponentCommon` | - `id` and `component` stay unprefixed. - On v1.0 surfaces, a catalog component may declare its own `metadata`, `catalogId`, `accessibility`, or `checks` property. It is an ordinary catalog property, separate from the `@` envelope key of the same name. - The prefix applies only at the root of a component object. A single-`@` key such as `"@metadata"` inside nested dynamic data (for example, an `Action.event.context` value or a literal map) is still rejected with `INVALID_RESERVED_KEY` unless escaped as `"@@metadata"`. - Keys in dedicated message or directive envelopes stay unprefixed: `createSurface.catalogId`, `createSurface.metadata`, `FunctionCall.catalogId`, `action.metadata`, catalog-level `catalogId` and `metadata`, and `ComponentDefinition.metadata`. - Pre-1.0 protocol versions (v0.8, v0.9, and v0.9.1) are unchanged. They keep bare `accessibility` and `checks`, treat a bare `metadata` or `catalogId` on a component as an ordinary property, and reject `@`-prefixed envelope keys. During the transition from early v1.0 drafts, renderers and Core SDKs still accept a legacy bare `catalogId`, `metadata`, `accessibility`, or `checks` on a v1.0 component when the component's schema does not declare a property of that name, and reject a component that carries both the `@` key and the bare key. Agent SDKs emit only the `@` keys. New payloads should use only the `@` keys. ## 3. Migration guide This section outlines the steps required to migrate existing applications and components from version 0.9 (including 0.9.1) to version 1.0. ### For agents - Set the `version` field in all streamed JSON envelopes to `"1.0"`. - Change the MIME type of A2UI payloads in transport layers from `application/json+a2ui` to `application/a2ui+json`. - Remove the `theme` field from `createSurface` messages. You can pass initial `components`, `dataModel`, and optional `metadata.extensions` directly in the `createSurface` payload, and `catalogId` is now optional (acting as the default catalog for that surface). - When mixing components from multiple catalogs, specify the optional `@catalogId` on individual components or `catalogId` on function calls. - Prefix component-level envelope mixin keys on flat component objects with `@`: `@metadata`, `@catalogId`, `@accessibility`, and `@checks` (`id` and `component` remain unprefixed). - Formalize the `functions` property in catalog definitions as a JSON object map keyed by function name. - Remove the `$defs/theme` catalog definition and the `primaryColor` field. - Ensure all generated catalog entity names conform to UAX #31 identifier rules. - Do not include `allowedCallers` or `returnType` properties in wire-level `FunctionCall` payloads. Set static `allowedCallers`, `returnType`, and optional `requiresUserActivation` metadata in catalog function definitions where needed. - Update `Video`, `TextField`, and `Slider` components to support optional `posterUrl`, `placeholder`, and `steps` properties, and migrate any `TextField.validationRegexp` usage to `@checks` with the `regex` validation function. Update `openUrl` functions to specify `"requiresUserActivation": true`. - Set `value` on every `TextField`, usually a binding such as `{"@path": "/form/email"}`. The path does not have to exist in the data model yet: the renderer creates it when the user types. - Explicitly set values to `null` in `updateDataModel` messages to delete keys at specified paths. The `value` property is now required, and omitting it is a schema validation error. - Handle remote function execution requests from renderers (`callAgentFunction`) and respond with `agentFunctionResponse` messages. - Rename all references, constants, and endpoints mapping to `server_to_client.json` or `server_capabilities.json` to use `agent_to_renderer.json` and `agent_capabilities.json`. ### For renderers - Implement multi-catalog mixing by supporting components and function calls from any catalog in `supportedCatalogIds` or negotiated `inlineCatalogs`. All catalogs mixed within a surface must use the same A2UI specification version. - Implement component and function resolution order: (1) explicit component `@catalogId` or function call `catalogId`, (2) surface default `catalogId`, (3) error if neither exists (no fallback to capabilities). - Read component envelope mixins from `@catalogId`, `@accessibility`, `@metadata`, and `@checks` on v1.0 surfaces, and pass a catalog-declared bare `catalogId`, `accessibility`, `metadata`, or `checks` property to the component as an ordinary property (see [Component envelope mixin keys](#29-component-envelope-mixin-keys)). - Implement function execution by adding support for parsing `callRendererFunction` messages from the agent, checking authorized callers in the catalog (`allowedCallers`), rejecting invalid calls with `INVALID_FUNCTION_CALL`, and returning `rendererFunctionResponse` messages. - Implement renderer-initiated function calls by sending `callAgentFunction` messages to the agent and handling incoming `agentFunctionResponse` messages. - Support simultaneous version handling during session initialization by inspecting the `version` property (e.g., `"1.0"`) to route payloads to version-specific controllers. - Enforce surface uniqueness by raising an error if `createSurface` is received for an existing `surfaceId`. - Update error reporting to handle `functionCallId` and enforce mutual exclusivity with `surfaceId`. - Enforce Unicode identifier naming by verifying that all catalog entity names (components, functions, prop keys) conform to UAX #31 identifier rules. - Support built-in `@index` evaluation during list template rendering (Collection Scope) to provide the 0-based iteration index, adjusted by any `offset` parameter. - Support dynamic `ValidationResult` objects (`valid`, `code`, `message`, `severity`) returned by component validation check conditions, falling back to static `CheckRule.message` if present. - Rename all references, constants, and endpoints mapping to `client_to_server.json` or `client_capabilities.json` to use `renderer_to_agent.json` and `renderer_capabilities.json`. ### For catalog authors - **Set catalog protocol version**: Catalogs targeting v1.0 must specify `"protocolVersion": "1.0"` in the catalog root. - **Custom properties may reuse envelope names**: v1.0 components may declare their own `metadata`, `catalogId`, `accessibility`, or `checks` property, because the envelope mixins use the `@` keys. - **Use relative standard type references**: Replace version-qualified schema URLs (`https://a2ui.org/specification/v1_0/common_types.json#/$defs/...`) with unversioned relative targets (`common_types.json#/$defs/...`). - **Breaking changes from v0.9 to v1.0**: Catalogs authored for v0.9 cannot be used directly with v1.0 protocol runtimes due to breaking structural changes (such as composing `ComponentCommon` at the envelope level, returning `ValidationResult` objects from check functions, and declaring caller and composition constraints). - **Future forward compatibility**: Using unversioned relative references allows v1.0 catalogs to potentially work against future protocol versions without requiring catalog authors to rewrite `$ref` URLs. --- # v1.0 JSON Schemas > Canonical JSON Schemas for A2UI v1.0 wire messages, common types, catalog definitions, and capabilities. Canonical URL: https://a2ui.org/specification/v1.0/schemas/ A2UI v1.0 defines wire envelopes, shared types, catalog formats, and capability negotiation payloads across 11 canonical JSON Schemas included from `specification/v1_0/json/`. For the reference component catalog schema, see the [v1.0 Basic Catalog Guide](https://a2ui.org/specification/v1.0-basic-catalog-implementation-guide/#basic_catalog.json). For v0.9.1, see the [v0.9.1 JSON Schemas](https://a2ui.org/specification/v0.9.1/schemas/). ## Wire messages ### agent_to_renderer.json - Raw JSON Schema: [agent_to_renderer.json](https://a2ui.org/specification/v1_0/json/agent_to_renderer.json) ### renderer_to_agent.json - Raw JSON Schema: [renderer_to_agent.json](https://a2ui.org/specification/v1_0/json/renderer_to_agent.json) ## Common types ### common_types.json - Raw JSON Schema: [common_types.json](https://a2ui.org/specification/v1_0/json/common_types.json) ## Catalog definition and data model ### catalog_definition.json - Raw JSON Schema: [catalog_definition.json](https://a2ui.org/specification/v1_0/json/catalog_definition.json) ### renderer_data_model.json - Raw JSON Schema: [renderer_data_model.json](https://a2ui.org/specification/v1_0/json/renderer_data_model.json) ## Capabilities ### agent_capabilities.json - Raw JSON Schema: [agent_capabilities.json](https://a2ui.org/specification/v1_0/json/agent_capabilities.json) ### renderer_capabilities.json - Raw JSON Schema: [renderer_capabilities.json](https://a2ui.org/specification/v1_0/json/renderer_capabilities.json) ## Batching and wrappers ### agent_to_renderer_list.json - Raw JSON Schema: [agent_to_renderer_list.json](https://a2ui.org/specification/v1_0/json/agent_to_renderer_list.json) ### renderer_to_agent_list.json - Raw JSON Schema: [renderer_to_agent_list.json](https://a2ui.org/specification/v1_0/json/renderer_to_agent_list.json) ### agent_to_renderer_list_wrapper.json - Raw JSON Schema: [agent_to_renderer_list_wrapper.json](https://a2ui.org/specification/v1_0/json/agent_to_renderer_list_wrapper.json) ### renderer_to_agent_list_wrapper.json - Raw JSON Schema: [renderer_to_agent_list_wrapper.json](https://a2ui.org/specification/v1_0/json/renderer_to_agent_list_wrapper.json) --- # Roadmap > The tools A2UI is building to take a generative UI agent from its first component to production, and what is available today at each stage. Canonical URL: https://a2ui.org/roadmap/ The A2UI roadmap describes the tools we are building around the protocol to help you take a generative UI (gen-UI) agent from its first component to production, and keep improving it after launch. Shipping a gen-UI agent involves five stages that repeat. You build components, configure your agent to use them, evaluate the results, distribute the agent to your users, and monitor how they respond. What you learn while monitoring feeds the next round of component work, so the stages form a loop. The diagram below shows that loop. Select a stage to see what is available today and what is planned for it. The A2UI CLI is at the center because we plan for it to cover every stage, so you or your coding agent can run the whole loop from a terminal. :::note[Plans can change] This page describes where we are investing. Priorities and scope may change as we learn from the community. ::: ## Build cross-platform components Our goal is for you to design a component once and render it natively on every platform. **Available today** 1. [Catalogs](/concepts/catalogs/) describe your components and functions in one JSON Schema that both agents and renderers read. [How to Define a Catalog](/guides/defining-a-catalog/) covers [prompt instructions and metadata](/guides/defining-a-catalog/#prompt-instructions-and-catalog-metadata), [theming with reusable definitions](/guides/defining-a-catalog/#reusable-schema-definitions-defs-and-theming), and [schema parity tests in CI](/guides/defining-a-catalog/#schema-parity-tests-in-ci). 2. The [Basic Catalog](/concepts/catalogs/#the-basic-catalog) ships with every official renderer and takes its styling from the host framework's theme. See the [v1.0 Basic Catalog Guide](/specification/v1.0-basic-catalog-implementation-guide/). 3. Official [renderers](/concepts/sdks/#renderer-sdk) exist for React, Lit, Angular, Flutter, SwiftUI, and Jetpack Compose. See [Registering a catalog in a renderer](/concepts/catalogs/#registering-a-catalog-in-a-renderer). 4. [Universal Web Components](/guides/authoring-universal-components/) let you write a custom web component once and register it in Lit, React, and Angular. 5. The `a2ui codegen` command in the [A2UI CLI](https://github.com/a2ui-project/a2ui/tree/main/dart/a2ui_cli) generates typesafe component builders from a catalog. 6. [A2UI Composer](https://a2ui-project.github.io/composer/) is a visual editor for A2UI layouts. **On the roadmap** 1. Primitives that you style to match your design system and describe declaratively. Today a custom component still needs an implementation on each native platform; described primitives remove that step. 2. A runtime templating engine that reads a component's description and renders it natively on each platform. 3. A secure sandbox for components that need shared business logic, such as validation or pricing rules, where a declarative description is not enough. We will investigate a sandbox that lets you write that logic once and run it safely on every client. ## Configure your agents Our goal is for your agent to know when and how to use each component. **Available today** 1. The skill generator (`SkillGenerator`) in the Python and TypeScript [Agent SDKs](/concepts/sdks/#agent-sdk) builds an A2UI skill from your catalog, with grammar rules for the chosen inference format and the signatures of your components and functions. Because the skill comes from the catalog, it changes when your components change. See [Generate the skill](/concepts/overview/#generate-the-skill). 2. [Catalog negotiation](/concepts/catalogs/#catalog-negotiation-and-multi-catalog-surfaces) selects the catalogs and protocol version that each client supports. See [Using a catalog in an agent](/concepts/catalogs/#using-a-catalog-in-an-agent). 3. [Inference formats](/concepts/inference-formats/), such as Express and Direct JSON, compile model output into standard A2UI messages without client changes. 4. The Agent SDKs [validate output against live surface state](/concepts/overview/#compile-and-validate-the-output), so errors go back to the model before anything reaches the client. 5. Integrations exist for Google ADK and the A2A protocol, and [How to Integrate A2UI into an Agent](/guides/agent-integration/) covers other agent servers. **On the roadmap** 1. Official Agent SDK support for decision models, such as [Jev](https://docs.typesafe.ai/) from TypeSafe, as an inference format. A decision model answers typed questions about layouts you define in advance instead of generating the component tree token by token, which makes layout decisions up to 80x faster. [Fast, reliable A2UI](https://developers.googleblog.com/fast-reliable-a2ui/) describes the approach, and [jev2ui](https://github.com/dglazkov/jev2ui) is an open-source example that builds A2UI screens from Jev decisions (try it hosted as [Apparite](https://apparite.cnfg.ai/)). 2. Clearer error handling and diagnostics in the skill generator when a catalog or inference format cannot be compiled. 3. Skill generation in the Dart Agent SDK, which does not support it yet. 4. Integrations with more agent frameworks. ## Evaluate gen-UI quality Our goal is for you to know whether your agent picks the right components and builds them correctly. **Available today** 1. The [A2UI evaluation framework](https://github.com/a2ui-project/a2ui/tree/main/eval) is built on Inspect AI and scores model output against the project's own datasets in three stages: schema and structure validation, deterministic assertions, and an LLM judge that grades intent and quality. See the [evaluation design](https://github.com/a2ui-project/a2ui/blob/main/eval/DESIGN.md). 2. [Token and latency benchmarks](/concepts/inference-formats/#token-and-latency-benchmarks) compare inference formats. **On the roadmap** Guidance and tooling to evaluate your own agents and catalogs, combining these approaches: 1. Deterministic rules that check payloads against your catalog and scenario requirements. 2. An LLM judge that grades whether the generated UI fits the user's intent. 3. Tool usage as a judge, which looks at the agent's tool calls and context to check whether it used components correctly. ## Distribute across platforms Our goal is for one agent to reach users wherever they are. **Available today** 1. Renderers cover web and desktop (React, Lit, and Angular) and mobile (Flutter, SwiftUI, and Jetpack Compose). See [How to Integrate A2UI into a Client](/guides/client-integration/) and the [Android Jetpack Compose guide](https://developer.android.com/develop/ui/compose/agentic). 2. MCP works in both directions. You can [render A2UI inside an MCP App](/guides/a2ui-in-mcp-apps/), or [embed MCP Apps in A2UI surfaces](/guides/mcp-apps-in-a2ui/), where `McpApp` already [shares data paths with sibling A2UI components](/guides/mcp-apps-in-a2ui/#synchronizing-state-and-tool-calls). The [MCP Integration Overview](/guides/mcp-overview/) compares these with [serving A2UI layouts from an MCP server](/guides/a2ui-over-mcp/) and [calling MCP tools from A2UI](/guides/mcp-catalog/). 3. [Community Projects](/ecosystem/renderers/) cover more frameworks and platforms. **On the roadmap** 1. Better tooling for MCP Apps, which is our first focus for distribution. This starts with sharing state between A2UI components and the MCP host and server when A2UI runs inside an MCP App, to match what `McpApp` already does in the other direction. 2. Guides and examples for distributing your agent through AI assistants such as Gemini, Claude, and ChatGPT, and through messaging apps such as Microsoft Teams and Slack. ## Monitor and evolve Our goal is for your gen-UI to improve as you learn from your users. **Available today** 1. [Renderer-to-agent messages](/concepts/messages/#renderer-to-agent-messages) already report user actions and function results to your agent, which gives you events to log. See [Handle user actions](/concepts/overview/#handle-user-actions). **On the roadmap** 1. Guidance on which logs to collect from gen-UI experiences, such as rendered surfaces, user actions, errors, and abandoned flows. 2. Ways to turn those signals into design and component updates, which takes you back to [building components](#build-cross-platform-components). ## The A2UI CLI The [A2UI CLI](https://github.com/a2ui-project/a2ui/tree/main/dart/a2ui_cli) connects the stages. Today it generates typesafe component builders from a catalog with `a2ui codegen`. We plan to extend it to every stage: | Stage | What the CLI will help with | | :--------- | :--------------------------------------------------------------- | | Build | Scaffold, generate, preview, and publish components and catalogs | | Configure | Generate skills and connect catalogs to agents | | Evaluate | Validate output and run evaluation suites | | Distribute | Package agents for native apps, assistants, and MCP Apps hosts | | Monitor | Pull usage and feedback data back into your project | The CLI runs in a terminal and is scriptable, so coding agents can run it directly. The repository also includes an [A2UI agent skill](https://github.com/a2ui-project/a2ui/tree/main/skills/a2ui) that teaches coding agents to work with A2UI. Together, they let a coding agent build a component, wire it into your agent, run evaluations, ship the result, and read the feedback. ## Protocol versions The protocol evolves alongside this tooling. [`specification/versions.json`](https://github.com/a2ui-project/a2ui/blob/main/specification/versions.json) is the source of truth for version status. | Version | Status | | :------------------------------------------------------------------------ | :--------- | | [v1.0](/specification/v1.0-a2ui/) | Current | | [v0.9.1](/specification/v0.9.1-a2ui/) | Previous | | [v0.9](https://github.com/a2ui-project/a2ui/tree/main/specification/v0_9) | Previous | | [v0.8](https://github.com/a2ui-project/a2ui/tree/main/specification/v0_8) | Deprecated | ## Get involved We want to build this with the community. Share feedback, propose features, or pick up work on [GitHub](https://github.com/a2ui-project/a2ui) and in [GitHub Discussions](https://github.com/a2ui-project/a2ui/discussions). --- # Community Projects > Community-maintained renderers, catalogs, agent libraries, and tools built around the A2UI protocol. Canonical URL: https://a2ui.org/ecosystem/renderers/ Community authors and organizations maintain client renderers, domain catalogs, server libraries, and related generative UI tools built around the A2UI protocol. > **Note** Third-party projects listed here are maintained by their respective authors and organizations, not by the core A2UI maintainers. Check each repository for license terms, protocol version support, and release status. > **Note** For first-party SDKs maintained by the A2UI team, see the [SDKs](/concepts/sdks/) page covering the official [Core SDK](/concepts/sdks/#core-sdk), [Agent SDK](/concepts/sdks/#agent-sdk) (TypeScript, Python, Dart), and [Renderer SDK](/concepts/sdks/#renderer-sdk) (Flutter, React, Lit, Angular, SwiftUI, and Jetpack Compose) packages. ## Community renderers The following table lists client-side renderers implemented and maintained by the community across web, mobile, desktop, and terminal environments. | Renderer | Platform / Framework | Versions | Status | Description | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :--------------- | :----------- | :---------------------------------------------------------------------------------------------------------------- | | **[easyops-cn/a2ui-sdk](https://github.com/easyops-cn/a2ui-sdk)**
(`@a2ui-sdk/react`) · [Docs](https://a2ui-sdk.js.org/) | React (Web) | `v0.8` | Active | React renderer with Radix UI primitives and Tailwind CSS styling. | | **[lmee/A2UI-Android](https://github.com/lmee/A2UI-Android)** | Android (Compose) | `v0.8` | Active | Jetpack Compose renderer covering Android 5.0+ with 20+ components and accessibility support. | | **[sivamrudram-eng/a2ui-react-native](https://github.com/sivamrudram-eng/a2ui-react-native)** | React Native | `v0.8` | Active | React Native renderer supporting iOS and Android clients from a single TypeScript codebase. | | **[zhama/a2ui](https://www.npmjs.com/package/@zhama/a2ui)** | React (Web) | `v0.8` | Active | React web renderer published on npm. | | **[jem-computer/A2UI-react](https://github.com/jem-computer/A2UI-react)** | React (Web) | `v0.8` | Active | Community React web renderer implementation. | | **[BBC6BAE9/a2ui-swift](https://github.com/BBC6BAE9/a2ui-swift)** | Apple native (SwiftUI, UIKit, AppKit) | `v0.8`, `v0.9.x` | Active | Native Apple renderer for iOS, iPadOS, macOS, tvOS, visionOS, and watchOS following Human Interface Guidelines. | | **[shawnwang15/a2ui-vue](https://github.com/shawnwang15/a2ui-vue)**
(`a2ui-vue`) · [Docs](https://shawnwang15.github.io/a2ui-vue/en/) | Vue (Web) | `v0.8`, `v0.9.x` | Active | Vue web renderer with interactive documentation and demos. | | **[AGenUI/AGenUI](https://github.com/AGenUI/AGenUI)**
[Website](https://genui.amap.com/) | iOS, Android, HarmonyOS | `v0.9.x` | Active | Cross-platform native renderer powered by a shared C++ core, with custom Table, Carousel, and Web components. | | **[lynx-family/lynx-stack](https://github.com/lynx-family/lynx-stack/tree/main/packages/genui/a2ui)**
(`@lynx-js/genui`) · [Docs](https://lynxjs.org/react/genui/a2ui.html) · [Playground](https://lynx-stack.dev/genui/#/a2ui) | Lynx (Mobile, Web, Desktop) | `v0.9.x` | Active | ReactLynx renderer for cross-platform apps, processing messages via a MessageStore. | | **[BoteAI/a2ui](https://github.com/BoteAI/a2ui)**
(`@boteai/a2ui-render`) | React (Web) | `v0.8`, `v0.9.x` | Active | Community React renderer package. | | **[kokoro-ele/a2ui-ink](https://github.com/kokoro-ele/a2ui-ink)**
(`@evanyu/a2ui-ink`) | Terminal / CLI (Ink) | `v0.9.x` | Active | Terminal UI renderer built on Ink for rendering A2UI components in command-line environments. | | **[charmbracelet/a2tea](https://github.com/charmbracelet/a2tea)** · [Docs](https://joestump-agent.github.io/a2tea/) | Go / Terminal (Bubble Tea) | `v0.9.x` | Active | Go terminal renderer bridge mapping A2UI surfaces to interactive Bubble Tea components. | | **[muncus/a2term](https://github.com/muncus/a2term)** | Go / Terminal (Bubble Tea + A2A) | `v0.9.x` | Active | Go terminal client for A2A agents that renders interactive A2UI surfaces in keyboard-navigable tabs. | | **[yessGlory17/generative-mui](https://github.com/yessGlory17/generative-mui)**
(`@yessglory/generative-mui-react`) | React + Material UI (Web) | `v0.9.x` | Active | Material UI renderer that inherits the host app ThemeProvider, with validation checks and streaming placeholders. | | **[ChaliceForAuri/a2ui-svelte](https://github.com/ChaliceForAuri/a2ui-svelte)**
(`svelte-a2ui`) · [Demo](https://chaliceforauri.github.io/a2ui-svelte/) | Svelte 5 (Web) | `v1.0` | Active | Svelte 5 runes renderer targeting the v1.0 spec, supporting HTTP JSONL/SSE and A2A transports. | | **[Language-Lit/material3-expressive-a2ui](https://github.com/Language-Lit/material3-expressive-a2ui)**
(`@language-lit/material3-expressive-a2ui`)
[Demo & Docs](https://m3e.language-lit.com/a2ui/) | React + Material 3 Expressive (Web) | `v0.9.x` | Active | Material 3 Expressive React renderer built on `@a2ui/web_core`. | | **[uny/a2ui-compose](https://github.com/uny/a2ui-compose)**
(`dev.ynagai.a2ui:a2ui-material3`)
[Docs](https://uny.github.io/a2ui-compose/) · [Maven](https://central.sonatype.com/namespace/dev.ynagai.a2ui) | Compose Multiplatform (Android, iOS, desktop, macOS, Web) | `v1.0` | Active | Compose Multiplatform renderer for desktop, mobile, and web from a single codebase. | | **[xpert-ai/a2ui-react](https://www.npmjs.com/package/@xpert-ai/a2ui-react)**
(`@xpert-ai/a2ui-react`) | React (Web) | — | Early stage | React renderer built with ShadCN UI components. | | **[josh-english-2k18/a2ui-3d-renderer](https://github.com/josh-english-2k18/a2ui-3d-renderer)** | WebGL / Three.js | — | Experimental | Three.js and WebGL 3D renderer for A2UI surfaces. | | **[AINative-Studio/ai-kit-a2ui](https://github.com/AINative-Studio/ai-kit-a2ui)** | React (Web) | — | Early stage | React and ShadCN renderer designed for the AIKit framework. | ## Agent libraries The following server-side libraries assist in generating and validating A2UI payloads: | Project | Language | Status | Description | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------- | :----- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **[Max-Health-Inc/prefab](https://github.com/Max-Health-Inc/prefab)**
(`@maxhealth.tech/prefab`) · [Docs](https://max-health-inc.github.io/prefab/guide/a2ui) | TypeScript | Active | Server-side component library that generates A2UI payloads from a typed component tree, validates catalog compatibility, and serves A2UI over MCP as an `a2ui://` resource. | | **[satorunooshie/a2ui-go](https://github.com/satorunooshie/a2ui-go)** | Go | Active | Transport-agnostic Go (`v0.9.1`) server primitives for schema validation, message decoding, and action routing. | | **[tmc/a2ui](https://github.com/tmc/a2ui)** · [pkg.go.dev](https://pkg.go.dev/github.com/tmc/a2ui) | Go | Active | Go (`v0.9.x`) protocol type definitions and streaming JSONL parser (`a2uistream`). | ## Catalogs and component libraries The following projects publish domain-specific catalogs together with the renderers and agent code that use them: | Project | Platforms | Versions | Status | Description | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | :------- | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **[googlemaps/a2ui](https://github.com/googlemaps/a2ui)**
(`@googlemaps/a2ui`, `maui-a2ui-python`) · [Docs](https://developers.google.com/maps/ai/agentic-ui-toolkit) · [Samples](https://github.com/googlemaps-samples/a2ui) | Web (Lit), Android (Views), iOS (SwiftUI), Python agent (ADK, A2A) | `v0.9.x` | Experimental | Google Maps Platform Agentic UI toolkit. Extends the basic catalog with `GoogleMap` and `PlaceDetailsCompact` components so agents can show maps, routes, and place details. | ## Related generative UI projects The following projects are related generative UI tools that interact with or support the A2UI protocol: | Project | Platform | Description | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **[vercel-labs/json-render](https://github.com/vercel-labs/json-render)**
(`@json-render/*`) · [Docs](https://json-render.dev/) | React, Vue, Svelte, Solid, React Native | Generative UI framework by Vercel that uses its own JSON schema with Zod-based component catalogs, streaming, and cross-platform rendering; includes an [A2UI adapter](https://json-render.dev/docs/a2ui). | | **[thesysdev/openui](https://github.com/thesysdev/openui/tree/main/packages/a2ui)**
(`@openuidev/a2ui`) · [Docs](https://www.openui.com/docs/api-reference/a2ui) | Framework-agnostic core with React adapter | Experimental generative UI profile from OpenUI that follows A2UI v1.0 surface and envelope lifecycles while sending component updates as [OpenUI Lang](https://www.openui.com/docs/openui-lang) statements rather than Basic Catalog JSON (not compatible with standard A2UI agents out of the box). | ## Community showcase and contributing The A2UI project welcomes community submissions and third-party tooling. ### How to submit a project Follow these steps to submit a renderer, library, or tool for inclusion in this directory: 1. Create a fork of the [a2ui-project/a2ui](https://github.com/a2ui-project/a2ui) repository on GitHub. 2. Sign the [Google Contributor License Agreement](https://cla.developers.google.com/) if you have not already done so. 3. Add your project row to `docs/site/src/content/docs/ecosystem/renderers.mdx` with package details, platform support, supported protocol versions, and repository links. 4. Open a pull request against `a2ui-project/a2ui` with a description of the project and verification details or demo links. 5. Share your project on [GitHub Discussions](https://github.com/a2ui-project/a2ui/discussions) with an optional demo video or walkthrough. ### Listing criteria Submissions are evaluated against the following guidelines: 1. The project should have an open-source repository, typically licensed under Apache 2.0 or MIT. 2. Documentation must explicitly state supported protocol versions (`v0.8`, `v0.9` / `v0.9.1`, or `v1.0`). 3. Client renderers should support core layout, text, input, and action components. 4. The repository should include clear installation instructions and a minimal runnable example. 5. Projects should reflect active maintenance. Projects that are no longer maintained should be marked as archived. ### Code of conduct and participation All community members and contributors are expected to follow [Google's Open Source Community Guidelines](https://opensource.google/conduct/) as described in [CONTRIBUTING.md](https://github.com/a2ui-project/a2ui/blob/main/CONTRIBUTING.md). For discussions and general questions, participate in [GitHub Discussions](https://github.com/a2ui-project/a2ui/discussions). To report bugs or feature requests, use [GitHub Issues](https://github.com/a2ui-project/a2ui/issues). To report security vulnerabilities, do not open a public issue. Follow the instructions in [Google Vulnerability Reporting](https://g.co/vulnz) or contact the project maintainers privately. --- # v0.9.1 Protocol Specification > A2UI Protocol v0.9.1 specification included directly from specification/v0_9_1/docs/a2ui_protocol.md. Canonical URL: https://a2ui.org/specification/v0.9.1-a2ui/ > [!NOTE] Supersedes v0.9 > **A2UI v0.9.1** supersedes `v0.9` with minor schema and patch refinements while retaining `v0.9` / `v0_9` in wire `"version"` examples and `$id` URIs for compatibility. See the [v0.9.1 Evolution Guide](/specification/v0.9.1-a2ui/v0.9.1-evolution-guide.md), [v0.9.1 JSON Schemas](/specification/v0.9.1-a2ui/v0.9.1-schemas.mdx), and [v1.0 Protocol Specification](/specification/v0.9.1-a2ui/v1.0-a2ui.md). New projects should target v1.0, the current version. A Specification for a JSON-Based, Streaming UI Protocol. **Version:** 0.9.1 **Status:** Closed **Created:** Nov 20, 2025 **Last Updated:** Dec 3, 2025 A Specification for a JSON-Based, Streaming UI Protocol ## Introduction The A2UI Protocol is designed for dynamically rendering user interfaces from a stream of JSON objects sent from a server (Agent). Its core philosophy emphasizes a clean separation of UI structure and application data, enabling progressive rendering as the client processes each message. Communication occurs via a stream of JSON objects. The client parses each object as a distinct message and incrementally builds or updates the UI. The server-to-client protocol defines four message types: - `createSurface`: Signals the client to create a new surface and begin rendering it. - `updateComponents`: Provides a list of component definitions to be added to or updated in a specific surface. - `updateDataModel`: Provides new data to be inserted into or to replace a surface's data model. - `deleteSurface`: Explicitly removes a surface and its contents from the UI. ## Changes from previous versions Version 0.9 introduced A2UI's prompt-first protocol family. While v0.8 was optimized for LLMs that support structured output, v0.9 is designed to be embedded directly within a model's prompt. The LLM is then asked to produce JSON that matches the provided examples and schema descriptions. This "prompt-first" approach offers several advantages: 1. **Richer schema:** The protocol is no longer limited by the constraints of structured output formats. This allows for more readable, complex, and expressive component catalogs. 2. **Modularity:** The schema is now refactored into separate, more manageable components (e.g., [`common_types.json`], [`catalogs/basic/catalog.json`], [`server_to_client.json`]), improving maintainability and modularity. The main disadvantage of this approach is that it requires more complex post-generation validation, as the LLM is not strictly constrained by the schema. This requires robust error handling and correction, so the system can identify discrepancies and attempt to fix them before rendering, or request a retry or correction from the LLM. See [the evolution guide](evolution_guide.md) for a detailed explanation of the differences between v0.9 and v0.9.1. ## Protocol overview & data flow The A2UI protocol uses a unidirectional stream of JSON messages from the server to the client to describe and update the UI. The client consumes this stream, builds the UI, and renders it. User interactions are handled separately, typically by sending events to a different endpoint, which may in turn trigger new messages on the UI stream. Here is an example sequence of events (which don't have to be in exactly this order): 1. **Create Surface:** The server sends a `createSurface` message to initialize the surface. 2. **Update Surface:** Once a surface has been created, the server sends one or more `updateComponents` messages containing the definitions for all the components that will be part of the surface. 3. **Update Data Model:** Once a surface has been created, the server can send `updateDataModel` messages at any time to populate or change the data that the UI components will display. 4. **Render:** The client renders the UI for the surface, using the component definitions to build the structure and the data model to populate the content. 5. **Dynamic updates:** As the user interacts with the application or as new information becomes available, the server can send additional `updateComponents` and `updateDataModel` messages to dynamically change the UI. 6. **Delete Surface:** When a UI region is no longer needed, the server sends a `deleteSurface` message to remove it. ```mermaid sequenceDiagram participant Server participant Client Server->>+Client: 1. createSurface(surfaceId: "main") Server->>+Client: 2. updateComponents(surfaceId: "main", components: [...]) Server->>+Client: 3. updateDataModel(surfaceId: "main", path: "/user", value: "Alice") User->>+Client: Interact with UI (e.g. click button) Client->>+Server: action(name: "submit", context: {...}) Client-->>-Server: (UI is displayed) Note over Client, Server: Time passes, user interacts, or new data arrives... Server->>+Client: 4. updateComponents or updateDataModel (Dynamic Update) Note right of Client: Client re-renders the UI to reflect changes Client-->>-Server: (UI is updated) Server->>+Client: 5. deleteSurface(surfaceId: "main") Note right of Client: Client removes the UI for the "main" surface Client-->>-Server: (UI is gone) ``` ## Transport decoupling The A2UI protocol is designed to be transport-agnostic. It defines the JSON message structure and the semantic contract between the server (Agent) and the client (Renderer), but it does not mandate a specific transport layer. ### The transport contract To support A2UI, a transport layer must fulfill the following contract: 1. **Reliable delivery**: Messages must be delivered in the order they were generated. A2UI relies on stateful updates (e.g., creating a surface before updating it), so out-of-order delivery can corrupt the UI state. 2. **Message framing**: The transport must clearly delimit individual JSON envelope messages (e.g., using newlines in JSONL, WebSocket frames, or SSE events). 3. **Metadata support**: The transport must provide a mechanism to associate metadata with messages. This is critical for: - **Data model synchronization**: The `sendDataModel` feature requires the client to send the current data model state as metadata alongside user actions. - **Capabilities exchange**: Client capabilities (supported catalogs, custom components) and Server capabilities are exchanged via metadata or transport-specific handshakes (like Agent Cards in A2A or initialization in MCP). 4. **Bidirectional capability (optional)**: While the rendering stream is unidirectional (Server -> Client), interactive applications require a return channel for `action` messages (Client -> Server). ### Transport bindings While A2UI is agnostic, it is most commonly used with the following transports. #### A2A (Agent2Agent) binding [A2A (Agent-to-Agent)](https://a2a-protocol.org/latest/) is an excellent transport option for A2UI in agentic systems, extending A2A with additional payloads. A2A is uniquely capable of handling remote agent communication, and can also provide a secure and efficient transport between an agentic backend and front end application. - **Message mapping**: Each A2UI envelope (e.g., `updateComponents`) corresponds to the payload of a single A2A message Part. - **Metadata**: - **Data model**: When `sendDataModel` is active, the client's `a2uiClientDataModel` object is placed in the `metadata` field of the A2A message. - **Capabilities**: The `a2uiClientCapabilities` object is placed in the `metadata` field of every A2A `Message` sent from the client to the server. - **Context**: A2UI sessions typically map to A2A `contextId`. All messages for a set of related surfaces should share the same `contextId`. #### AG UI (Agent to User Interface) binding **[AG-UI](https://docs.ag-ui.com/introduction)** is also an excellent transport option for A2UI Agent–User Interaction protocol. AG UI provides convenient integrations into many agent frameworks and frontends. AG UI provides low latency and shared state message passing between front ends and agentic backends. #### Other transports A2UI can also be carried over: - **[MCP (Model Context Protocol)](https://modelcontextprotocol.io/docs/getting-started/intro)**: Delivered as tool outputs or resource subscriptions. - **[SSE](https://en.wikipedia.org/wiki/Server-sent_events) with [JSON RPC](https://www.jsonrpc.org/)**: Standard server-sent events for web integrations that support streaming, and JSON RPC for client-server communication. - **[WebSockets](https://en.wikipedia.org/wiki/WebSocket)**: For bidirectional, real-time sessions. - **[REST](https://cloud.google.com/discover/what-is-rest-api?hl=en)**: For simple use case, REST APIs will work but lack streaming capabilities. ## The protocol schemas A2UI v0.9.1 is defined by three interacting JSON schemas. ### Common types The [`common_types.json`] schema defines reusable primitives used throughout the protocol. - **`DynamicString` / `DynamicNumber` / `DynamicBoolean` / `DynamicStringList`**: The core of the data binding system. Any property that can be bound to data is defined as a `Dynamic*` type. It accepts either a literal value, a `path` string ([JSON Pointer]), or a `FunctionCall` (function call). - **`ChildList`**: Defines how containers hold children. It supports: - `array`: A static array of `ComponentId` component references. - `object`: A template for generating children from a data binding list (requires a template `componentId` and a data binding `path`). - **`ComponentId`**: A reference to the unique ID of another component within the same surface. ### Server to client message structure: the envelope The [`server_to_client.json`] schema is the top-level entry point. Every message streamed by the server must validate against this schema. It handles the message dispatching. ### The Basic Catalog The [`catalogs/basic/catalog.json`] schema contains the definitions for all specific UI components (e.g., `Text`, `Button`, `Row`), functions (e.g., `required`, `email`), and the theme schema. **Swappable Catalogs & Validation:** The [`server_to_client.json`] envelope schema is designed to be catalog-agnostic. It references components and themes using a placeholder filename: `catalog.json` (specifically `$ref: "catalog.json#/$defs/anyComponent"` and `$ref: "catalog.json#/$defs/theme"`). To validate A2UI messages: 1. **Basic Catalog**: Map `catalog.json` to `catalogs/basic/catalog.json`. 2. **Client Catalog**: Map `catalog.json` to your own catalog file (e.g., `my_company_catalog.json`). This indirection allows the same core envelope schema to be used with any compliant component catalog without modification. Defining your own catalog allows you to restrict the agent to using exactly the components and visual language that exist in your application. To use your own catalog, simply include it in the prompt in place of the basic catalog. It should have the same form as the basic catalog and use common elements in the [`common_types.json`] schema. ### Validator compliance when defining catalogs To ensure that automated validators can verify the integrity of your UI tree (checking that parents reference existing children), any catalog you define MUST adhere to the following strict typing rules: 1. **Single child references:** Any property that holds the ID of another component MUST use the `ComponentId` type defined in `common_types.json`. - Use: `"$ref": "common_types.json#/$defs/ComponentId"` - Do NOT use: `"type": "string"` 2. **List references:** Any property that holds a list of children or a template MUST use the `ChildList` type. - Use: `"$ref": "common_types.json#/$defs/ChildList"` Validators determine which fields represent structural links by looking for these specific schema references. If you use a raw string type for an ID, the validator will treat it as static text (like a URL or label) and will not check if the target component exists. ## Envelope message structure The envelope defines four primary message types, and every message streamed by the server must be a JSON object containing exactly one of the following keys: `createSurface`, `updateComponents`, `updateDataModel`, or `deleteSurface`. The key indicates the type of message, and these are the messages that make up each message in the protocol stream. ### `createSurface` This message signals the client to create a new surface and begin rendering it. A surface must be created before any `updateComponents` or `updateDataModel` messages can be sent to it. While typically achieved by the agent sending a `createSurface` message, an agent may skip this if it knows the surface has already been created (e.g., by another agent). Once a surface is created, its `surfaceId` and `catalogId` are fixed; to reconfigure them, the surface must be deleted and recreated. It is an error to send `createSurface` for a `surfaceId` that already exists without first deleting it. One of the components in one of the component lists MUST have an `id` of `root` to serve as the root of the component tree. **Properties:** - `surfaceId` (string, required): The unique identifier for the UI surface to be rendered. - `catalogId` (string, required): A string that uniquely identifies the catalog (components and functions) used for this surface. Note that `catalogId` is a string identifier, not a resolvable URI; while it is conventionally formatted as a URI (e.g., `https://mycompany.com/1.0/somecatalog`) to avoid naming collisions across organizations, it does not need to point to any deployed resource or downloadable file. Client and server developers must agree on shared catalogs with well-known IDs in order to build systems that are compatible with each other. - `theme` (object, optional): A JSON object containing theme parameters (e.g., `primaryColor`) defined in the catalog's theme schema. - `sendDataModel` (boolean, optional): If true, the client will send the full data model of this surface in the metadata of every message sent to the server (via the Transport's metadata mechanism). This ensures the surface owner receives the full current state of the UI alongside the user's action or query. Defaults to false. **Example:** ```json { "version": "v0.9.1", "createSurface": { "surfaceId": "user_profile_card", "catalogId": "https://a2ui.org/specification/v0_9_1/catalogs/basic/catalog.json", "theme": { "primaryColor": "#00BFFF" }, "sendDataModel": true } } ``` ### `updateComponents` This message provides a list of UI components to be added to or updated within a specific surface. The components are provided as a flat list, and their relationships are defined by ID references in an adjacency list. This message may only be sent to a surface that has already been created. Note that components may reference children or data bindings that do not yet exist; clients should handle this gracefully by rendering placeholders (progressive rendering). **Properties:** - `surfaceId` (string, required): The unique identifier for the UI surface to be updated. This is typically a name with meaning (e.g. "user_profile_card"), and it has to be unique within the context of the GenUI session. - `components` (array, required): A list of component objects. The components are provided as a flat list, and their relationships are defined by ID references in an adjacency list. **Example:** ```json { "version": "v0.9.1", "updateComponents": { "surfaceId": "user_profile_card", "components": [ { "id": "root", "component": "Column", "children": ["user_name", "user_title"] }, { "id": "user_name", "component": "Text", "text": "John Doe" }, { "id": "user_title", "component": "Text", "text": "Software Engineer" } ] } } ``` ### `updateDataModel` This message is used to send or update the data that populates the UI components. It allows the server to change the UI's content without resending the entire component structure. The `updateDataModel` message replaces the value at the specified `path` with the new content. If `path` is omitted (or is `/`), the entire data model for the surface is replaced. **Properties:** - `surfaceId` (string, required): The unique identifier for the UI surface this data model update applies to. - `path` (string, optional): A JSON Pointer to the location in the data model to update. Defaults to `/`. - `value` (any, optional): The new value for the specified path. If omitted, the key at `path` is removed. **Example:** ```json { "version": "v0.9.1", "updateDataModel": { "surfaceId": "user_profile_card", "path": "/user/name", "value": "Jane Doe" } } ``` ### `deleteSurface` This message instructs the client to remove a surface and all its associated components and data from the UI. **Properties:** - `surfaceId` (string, required): The unique identifier for the UI surface to be deleted. **Example:** ```json { "version": "v0.9.1", "deleteSurface": { "surfaceId": "user_profile_card" } } ``` ## Example Stream The following example demonstrates a complete interaction to render a Contact Form, expressed as a JSONL stream. ```jsonl {"version": "v0.9.1", "createSurface":{"surfaceId":"contact_form_1","catalogId":"https://a2ui.org/specification/v0_9_1/catalogs/basic/catalog.json"}} {"version": "v0.9.1", "updateComponents":{"surfaceId":"contact_form_1","components":[{"id":"root","component":"Card","child":"form_container"},{"id":"form_container","component":"Column","children":["header_row","name_row","email_group","phone_group","pref_group","divider_1","newsletter_checkbox","submit_button"],"justify":"start","align":"stretch"},{"id":"header_row","component":"Row","children":["header_icon","header_text"],"align":"center"},{"id":"header_icon","component":"Icon","name":"mail"},{"id":"header_text","component":"Text","text":"# Contact Us","variant":"h2"},{"id":"name_row","component":"Row","children":["first_name_group","last_name_group"],"justify":"spaceBetween"},{"id":"first_name_group","component":"Column","children":["first_name_label","first_name_field"],"weight":1},{"id":"first_name_label","component":"Text","text":"First Name","variant":"caption"},{"id":"first_name_field","component":"TextField","label":"First Name","value":{"path":"/contact/firstName"},"variant":"shortText"},{"id":"last_name_group","component":"Column","children":["last_name_label","last_name_field"],"weight":1},{"id":"last_name_label","component":"Text","text":"Last Name","variant":"caption"},{"id":"last_name_field","component":"TextField","label":"Last Name","value":{"path":"/contact/lastName"},"variant":"shortText"},{"id":"email_group","component":"Column","children":["email_label","email_field"]},{"id":"email_label","component":"Text","text":"Email Address","variant":"caption"},{"id":"email_field","component":"TextField","label":"Email","value":{"path":"/contact/email"},"variant":"shortText","checks":[{"call":"required","args":{"value":{"path":"/contact/email"}},"message":"Email is required."},{"call":"email","args":{"value":{"path":"/contact/email"}},"message":"Please enter a valid email address."}]},{"id":"phone_group","component":"Column","children":["phone_label","phone_field"]},{"id":"phone_label","component":"Text","text":"Phone Number","variant":"caption"},{"id":"phone_field","component":"TextField","label":"Phone","value":{"path":"/contact/phone"},"variant":"shortText","checks":[{"call":"regex","args":{"value":{"path":"/contact/phone"},"pattern":"^\\d{10}$"},"message":"Phone number must be 10 digits."}]},{"id":"pref_group","component":"Column","children":["pref_label","pref_picker"]},{"id":"pref_label","component":"Text","text":"Preferred Contact Method","variant":"caption"},{"id":"pref_picker","component":"ChoicePicker","variant":"mutuallyExclusive","options":[{"label":"Email","value":"email"},{"label":"Phone","value":"phone"},{"label":"SMS","value":"sms"}],"value":{"path":"/contact/preference"}},{"id":"divider_1","component":"Divider","axis":"horizontal"},{"id":"newsletter_checkbox","component":"CheckBox","label":"Subscribe to our newsletter","value":{"path":"/contact/subscribe"}},{"id":"submit_button_label","component":"Text","text":"Send Message"},{"id":"submit_button","component":"Button","child":"submit_button_label","variant":"primary","action":{"event":{"name":"submitContactForm","context":{"formId":"contact_form_1","clientTime":{"call":"formatDate","args":{"value": "2026-02-02T15:17:00Z", "format": "E MMM d, YYYY h:mm a"},"returnType":"string"},"isNewsletterSubscribed":{"path":"/contact/subscribe"}}}}}]}} {"version": "v0.9.1", "updateDataModel":{"surfaceId":"contact_form_1","path":"/contact","value":{"firstName":"John","lastName":"Doe","email":"john.doe@example.com","phone":"1234567890","preference":["email"],"subscribe":true}}} {"version": "v0.9.1", "deleteSurface":{"surfaceId":"contact_form_1"}} ``` ## Component model A2UI's component model is designed for flexibility, separating the protocol's structure from the set of available UI components. ### The component object Each object in the `components` array of an `updateComponents` message defines a single UI component. It has the following structure: - `id` (`ComponentId`, required): A unique string that identifies this specific component instance. This is used for parent-child references. - `component` (string, required): Specifies the component's type (e.g., `"Text"`). - **Component Properties**: Other properties relevant to the specific component type (e.g., `text`, `url`, `children`) are included directly in the component object. This structure is designed to be both flexible and strictly validated. ### The component catalog The set of available UI components and functions is defined in a **Catalog**. The basic catalog is defined in [`catalogs/basic/catalog.json`]. While the Basic Catalog is useful for starting out, most production applications will define their own catalog to reflect their specific design system. The server must generate messages that conform to the catalog understood by the client. #### Catalog Identification & Compatibility Each catalog is identified by a `catalogId` string. The `catalogId` is a string identifier used for matching catalogs between the client and server. While it is conventional to format catalog IDs as URIs (e.g., `https://mycompany.com/catalogs/v1`) to prevent naming collisions across organizations, a `catalogId` is not required to be a resolvable network resource. Because A2UI catalogs are represented as JSON Schema documents, catalog definitions should include both `$id` (used by JSON Schema tooling) and `catalogId` (used by A2UI SDKs and catalog negotiation), and both fields should be set to the same URI. It is up to client and server developers to agree on shared catalogs with well-known IDs in order to build systems that are compatible with each other. ### UI composition: the adjacency list model The A2UI protocol defines the UI as a flat list of components. The tree structure is built implicitly using ID references. This is known as an adjacency list model. Container components (like `Row`, `Column`, `List`, and `Card`) have properties that reference the `id` of their child component(s). The client is responsible for storing all components in a map (e.g., `Map`) and recreating the tree structure at render time. This model allows the server to send component definitions in any order. Rendering can begin as soon as the `root` component is defined, with the client filling in or updating the rest of the tree progressively as additional definitions arrive. There must be exactly one component with the ID `root` in the component tree, acting as the root of the component tree. Until that component is defined, other component updates will have no visible effect, and they will be buffered until a root component is defined. Once a root component is defined, the client is responsible for rendering the tree in the best way possible based on the available data, skipping invalid references. ```mermaid flowchart TD subgraph "Server Stream" A("updateComponents
components: [root, title, button]") end subgraph "Client-Side Buffer (Map)" C("root: {id: 'root', component: 'Column', children: ['title', 'button']}") D("title: {id: 'title', component: 'Text', text: 'Welcome'}") E("button: {id: 'button', component: 'Button', child: 'button_label'}") end subgraph "Rendered Widget Tree" F(Column) --> G(Text: 'Welcome') F --> H(Button) end A -- "Parsed and stored" --> C A -- "Parsed and stored" --> D A -- "Parsed and stored" --> E ``` ### Defining actions Interactive components (like `Button`) use an `action` property to define what happens when the user interacts with them. Actions can either trigger an event sent to the server or execute a local client-side function. #### Server actions To send an event to the server, use the `event` property within the `action` object. It requires a `name` and an optional `context`. ```json { "component": "Button", "text": "Submit", "action": { "event": { "name": "submit_form", "context": { "itemId": "123" } } } } ``` #### Local actions To execute a local function, use the `functionCall` property within the `action` object. This property references a standard `FunctionCall` object. ```json { "component": "Button", "text": "Open Link", "action": { "functionCall": { "call": "openUrl", "args": { "url": "${/url}" } } } } ``` ## Data model representation: binding, scope This section describes how UI components **represent** and reference data from the Data Model. A2UI relies on a strictly defined relationship between the UI structure (Components) and the state (Data Model), defining the mechanics of path resolution, variable scope during iteration. ### Path resolution & scope Data bindings in A2UI are defined using **JSON Pointers** ([RFC 6901]). How a pointer is resolved depends on the current **Evaluation Scope**. > [!NOTE] > A2UI extends JSON Pointer to support **Relative Paths** that do not start with a forward slash `/`. This is a deviation from strict RFC 6901 to support template-based list rendering. > **Note on progressive rendering:** During the initial streaming phase, data paths may resolve to `undefined` if the `updateDataModel` message containing that data has not yet arrived. Renderers should handle `undefined` values gracefully (e.g., by treating them as empty strings or showing a loading indicator) to support progressive rendering. #### The root scope By default, all components operate in the **Root Scope**. - Paths starting with `/` (e.g., `/user/profile/name`) are **Absolute Paths**. They always resolve from the root of the Data Model, regardless of where the component is nested in the UI tree. #### Collection scopes (relative paths) When a container component (such as `Column`, `Row`, or `List`) utilizes the **Template** feature of `ChildList`, it creates a new **Child Scope** for each item in the bound array. - **Template definition:** When a container binds its children to a path (e.g., `path: "/users"`), the client iterates over the array found at that location. - **Scope instantiation:** For every item in the array, the client instantiates the template component. - **Relative resolution:** Inside these instantiated components, any path that **does not** start with a forward slash `/` is treated as a **Relative Path**. - A relative path `firstName` inside a template iterating over `/users` resolves to `/users/0/firstName` for the first item, `/users/1/firstName` for the second, etc. - It is an error to use a non-numeric index on a path segment that refers to an array. - **Mixing scopes:** Components inside a Child Scope can still access the Root Scope by using an Absolute Path. #### Example: scope resolution **Data model:** ```json { "company": "Acme Corp", "employees": [ {"name": "Alice", "role": "Engineer"}, {"name": "Bob", "role": "Designer"} ] } ``` **Component definition:** ```json { "id": "employee_list", "component": "List", "children": { "path": "/employees", "componentId": "employee_card_template" } }, { "id": "employee_card_template", "component": "Column", "children": ["name_text", "company_text"] }, { "id": "name_text", "component": "Text", "text": { "path": "name" } // "name" is Relative. Resolves to /employees/N/name }, { "id": "company_text", "component": "Text", "text": { "path": "/company" } // "/company" is Absolute. Resolves to "Acme Corp" globally. } ``` #### Type conversion When a non-string value is interpolated, the client converts it to a string: - **Numbers/Booleans**: Standard string representation. - **null/undefined**: An empty string `""`. - **Objects/Arrays**: Stringified as JSON to ensure consistency across different client implementations. ### Two-way binding & input components Interactive components that accept user input (`TextField`, `CheckBox`, `Slider`, `ChoicePicker`, `DateTimeInput`) establish a **Two-Way Binding** with the Data Model. #### The read/write contract Unlike static display components (like `Text`), input components modify the client-side data model immediately upon user interaction. 1. **Read (Model -> View):** When the component renders, it reads its value from the bound `path`. If the Data Model is updated via `updateDataModel`, the component re-renders to reflect the new value. 2. **Write (View -> Model):** When the user interacts with the component (e.g., types a character, toggles a box), the client **immediately** updates the value at the bound `path` in the local Data Model. #### Reactivity Because the local Data Model is the single source of truth, updates from input components are **reactive**. - If a `TextField` is bound to `/user/name`, and a separate `Text` label is also bound to `/user/name`, the label must update in real-time as the user types in the text field. #### Server synchronization It is critical to note that Two-Way Binding is **local to the client**. - User inputs (keystrokes, toggles) do **not** automatically trigger network requests to the server. - The updated state is sent to the server only when a specific **User Action** is triggered (e.g., a `Button` click). - When an `action` is dispatched, the `context` property of the action can reference the modified data paths to send the user's input back to the server. #### Example: form submission pattern 1. **Bind:** `TextField` is bound to `/formData/email`. 2. **Interact:** User types "jane@example.com". The local model at `/formData/email` is updated. 3. **Action:** A "Submit" button has the following action definition: ```json "action": { "event": { "name": "submit_form", "context": { "email": { "path": "/formData/email" } } } } ``` 4. **Send:** When clicked, the client resolves `/formData/email` (getting "jane@example.com") and sends it in the `action` payload. ## Data model updates: synchronization and convergence While the sections above describe how components reference data, this section defines how the Data Model itself is **updated** and synchronized. To support reliable data synchronization between the Renderer and the Agent that created the surface, the A2UI protocol uses a simple synchronization mechanism controlled by the `sendDataModel` property in the `createSurface` message. ### Server to client updates The server sends `updateDataModel` messages to modify the client's data model. These updates follow strict upsert semantics: - If the path exists, the value is updated. - If the path does not exist, the value is created. - If the value is omitted (or set to `undefined`), the key is removed. For arrays, the value at the index is set to `undefined`, preserving length. The `updateDataModel` message replaces the value at the specified `path` with the new content. If `path` is omitted (or is `/`), the entire data model for the surface is replaced. **Properties:** - `surfaceId` (string, required): The ID of the surface to update. - `path` (string, optional): A JSON Pointer to the location in the data model to update. Defaults to `/`. - `value` (any, optional): The new value for the specified path. If omitted, the key at `path` is removed. **Examples:** _Update a specific field:_ ```json { "version": "v0.9.1", "updateDataModel": { "surfaceId": "surface_123", "path": "/user/firstName", "value": "Alice" } } ``` _Remove a field:_ ```json { "version": "v0.9.1", "updateDataModel": { "surfaceId": "surface_123", "path": "/user/tempData" } } ``` _Replace the entire data model:_ ```json { "version": "v0.9.1", "updateDataModel": { "surfaceId": "surface_123", "value": { "user": {"firstName": "Alice", "lastName": "Smith"}, "preferences": {"theme": "dark"} } } } ``` ### Client to server updates When `sendDataModel` is set to `true` for a surface, the client automatically appends the **entire data model** of that surface to the metadata of every message (such as `action` or user query) sent to the server that created the surface. The data model is included using the transport's metadata facility (e.g., the `metadata` field in A2A or a header in HTTP). The payload follows the schema in [`client_data_model.json`](/specification/json/client_data_model.json). - **Targeted Delivery**: The data model is sent exclusively to the server that created the surface. Data cannot leak to other agents or servers. - **Trigger:** Data is sent only when a client-to-server message is triggered (e.g., by a user action like a button click). Passive data changes (like typing in a text field) do not trigger a network request on their own; they simply update the local state, which will be sent with the next action. - **Payload:** The data model is included in the transport metadata, tagged by its `surfaceId`. - **Convergence:** The server treats the received data model as the current state of the client at the time of the action. ## Client-side logic & validation A2UI v0.9 generalizes client-side logic into **Functions**. These can be used for validation, data transformation, and dynamic property binding. ### Registered functions The client supports a set of named **Functions** (e.g., `required`, `regex`, `email`, `add`, `concat`) which are defined in the JSON schema (e.g. `catalogs/basic/catalog.json`) alongside the component definitions. The server references these functions by name in `FunctionCall` objects. This avoids sending executable code. The client determines each function's execution boundary at runtime by reading its configuration from the active catalog definition. Input components (like `TextField`, `CheckBox`) can define a list of checks. Each failure produces a specific error message that can be displayed when the component is rendered. Note that for validation checks, the function must return a boolean. ```json "checks": [ { "call": "required", "args": { "value": { "path": "/formData/zip" } }, "message": "Zip code is required" }, { "call": "regex", "args": { "value": { "path": "/formData/zip" }, "pattern": "^[0-9]{5}$" }, "message": "Must be a 5-digit zip code" } ] ``` ### Example: button validation Buttons can also define `checks`. If any check fails, the button is automatically disabled. This allows the button's state to depend on the validity of data in the model. ```json { "component": "Button", "text": "Submit", "checks": [ { "condition": { "call": "and", "args": { "values": [ { "call": "required", "args": {"value": {"path": "/formData/terms"}} }, { "call": "or", "args": { "values": [ { "call": "required", "args": {"value": {"path": "/formData/email"}} }, { "call": "required", "args": {"value": {"path": "/formData/phone"}} } ] } } ] } }, "message": "You must accept terms AND provide either email or phone" } ] } ``` ## Basic Component Catalog The [`catalogs/basic/catalog.json`] provides the baseline set of components and functions. ### Components | Component | Description | | :---------------- | :------------------------------------------------------------------------------------------ | | **Text** | Displays text. Supports simple Markdown. | | **Image** | Displays an image from a URL. | | **Icon** | Displays a system-provided icon from a predefined list. | | **Video** | Displays a video from a URL. | | **AudioPlayer** | A player for audio content from a URL. | | **Row** | A horizontal layout container. | | **Column** | A vertical layout container. | | **List** | A scrollable list of components. | | **Card** | A container with card-like styling. | | **Tabs** | A set of tabs, each with a title and child component. | | **Divider** | A horizontal or vertical dividing line. | | **Modal** | A dialog that appears over the main content triggered by a button in the main content. | | **Button** | A clickable button that dispatches an action. Supports 'primary' and 'borderless' variants. | | **CheckBox** | A checkbox with a label and a boolean value. | | **TextField** | A field for user text input. | | **DateTimeInput** | An input for date and/or time. | | **ChoicePicker** | A component for selecting one or more options. | | **Slider** | A slider for selecting a numeric value within a range. | ### Functions | Function | Description | | :----------------- | :----------------------------------------------------------------------- | | **required** | Checks that the value is not null, undefined, or empty. | | **regex** | Checks that the value matches a regular expression string. | | **length** | Checks string length constraints. | | **numeric** | Checks numeric range constraints. | | **email** | Checks that the value is a valid email address. | | **formatString** | Does string interpolation of data model values and registered functions. | | **formatNumber** | Formats a number with grouping and precision. | | **formatCurrency** | Formats a number as a currency string. | | **formatDate** | Formats a date/time using a pattern. | | **pluralize** | Selects a localized string based on a numeric count. | | **openUrl** | Opens a URL in a browser. | | **and** | Logical AND operation on a list of boolean values. | | **or** | Logical OR operation on a list of boolean values. | | **not** | Logical NOT operation on a boolean value. | ### Theme The basic catalog defines the following theme properties that can be set in the `createSurface` message: | Property | Type | Description | | :------------------- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **primaryColor** | String | The primary brand color used for highlights throughout the UI (e.g., primary buttons, active borders). The renderer may generate variants, such as lighter shades, as needed. Format: Hexadecimal code (e.g., '#00BFFF'). | | **iconUrl** | URI | A URL for an image (e.g., logo or avatar) that identifies the agent or tool associated with the surface. | | **agentDisplayName** | String | Text to be displayed next to the surface to identify the agent or tool that created it (e.g. "Weather Bot"). | #### Identity and attribution The `iconUrl` and `agentDisplayName` fields are used to provide attribution to the user, identifying which sub-agent or tool is responsible for a particular UI surface. In multi-agent systems or orchestrators, the orchestrator is responsible for setting or validating these fields. This ensures that the identity displayed to the user matches the actual agent server being contacted, preventing malicious agents from impersonating trusted services. For example, an orchestrator might overwrite these fields with the verified identity of the sub-agent before forwarding the `createSurface` message to the client. ### The `formatString` function The `formatString` function supports embedding dynamic expressions directly within string properties. This allows for mixing static text with data model values and function results. #### `formatString` syntax Interpolated expressions are enclosed in `${...}`. To include a literal `${` in a string, it must be escaped as `\${`. #### `formatString` data model binding Values from the data model can be interpolated using their JSON Pointer path. - `${/user/profile/name}`: Absolute path. - `${firstName}`: Relative path (resolved against the current collection scope). **Example:** ```json { "id": "user_welcome", "component": "Text", "text": { "call": "formatString", "args": { "value": "Hello, ${/user/firstName}! Welcome back to ${/appName}." } } } ``` #### `formatString` client-side functions Results of client-side functions can be interpolated. Function calls are identified by the presence of parentheses `()`. - `${now()}`: A function with no arguments. - `${formatDate(value:${/currentDate}, format:'yyyy-MM-dd')}`: A function with named arguments. Arguments can be **Literals** (quoted strings, numbers, or booleans), or **Nested Expressions**. #### `formatString` nested interpolation Expressions can be nested using additional `${...}` wrappers inside an outer expression to make bindings explicit or to chain function calls. - **Explicit Binding**: `${formatDate(value:${/currentDate}, format:'yyyy-MM-dd')}` - **Nested Functions**: `${upper(${now()})}` #### `formatString` type conversion When a non-string value is interpolated, the client converts it to a string: - **Numbers/Booleans**: Standard string representation. - **Null/Undefined**: An empty string `""`. - **Objects/Arrays**: Stringified as JSON to ensure consistency across different client implementations. ## Usage pattern: the prompt-generate-validate loop The A2UI protocol is designed to be used in a three-step loop with a Large Language Model: 1. **Prompt**: Construct a prompt for the LLM that includes: - The desired UI to be generated. - The A2UI JSON schema, including the component catalog. - Examples of valid A2UI JSON. 2. **Generate**: Send the prompt to the LLM and receive the generated JSON output. 3. **Validate**: Validate the generated JSON against the A2UI schema. If the JSON is valid, it can be sent to the client for rendering. If it is invalid, the errors can be reported back to the LLM in a subsequent prompt, allowing it to self-correct. This loop allows for a high degree of flexibility and robustness, as the system can leverage the generative capabilities of the LLM while still enforcing the structural integrity of the UI protocol. ### Standard validation error format If validation fails, the client (or the system acting on behalf of the client) should send an `error` message back to the LLM. To ensure the LLM can understand and correct the error, use the following standard format within the `error` message payload: - `code` (string, required): Must be `"VALIDATION_FAILED"`. - `surfaceId` (string, required): The ID of the surface where the error occurred. - `path` (string, required): The JSON pointer to the field that failed validation (e.g. `/components/0/text`). - `message` (string, required): A short one-sentence description of why validation failed. **Example error message:** ```json { "error": { "code": "VALIDATION_FAILED", "surfaceId": "user_profile_card", "path": "/components/0/text", "message": "Expected stringOrPath, got integer" } } ``` ## Client-to-server messages The protocol also defines messages that the client can send to the server, which are defined in the [`client_to_server.json`] schema. These are used for handling user interactions and reporting client-side information. ### `action` This message is sent when the user interacts with a component that has an `action` defined, such as a `Button`. **Properties:** - `name` (string, required): The name of the action. - `surfaceId` (string, required): The ID of the surface where the action originated. - `sourceComponentId` (string, required): The ID of the component that triggered the action. - `timestamp` (string, required): An ISO 8601 timestamp. - `context` (object, required): A JSON object containing any context provided in the component's `action` property. ### Capabilities & metadata In A2UI v0.9, capabilities and other metadata are exchanged via **Transport metadata** or initialization payloads (e.g., A2A metadata, Agent Cards, or MCP initialization), rather than as first-class A2UI messages. #### Server capabilities A server (or agent) advertises its capabilities using the [`server_capabilities.json`] schema. This indicates which catalogs it can generate UI for, and whether it accepts inline catalogs from the client. The exact mechanism depends on the transport (e.g., the `params` object in an A2A AgentCard, or server capabilities in MCP). #### Client capabilities The `a2uiClientCapabilities` object in the A2A `Message`'s `metadata` field follows the [`client_capabilities.json`] schema. **Properties:** - `supportedCatalogIds` (array of strings, required): String identifiers of supported catalogs. - `inlineCatalogs`: An array of inline catalog definitions provided directly by the client (useful for custom or ad-hoc components and functions). #### Client data model When `sendDataModel` is enabled for a surface, the client includes the `a2uiClientDataModel` object in the metadata, following the [`client_data_model.json`] schema. **Properties:** - `surfaces` (object, required): A map of surface IDs to their current data models. ### `error` This message is used to report a client-side error to the server. [`catalogs/basic/catalog.json`]: ../catalogs/basic/catalog.json [`common_types.json`]: ../json/common_types.json [`server_to_client.json`]: ../json/server_to_client.json [`client_to_server.json`]: ../json/client_to_server.json [`server_capabilities.json`]: ../json/server_capabilities.json [`client_capabilities.json`]: ../json/client_capabilities.json [`client_data_model.json`]: ../json/client_data_model.json [JSON Pointer]: https://datatracker.ietf.org/doc/html/rfc6901 [RFC 6901]: https://datatracker.ietf.org/doc/html/rfc6901 --- # v0.9.1 A2A Extension > A2UI v0.9.1 extension specification for the Agent-to-Agent (A2A) protocol. Canonical URL: https://a2ui.org/specification/v0.9.1-a2ui-extension-specification/ ## Overview This document is intended for developers implementing the A2UI A2A extension. The extension adds A2UI v0.9.1 support to A2A, a format for agents to send streaming, interactive user interfaces to clients. Note that A2UI extension activation is optional as clients and agents can negotiate A2UI support using A2A `message.metadata["a2uiClientCapabilities"]` which is attached to every A2A message from the client and contains the supported protocol version and catalogs. Agents advertising A2UI support in their AgentCard is encouraged as clients may rely on it to determine if they should send `message.metadata["a2uiClientCapabilities"]`, however it is not explicitly required. ## Extension URI The URI of this extension is https://a2ui.org/a2a-extension/a2ui/v0.9.1 This URI is the canonical way to communicate protocol versioning between clients and agents. The extension URI explicitly encodes the version (e.g., `v0.9.1`). A client requesting this specific URI indicates it supports the v0.9.1 schema format. ## Agent Card Agents are encouraged to advertise their A2UI capabilities in their AgentCard within the `AgentCapabilities.extensions` list. This advertisement is optional, but it informs the client whether to send `message.metadata["a2uiClientCapabilities"]`. The `params` object defines the agent's specific UI support and corresponds directly to the [Server Capabilities Schema](/specification/json/server_capabilities.json). Example AgentCard payload: ```json { "name": "Dashboard Agent", "description": "Agent capable of generating dynamic UI dashboards.", "capabilities": { "extensions": [ { "uri": "https://a2ui.org/a2a-extension/a2ui/v0.9.1", "description": "Ability to render A2UI v0.9.1", "required": false, "params": { "supportedCatalogIds": [ "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json", "https://my-company.com/a2ui/v0.9/my_custom_catalog.json" ], "acceptsInlineCatalogs": true } } ] } } ``` The `params` object corresponds to the `v0.9.1` object in the `server_capabilities.json` schema: - `params.supportedCatalogIds` (optional): An array of strings, where each string is an ID identifying a Catalog Definition Schema that the agent can generate. This is not necessarily a resolvable URI. - `params.acceptsInlineCatalogs` (optional): A boolean indicating if the agent can accept an `inlineCatalogs` array in the client's `a2uiClientCapabilities`. If omitted, this defaults to `false`. ## A2A Extension activation Activating the A2UI extension is optional. Clients and agents can negotiate A2UI support using `message.metadata["a2uiClientCapabilities"]` and A2A `DataPart.metadata["mimeType"] = "application/a2ui+json"`. Specifically: - If a client includes `message.metadata["a2uiClientCapabilities"]`, the agent can use this object to determine the supported A2UI protocol version and catalogs. - If an agent returns A2A `DataPart.metadata["mimeType"] = "application/a2ui+json"`, the client knows the payload contains A2UI messages. While explicit activation is not required, clients can still explicitly activate the extension using the transport-defined A2A extension activation mechanism. The [A2A Extensions Guide](https://a2a-protocol.org/latest/topics/extensions/) defines this process. Note: You should not use `accepted_output_modes: ['a2ui']` (which is not an A2UI standard) to trigger A2UI. ### JSON-RPC and HTTP transports To activate the A2UI A2A Extension, the `X-A2A-Extensions` HTTP header includes the extension URI. **Example HTTP `SendMessageRequest`:** ```http POST /v1/messages HTTP/1.1 Host: agent.example.com X-A2A-Extensions: https://a2ui.org/a2a-extension/a2ui/v0.9.1 Content-Type: application/json { "message": { "parts": [ { "text": "Hello, show me the dashboard" } ] } } ``` To see how the agent parses the extension URI, see [`extension.py`](/python/a2ui_a2a/src/a2ui/a2a/extension.py). ### GRPC transport To activate the A2UI A2A Extension, the client adds the extension URI to A2A `sendMessageParams.metadata["X-A2A-Extensions"]`. **Example gRPC `SendMessageRequest`:** ```json { "metadata": { "X-A2A-Extensions": "https://a2ui.org/a2a-extension/a2ui/v0.9.1" }, "message": { "parts": [ { "text": "Hello, show me the dashboard" } ] } } } ``` ## A2A Client to Server Metadata Clients attach `a2uiClientCapabilities` and `a2uiClientDataModel` to A2A messages to communicate their state and supported catalogs. ### `a2uiClientCapabilities` The client sends `sendMessageRequest.message["a2uiClientCapabilities"]` = [Client Capabilities Schema](/specification/json/client_capabilities.json) to advertise which catalogs the renderer supports. **Example `SendMessageRequest` with Capabilities:** ```json { "message": { "parts": [ { "text": "Show me the dashboard." } ], "metadata": { "a2uiClientCapabilities": { "v0.9.1": { "supportedCatalogIds": [ "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json", "https://my-company.com/a2ui/v0.9/my_custom_catalog.json" ] } } } } } ``` ### `a2uiClientDataModel` When a surface enables Data Model Sync, the client sends `sendMessageRequest.message["a2uiClientDataModel"]` = [Client Data Model Schema](/specification/json/client_data_model.json) on every message. This model provides the agent with the latest UI state. For more details, see the [Actions Guide](/docs/public/concepts/actions.md). **Example `SendMessageRequest` with Data Model:** ```json { "message": { "parts": [ { "text": "Submit the form." } ], "metadata": { "a2uiClientDataModel": { "version": "v0.9.1", "surfaces": { "main_surface_id": { "user_id": "12345", "email": "user@example.com" } } } } } } ``` ## Data encoding Agents and clients encode A2UI messages as an A2A `DataPart`. To identify a `DataPart` as containing A2UI data, it must have the following metadata: - `DataPart.metadata["mimeType"] = "application/a2ui+json"` The `data` field of the `DataPart` contains a list of A2UI JSON messages (e.g., `createSurface`, `updateComponents`, `action`). It MUST be an array of messages. ### Processing Rules The `data` field contains a list of messages. This list is NOT a transactional unit. Receivers (both Clients and Agents) MUST process messages in the list sequentially. If a single message in the list fails to validate or apply (e.g., due to a schema violation or invalid reference), the receiver SHOULD report/log the error for that specific message and MUST continue processing the remaining messages in the list. Atomicity is guaranteed only at the individual message level. However, for a better user experience, a renderer SHOULD NOT repaint the UI until all messages in the list have been processed. This prevents intermediate states from flickering to the user. ### Server-to-client messages When an agent sends a message to a client (or another agent acting as a client/renderer), the `data` payload must validate against the [Server-to-Client Message List Schema](/specification/json/server_to_client_list.json). Example DataPart: ```json { "data": [ { "version": "v0.9.1", "createSurface": { "surfaceId": "example_surface", "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } }, { "version": "v0.9.1", "updateComponents": { "surfaceId": "example_surface", "components": [ { "id": "root", "component": "Text", "text": "Hello!" } ] } } ], "kind": "data", "metadata": { "mimeType": "application/a2ui+json" } } ``` ### Client-to-server events When a client (or an agent forwarding an event) sends a message to an agent, it also uses a `DataPart` with the same `application/a2ui+json` MIME type. However, the `data` payload must validate against the [Client-to-Server Message List Schema](/specification/json/client_to_server_list.json). Example `action` DataPart: ```json { "data": [ { "version": "v0.9.1", "action": { "name": "submit_form", "surfaceId": "contact_form_1", "sourceComponentId": "submit_button", "timestamp": "2026-01-15T12:00:00Z", "context": { "email": "user@example.com" } } } ], "kind": "data", "metadata": { "mimeType": "application/a2ui+json" } } ``` --- # v0.9.1 Basic Catalog Guide > Implementation guide for components and client-side functions in the A2UI v0.9.1 Basic Catalog. Canonical URL: https://a2ui.org/specification/v0.9.1-basic-catalog-implementation-guide/ > [!NOTE] Reference Catalog > The Basic Catalog is an optional, styleable reference catalog included for convenience (`v0.9.1` supersedes `v0.9` while keeping the `v0_9` catalog URI). Applications can style these components to match their own design system or define custom domain-specific catalogs; see [How to Define a Catalog](/specification/guides/defining-a-catalog.mdx) and the [`basic_catalog.json` schema](#basic_catalog.json). --8<-- "specification/v0_9_1/docs/basic_catalog_implementation_guide.md" ## Catalog schema ### basic_catalog.json - Raw JSON Schema: [basic_catalog.json](https://a2ui.org/specification/v0_9_1/json/basic_catalog.json) --- # v0.9.1 Evolution Guide > Patch evolution notes covering protocol refinements between A2UI v0.9 and v0.9.1. Canonical URL: https://a2ui.org/specification/v0.9.1-evolution-guide/ This document describes the changes between A2UI version 0.9 and version 0.9.1. For changes from version 0.8 to 0.9, please refer to the [v0.9 Evolution Guide](/v0_9/docs/evolution_guide.md). ## 1. Executive Summary Version 0.9.1 is a minor refinement of the v0.9 specification. The key changes are: 1. **MIME Type Standardization**: Standardized all references to the A2UI message payload MIME type to `application/a2ui+json` (replacing the legacy `application/json+a2ui`). 2. **Surface ID Uniqueness Relaxation**: Relaxed the requirement for `surfaceId` to be globally unique for the renderer's lifetime. It must still be unique among currently active surfaces, and it remains an error to call `createSurface` on an existing surface without first deleting it, but the explicit lifetime uniqueness constraint is removed. ## 2. Detailed Changes ### 2.1. MIME Type Standardization In previous draft iterations of the v0.9 extension specification, the legacy `application/json+a2ui` MIME type was referenced. Version 0.9.1 standardizes all specification guides and extension metadata examples to use `application/a2ui+json`. ### 2.2. Surface ID Requirements In `a2ui_protocol.md`, the definition and requirements for `surfaceId` were updated: - The restriction that `surfaceId` "must be globally unique for the renderer's lifetime" is removed. - Instead, the uniqueness constraint is limited to active surfaces: it remains an error to send `createSurface` for a `surfaceId` that already exists without first deleting it. ## 3. Migration Guide Since v0.9.1 is fully compatible with v0.9 payloads (the version fields in schemas accept both `"v0.9"` and `"v0.9.1"`), clients and servers can upgrade seamlessly. - **Action for Implementers**: Update any hardcoded MIME type references from `application/json+a2ui` to `application/a2ui+json`. --- # v0.9.1 JSON Schemas > Canonical JSON Schemas for A2UI v0.9.1 wire messages, common types, data models, and capabilities. Canonical URL: https://a2ui.org/specification/v0.9.1/schemas/ A2UI v0.9.1 defines wire messages, shared types, data models, and capability negotiation payloads across 10 canonical JSON Schemas included from `specification/v0_9_1/json/`. Version `v0.9.1` supersedes `v0.9` while retaining `v0_9` in `$id` URIs for backward compatibility. For the reference component catalog schema, see the [v0.9.1 Basic Catalog Guide](https://a2ui.org/specification/v0.9.1-basic-catalog-implementation-guide/#basic_catalog.json). For the current version, see the [v1.0 JSON Schemas](https://a2ui.org/specification/v1.0/schemas/). ## Wire messages ### server_to_client.json - Raw JSON Schema: [server_to_client.json](https://a2ui.org/specification/v0_9_1/json/server_to_client.json) ### client_to_server.json - Raw JSON Schema: [client_to_server.json](https://a2ui.org/specification/v0_9_1/json/client_to_server.json) ## Common types ### common_types.json - Raw JSON Schema: [common_types.json](https://a2ui.org/specification/v0_9_1/json/common_types.json) ## Data model ### client_data_model.json - Raw JSON Schema: [client_data_model.json](https://a2ui.org/specification/v0_9_1/json/client_data_model.json) ## Capabilities ### server_capabilities.json - Raw JSON Schema: [server_capabilities.json](https://a2ui.org/specification/v0_9_1/json/server_capabilities.json) ### client_capabilities.json - Raw JSON Schema: [client_capabilities.json](https://a2ui.org/specification/v0_9_1/json/client_capabilities.json) ## Batching and wrappers ### server_to_client_list.json - Raw JSON Schema: [server_to_client_list.json](https://a2ui.org/specification/v0_9_1/json/server_to_client_list.json) ### client_to_server_list.json - Raw JSON Schema: [client_to_server_list.json](https://a2ui.org/specification/v0_9_1/json/client_to_server_list.json) ### server_to_client_list_wrapper.json - Raw JSON Schema: [server_to_client_list_wrapper.json](https://a2ui.org/specification/v0_9_1/json/server_to_client_list_wrapper.json) ### client_to_server_list_wrapper.json - Raw JSON Schema: [client_to_server_list_wrapper.json](https://a2ui.org/specification/v0_9_1/json/client_to_server_list_wrapper.json)