DEV Community

vishalmysore
vishalmysore

Posted on

A2UI Protocol: Building Intelligent Agent-to-User Interfaces

Introduction

A2UI (Agent-to-User Interface) is a revolutionary protocol developed by Google that enables AI agents to dynamically render rich, interactive user interfaces. Unlike traditional REST APIs that return static JSON data, A2UI allows AI agents to generate complete UI experiences on-the-fly, creating a seamless bridge between intelligent backend services and modern frontend applications.

Complete demo is deployed here https://vishalmysore.github.io/simplea2ui/

You can use queries like

  • Compare Honda and Toyota for me?
  • What Food does Vishal like to eat?
  • Can you book a restaruant for me? I need to eat food I am very hungry

What is A2UI Protocol?

A2UI v0.8 is a specification that defines how AI agents can communicate structured UI components to client applications. Instead of hardcoding UI logic in the frontend, agents can decide what interface to present based on context, user input, and application state.

Key Features

  • Dynamic UI Generation: Agents create UI components programmatically based on business logic
  • Component-Based Architecture: Uses standard UI components (Text, TextField, Button, Column, etc.)
  • Data Model Binding: Two-way data binding between UI components and backend data models
  • Action Handling: Buttons trigger backend actions with contextual data
  • Protocol Flexibility: Supports both rich UI rendering and plain text responses

Technical Architecture

Server Implementation: A2AJava Framework

This project implements A2UI protocol using a2ajava (Actions for AI Java), a powerful framework that:

  • Automatically exposes Java methods as AI-callable actions
  • Provides annotation-driven agent and action definitions
  • Integrates with Spring Boot for enterprise-grade applications
  • Supports multiple callback types including A2UI, text, and custom protocols

Core Components

1. Agent Definition

Agents are defined using @Agent annotation with descriptive metadata:

@Service
@Agent(groupName = "compareCar", groupDescription = "compare 2 cars")
@Slf4j
public class CompareCarService implements A2UIDisplay {
    private ThreadLocal<ActionCallback> callback = new ThreadLocal<>();
    // ... implementation
}
Enter fullscreen mode Exit fullscreen mode

2. Action Methods

Actions are Java methods annotated with @Action that can be invoked by AI systems:

@Action(description = "compare 2 cars")
public Object compareCar(String car1, String car2) {
    log.info("Comparing cars: {} vs {}", car1, car2);

    String betterCar = determineBetterCar(car1, car2);

    // Dual-mode support: UI or text response
    if(isUICallback(callback)) {
        return createComparisonUI(car1, car2, betterCar, result);
    } else {
        return "Text-based response: " + betterCar + " is better";
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Dual-Mode Protocol Support

The framework supports both text and UI protocols through intelligent callback detection:

  • Text Mode: When called directly or via text-based AI systems, returns plain String responses
  • UI Mode: When called with A2UI callback, returns structured component trees
// Detection logic
if(isUICallback(callback)) {
    // Return A2UI component structure
    return buildA2UIMessage(surfaceId, rootId, components);
} else {
    // Return plain text
    return "Simple text response";
}
Enter fullscreen mode Exit fullscreen mode

A2UI Component Structure

Components Catalog

The protocol defines standard components:

  • Text: Display static or dynamic text content
  • TextField: Input fields with data model binding
  • Button: Interactive buttons with action callbacks
  • Column: Layout container for vertical arrangement

Data Model Format (A2UI v0.8 Spec)

Data models use adjacency list format for efficient state management:

{
  "dataModelUpdate": {
    "contents": [
      {
        "key": "form",
        "valueMap": [
          {
            "key": "name",
            "valueString": ""
          },
          {
            "key": "email",
            "valueString": ""
          }
        ]
      }
    ],
    "surfaceId": "user_form"
  }
}
Enter fullscreen mode Exit fullscreen mode

TextField Component

TextFields bind to data model paths using the text property:

{
  "component": {
    "TextField": {
      "label": { "literalString": "Person's Name" },
      "text": { "path": "/form/name" }
    }
  },
  "id": "name_input"
}
Enter fullscreen mode Exit fullscreen mode

Button with Context

Buttons specify which data to send via context array:

{
  "component": {
    "Button": {
      "action": {
        "name": "whatThisPersonFavFood",
        "context": [
          {
            "key": "name",
            "value": { "path": "/form/name" }
          }
        ]
      },
      "child": "submit_button_text"
    }
  },
  "id": "submit_button"
}
Enter fullscreen mode Exit fullscreen mode

Implementation Examples

Simple Service with Form Submission

@Service
@Agent(groupName = "whatThisPersonFavFood", 
       groupDescription = "Provide persons name and find what they like")
public class SimpleService implements A2UIDisplay {

    @Action(description = "Get the favourite food of a person")
    public Object whatThisPersonFavFood(String name) {
        String favFood = lookupFavoriteFood(name);

        if(callback != null && callback.getType().equals(CallBackType.A2UI.name())) {
            return createFavoriteFoodUI(name, favFood);
        } else {
            return favFood;
        }
    }

    private Map<String, Object> createFavoriteFoodUI(String name, String favFood) {
        // Create UI components
        List<Map<String, Object>> components = new ArrayList<>();

        // Add title
        components.add(createTextComponent("title", "Favorite Food Finder", "h2"));

        // Add result display
        components.add(createTextComponent("result", 
            name + "'s favorite food is: " + favFood));

        // Add form for next query
        components.add(createTextFieldComponent("name_input", 
            "Person's Name", "/form/name"));

        // Add submit button with context binding
        Map<String, String> contextBindings = new HashMap<>();
        contextBindings.put("name", "/form/name");
        components.add(createButtonComponent("submit_button", 
            "Find Favorite Food", "whatThisPersonFavFood", contextBindings));

        // Initialize data model
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("/form/name", "");

        return buildA2UIMessageWithData(surfaceId, rootId, components, dataModel);
    }
}
Enter fullscreen mode Exit fullscreen mode

Multi-Step Workflow: Restaurant Booking

Complex workflows with multiple action methods:

@Service
@Agent(groupName = "restaurantBooking", 
       groupDescription = "Book restaurant reservations with menu selection")
public class RestaurantBookingService implements A2UIDisplay {

    @Action(description = "Book a restaurant reservation - shows form")
    public Object bookRestaurantReservation(String restaurantName) {
        if(isUICallback(callbackThreadLocal)) {
            return createReservationFormUI(restaurantName);
        }
        return "Please provide reservation details...";
    }

    @Action(description = "Confirm restaurant reservation with all details")
    public Object confirmReservation(String restaurantName, String date, 
                                     String time, int numberOfPeople, 
                                     String menuType, String specialRequests) {
        String confirmationNumber = generateConfirmationNumber();

        if(isUICallback(callbackThreadLocal)) {
            return createConfirmationUI(restaurantName, date, time, 
                                       numberOfPeople, menuType, 
                                       specialRequests, confirmationNumber);
        }
        return "Reservation confirmed! Confirmation #" + confirmationNumber;
    }
}
Enter fullscreen mode Exit fullscreen mode

Technical Benefits

1. Separation of Concerns

  • Backend controls UI logic and workflow
  • Frontend focuses on rendering and user interaction
  • Clean API boundaries with strong typing

2. Dynamic Adaptation

  • UI adapts to business rules without frontend redeployment
  • A/B testing at the protocol level
  • Personalized experiences based on user context

3. Type Safety

  • Java type system ensures correct data structures
  • Compile-time validation of component hierarchies
  • IDE support for autocomplete and refactoring

4. Backward Compatibility

  • Same codebase supports legacy text-based clients
  • Graceful degradation for unsupported clients
  • Progressive enhancement for modern interfaces

A2UIDisplay Utility Interface

The project provides a utility interface with helper methods:

public interface A2UIDisplay {
    // Create text components
    Map<String, Object> createTextComponent(String id, String text, String usageHint);

    // Create input fields with data binding
    Map<String, Object> createTextFieldComponent(String id, String label, String dataPath);

    // Create buttons with action callbacks and context
    Map<String, Object> createButtonComponent(String id, String buttonText, 
                                               String actionName, 
                                               Map<String, String> contextBindings);

    // Create layout containers
    Map<String, Object> createRootColumn(String rootId, List<String> childIds);

    // Build complete A2UI messages
    Map<String, Object> buildA2UIMessageWithData(String surfaceId, String rootId,
                                                  List<Map<String, Object>> components,
                                                  Map<String, Object> dataModelValues);
}
Enter fullscreen mode Exit fullscreen mode

Data Flow Architecture

Request-Response Cycle

  1. AI Request: AI system calls action method with parameters
  2. Callback Detection: Service checks callback type (A2UI vs text)
  3. Business Logic: Execute core functionality
  4. Response Generation:
    • A2UI: Build component tree with data model
    • Text: Return simple string
  5. Client Rendering: Frontend processes A2UI message and renders UI
  6. User Interaction: User fills form and clicks button
  7. Context Submission: Button action sends bound data back to backend
  8. Next Action: Cycle repeats with new action method

Data Model Lifecycle

1. Backend initializes data model with empty values
   └─> dataModelUpdate.contents = [{ key: "form", valueMap: [...] }]

2. Frontend binds TextField.text to data model paths
   └─> { "text": { "path": "/form/name" } }

3. User types input → Frontend updates data model in real-time

4. User clicks button → Frontend extracts context values
   └─> Button.action.context specifies which paths to send

5. Backend receives action call with populated parameters
   └─> confirmReservation(restaurantName="Joe's Diner", date="2026-01-15", ...)
Enter fullscreen mode Exit fullscreen mode

Best Practices

1. Consistent Naming

  • Use descriptive action names matching button actions
  • Data model paths follow REST-like conventions: /resource/field

2. Error Handling

  • Validate input in action methods
  • Return user-friendly error messages in UI mode

3. State Management

  • Use ThreadLocal for callback storage in multi-threaded environments
  • Initialize data models with sensible defaults

4. Component Hierarchy

  • Keep component trees shallow for better performance
  • Use semantic component IDs for debugging

5. Context Binding

  • Only bind necessary data to button context
  • Use explicit parameter names matching action method signatures

Integration with Spring Boot

The framework seamlessly integrates with Spring Boot:

@Service  // Spring managed bean
@Agent(groupName = "myAgent", groupDescription = "Agent description")
public class MyService implements A2UIDisplay {

    @Autowired
    private SomeDependency dependency;  // Spring dependency injection works

    private ActionCallback callback;  // Autowired by tools4ai framework
    private AIProcessor processor;    // Autowired by tools4ai framework

    @Action(description = "Action description")
    public Object myAction(String param) {
        // Use injected dependencies
        // Access callback and processor
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

A2UI protocol represents a paradigm shift in how AI agents interact with users. By combining the power of intelligent backend services with dynamic UI generation, developers can build highly adaptive applications that respond to user needs in real-time.

The a2ajava framework makes implementing A2UI straightforward through:

  • Annotation-driven development
  • Dual-mode protocol support (text and UI)
  • Spring Boot integration
  • Type-safe component generation
  • Reusable utility interfaces

This architecture enables teams to build sophisticated conversational UIs, multi-step workflows, and context-aware applications while maintaining clean separation between business logic and presentation layers.

Resources


Article written for springa2ui project - A demonstration of A2UI protocol implementation using a2ajava framework and Spring Boot

Top comments (0)