Skip to main content
             ___    _    _   _____ 
     /\     |__ \  | |  | | |_   _|
    /  \       ) | | |  | |   | |  
   / /\ \     / /  | |  | |   | |  
  / ____ \   / /_  | |__| |  _| |_ 
 /_/    \_\ |____|  \____/  |_____|

anthropic-a2ui

Let Claude generate user interfaces with A2UI in its responses.

anthropic-a2ui bridges the A2UI protocol with the Anthropic SDK, enabling Claude to emit declarative, interactive user interfaces as part of its natural responses. Instead of returning plain text, Claude can now build forms, dashboards, product cards, surveys, calendars, configuration panels, modals, and any other UI composed from the A2UI component catalog. Payloads are repaired when possible and checked against the active catalog before they are handed to an A2UI-compatible renderer (Lit, React, Angular, Flutter, SwiftUI, and more).

The package does not wrap or replace the Anthropic SDK. It composes with it: you use anthropic directly and plug in the A2UI pieces where they add value. The system prompt, the tool definition, the stream parser, the validator, the automatic repair layer, and the retry-with-feedback loop are all designed to be transparent and composable.


How it works

When a user asks Claude for an interface — "make me a contact form", "show me a shopping cart", "build me a weather panel" — the following happens behind the scenes:

  1. Prompt injection: ClaudeA2uiPromptBuilder constructs a system prompt containing the A2UI JSON Schema, few-shot examples, the list of 59 valid icon names, and the 14 catalog functions. Claude receives this as its system context and knows how to speak A2UI.

  2. Tool definition: create_a2ui_tool produces a tool definition (send_a2ui_json_to_client) that Claude can invoke to deliver UI. The schema is wrapped to comply with Anthropic's constraint against oneOf/allOf/anyOf at the top level of input_schema.

  3. Streaming parse: As Claude streams its response, ClaudeStreamParser intercepts tool-use events (or <a2ui-json> text blocks) and emits ResponsePart objects containing either conversational text or a complete A2UI payload.

  4. Automatic repair: Before validation, the payload passes through six repair functions that fix known, unambiguous issues — semantic icon aliases, non-existent catalog functions, orphaned components, ambiguous DateTimeInput formats, dynamic child lists in Row/Column, and Markdown markers in visible UI text. An icon with no faithful catalog equivalent is rejected instead of being disguised as an unrelated generic icon.

  5. Validation: The repaired payload is checked against the A2UI schema and its topology. If a tool call fails that validation, the error is fed back to Claude using the original tool-use ID, and Claude can correct itself on the next attempt (up to max_retries).

  6. Delivery: The validated payload is wrapped in an A2uiPart with MIME application/a2ui+json, ready to be sent to any A2UI renderer.

The user never mentions A2UI, components, functions, or schemas. They just talk naturally, and Claude responds with a working interface.


Installation

uv add anthropic-a2ui
# or
pip install anthropic-a2ui

Requires ANTHROPIC_API_KEY in the environment.


Security boundary

An A2UI payload is model output and must be treated as untrusted input. This package validates protocol structure and performs narrowly defined repairs; it does not sanitize strings, authorize actions, fetch URLs, or make a renderer safe.

The application that renders the payload must escape text, reject raw HTML and executable expressions, apply URL protocol and host allowlists, constrain resource and render sizes, and use an appropriate CSP or sandbox for web views. Do not grant actions from an A2UI payload access to credentials or privileged server operations without application-side authorization.


Quick start

The simplest way to use the package is generate_a2ui. One function call handles everything: prompt construction, Claude invocation, streaming, parsing, repair, validation, and retry-on-failure.

import anthropic
from anthropic_a2ui import generate_a2ui

client = anthropic.Anthropic()

result = generate_a2ui(
    client,
    "make me a registration form with name, email, and a submit button",
    model="claude-haiku-4-5-20251001",
    log_repairs=True,
)

if result.success:
    # result.a2ui_json passed structural validation for the active catalog.
    # Apply your renderer's own content and action security policy.
    render(result.a2ui_json)
    for repair in result.repairs:
        print(f"Repaired: {repair}")
else:
    print(f"Failed after {result.attempts} attempts: {result.error}")

An async version is also available for integration with async web frameworks like FastAPI or Starlette:

import anthropic
from anthropic_a2ui import generate_a2ui_async

client = anthropic.AsyncAnthropic()
result = await generate_a2ui_async(
    client,
    "make me a registration form",
    model="claude-haiku-4-5-20251001",
)

Usage modes

The package offers four complementary ways to generate A2UI, depending on the level of control and the use case.

Mode 1: Single call with generate_a2ui

Best for one-shot UI generation. The user asks for something, Claude responds with a complete A2UI payload. If the payload is invalid, the package automatically feeds the validation error back to Claude and retries (up to max_retries times, default 2). Claude corrects itself and resubmits.

result = generate_a2ui(
    client,
    "make me a contact form with name, email, subject, and message",
    model="claude-opus-4-8",
    max_retries=3,
)

if result.success:
    render(result.a2ui_json)

Key parameters:

  • builder: custom ClaudeA2uiPromptBuilder (defaults to v0.9 Basic Catalog).
  • model: any Claude model that supports tool use.
  • max_tokens: per-attempt token budget (default 8192).
  • max_retries: how many times to retry on validation failure (default 2).
  • use_cache: enable prompt caching to reduce cost ~80% on repeated calls (default True).
  • log_repairs: record what was repaired for debugging (default False).
  • repair: apply the safe repair chain before validation (default True).
  • payload_validator: host-side policy called after A2UI validation. Raise an exception to reject the payload and give Claude the reason on the next retry.

Content and media policy

Schema-valid A2UI can still contain an external image, video, or link that does not meet the host application's trust policy. Use payload_validator to make that policy part of generation rather than silently rewriting model content. The callback receives the repaired, schema-valid payload; raising an exception makes the current attempt fail and sends the reason to Claude for a correction.

from urllib.parse import urlparse

from anthropic_a2ui import generate_a2ui


def require_trusted_media(payload):
    for message in payload:
        for component in message.get("updateComponents", {}).get("components", []):
            if component.get("component") not in {"Image", "Video"}:
                continue
            host = urlparse(component.get("url", "")).hostname
            if host not in {"media.example.com", "images.example.com"}:
                raise ValueError("Media URL must use an approved application domain")


result = generate_a2ui(
    client,
    "make me a product page with a video",
    payload_validator=require_trusted_media,
)

Use a media proxy or an asset service controlled by the application in production. The library never downloads or rewrites arbitrary third-party URLs on the user's behalf.

Mode 2: Multi-turn conversation with A2uiConversation

Best for iterative UI design. The user starts with a request, then refines it across multiple turns: "make me a form" -> "add a phone field" -> "change the button color to blue". Claude retains the most recent context turns and generates a new A2UI payload with all changes applied in each turn. max_context_turns defaults to 10 to keep provider context bounded; set it to None only when the application deliberately wants unbounded context. The local turns list remains available as the complete application record.

import anthropic
from anthropic_a2ui import A2uiConversation

client = anthropic.Anthropic()
conv = A2uiConversation(client, model="claude-haiku-4-5-20251001")

# Turn 1: create the initial form
r1 = conv.send("make me a contact form")
# r1.a2ui_json contains the form
# r1.success is True

# Turn 2: add a field
r2 = conv.send("add a phone number field")
# r2.a2ui_json contains the form + phone field

# Turn 3: change the styling
r3 = conv.send("make the submit button red")
# r3.a2ui_json contains the form + phone field + red button

# Access the full conversation history
print(f"Turns: {len(conv.turns)}")
print(f"Last valid A2UI: {conv.last_a2ui_json is not None}")

# Reset to start a new conversation
conv.reset()

The async version works identically:

import anthropic
from anthropic_a2ui import A2uiConversationAsync

client = anthropic.AsyncAnthropic()
conv = A2uiConversationAsync(client)

r1 = await conv.send("make me a form")
r2 = await conv.send("add an email field")

Mode 3: Forced JSON with structured outputs

Best when you want pure UI output with no conversational text. This mode uses Anthropic's output_config.format structured output parameter with json_schema to request a parseable JSON envelope. No tools and no tags: just a structured payload. Anthropic JSON outputs do not accept A2UI's oneOf schema, so the envelope contains a2ui_json as a JSON-serialized string; parse_json_response deserializes it and applies strict local A2UI validation.

import anthropic
from anthropic_a2ui import (
    ClaudeA2uiPromptBuilder,
    create_a2ui_output_config,
    parse_json_response,
)

builder = ClaudeA2uiPromptBuilder()
allowed_components = ["Text", "TextField", "Button", "Column"]
catalog = builder.get_catalog(allowed_components=allowed_components)
output_config = create_a2ui_output_config(
    catalog,
    allowed_components=allowed_components,
)

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-sonnet-4-6",
    system=builder.build(
        role_description=(
            "You create user interfaces. Serialize the complete A2UI message "
            "array as JSON inside a2ui_json."
        ),
        allowed_components=allowed_components,
    ),
    output_config=output_config,
    max_tokens=8192,
    messages=[{"role": "user", "content": "make me a form"}],
)

# Extract, unwrap, validate, and repair the A2UI payload
a2ui_json = parse_json_response(
    response,
    catalog,
    allowed_components=allowed_components,
)
render(a2ui_json)

With allowed_components or allowed_messages, pass the same restrictions to parse_json_response. Anthropic's schema constrains the outer envelope; the package enforces the A2UI subset during its local validation step. parse_json_response also accepts repair, payload_validator, and max_response_chars (2 MB by default), matching the safety controls of the tool-based modes.

Mode 4: Manual streaming

Best when you need full control over the stream, token by token. You construct the system prompt, define the tool, and process the stream yourself. The parser handles unwrapping, repair, and validation.

import anthropic
from anthropic_a2ui import (
    ClaudeA2uiPromptBuilder,
    create_a2ui_tool,
    ClaudeStreamParser,
)

builder = ClaudeA2uiPromptBuilder()
tool = create_a2ui_tool(builder.get_catalog())
parser = ClaudeStreamParser(catalog=builder.get_catalog())

client = anthropic.Anthropic()
with client.messages.stream(
    model="claude-sonnet-4-6",
    system=builder.build(role_description="You create user interfaces."),
    tools=[tool],
    max_tokens=8192,
    messages=[{"role": "user", "content": "make me a form"}],
) as stream:
    for event in stream:
        for part in parser.process_event(event):
            if part.a2ui_json:
                # A complete payload checked against the active catalog
                render(part.a2ui_json)
            if part.text:
                # Conversational text from Claude
                print(part.text, end="")

The parser also supports the <a2ui-json> tag mode (no tool use), where Claude embeds the JSON directly in its text response between <a2ui-json> and </a2ui-json> tags.


Automatic repairs

Claude is remarkably good at generating valid A2UI, but it occasionally makes mistakes — especially the smaller models. The package includes six repair functions that fix these issues transparently before validation, so the caller never sees a false rejection:

Repair What it fixes Example
patch_catalog_schema DateTimeInput.min/max uses oneOf of three date formats, which jsonschema rejects as ambiguous Changes oneOf to anyOf in the schema
repair_orphans Components created by Claude but not connected to the root tree Reconnects orphans as children of the root container
repair_icons Icon aliases that have a faithful equivalent in the active catalog (trash, pencil, gear, ...) Maps only semantic aliases; values such as cloud remain invalid so Claude can retry without a misleading substitute
repair_functions FunctionCall with functions that don't exist in the active catalog (ternary, if, switch) Replaces with a contextual literal or removes an invalid check without altering valid custom functions
repair_childlists Dynamic child lists ({componentId, path}) used in Row or Column, which only accept static string arrays Converts to a static array of component IDs
repair_markdown_text Markdown markers in labels, text, tabs, options, accessibility content, and validation messages Preserves the visible content without relying on a Markdown renderer

All repairs are enabled by default in generate_a2ui, A2uiConversation, and ClaudeStreamParser. They can be disabled with repair=False for strict validation.

When log_repairs=True, the RetryResult.repairs list contains human-readable descriptions of what was repaired:

result = generate_a2ui(client, "make me a weather panel", log_repairs=True)
for r in result.repairs:
    print(f"Repaired: {r}")
# Repaired: Icon 'delete_icon': 'trash' substituted with 'delete'
# Repaired: Text 'star1' emptied (possible function removed)

Prompt caching

The A2UI system prompt is large: it includes the full JSON Schema, few-shot examples, the list of 59 valid icon names, and the 14 catalog functions. Sending this on every call would be expensive and slow.

generate_a2ui and A2uiConversation enable prompt caching by default (use_cache=True). This adds cache_control: {type: "ephemeral"} to the system prompt block, allowing Anthropic to cache it and reduce the cost by approximately 80% on subsequent calls within the cache window.

To disable caching (for debugging or testing):

result = generate_a2ui(client, "make me a form", use_cache=False)

Type safety

The package ships with a py.typed marker for typed-editor integrations. Its own source is checked with Mypy in CI; downstream applications should still type-check their renderer and action adapters.


Supported versions

A2UI is an evolving protocol. The package supports three versions:

Version Catalog Components Functions
v0.8 standard (legacy) 18 (includes MultipleChoice) 0
v0.9 basic 18 (includes ChoicePicker) 14
v0.9 minimal 5 (Text, Row, Column, Button, TextField) 1 (capitalize)
v0.9.1 basic 18 14

The default is v0.9 with the Basic Catalog. To use a different version or catalog:

from anthropic_a2ui import ClaudeA2uiPromptBuilder

# v0.8 legacy
builder = ClaudeA2uiPromptBuilder(version="0.8")

# Pruned to specific components (saves tokens)
builder = ClaudeA2uiPromptBuilder()
prompt = builder.build(
    role_description="You create simple forms.",
    allowed_components=["Text", "TextField", "Button"],
)

The compatibility layer supports a2ui-agent-sdk from 0.2.4 through the 0.4.x line. CI runs the complete suite against both the minimum supported SDK and the newest version allowed by the package.


Model verification

The automated suite uses local stream doubles plus a real Chromium renderer regression for Tabs and Modal interaction. Release 0.1.4 generated 30 schema-valid natural UI requests against Haiku 4.5, Opus 4.7, and Opus 4.8. The current fire-test runner only records final ok after desktop and mobile rendering, media loading, modal cancellation, tab changes, overflow checks, and browser error checks also pass. Model names and capabilities evolve, so run the credentialed fire test with the exact model and catalog before production use.


API reference

High-level functions

  • generate_a2ui(client, prompt, **kwargs) -> RetryResult: one-shot generation with retries and caching.
  • generate_a2ui_async(client, prompt, **kwargs) -> RetryResult: async version.
  • A2uiConversation(client, **kwargs): multi-turn conversation. .send(prompt) -> ConversationTurn, .reset().
  • A2uiConversationAsync(client, **kwargs): async version.
  • create_a2ui_output_config(catalog) -> dict: output_config for forced JSON mode. Pair with parse_json_response(message, catalog).
  • create_a2ui_response_format(catalog) -> dict: low-level output_config.format object for forced JSON mode.

Building blocks

  • ClaudeA2uiPromptBuilder(version, catalogs): constructs the system prompt. .build(role_description, ...) returns the prompt string. .get_catalog() returns the A2uiCatalog.
  • create_a2ui_tool(catalog, **kwargs) -> dict: tool definition for Anthropic.
  • ClaudeStreamParser(catalog, **kwargs): stream parser. .process_event(event) -> list[ResponsePart]. .parse_stream(stream) is an iterator shortcut.
  • validate_tool_input(catalog, input_json, *, repair, strict_integrity): validates a payload against the schema and returns the repaired payload.
  • to_a2ui_part(payload) -> A2uiPart: wraps a payload for transport with MIME application/a2ui+json.
  • to_a2a_datapart(part): returns the compatible legacy A2A DataPart when that API is installed, otherwise returns the portable dictionary.

Repair functions

  • repair_orphans(payload): reconnects orphaned components.
  • repair_icons(payload): fixes invalid icon names.
  • repair_functions(payload): fixes non-existent function calls.
  • repair_childlists(payload): fixes dynamic child lists in Row/Column.
  • repair_markdown_text(payload): removes Markdown markers from visible UI text while preserving data values, URLs, paths, and functions.
  • repair_payload(payload, catalog=...): applies the full repair chain using the active catalog's icons and functions.
  • patch_catalog_schema(schema): patches the DateTimeInput schema.
  • find_orphans(payload) -> list[str]: diagnostic, returns orphan IDs.

License

Apache-2.0.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anthropic_a2ui-0.1.5.tar.gz (39.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anthropic_a2ui-0.1.5-py3-none-any.whl (40.6 kB view details)

Uploaded Python 3

File details

Details for the file anthropic_a2ui-0.1.5.tar.gz.

File metadata

  • Download URL: anthropic_a2ui-0.1.5.tar.gz
  • Upload date:
  • Size: 39.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for anthropic_a2ui-0.1.5.tar.gz
Algorithm Hash digest
SHA256 b618c8f9a704f4505c4070a702f96640873524e7c3399422d6e84a2db88126f7
MD5 bac58b0536df3ec5981e10681a00d1cb
BLAKE2b-256 246c63817f7303925c571f3382a83c35bd3eb44b2b3c09d965ff25db4bedaf9a

See more details on using hashes here.

Provenance

The following attestation bundles were made for anthropic_a2ui-0.1.5.tar.gz:

Publisher: publish.yml on 686f6c61/anthropic-a2ui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file anthropic_a2ui-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: anthropic_a2ui-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 40.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for anthropic_a2ui-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 f694a1f8bbddc2bc7d6c4ba62281cc768b8fed6b62192d77608d4da36fc747fd
MD5 e9f2473ff514806173d8f9353bba3724
BLAKE2b-256 451c0f92f6e2db7ea25e7435c8bd5fca01baa6d5e52347a134be9802d1bca563

See more details on using hashes here.

Provenance

The following attestation bundles were made for anthropic_a2ui-0.1.5-py3-none-any.whl:

Publisher: publish.yml on 686f6c61/anthropic-a2ui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page