Copied to Clipboard
Where Does AG-UI come in?
The first version used a Vanilla TS interface to the backend agent system. AG-UI provides a standard way to look at the components:
So What is all this Doing?
All of the setup and configuration allows you to start debugging and visualizing Agent flows using the A2A protocol. The currency Agent has been validated using the A2A Inspector and the MCP interface via Antigravity CLI.
The make help provides the main project targets:
> make help
2.7k in | 204 out | 135 thinking
●くろまる Bash(make help) (ctrl+o to expand)
Here is the output from running make help in the project root directory:
Usage: make <target>
Services:
start - Start background Rust services (MCP + Agent)
stop - Stop background Rust & Frontend services
status - Check status of running services
mcp - Start the Rust MCP Server (foreground)
agent - Start the Rust Currency Agent (foreground)
Vanilla TS Frontend (Port 4173):
frontend-install - Install dependencies for Vanilla TS Frontend
frontend-build - Build production assets for Vanilla TS Frontend
frontend - Build and preview the Vanilla TS Frontend
React Frontend (Port 3000):
react-install - Install dependencies for React Frontend
react-ui - Start React Frontend UI
Development & Quality:
install - Install all Rust & Frontend dependencies
test - Run Rust end-to-end tests (MCP + Agent + Test Client)
test-rust-e2e - Cleanly compile, launch, and run end-to-end client tests
lint - Run cargo clippy and format checks
format - Auto-format Rust code
clean - Remove logs and caches
Cloud Deployment:
deploy - Deploy to Cloud Run using Cloud Build
logs - Read logs from Cloud Run
endpoint - Get the Cloud Run service endpoint
remote-status - Check the status of the remote endpoint
The Makefile provides targets to build and manage the project:
> make install
3.7k in | 174 out | 104 thinking
●くろまる Bash(make install) (ctrl+o to expand)
The installation completed successfully! Here is a summary of what was done:
1. Vanilla TS Frontend ( frontend/frontend/ ): Installed all Node.js dependencies ( npm install ).
2. React Frontend ( frontend-react/ ): Installed all React/Next.js/CopilotKit dependencies ( npm install ).
3. Rust Projects: Cleanly compiled the workspace binaries:
• Built the main Rust Currency Agent ( cargo build ).
• Built the Rust MCP Server ( cargo build --manifest-path mcp-server/Cargo.toml ).
Lint:
> make lint
●くろまる Bash(make lint) (ctrl+o to expand)
The lints passed cleanly with zero warnings or formatting issues!
The check verified:
• Cargo Clippy on both the main Agent codebase and the MCP Server codebase (with -D warnings enforced).
• Cargo Fmt checks on all source files across both projects to ensure standard formatting is ensuring correct, idiomatic
Rust code formatting.
Test:
●くろまる Bash(make test) (ctrl+o to expand)
I have started the end-to-end tests via make test as a background task. I will monitor its progress and notify you as
soon as the test suite completes.
The end-to-end test suite ( make test ) completed successfully!
Here are the key validation details from the run:
1. Service Startup: Both the background Rust services (the MCP Server on 8080 and the Currency Agent Server on 10000 )
successfully booted.
2. End-to-End Query Test:
• Simulated user query: "how much is 100 USD in CAD?"
• The Rust A2A server successfully invoked the Gemini model and the Frankfurter MCP tool.
• A2UI Rendering: It correctly returned a schema-validated, premium visual payload ( <a2ui-json> ) containing
structured components ( Card , Column , LineChart , Table elements) along with the textual response: "100 USD is
equal to 141.82 CAD".
3. Multi-Turn Integration Test:
• Simulated user query: "how much is 100 USD?"
• The agent successfully completed the turn by proactively clarifying the target currency: "To which currency would
you like to convert 100 USD to?"
4. Service Cleanliness: The test client cleanly wrapped up and invoked make stop to shutdown all active background
services.
Time to Start some Currency Arbitrage!
The servers are started:
xbill@penguin:~/currency-agent-rust-agui$make start
Building Rust projects...
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
Starting Rust MCP Server in background...
Waiting for MCP Server to initialize...
Starting Rust Currency Agent in background...
Services started. Logs: mcp.log, agent.log
xbill@penguin:~/currency-agent-rust-agui$make status
Checking status of services...
MCP Server: Running on port 8080 (PID 225534 )
Currency Agent: Running on port 10000 (PID 225564 )
Vite Preview: Running on port 4173 (PID 225593 225594 )
React Frontend: Running on port 3000 (PID 218673 218674 225597 225598 )
and checked end-to-end:
xbill@penguin:~/currency-agent-rust-agui$make test-rust-e2e
Running Rust end-to-end test...
make stop
make[1]: Entering directory '/home/xbill/currency-agent-rust-agui'
Stopping servers...
make[1]: Leaving directory '/home/xbill/currency-agent-rust-agui'
Starting Rust MCP Server (port 8080)...
Starting Rust Currency Agent (port 10000)...
Running Rust Test Client...
cargo run --bin test_client
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
Running `target/debug/test_client`
--- 🔄 Connecting to agent at http://127.0.0.1:10000... ---
--- ✅ Connection successful. Agent Card retrieved: ---
Agent Name: currency_agent
--- 🚀 First turn completed, no further input required for this test case. ---
make stop
make[1]: Entering directory '/home/xbill/currency-agent-rust-agui'
Stopping servers...
make[1]: Leaving directory '/home/xbill/currency-agent-rust-agui'
xbill@penguin:~/currency-agent-rust-agui$
Start the Currency Session
Once all the agents have been started — the react interface on port 3000 is connected with a standard web browser:
The new AG-UI tools are presented:
And in plain text:
250 USD is equal to 219.28 EUR based on the exchange rate of 1 USD = 0.87712 EUR as of 2026年06月26日.
The Co-Pilot chat-style box allows for more complex queries:
Data can be summarized in tables:
bar charts:
even complex dashboards can be built:
JSON Payloads
The key to AG-UI working with A2UI is the clean JSON payload generated by the A2A Agent. The Agent generates the raw data to display and leaves the actual presentation to the AG-UI client.
The JSON will look something like:
<a2ui-json>{"version":"v0.9","updateComponents":{"surfaceId":"currency_agent","components":[{"id":"root","component":"Card","props":{"title":"USD to EUR Currency Conversion","children":["rate_headline","actions_row"]}},{"id":"rate_headline","component":"Text","props":{"value":"250.00 USD is currently equivalent to 229.50 EUR (Rate: 0.9180).","variant":"h3"}},{"id":"actions_row","component":"Row","props":{"children":["btn_reverse","btn_trends"]}},{"id":"btn_reverse","component":"Button","props":{"child":"🔄 Reverse (EUR to USD)","primary":true,"action":{"name":"convert","params":{"amount":250,"from":"EUR","to":"USD"}}}},{"id":"btn_trends","component":"Button","props":{"child":"📈 View Trends","primary":false,"action":{"name":"show_trends","params":{"from":"USD","to":"EUR"}}}}]}}</a2ui-json>
So how does AG-UI actually work?
Antigravity cli was used for a detailed breakdown:
> how does AG-UI actually work in this project?
30.6k in | 1.3k out | 641 thinking
●くろまる Read(/home/xbill/currency-agent-rust-agui/extending-a...ncy-agent-with-ag-ui-and-antigravity-cli-4kd1.md) (ctrl+o to expand)
In this project, the integration of AG-UI (Agent-User Interaction) and A2UI (Agent-to-User Interface) allows the backend
Rust agent to stream interactive, declarative UI components (like cards, tables, and charts) directly to the frontend
rather than relying solely on plain text.
The system is split into three main layers: Backend Schema Provisioning & Generation, Frontend Interception, and Client-
Side Component Catalog Rendering.
──────
### 1. The Backend Agent (Rust)
The compiled Rust agent (main.rs) is fully instructed on how to structure UI components:
• The System Instruction: The agent's system prompt embeds full JSON schemas for A2UI version 0.9 (including
basic_catalog.json containing definitions for Card , Table , LineChart , and BarChart ).
• MCP Integration: When you ask for an exchange rate, the agent uses the get_exchange_rate tool exposed by the Rust MCP
server (main.rs) on port 8080 to query the Frankfurter API.
• Markup Packaging: If the request involves comparative or historical data, the agent generates a declarative JSON payload
representing the visual UI components and wraps it inside custom XML tags:
<a2ui-json>
{
"updateComponents": {
"components": [
{
"id": "chart_rate_history",
"type": "LineChart",
"props": {
"title": "USD to EUR (Last 5 Days)",
"labels": ["Mon", "Tue", "Wed", "Thu", "Fri"],
"values": [0.91, 0.92, 0.91, 0.93, 0.92]
}
}
]
}
}
</a2ui-json>
──────
### 2. Frontend Interception (React UI & Vanilla TS Sandbox)
When the agent sends its token stream, the frontends intercept and parse the <a2ui-json> blocks so that raw JSON never
leaks into the user's chat bubble.
Taking the React Next.js frontend ( frontend-react/ ) as an example:
#### Step A: Catalog Registration ( CopilotProvider.tsx & A2UICustomCatalog.tsx )
In frontend-react/src/components/A2UICustomCatalog.tsx , the client maps the JSON schemas to real visual components using
Zod definitions and custom React renderers:
import { createCatalog } from "@copilotkit/a2ui-renderer";
const customCatalogDefinitions = {
BarChart: {
props: z.object({
title: z.string().optional(),
labels: z.array(z.string()),
values: z.array(z.number()),
color: z.string().optional(),
}),
},
};
const customCatalogRenderers = {
BarChart: ({ props }: any) => {
// Custom premium Tailwind/React visual rendering logic goes here...
}
};
export const customCatalog = createCatalog(customCatalogDefinitions, customCatalogRenderers, {
catalogId: "https://a2ui.org/specification/v0_9/basic_catalog.json"
});
The application root is wrapped in <A2UIProvider catalog={customCatalog}> in CopilotProvider.tsx to make this registry
globally accessible.
#### Step B: Stream Parsing and Surface Binding ( page.tsx )
The Markdown renderer in the assistant's message bubble intercepts the text payload:
1. parseA2UIContent strips out the <a2ui-json> tag block and returns cleanText (displayed as standard text in the
chat bubble) and parsedJson containing the schema block.
2. If valid A2UI JSON exists, an <A2UIContainer> mounts and uses the useA2UI hook:
• It calls processMessages() to dispatch the declarative update to the client-side state machine.
• It renders <A2UIRenderer surfaceId={surfaceId} /> , which reads the active component tree from state and matches it
against your customCatalog to mount the interactive React-render the component natively.
──────
### 3. Summary of Workflow
sequenceDiagram
autonumber
User->>Frontend: "Compare USD, EUR, and GBP"
Frontend->>Rust Agent: Forward User Request (Port 10000)
Rust Agent->>MCP Server: Call get_exchange_rate (Port 8080)
MCP Server->>Frankfurter API: Fetch Exchange Rates
Frankfurter API-->>MCP Server: Rates Data
MCP Server-->>Rust Agent: Rates Data
Rust Agent->>Frontend: Stream Markdown Response + <a2ui-json> payload
Note over Frontend: Intercepts & hides <a2ui-json> markup
Frontend->>A2UI Engine: processMessages(updateComponents)
A2UI Engine-->>Frontend: Render visual components (Table/Chart) inside UI Surface
This architecture ensures a separation of concerns: the backend agent acts purely as an orchestrator of data and layout
structures, and the frontends execute native, high-performance UI rendering on the client side.
Summary
The goal of the demo/article was to enable AG-UI integration on a functional agent supporting common protocols like A2A and MCP.
The currency agent was chosen as a starting point and new AG-UI presentation objects were added to the user interface with Antigravity CLI.
This interface was tested with a frontend local React server.
Finally- the updated project integration was summarized, and code reviewed with Antigravity CLI.