diff --git a/docs/en/learn/a2ui.mdx b/docs/en/learn/a2ui.mdx new file mode 100644 index 000000000..1d5e6b9f9 --- /dev/null +++ b/docs/en/learn/a2ui.mdx @@ -0,0 +1,344 @@ +--- +title: Agent-to-UI (A2UI) Protocol +description: Enable agents to generate declarative UI surfaces for rich client rendering via the A2UI extension. +icon: window-restore +mode: "wide" +--- + +## A2UI Overview + +A2UI is a declarative UI protocol extension for [A2A](/en/learn/a2a-agent-delegation) that lets agents emit structured JSON messages describing interactive surfaces. Clients receive these messages and render them as rich UI components — forms, cards, lists, modals, and more — without the agent needing to know anything about the client's rendering stack. + +A2UI is built on the A2A extension mechanism and identified by the URI `https://a2ui.org/a2a-extension/a2ui/v0.8`. + + + A2UI requires the `a2a-sdk` package. Install with: `uv add 'crewai[a2a]'` or `pip install 'crewai[a2a]'` + + +## How It Works + +1. The **server extension** scans agent output for A2UI JSON objects +2. Valid messages are wrapped as `DataPart` entries with the `application/json+a2ui` MIME type +3. The **client extension** augments the agent's system prompt with A2UI instructions and the component catalog +4. The client tracks surface state (active surfaces and data models) across conversation turns + +## Server Setup + +Add `A2UIServerExtension` to your `A2AServerConfig` to enable A2UI output: + +```python Code +from crewai import Agent +from crewai.a2a import A2AServerConfig +from crewai.a2a.extensions.a2ui import A2UIServerExtension + +agent = Agent( + role="Dashboard Agent", + goal="Present data through interactive UI surfaces", + backstory="Expert at building clear, actionable dashboards", + llm="gpt-4o", + a2a=A2AServerConfig( + url="https://your-server.com", + extensions=[A2UIServerExtension()], + ), +) +``` + +### Server Extension Options + + + Component catalog identifiers the server supports. When set, only these catalogs are advertised to clients. + + + + Whether to accept inline catalog definitions from clients in addition to named catalogs. + + +## Client Setup + +Add `A2UIClientExtension` to your `A2AClientConfig` to enable A2UI rendering: + +```python Code +from crewai import Agent +from crewai.a2a import A2AClientConfig +from crewai.a2a.extensions.a2ui import A2UIClientExtension + +agent = Agent( + role="UI Coordinator", + goal="Coordinate tasks and render agent responses as rich UI", + backstory="Expert at presenting agent output in interactive formats", + llm="gpt-4o", + a2a=A2AClientConfig( + endpoint="https://dashboard-agent.example.com/.well-known/agent-card.json", + extensions=[A2UIClientExtension()], + ), +) +``` + +### Client Extension Options + + + Preferred component catalog identifier. Defaults to `"standard (v0.8)"` when not set. + + + + Restrict which components the agent may use. When `None`, all 18 standard catalog components are available. + + +## Message Types + +A2UI defines four server-to-client message types. Each message targets a **surface** identified by `surfaceId`. + + + + Initializes a new surface with a root component and optional styles. + + ```json + { + "beginRendering": { + "surfaceId": "dashboard-1", + "root": "main-column", + "catalogId": "standard (v0.8)", + "styles": { + "primaryColor": "#EB6658" + } + } + } + ``` + + + + Sends or updates one or more components on an existing surface. + + ```json + { + "surfaceUpdate": { + "surfaceId": "dashboard-1", + "components": [ + { + "id": "main-column", + "component": { + "Column": { + "children": { "explicitList": ["title", "content"] }, + "alignment": "start" + } + } + }, + { + "id": "title", + "component": { + "Text": { + "text": { "literalString": "Dashboard" }, + "usageHint": "h1" + } + } + } + ] + } + } + ``` + + + + Updates the data model bound to a surface, enabling dynamic content. + + ```json + { + "dataModelUpdate": { + "surfaceId": "dashboard-1", + "path": "/data/model", + "contents": [ + { + "key": "userName", + "valueString": "Alice" + }, + { + "key": "score", + "valueNumber": 42 + } + ] + } + } + ``` + + + + Removes a surface and all its components. + + ```json + { + "deleteSurface": { + "surfaceId": "dashboard-1" + } + } + ``` + + + +## Component Catalog + +A2UI ships with 18 standard components organized into three categories: + +### Content + +| Component | Description | Required Fields | +|-----------|-------------|-----------------| +| **Text** | Renders text with optional heading/body hints | `text` (StringBinding) | +| **Image** | Displays an image with fit and size options | `url` (StringBinding) | +| **Icon** | Renders a named icon from a set of 47 icons | `name` (IconBinding) | +| **Video** | Embeds a video player | `url` (StringBinding) | +| **AudioPlayer** | Embeds an audio player with optional description | `url` (StringBinding) | + +### Layout + +| Component | Description | Required Fields | +|-----------|-------------|-----------------| +| **Row** | Horizontal flex container | `children` (ChildrenDef) | +| **Column** | Vertical flex container | `children` (ChildrenDef) | +| **List** | Scrollable list (vertical or horizontal) | `children` (ChildrenDef) | +| **Card** | Elevated container for a single child | `child` (str) | +| **Tabs** | Tabbed container | `tabItems` (list of TabItem) | +| **Divider** | Visual separator (horizontal or vertical) | — | +| **Modal** | Overlay triggered by an entry point | `entryPointChild`, `contentChild` (str) | + +### Interactive + +| Component | Description | Required Fields | +|-----------|-------------|-----------------| +| **Button** | Clickable button that triggers an action | `child` (str), `action` (Action) | +| **CheckBox** | Boolean toggle with a label | `label` (StringBinding), `value` (BooleanBinding) | +| **TextField** | Text input with type and validation options | `label` (StringBinding) | +| **DateTimeInput** | Date and/or time picker | `value` (StringBinding) | +| **MultipleChoice** | Selection from a list of options | `selections` (ArrayBinding), `options` (list) | +| **Slider** | Numeric range slider | `value` (NumberBinding) | + +## Data Binding + +Components reference values through **bindings** rather than raw literals. This allows surfaces to update dynamically when the data model changes. + +There are two ways to bind a value: + +- **Literal values** — hardcoded directly in the component definition +- **Path references** — point to a key in the surface's data model + +```json +{ + "surfaceUpdate": { + "surfaceId": "profile-1", + "components": [ + { + "id": "greeting", + "component": { + "Text": { + "text": { "path": "/data/model/userName" }, + "usageHint": "h2" + } + } + }, + { + "id": "status", + "component": { + "Text": { + "text": { "literalString": "Online" }, + "usageHint": "caption" + } + } + } + ] + } +} +``` + +In this example, `greeting` reads the user's name from the data model (updated via `dataModelUpdate`), while `status` uses a hardcoded literal. + +## Handling User Actions + +Interactive components like `Button` trigger `userAction` events that flow back to the server. Each action includes a `name`, the originating `surfaceId` and `sourceComponentId`, and an optional `context` with key-value pairs. + +```json +{ + "userAction": { + "name": "submitForm", + "surfaceId": "form-1", + "sourceComponentId": "submit-btn", + "timestamp": "2026-03-12T10:00:00Z", + "context": { + "selectedOption": "optionA" + } + } +} +``` + +Action context values can also use path bindings to send current data model values back to the server: + +```json +{ + "Button": { + "child": "confirm-label", + "action": { + "name": "confirm", + "context": [ + { + "key": "currentScore", + "value": { "path": "/data/model/score" } + } + ] + } + } +} +``` + +## Validation + +Use `validate_a2ui_message` to validate server-to-client messages and `validate_a2ui_event` for client-to-server events: + +```python Code +from crewai.a2a.extensions.a2ui import validate_a2ui_message +from crewai.a2a.extensions.a2ui.validator import ( + validate_a2ui_event, + A2UIValidationError, +) + +# Validate a server message +try: + msg = validate_a2ui_message({"beginRendering": {"surfaceId": "s1", "root": "r1"}}) +except A2UIValidationError as exc: + print(exc.errors) + +# Validate a client event +try: + event = validate_a2ui_event({ + "userAction": { + "name": "click", + "surfaceId": "s1", + "sourceComponentId": "btn-1", + "timestamp": "2026-03-12T10:00:00Z", + } + }) +except A2UIValidationError as exc: + print(exc.errors) +``` + +## Best Practices + + + + Begin with a `beginRendering` message and a single `surfaceUpdate`. Add data binding and interactivity once the basic flow works. + + + + Prefer path bindings over literal values for content that changes. Use `dataModelUpdate` to push new values without resending the full component tree. + + + + Use the `allowed_components` option on `A2UIClientExtension` to restrict which components the agent may emit, reducing prompt size and keeping output predictable. + + + + Use `validate_a2ui_message` and `validate_a2ui_event` to catch malformed payloads early, especially when building custom integrations. + + + +## Learn More + +- [A2A Agent Delegation](/en/learn/a2a-agent-delegation) — configure agents for remote delegation via the A2A protocol +- [A2A Protocol Documentation](https://a2a-protocol.org) — official protocol specification