a2ui-rs
A runtime-agnostic, stateless Rust toolkit implementing the A2UI protocol (v0.9, v0.9.1, and v1.0) for server-side agent applications. It provides typed message models, catalog negotiation, LLM prompt generation, JSON Schema validation, and message builders — all as pure functions and minimal traits.
All session management, agent memory, and conversation state are owned by the agent harness (e.g. Rig), not this library.
Note: v1.0 is the current primary target. The v0.9-family modules (
v09) are retained for compatibility; message builders default to emitting"v0.9.1"(the current production release).
Architecture
┌──────────────────────────────────────────────────────────────┐
│ Agent Harness (Rig etc.) │
│ Owns: LLM loop, session state, memory, surface tracking │
│ Uses a2ui-rs to: │
│ - Build system prompt context (catalog + protocol schema) │
│ - Construct typed server-to-client messages │
│ - Validate LLM output before sending to client │
│ - Parse incoming client-to-server messages │
│ - Negotiate catalogs with client capabilities │
│ - Send validated messages via ClientTransport trait │
└──────┬──────────────────┬──────────────────┬─────────────────┘
│ │ │
▼ ▼ ▼
CatalogProvider a2ui-core ClientTransport
(trait impl by (pure functions (trait impl by
harness/plugin) + utilities) harness/plugin)
The library is a toolkit, not an engine. The harness calls into it; it never drives control flow.
Crate Structure
| Crate | Description |
|---|---|
a2ui-types |
Serde-based typed models for v0.9, v0.9.1, and v1.0 messages, capabilities, catalogs, and data binding types. Pure data — no logic, no async. |
a2ui-core |
Traits (ClientTransport, CatalogProvider), pure functions for negotiation, validation, prompt building, message construction, and parsing. |
a2ui-validation |
JSON Schema validation engine. Validates outgoing messages against catalog schemas and returns structured ValidationErrors matching the spec's VALIDATION_FAILED format. |
Module Map
a2ui-types/src/
├── common.rs # SurfaceId, ComponentId, CatalogId, SpecVersion
├── v09/
│ ├── server_to_client.rs # createSurface, updateComponents, updateDataModel, deleteSurface
│ ├── client_to_server.rs # action, error (VALIDATION_FAILED)
│ ├── capabilities.rs # ClientCapabilities, ServerCapabilities
│ ├── client_data_model.rs # ClientDataModel (sendDataModel sync)
│ ├── common_types.rs # DynamicString/Number/Boolean, ChildList, Action, FunctionCall, CheckRule
│ └── catalog.rs # Catalog, FunctionDefinition
└── v10/
├── agent_to_renderer.rs # createSurface, updateComponents, updateDataModel, deleteSurface,
│ # callRendererFunction, agentFunctionResponse
├── renderer_to_agent.rs # action, callAgentFunction, rendererFunctionResponse, error
├── capabilities.rs # RendererCapabilities, AgentCapabilities
├── common_types.rs # DataBinding (@path), FunctionCall (@call), Dynamic*,
│ # ChildList, Action, CheckRule, FunctionResponse, ValidationResult
└── catalog.rs # Catalog
a2ui-core/src/
├── traits.rs # ClientTransport, CatalogProvider
├── negotiation.rs # negotiate_catalog(), negotiate_catalog_with_inline()
├── validation.rs # validate_message(), parse_client_message_*(), serialize_message_*()
├── prompt.rs # build_prompt_context_v09*(), build_prompt_context_v10()
├── message.rs # v0.9 and v1.0 message builders
└── error.rs # A2uiError
a2ui-validation/src/
└── schema_validator.rs # SchemaValidator, validate()
Quick Start (v0.9 / v0.9.1)
1. Implement the Traits
use ;
use CatalogId;
use Catalog;
use async_trait;
2. Negotiate a Catalog
use negotiate_catalog;
// Client sends its supported catalog IDs (e.g. from a2uiClientCapabilities)
let client_supported = vec!;
let result = negotiate_catalog?;
println!;
3. Build LLM Prompt Context
use build_prompt_context_v09;
let catalog = my_catalog_provider.get_catalog.unwrap;
let schema = my_catalog_provider.get_catalog_schema.unwrap;
// Defaults to protocol version v0.9.1
let prompt_ctx = build_prompt_context_v09;
// Inject into your LLM system prompt
let system_prompt = format!;
4. Construct & Validate Messages
use *;
use *;
use json;
// Build a createSurface message (emits "v0.9.1" by default)
let create_msg = new
.send_data_model
.build;
// Build a component update
let update_msg = new
.add_component
.add_component
.build;
// Validate before sending
let msg_json = to_value?;
validate_message?;
5. Serialize & Send
use serialize_message_v09;
let bytes = serialize_message_v09?;
my_transport.send_to_client.await?;
6. Parse Incoming Client Messages
use ;
let parsed = parse_client_message_auto?;
match parsed
Quick Start (v1.0)
The v1.0 API lives under a2ui_types::v10 and uses the reserved @path / @call
keys for data bindings and function calls.
use *;
use serialize_message_v10;
use FunctionCall;
use json;
// createSurface can embed the initial component tree and data model
let create_msg = new
.catalog_id
.add_component
.add_component
.data_model
.build;
let bytes = serialize_message_v10?;
// Execute a function on the renderer
let call = FunctionCall ;
let call_msg = call_renderer_function_v10;
Handle Validation Errors
When the LLM generates invalid A2UI JSON, the validation functions return structured errors that can be fed back to the LLM:
use A2uiError;
match validate_message
Spec Version Support Matrix
| Feature | v0.9 | v0.9.1 | v1.0 |
|---|---|---|---|
| Surface creation | createSurface |
createSurface |
createSurface (can embed components + dataModel) |
| Component updates | updateComponents |
updateComponents |
updateComponents |
| Data model updates | updateDataModel |
updateDataModel |
updateDataModel (value required) |
| Surface deletion | deleteSurface |
deleteSurface |
deleteSurface |
| Client actions | action |
action |
action (adds userMessage, metadata) |
| Function calls | FunctionCall (call) |
FunctionCall (call) |
FunctionCall (@call); bidirectional callRendererFunction / callAgentFunction |
| Data binding | path |
path |
@path (reserved @ namespace) |
| Check rules | CheckRule |
CheckRule |
CheckRule (structured ValidationResult) |
| Data model sync | sendDataModel |
sendDataModel |
sendDataModel |
| Capabilities | ClientCapabilities / ServerCapabilities |
same | RendererCapabilities / AgentCapabilities |
| Version field | "v0.9" |
"v0.9" or "v0.9.1" |
"v1.0" |
| Catalog negotiation | ✓ | ✓ | ✓ (mixable catalogs) |
| Message validation | ✓ | ✓ | ✓ |
| Prompt generation | ✓ | ✓ | ✓ |
Design Philosophy
- Stateless toolkit: No session management, no surface tracking, no data model storage. The harness owns all state and calls library functions as needed.
- Runtime-agnostic: Only the
ClientTransporttrait is async (viaasync-trait). Everything else is synchronous pure functions. No tokio/async-std dependency in the library itself. - Separate type hierarchies:
v09::*andv10::*are distinct types. The harness chooses which module to use. No version-polymorphism overhead. - Catalog as typed + raw JSON:
Catalogstructs for negotiation metadata; rawserde_json::Valuefor validation and LLM prompt injection. - Spec-compatible error reporting:
ValidationErrormirrors the A2UIVALIDATION_FAILEDformat so the harness can forward errors directly to the LLM or client.
v1.0 validation note
The official v1.0 envelope schemas (agent_to_renderer.json, renderer_to_agent.json)
reference common_types.json and catalog.json via relative $ref. a2ui_validation
validates against whatever self-contained schema the harness supplies (it does not
bundle or resolve the official spec files). To validate v1.0 messages against the full
envelope schema, pass a schema that has been bundled/resolved (e.g. via a JSON Schema
bundler) or a catalog-specific schema. The jsonschema backend supports JSON Schema
draft 2020-12 (including unevaluatedProperties).
TODO
- Typed v1.0
CatalogProvider: TheCatalogProvidertrait'sget_catalogstill returns the v0.9a2ui_types::v09::catalog::Catalogtype. v1.0 negotiation and prompt building don't depend on it (they use catalog IDs and raw schemas), but a fully v1.0-typed provider is not yet available. Add a v1.0 catalog-returning trait (or makeCatalogProviderversion-agnostic) so v1.0 harnesses can supply a typeda2ui_types::v10::catalog::Catalog.
Dependencies
| Crate | Purpose |
|---|---|
serde + serde_json |
Serialization / deserialization |
async-trait |
Async trait support for ClientTransport |
thiserror |
Ergonomic error types |
jsonschema |
JSON Schema validation engine |
Building & Testing
# Build all crates
# Run all tests
# Run tests for a specific crate
License
MIT