What the user sees
When the agent calls an A2UI tool, the chat client renders the surface in the conversation flow, not as a code block or screenshot, but as a real component the user can click, type into, or scroll.
The user’s response (submitted form values, selected option, button click) is dispatched as an event that the agent receives on its next turn.
How it works
The MCP server is just a regular Prisme.ai workspace with an HTTP endpoint that speaks JSON-RPC 2.0. Each tool’s response contains a__surface payload describing the components to render.
Add A2UI tools to your agent
You bring A2UI tools into an agent in three steps:- Create a workspace in Builder to host the MCP server
- Declare the tools in the workspace config and implement one automation per tool
- Attach the MCP server to your agent under Capabilities
Step 1: Create the workspace
In Builder, create a new workspace (or use an existing one). The fastest way to bootstrap is to start from the starter-mcp repository on GitHub; it contains the JSON-RPC routing automation, a sample tool, and the workspace config layout. Clone it, push it to your environment, and you have a working MCP server you can extend. If you’d rather build from scratch, the minimal layout is:Step 2: Declare your tools
The MCP server advertises its tools to the agent throughtools/list. The list lives in the workspace’s config.value.mcpTools so you can add, remove, or update tools without editing automations.
Add one entry per tool with name, description, and inputSchema:
output must contain a content array (MCP convention) and a __surface object describing the UI:
content[].textis what the LLM sees as the tool’s textual result (it can reason about what was displayed).__surfaceis what the chat client renders for the user.
Step 3: Attach the MCP server to your agent
- Open the agent in Agent Creator
- Click Capabilities → Add Capability
- Pick MCP Server
- Configure:
- Server URL: the MCP endpoint of your workspace, e.g.
https://api.studio.prisme.ai/v2/workspaces/slug:my-a2ui-server/webhooks/mcp - Display name: what shows up in the capability list (e.g. “A2UI Surfaces”)
- Headers: any auth your server expects (typically none if it’s a workspace-scoped MCP, or
Authorization: Bearer <token>if you require one)
- Server URL: the MCP endpoint of your workspace, e.g.
- Click Add
initialize and tools/list on the server, discovers your tools, and exposes them to the LLM.
Step 4: Nudge the agent in your instructions
Add a few lines to the agent’s Instructions so the LLM actually reaches for the surfaces instead of falling back to text:When the user needs to fill multiple fields, useWithout this nudge, most models will default to plain text because that’s their training prior. The surfaces are tools; they need to be promoted as the better choice for the right situations.show_forminstead of asking questions one at a time. When presenting tabular data (search results, records), useshow_table. For binary decisions or approvals, useshow_confirmationorshow_action_cardand wait for the user’s response before continuing.
Interactive surfaces
A surface is read-only by default. To make buttons clickable and forms submittable, the tool must declare that it waits for the user:wait_for_action: true, the tool call stays pending: the agent pauses, the surface becomes interactive, and the user’s click resolves the call. Without it, the surface renders but every Button is disabled and clicks do nothing. This is the single most common mistake when a surface “renders but doesn’t react”.
The action property
A Button (and the Loader) triggers an action. Exactly two forms are supported:
event: wrapper, a sendMessage verb, a path binding inside the action) is not part of the catalog and results in a button with no handler.
What the agent receives
When the user clicks, the pending tool call resolves and the agent receives:action.nameis the button’s action name: this is how the agent knows what was chosendataModelcontains the full form state when the surface setssend_data_model: true(empty otherwise)
Example: clickable suggested questions
A common pattern: propose follow-up questions as buttons, and treat the clicked one as the user’s next request. The trick is to use the question text itself as the action name:description, tell the LLM what comes back: “The conversation pauses until the user clicks one; the text of the clicked question is returned to the agent as the user’s new request.”
Forms and two-way binding
Input components (TextField, TextArea, Select, CheckBox, Switch, Slider) bind to the data_model through a { path: ... } value (JSON Pointer, RFC 6901):
data_model is attached to the action (set send_data_model: true on the surface), so the agent receives every field in one payload. Text inputs bind on value (or text); CheckBox and Switch bind on checked (or value).
Component reference
Theprisme://blocks/v1 catalog. Every component shares four base properties:
All other props are flat siblings of
component. Any prop value can be a static value or a binding { path: /json/pointer, default: ... } resolved against the surface’s data_model.
Layout
Containers
Display
Interactive
Data
Media
Loader
The
Loader is the one component whose action fires without a click. In poll mode it fetches the URL (same-origin relative path only) every interval seconds; the endpoint responds with { done, progress, message }, and when done is true the action fires with the final response attached. After 3 consecutive failed polls, a <action>_error action fires instead.
Going further
- Boilerplate:
prismeai/starter-mcpis the recommended scaffold - MCP authentication: for OAuth-protected MCP servers (rare for A2UI but possible), see MCP Connections & OAuth
- User-first activation: if some surfaces should only be summoned by the user (e.g. a feedback form), pair the MCP with user-first tools