Build MCP widgets
Tools let an AI agent call your API; widgets let it show the result as an interactive UI. A widget is an HTML document the server publishes as a ui:// resource. When you link a widget to a tool, the AI host renders the widget inline in the chat surface instead of just printing the tool's text — a contact picker, an order summary card, a chart, and so on.
Widgets are additive and degrade gracefully: the linked tool still returns its normal JSON/text, so clients that don't support widgets keep working.
How clients consume a widget
When a widget is enabled and linked to a tool, the runtime:
Publishes the HTML as a resource at
ui://<slug>(and a parallelui://<slug>/skybridgevariant), so both Anthropic MCP Apps hosts (Claude) and OpenAI Apps SDK hosts (ChatGPT) can render it.Decorates the linked tool's metadata so the host knows which resource to render when the tool runs (
_meta.ui.resourceUrifor Claude,openai/outputTemplatefor ChatGPT).
The host renders the widget in a sandboxed iframe and brokers messages between the widget and your server.
Managing widgets from an agent
Widgets can be managed two ways: from the dashboard (below) or by an AI agent connected to the FlowCP MCP server. The agent surface exposes widgets as tools — list_widgets, create_widget, update_widget, and delete_widget — so an agent can author and toggle widgets for a server from the chat. Like the dashboard, new widgets start disabled.
Opening the widgets list
From your app's dashboard, click the Widgets tab. The page lists every widget on the selected server, each with an on/off switch. If your app has multiple environments (branches), use the branch buttons to switch between them.
Starting from the library
Don't want to write a widget from scratch? Click Browse library to open the Widget library — a gallery of prebuilt Generative UI templates (an order summary card, a data table, stat cards, an image gallery, a key/value list, and a contact form). Filter by category, then click Use template on the one you want.
The template opens in the widget editor pre-filled with a working name, description, and HTML. Customize it however you like — the HTML is yours to edit — and click Create widget to save it. Library templates are just a starting point: once saved, a widget behaves exactly like one you authored by hand, including the default-deny rule (it starts disabled until you turn it on).
Creating a widget
Click New widget (or start from a template — see above).
Fill in the fields:
Name — a
snake_caseidentifier, e.g.order_summary. The resource is served atui://order-summary.Description — a one-line summary of what the widget shows.
Widget HTML — the HTML/CSS/JS for the component. A starter template is pre-filled.
Allowed connect domains (optional) — comma-separated origins the widget may call. The host blocks all others by default.
Click Create widget.
New widgets are created disabled, following the platform's default-deny model — see Default-deny model.
The ext-apps runtime
Widgets talk to the host through the App class from @modelcontextprotocol/ext-apps. Because host security policy blocks fetching scripts from a CDN, the runtime inlines that library for you: put the placeholder /*__EXT_APPS_BUNDLE__*/ inside a <script type="module"> tag and the server replaces it with the library at serve time, exposing globalThis.ExtApps:
app.ontoolresult receives the linked tool's return value. Other App methods let the widget send a message into the conversation (sendMessage), call another tool (callServerTool), or open a link (openLink).
Linking a widget to a tool
A widget only renders when a tool it's linked to runs. On the Tools page, each tool row has a widget selector (shown once the server has at least one widget). Pick your widget to link it, or choose No widget to unlink. Keep the linked tool's text output meaningful so the experience degrades cleanly on clients without widget support.
Enabling a widget
Toggle the switch on a widget's row to On to publish it. Changes take effect immediately — no re-publish required. Disabled widgets are never exposed as a resource, and tools linked to a disabled widget carry no widget metadata.
Editing and deleting
Click the pencil icon to edit a widget's name, description, HTML, or allowed domains. Renaming re-derives the
ui://slug.Click the trash icon to delete a widget. This cannot be undone. Any tool linked to it is automatically unlinked.
Tips for effective widgets
One tool, one focused widget. Small, single-purpose widgets render and reason better than a catch-all UI.
Mention the widget in the tool description. It helps the agent decide when to invoke the tool.
Return a useful text summary too. The tool's text content is what users on non-widget clients see.
Declare only the domains you need. Leave Allowed connect domains empty unless the widget genuinely calls an external origin.
Last updated