a2ui-project / a2ui-project/a2ui
[v0.10] Configuration Schemas for Data-Bound Components
- Dominant language
- TypeScript
- Stars
- 16.4k
- Forks
- 1.3k
- Avg merge
- 2d 13h
- Merged PRs (30d)
- 134
Description
## Problem
The A2UI spec cleanly separates component structure (`updateComponents`) from component data (`updateDataModel`). Components bind to data model values via `DataBinding` — a JSON Pointer path into the data model.
However, **the data model is completely untyped**. The `updateDataModel` message accepts any JSON value at any path:
```json
"updateDataModel": {
"surfaceId": "string",
"path": "string",
"value": "any" // ← no schema, no constraints
}
```
This works fine for simple components with inline properties (like `Text` or `Button` in the basic catalog). But for **complex data-bound components** — where a component binds to a rich configuration object via `DataBinding` — there is no spec-level way for a catalog to declare what shape of data the component expects.
### Why this matters
- **LLM agents** generate data model values with no schema guidance. Common errors include missing required fields, wrong types, broken enum values, and malformed nested structures. These errors are only caught at the client renderer — too late for the agent to self-correct.
- **Validators** check component tree topology (IDs, cycles, orphans, recursion limits, path syntax) but have zero visibility into data model content. Invalid data passes all 5 validation levels.
- **Clients** receive `updateDataModel` payloads with no way to validate before rendering.
- **Catalog authors** can define complex data expectations but must resort to description strings or proprietary extensions — nothing machine-readable.
- **Tooling** (TypeScript generators, mock data generators, documentation tools) cannot extract data shape information from catalogs.
### Example
A catalog defines a `ContactCard` component that binds to a config object:
```json
"ContactCard": {
"properties": {
"component": { "const": "ContactCard" },
"configuration": {
"$ref": "common_types.json#/$defs/DataBinding",
"description": "Path to a ContactCardConfig object in the data model."
}
}
}
```
The component expects an object with `name` (string), `email` (string, format: email), and `role` (enum) at the bound path — but that expectation lives only in a description string. The spec has no mechanism to express it formally.
---
## Proposed Solution
Two additions to the catalog format, with no wire protocol changes:
### 1. `configSchemas` — a new top-level catalog section
A map of schema name to JSON Schema (Draft 2020-12), declaring the expected shapes of data model values:
```json
{
"catalogId": "urn:example:my-catalog:v1",
"components": { ... },
"functions": { ... },
"configSchemas": {
"ContactCardConfig": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" },
"role": { "type": "string", "enum": ["engineer", "manager", "designer"] }
},
"required": ["name", "email", "role"]
}
}
}
```
### 2. `expectedSchema` — an annotation on DataBinding properties
A machine-readable link from a component's binding property to its expected data shape:
```json
"ContactCard": {
"allOf": [
{ "$ref": "common_types.json#/$defs/ComponentCommon" },
{
"properties": {
"component": { "const": "ContactCard" },
"configuration": {
"$ref": "common_types.json#/$defs/DataBinding",
"expectedSchema": { "$ref": "#/configSchemas/ContactCardConfig" }
}
},
"required": ["component", "configuration"]
}
]
}
```
### What this enables
- **Data model validation** — validators can check `updateDataModel` values against the expected schema, adding a new validation level alongside the existing 5
- **LLM self-correction** — agents can validate generated data before sending to the client, and feed structured errors back to the LLM for retry
- **Prompt generation** — schema managers can automatically produce data shape documentation for LLM prompts, instead of relying on prose descriptions
- **Client-side validation** — clients can optionally validate incoming data before rendering
- **Type generation** — tooling can produce TypeScript interfaces from `configSchemas`
### What this does NOT change
- **No wire protocol changes** — `createSurface`, `updateComponents`, `updateDataModel`, `deleteSurface` are unchanged
- **No mandatory validation** — `configSchemas` and `expectedSchema` are optional; validators MAY check them
- **Fully backward compatible** — catalogs without `configSchemas` work exactly as before; validators that don't understand `expectedSchema` skip it
---
## Design Rationale
**Why a top-level section, not inline in components?**
Multiple components may share config schema fragments (e.g., a shared header object used across several components). A top-level section with `$ref` support enables reuse. It also separates UI structure concerns from data shape concerns.
**Why annotate the property, not the DataBinding type?**
`DataBinding` is a runtime type that appears in `updateComponents` payloads. Adding schema metadata to it would bloat every runtime message. `expectedSchema` is catalog metadata — it belongs on the component schema, not the runtime value.
**Why not a `dataModelSchema` on `createSurface`?**
A surface-level schema requires the agent to know the full data shape upfront, conflicts with incremental/streaming updates, and creates a monolithic schema instead of per-component contracts.
Contributor guide
Research direction
The issue proposes adding `configSchemas` and `expectedSchema` to the catalog format. Start by reading the A2UI spec to understand the catalog structure, especially `common_types.json#/$defs/DataBinding`. Look at existing catalog examples and validation code. The change involves JSON Schema (Draft 2020-12) integration. 'Done' means the catalog format supports these new fields and validators can optionally check data model values against the schemas.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- json, typescript
- Domain
- backend-api-design, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100