# Agent to UI Protocol (A2UI) with Agent Development Kit (ADK)

Author: [Mete Atamel](https://atamel.dev/) | Published: 2026-03-30 | Topics: GenAI, Agents, A2UI, ADK | Canonical: https://atamel.dev/posts/2026/03-30_a2ui_with_adk/


![A2UI and ADK](https://github.com/meteatamel/genai-beyond-basics/raw/main/samples/protocols/a2ui/images/a2ui.png)

In my [Agent Protocols - MCP, A2A, A2UI, AG-UI](https://atamel.dev/posts/2026/03-17_agent_protocols_mcp_a2a_a2ui_agui/)
post, I provided a high-level overview of some of the main agent protocols: MCP, A2A, A2UI, and AG-UI. In the
[Agent-User Interaction Protocol (AG-UI) with Agent Development Kit
(ADK)](https://atamel.dev/posts/2026/03-23_agui_with_adk/) post, I zoomed into the AG-UI protocol and showed how it can
be used with Google's Agent Development Kit (ADK). 

In today's post, I'll zoom into the A2UI protocol and show how it can be used with Google's Agent Development Kit (ADK).

## Agent to UI Protocol (A2UI)

[A2UI](https://a2ui.org/) is a generative UI protocol, from Google, that enables AI agents to generate rich,
interactive user interfaces across web, mobile, and desktop. The idea with A2UI is that when a user asks a question to an
agent, e.g. `What are some good restaurants in New York?`, the agent can not only return the list of restaurants, 
but also return UI descriptions that can be used by renderers to display the restaurants in a rich and interactive format.

You can read the [Core Concepts](https://a2ui.org/concepts/overview/) for the details of the protocol but here's a summary.

### Message types and format

There are 4 primary message types in A2UI:

* **createSurface**: Create a new surface and specify its catalog.
* **updateComponents**: Add or update UI components in a surface.
* **updateDataModel**: Update application state.
* **deleteSurface**: Remove a UI surface.

They're in JSON format and describe the UI and data for the UI:

```json
{
  "version": "v0.9",
  "createSurface": {
    "surfaceId": "main",
    "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"
  }
}
{
  "version": "v0.9",
  "updateComponents": {
    "surfaceId": "main",
    "components": [...]
  }
}
{
  "version": "v0.9",
  "updateDataModel": {
    "surfaceId": "main",
    "path": "/user",
    "value": { "name": "Alice" }
  }
}
```

### Components

A2UI defines a standard catalog of UI components organized by purpose:

* **Layout**: Row, Column, List - arrange other components
* **Display**: Text, Image, Icon, Video, Divider - show information
* **Interactive**: Button, TextField, CheckBox, DateTimeInput, Slider - handle user input
* **Container**: Card, Tabs, Modal - group and organize content

For the complete component gallery with examples, see the [Component Reference](https://a2ui.org/reference/components/)

### Message Flow

Here's an example [Restaurant Booking](https://a2ui.org/concepts/data-flow/#lifecycle-example-restaurant-booking) message
flow from the documentation.

1. Agent creates surface:

```json
{
  "version": "v0.9",
  "createSurface": {
    "surfaceId": "booking",
    "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"
  }
}
```

2. Agent defines UI structure:

```json
{
  "version": "v0.9",
  "updateComponents": {
    "surfaceId": "booking",
    "components": [
      {
        "id": "root",
        "component": "Column",
        "children": ["header", "guests-field", "submit-btn"]
      },
      {
        "id": "header",
        "component": "Text",
        "text": "Confirm Reservation",
        "variant": "h1"
      },
      {
        "id": "guests-field",
        "component": "TextField",
        "label": "Guests",
        "value": { "path": "/reservation/guests" }
      },
      {
        "id": "submit-btn",
        "component": "Button",
        "child": "submit-text",
        "variant": "primary",
        "action": {
          "event": {
            "name": "confirm",
            "context": {
              "details": { "path": "/reservation" }
            }
          }
        }
      }
    ]
  }
}
```

3. Agent populates data:

```json
{
  "version": "v0.9",
  "updateDataModel": {
    "surfaceId": "booking",
    "path": "/reservation",
    "value": {
      "datetime": "2025-12-16T19:00:00Z",
      "guests": "2"
    }
  }
}
```

4. User edits guests to "3" → Client updates `/reservation/guests` automatically

5. User clicks "Confirm" → Client sends action:

```json
{
  "version": "v0.9",
  "action": {
    "name": "confirm",
    "surfaceId": "booking",
    "context": {
      "details": {
        "datetime": "2025-12-16T19:00:00Z",
        "guests": "3"
      }
    }
  }
}
```

6. Agent responds → Updates UI or sends:

```json
{
  "version": "v0.9",
  "deleteSurface": { "surfaceId": "booking" }
}
``` 

### Transport Options

A2UI is transport-agnostic, meaning any mechanism that can deliver JSON messages works. Currently, A2A and AG-UI are
supported as transport layers, with REST API, WebSockets, and SSE as planned or proposed. See
[Transports](https://a2ui.org/concepts/transports/) for the latest supported transports.

### Renderers

Once the A2UI JSON messages are generated by the agent, they need to be converted into native
UI components by renderers. For web, there are Lit and Angular renderers and Flutter (GenUI SDK) for mobile/desktop/web.
You can see [Maintained Renderers](https://a2ui.org/reference/renderers/#maintained-renderers) for the full list.

## A2UI vs. AG-UI

Now that we understand the basics of A2UI, let's also understand its relationshis with AG-UI. Despite similar names,
AG-UI and A2UI serve very different and complementary roles in the agentic application stack:

* **AG-UI** connects your user-facing application to the agentic backend.
* **A2UI** is a declarative generative UI spec, that agents use to return UI widgets as part of their responses.

<img src="https://github.com/meteatamel/genai-beyond-basics/raw/main/samples/protocols/images/agui-and-a2ui.png" alt="AG-UI and A2UI" width="50%" />

See [AG-UI and Generative UI Specs](https://docs.ag-ui.com/concepts/generative-ui-specs) and [AG-UI and
A2UI](https://www.copilotkit.ai/ag-ui-and-a2ui) for more details.


## A2UI with Agent Development Kit (ADK)

Let's look at some samples of how you can generate UI with A2UI and ADK agents. 

### Before you start

Clone the repository:

```sh
git clone https://github.com/google/a2ui.git
cd a2ui
```

Set your Gemini API key:

```sh
export GEMINI_API_KEY="your_gemini_api_key_here"
```

Samples have server (agent) and client (renderer) parts that both need to be started. You can find different ADK agent
samples in [adk](https://github.com/google/A2UI/tree/main/samples/agent/adk) and client renderers in
[client](https://github.com/google/A2UI/tree/main/samples/client) folders in the A2UI repo.

### Restaurant Finder app

To run the restaurant finder app, start both the agent and the client servers.

Inside the [`samples/client/lit`](https://github.com/google/A2UI/tree/main/samples/client/lit) folder:

```sh
npm run demo:restaurant
```

Navigate to `http://localhost:5173/?app=restaurant` and start  prompting your AI agent. For example `Top 5 Chinese restaurants in New York`:

You should see the UI generated by the agent and rendered on the client side:

![Restaurant finder app](https://github.com/meteatamel/genai-beyond-basics/raw/main/samples/protocols/a2ui/adk/images/restaurant-finder-app.png)

If you look into the logs, you should also see the A2UI JSON response:

```json
[REST] LiteLLM completion() model= gemini-2.5-flash; provider = gemini
[REST] INFO:agent:Event from runner: model_version='gemini-2.5-flash' content=Content(
[REST]   parts=[
[REST]     Part(
[REST]       text="""<a2ui-json>
[REST] [
[REST]   {
[REST]     "beginRendering": {
[REST]       "surfaceId": "default",
[REST]       "root": "root-column",
[REST]       "styles": {
[REST]         "primaryColor": "#FF0000",
[REST]         "font": "Roboto"
[REST]       }
[REST]     }
[REST]   },
[REST]   {
[REST]     "surfaceUpdate": {
[REST]       "surfaceId": "default",
[REST]       "components": [
[REST]         {
[REST]           "id": "root-column",
[REST]           "component": {
[REST]             "Column": {
[REST]               "children": {
[REST]                 "explicitList": [
[REST]                   "title-heading",
[REST]                   "item-list"
[REST]                 ]
[REST]               }
[REST]             }
[REST]           }
[REST]         },
...
```

Let's take a look at what's happening under the hood. 

On the agent side, you can review the agent code in [`samples/agent/adk/restaurant_finder`](https://github.com/google/A2UI/tree/main/samples/agent/adk/restaurant_finder). Specifically, [`prompt_builder.py`](https://github.com/google/A2UI/blob/main/samples/agent/adk/restaurant_finder/prompt_builder.py) shows how to build the prompt for the agent to generate A2UI responses.

We're instructing the agent to generate A2UI responses using the following prompt:

```python
ROLE_DESCRIPTION = (
    "You are a helpful restaurant finding assistant. Your final output MUST be a a2ui"
    " UI JSON response."
)

UI_DESCRIPTION = """
-   If the query is for a list of restaurants, use the restaurant data you have already received from the `get_restaurants` tool to populate the `dataModelUpdate.contents` array (e.g., as a `valueMap` for the "items" key).
-   If the number of restaurants is 5 or fewer, you MUST use the `SINGLE_COLUMN_LIST_EXAMPLE` template.
-   If the number of restaurants is more than 5, you MUST use the `TWO_COLUMN_LIST_EXAMPLE` template.
-   If the query is to book a restaurant (e.g., "USER_WANTS_TO_BOOK..."), you MUST use the `BOOKING_FORM_EXAMPLE` template.
-   If the query is a booking submission (e.g., "User submitted a booking..."), you MUST use the `CONFIRMATION_EXAMPLE` template.
"""
```

The `A2uiSchemaManager` loads component catalogs, generates system prompts that teach the LLM how to produce valid A2UI
JSON, and `generate_system_prompt` method  combines your agent's role description with the A2UI JSON schema and few-shot
examples, so the LLM knows exactly how to format its output:

```python
version = VERSION_0_9
restaurant_prompt = A2uiSchemaManager(
    version,
    catalogs=[
        BasicCatalog.get_config(
            version=version,
            examples_path=f"examples/{version}",
        )
    ],
    schema_modifiers=[remove_strict_validation],
).generate_system_prompt(
    role_description=ROLE_DESCRIPTION,
    ui_description=UI_DESCRIPTION,
    include_schema=True,
    include_examples=True,
    validate_examples=True,
)
```

Then you create your agent with the system prompt in [agent.py](https://github.com/google/A2UI/blob/main/samples/agent/adk/restaurant_finder/agent.py):

```python
return LlmAgent(
    model=LiteLlm(model=LITELLM_MODEL),
    name="restaurant_agent",
    description="An agent that finds restaurants and helps book tables.",
    instruction=instruction,
    tools=[get_restaurants],
)
```

Once the agent generated the JSON output, you need to validate it against the A2UI schema before streaming back to the
client:

```python
# --- Validation Steps ---
# Check if it validates against the A2UI_SCHEMA
# This will raise jsonschema.exceptions.ValidationError if it fails
logger.info(
    "--- RestaurantAgent.stream: Validating against A2UI_SCHEMA... ---"
)
selected_catalog.validator.validate(parsed_json_data)
```

You can see [A2UI - Agent-to-UI for ADK](https://google.github.io/adk-docs/integrations/a2ui/) for more details.

On the client side, you can take a look at the Lit implementation of A2UI in [`renderers/lit`](https://github.com/google/A2UI/tree/main/renderers/lit) which takes the generated JSON and renders it.

There are more samples in the A2UI repo, you can check out my repo on how to run the [Contact manager
app](https://github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/a2ui/adk#contact-manager-app) and
[A2UI Component Gallery
Agent](https://github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/a2ui/adk#a2ui-component-gallery-agent)

## CopilotKit A2UI Starter and A2UI Composer

Last but not least, CopilotKit has a great [A2UI
Starter](https://github.com/CopilotKit/CopilotKit/tree/main/examples/integrations/a2a-a2ui) template to help you get
started with A2UI, as well as the [A2UI Composer](https://a2ui-composer.ag-ui.com/) to experiment with A2UI components. 

![A2UI Composer](/img/2026/a2ui-composer.png)

You can check out my repo on how to run the [A2UI
Starter](https://github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/a2ui/copilotkit#a2ui-starter).

## Summary

In this blog post, we looked into A2UI in more detail, learned how to use A2UI with ADK, and explored some of the
available tools to help you get started with A2UI. Here are some resources to learn more:

* [Docs: A2UI protocol](https://a2ui.org/)
* [GitHub: A2UI](https://github.com/google/A2UI) & [A2UI protocol spec](https://github.com/google/A2UI/tree/main/specification)
* [Blog: Introducing A2UI: An open project for agent-driven interfaces](https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/)
* [A2UI — Agent-to-UI for ADK](https://google.github.io/adk-docs/integrations/a2ui/)
* [CopilotKit A2UI Composer](https://a2ui-composer.ag-ui.com/)
* [GitHub: CopilotKit <> A2A + A2UI Starter](https://github.com/CopilotKit/CopilotKit/tree/main/examples/integrations/a2a-a2ui)
* [YouTube: A2UI: The Protocol That Makes AI Design Functional UIs](https://youtu.be/PS8mZFhodfE)
* [YouTube: AI Agents Can Now Build Their Own UI in Real Time](https://youtu.be/MD8VQzvMVek)


