Dynamic interfaces from structured JSON. Agents send component specs, frontend renders securely. Charts, tables, forms, actions—all type-safe and composable.
AG-UI (Agent-Generated UI) allows AI agents to create dynamic, interactive user interfaces by sending JSON specifications instead of building UI code. The frontend safely renders these specifications into beautiful, interactive components.
This enables agents to create custom dashboards, forms, tables, and visualizations tailored to each user’s needs, all without requiring UI development skills.
AG-UI integrates with the 6-dimension ontology:
const dashboard = {
type: "layout",
layout: "grid",
grid: { columns: 2, gap: 16 },
children: [
{
type: "card",
layout: { gridColumn: "span 2" },
title: "Sales Overview",
children: [
{
type: "chart",
data: salesData,
width: "full"
}
]
},
{
type: "card",
title: "Recent Orders",
children: [
{
type: "table",
columns: orderColumns,
data: recentOrders
}
]
},
{
type: "card",
title: "Team Performance",
children: [
{
type: "chart",
data: performanceData
}
]
}
]
};
AG-UI implements security through:
AG-UI supports WebSocket-based real-time updates:
Agent WebSocket Connection User
│ │
├─ Send initial UI spec ──────────────────────→ │
│ │ Render
│ │
├─ Send update: { data: newData } ──────────→ │
│ │ Update
│ │
← [ User interacts: { action: submit } ] ────── │
│ │
│ Handle action │
│ Send response: { status: success } ───────→ │
│ │ Show confirmation
When users interact with AG-UI components:
// User clicks button or submits form
user_action = {
componentId: "form_contact",
action: "submit",
values: {
email: "[email protected]",
message: "Hello"
},
timestamp: Date.now()
};
// Agent receives and handles
if (user_action.action === "submit") {
const result = await handleSubmission(user_action.values);
// Send response back to UI
await ui.sendResponse({
componentId: user_action.componentId,
status: "success",
message: "Message sent successfully!",
nextAction: "close" // Close form after success
});
// Log interaction
await ctx.db.insert("events", {
type: "ui_interaction",
metadata: {
protocol: "agui",
componentId: user_action.componentId,
action: user_action.action
},
timestamp: Date.now()
});
}
AG-UI includes performance features:
| Aspect | AG-UI | ACP | MCP |
|---|---|---|---|
| Primary Use | UI rendering | Messaging | Resource access |
| User Interaction | Full UX | None | Tool calling |
| Real-time | WebSocket | Async queue | Streaming |
| Components | Rich variety | Message-based | Tool-based |
✅ Keep specs small: Smaller JSON = faster transmission ✅ Use real-time wisely: Limit WebSocket updates ✅ Provide feedback: Show loading, success, error states ✅ Validate input: Server-side validation in handlers ✅ Monitor performance: Track render times and user satisfaction ✅ Version your specs: Support multiple schema versions
How this protocol maps to the 6-dimension ontology
UI components scoped to groups for multi-tenancy
Role determines which UI components and actions are available
UI component messages stored as things
User interactions with components tracked as connections
Component renders, interactions, and updates logged as events
Component patterns and user interaction data used for optimization
JSON Schema validation ensures safe component rendering
WebSocket streaming for live UI updates
Build complex UIs from simple component definitions
Users can interact with generated UIs (click, form submit, etc)
Agents create custom dashboards showing real-time data
Agents generate forms with conditional fields based on context
Agents create sortable, filterable tables of data
Agents generate charts visualizing data analysis results
// Agent creates and sends a chart UI
import { AgentUI } from "@agui/sdk";
const ui = new AgentUI();
// Create chart spec
const chartSpec = {
type: "chart",
data: {
type: "bar",
labels: ["Q1", "Q2", "Q3", "Q4"],
datasets: [
{
label: "Revenue",
data: [120000, 150000, 180000, 210000],
borderColor: "rgb(75, 192, 192)",
fill: false
}
]
},
options: {
responsive: true,
title: "Annual Revenue Trend"
}
};
// Send to user
await ui.send({
componentType: "chart",
spec: chartSpec,
layout: {
width: "full",
height: "400px"
}
});
// Store in ontology
const thingId = await ctx.db.insert("things", {
type: "message",
name: "Revenue Chart",
groupId: ctx.auth?.getOrganizationId?.(),
properties: {
protocols: {
agui: {
componentType: "chart",
spec: chartSpec,
createdBy: "agent_analyst"
}
}
},
status: "active",
createdAt: Date.now(),
updatedAt: Date.now()
});
// Agent creates form for user interaction
const formSpec = {
type: "form",
fields: [
{
name: "email",
type: "email",
label: "Email Address",
required: true,
validation: {
pattern: "^[^@]+@[^@]+\\.[^@]+$",
message: "Invalid email format"
}
},
{
name: "messageType",
type: "select",
label: "Message Type",
options: [
{ value: "feedback", label: "Feedback" },
{ value: "bug", label: "Bug Report" },
{ value: "feature", label: "Feature Request" }
]
},
{
name: "message",
type: "textarea",
label: "Your Message",
required: true,
placeholder: "Please describe..."
}
],
actions: [
{
type: "submit",
label: "Send",
style: "primary",
action: {
type: "send_message",
handler: "handleFormSubmit"
}
},
{
type: "button",
label: "Cancel",
style: "secondary",
action: { type: "close" }
}
]
};
// Send form to user
const formId = await ui.send({
componentType: "form",
spec: formSpec,
onSubmit: async (values) => {
// Handle form submission
console.log("Form submitted:", values);
// Log interaction
await ctx.db.insert("events", {
type: "ui_interaction",
groupId: ctx.auth?.getOrganizationId?.(),
metadata: {
protocol: "agui",
componentType: "form",
action: "submit",
values: values
},
timestamp: Date.now()
});
}
});
// Agent creates table that updates in real-time
const tableSpec = {
type: "table",
columns: [
{ key: "id", title: "ID", width: "60px" },
{ key: "name", title: "Name", width: "200px" },
{ key: "status", title: "Status", width: "100px" },
{ key: "progress", title: "Progress", width: "150px" }
],
data: [
{ id: 1, name: "Task A", status: "active", progress: 65 },
{ id: 2, name: "Task B", status: "pending", progress: 0 },
{ id: 3, name: "Task C", status: "completed", progress: 100 }
],
features: {
sortable: true,
filterable: true,
pagination: { pageSize: 10 }
}
};
// Send table
const table = await ui.send({
componentType: "table",
spec: tableSpec,
live: true // Enable real-time updates
});
// Update table as data changes
const taskStream = db.query("things")
.withIndex("by_type", q => q.eq("type", "task"))
.watch();
for await (const change of taskStream) {
const updatedData = await fetchTableData();
// Push update to client
await table.update({
data: updatedData
});
// Log update event
await ctx.db.insert("events", {
type: "ui_updated",
metadata: {
protocol: "agui",
componentType: "table",
rowCount: updatedData.length
},
timestamp: Date.now()
});
}