How to Customize A2UI Outputs: Schema Management, Prompt Engineering, and Response Parsing

Customize A2UI outputs by controlling the component schema catalog, pruning allowed components in system prompts, and parsing LLM responses into structured A2A parts using A2uiSchemaManager and parse_response_to_parts.

A2UI (Agent-to-UI) enables large language models to drive rich, component-based user interfaces by returning JSON payloads wrapped in special delimiters. When you customize A2UI outputs in the google/A2UI repository, you manipulate three distinct layers: the schema catalog defining available UI components, the prompt generation controlling which components the LLM may use, and the response parsing converting raw text into typed A2A parts. This guide demonstrates the specific SDK classes, configuration options, and source file locations required to tailor these outputs for specialized use cases.

The Three Layers of Output Customization

A2UI customization operates across three architectural layers defined in the source code:

  • Schema / Catalog: The JSON contracts describing UI components (cards, charts, maps), managed through A2uiSchemaManager in agent_sdks/python/src/a2ui/core/schema/manager.py. You control this by loading bundled catalogs, registering custom catalogs, or accepting inline catalogs from clients.

  • Prompt Generation: The assembly of system instructions via generate_system_prompt, which injects schema definitions and examples into the LLM context. You customize this through parameters like allowed_components, include_schema, and ui_description.

  • Response Parsing: The transformation of delimited LLM output (<|A2UI|>...</|A2UI|>) into A2A protocol parts. You handle this using parse_response_to_parts and create_a2ui_part in agent_sdks/python/src/a2ui/a2a.py.

Loading and Extending Component Catalogs

The catalog is a JSON document describing every renderable component and its properties. A2UI ships with a default standard catalog at catalogs/standard.json, but you must extend or replace this to customize available outputs.

Registering Server-Side Catalogs

Register custom catalogs by passing a CatalogConfig to the A2uiSchemaManager constructor. The manager loads schemas via _load_schemas and selects the active catalog through _select_catalog logic:

from a2ui.core.schema.manager import A2uiSchemaManager
from a2ui.basic_catalog.provider import BasicCatalog
from a2ui.core.schema.constants import VERSION_0_9

schema_manager = A2uiSchemaManager(
    version=VERSION_0_9,
    catalogs=[
        BasicCatalog.get_config(
            version=VERSION_0_9,
            examples_path="examples",
        )
    ],
    accepts_inline_catalogs=False,
)

For complete custom schemas, reference samples/agent/adk/component_gallery/a2ui_schema.py, which demonstrates how to define component fields like outputFormat for DateTimeInput inputs.

Accepting Inline Catalogs from Clients

To allow dynamic customization without server redeployment, enable accepts_inline_catalogs=True. Clients then submit their own catalogs in the client_ui_capabilities payload:

{
  "client_ui_capabilities": {
    "inlineCatalogs": [
      { "$ref": "https://my.cdn/custom_catalog.json" }
    ]
  }
}

The manager automatically selects the first inline catalog entry when processing the request.

Pruning Components for Targeted Outputs

Restrict which components the LLM can emit by passing an allowed_components list to generate_system_prompt. This is essential for channel-specific customization, such as hiding GoogleMap for text-only interfaces:

prompt = schema_manager.generate_system_prompt(
    role_description="You are a helpful travel assistant.",
    ui_description="Render results using cards and lists.",
    allowed_components=["Card", "Text", "DateTimeInput"],
    include_schema=True,
    include_examples=True,
)

Internally, the manager calls catalog.with_pruned_components(allowed_components) to filter the schema before injection into the prompt, as implemented in agent_sdks/python/src/a2ui/core/schema/manager.py.

Engineering the System Prompt

The generate_system_prompt method assembles the final LLM instructions by concatenating:

  1. Your role_description (static persona text)
  2. The ui_description (human-readable UI hints)
  3. The catalog schema JSON (if include_schema=True)
  4. Example UI snippets (if include_examples=True and examples_path is configured)

A typical implementation from samples/agent/adk/restaurant_finder/agent.py demonstrates conditional prompt creation:

instruction = (
    schema_manager.generate_system_prompt(
        role_description=ROLE_DESCRIPTION,
        ui_description=UI_DESCRIPTION,
        include_schema=True,
        include_examples=True,
        validate_examples=True,
    )
    if use_ui
    else get_text_prompt()
)

Set validate_examples=True to ensure included snippets conform to the active catalog schema.

Parsing LLM Responses into A2A Parts

LLM responses contain A2UI JSON wrapped in <|A2UI|> and </|A2UI|> delimiters. The SDK provides two primary methods for converting this raw output into structured parts for the A2A protocol.

Extracting JSON from Delimited Responses

Use parse_response_to_parts from agent_sdks/python/src/a2ui/a2a.py to separate plain text from UI JSON and optionally validate against the catalog:

from a2ui.a2a import parse_response_to_parts

parts = parse_response_to_parts(
    content=llm_raw_output,
    validator=schema_manager.get_selected_catalog().validator,
    fallback_text="Sorry, I couldn't generate a UI response."
)

This function returns a list of Part objects ready for inclusion in the A2A response.

Manual Part Creation

For programmatic UI generation or post-processing, construct parts manually using create_a2ui_part:

from a2ui.a2a import create_a2ui_part

a2ui_json = {
    "beginRendering": {
        "surfaceId": "main",
        "components": [
            {"id": "c1", "component": {"Text": {"text": {"literalString": "Hello"}}}}
        ],
    }
}
a2ui_part = create_a2ui_part(a2ui_json)

This returns a Part containing a DataPart with the specified JSON payload.

Configuring Output Modes and MIME Types

Agents declare supported content types via AgentCard fields default_input_modes and default_output_modes. While standard samples use ["text", "text/plain"], A2UI parts carry the MIME type defined by A2UI_MIME_TYPE in agent_sdks/python/src/a2ui/a2a.py.

To support proprietary clients or custom media types, modify the constant:

A2UI_MIME_TYPE = "application/vnd.myapp+a2ui"

All parts created via create_a2ui_part will now carry this type, allowing clients to differentiate A2UI payloads from standard text responses.

Complete Customization Example

The following end-to-end demonstration shows loading a custom catalog, pruning components, and parsing responses:

import json
from a2ui.core.schema.manager import A2uiSchemaManager
from a2ui.core.schema.catalog import CatalogConfig
from a2ui.core.schema.constants import VERSION_0_9
from a2ui.a2a import parse_response_to_parts

# 1. Load custom catalog from JSON file

custom_schema = json.load(open("my_catalogs/custom_v0_9.json"))
catalog_config = CatalogConfig(
    name="my_custom",
    provider=lambda: custom_schema,
    examples_path="my_catalogs/examples",
)

schema_manager = A2uiSchemaManager(
    version=VERSION_0_9,
    catalogs=[catalog_config],
    accepts_inline_catalogs=False,
)

# 2. Generate constrained prompt

system_prompt = schema_manager.generate_system_prompt(
    role_description="You are a finance assistant.",
    ui_description="Show results using Cards and Tables only.",
    allowed_components=["Card", "Table"],
    include_schema=True,
    include_examples=False,
)

# 3. Process LLM response with validation

llm_response = llm.generate(system_prompt, user_query="Show last 5 months revenue.")
parts = parse_response_to_parts(
    content=llm_response,
    validator=schema_manager.get_selected_catalog().validator,
    fallback_text="I couldn't render the UI."
)

Summary

  • Catalog Management: Customize available UI components by registering custom JSON catalogs via CatalogConfig in A2uiSchemaManager, either server-side or through inline client capabilities.
  • Component Filtering: Restrict LLM output options using the allowed_components parameter in generate_system_prompt, which internally calls with_pruned_components to filter the schema.
  • Response Processing: Convert delimited LLM outputs to A2A parts using parse_response_to_parts for automatic extraction and validation, or create_a2ui_part for manual construction.
  • Protocol Configuration: Adjust MIME types in agent_sdks/python/src/a2ui/a2a.py and output modes in AgentCard declarations to match specific client requirements.

Frequently Asked Questions

How do I add a custom property to an existing A2UI component?

Edit your custom catalog JSON to extend the component schema. For example, adding an outputFormat field to DateTimeInput allows the LLM to specify date formatting strings:

{
  "components": {
    "DateTimeInput": {
      "properties": {
        "outputFormat": {
          "type": "string",
          "description": "Desired string format (e.g., YYYY-MM-DD)."
        }
      }
    }
  }
}

Register this catalog with A2uiSchemaManager and set include_schema=True when generating prompts to expose the new property to the LLM.

What is the difference between inline catalogs and server-side catalogs?

Server-side catalogs are registered when initializing A2uiSchemaManager via the catalogs parameter, giving you full control over available components through the _load_schemas mechanism. Inline catalogs are sent by the client in the client_ui_capabilities payload and loaded dynamically when accepts_inline_catalogs=True. Use server-side catalogs for consistent, controlled UI experiences; use inline catalogs to support diverse client capabilities without redeploying the agent.

How does parse_response_to_parts handle invalid JSON from the LLM?

The function extracts content between <|A2UI|> and </|A2UI|> delimiters and attempts JSON parsing. If you provide a validator parameter (typically from schema_manager.get_selected_catalog().validator), it validates the structure against the active catalog schema. When parsing or validation fails, the function returns a text part containing your fallback_text instead of crashing, ensuring graceful degradation in production environments.

Can I use A2UI with plain text outputs only?

Yes. Set include_schema=False in generate_system_prompt to omit the JSON schema from the LLM context, or provide an empty allowed_components list to prevent UI generation entirely. The parse_response_to_parts function will return the raw text content as a text part without attempting JSON extraction, allowing you to support text-only channels while maintaining the same agent architecture and response parsing pipeline.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →