Tutorials
Let your AI agent build MCP Apps widgets
Most ways to create an MCP App with Claude Code generate code in your repo. This guide shows the other route: give your agent tools to build, design and publish widgets, with the same powers you have on the web.
8 min read
How did the online shop do in September compared with August?
Used show_metrics
#The short answer
You can let an AI agent build MCP Apps widgets by connecting it to an MCP server whose tools create and edit widgets. Widgetry's endpoint, https://widgets.gonzaloverdugo.com/mcp, is one: connect Claude, Claude Code or any MCP client with edit access, and the agent can list templates, create a widget from one, change its data schema, sample data, markup and styles, make a design, publish the widget and hand you the code to link it from your own server. It does what a person does in the web app, through 15 widgetry_* tools.
That is different from asking an agent to write an MCP App from scratch in your codebase. Both are useful; the last section compares them.
#Why give an agent a UI-building MCP server
An MCP App widget is an HTML view that a host such as ChatGPT or Claude draws inside the conversation when a tool runs (see what MCP Apps are). Writing one by hand means a bundled view, the postMessage bridge, a Content Security Policy and a server that registers the resource and the tool.
When the agent works through tools instead, it does not touch any of that. It edits a widget that already works: a Liquid template, CSS that reads design tokens, a JSON Schema for the data and sample data that satisfies it. Every change is checked by the server, and an invalid one comes back with what to fix. The agent iterates on the parts that matter (what the widget shows and how it looks) and you open the result in the editor while it works.
#How to connect your agent
The MCP URL is the same for every client: https://widgets.gonzaloverdugo.com/mcp. What changes is how the client proves who it is. Either way, the credential names one organization, and the agent only sees and changes that organization's widgets and designs.
#Claude and Claude Desktop: OAuth
Add the URL as a custom connector in claude.ai or Claude Desktop. The client sends you to Widgetry, you sign in with Google and pick the organization on the consent screen. The consent screen also says whether the client asks only to read or also to write:
widgets:read: the agent sees the organization and can show its published widgets.widgets:write: the agent can also create, edit, publish and delete widgets and designs, if your role in that organization allows it.
The same flow works in other clients that connect remote MCP servers with OAuth.
#Claude Code, Codex and other agents: an API key
Coding agents and scripts use an API key that starts with wgt_, sent as a Bearer token. An owner of the organization creates it in Settings, with permission Read or Read and write. It is shown once, so save it when it appears.
In Claude Code, the command is:
claude mcp add --transport http widgetry https://widgets.gonzaloverdugo.com/mcp \
--header "Authorization: Bearer $WIDGETRY_API_KEY"Any other client that supports HTTP MCP servers with custom headers works the same way: the URL plus Authorization: Bearer wgt_.... A read key gives the agent the reading tools; a read and write key gives it all of them.
#The tools your agent gets
Every credential gets eight tools to read. Only a credential that may write gets the other seven. The server also sends the agent a short authoring guide in its instructions (the Liquid context, the --wg-* tokens, scripts, libraries and image domains), so a capable model can start without reading docs.
#Reading (every credential)
| Tool | What it does |
|---|---|
widgetry_get_organization | Where the credential works, whether it can write, how many widgets and designs there are, and the URLs |
widgetry_list_starters | The ready-made widget types and the script libraries available (three, gsap, d3, chartjs) |
widgetry_get_starter | One starter in full: markup, styles, script, data schema and sample data |
widgetry_list_widgets | The organization's widgets, drafts included |
widgetry_get_widget | One widget in full, with its image domains and libraries |
widgetry_export_widget | The manifest (tool and resource with its CSP), the live URL to link and the TypeScript to register it |
widgetry_list_designs | Built-in designs and the organization's own |
widgetry_get_design | A design's light and dark tokens, its extra CSS and every token a design can set |
#Writing (write key, or OAuth with widgets:write from an editor or owner)
| Tool | What it does |
|---|---|
widgetry_create_widget | Creates a draft as a copy of a starter, with an optional fixed slug, design and sample data language |
widgetry_update_widget | Changes only the fields you send: name, description, design, markup, styles, script, libraries, image domains, data schema, sample data |
widgetry_set_published | Publishes or unpublishes a widget |
widgetry_delete_widget | Deletes a widget for good |
widgetry_create_design | Creates a design on top of any other, with tokens, dark tokens and CSS |
widgetry_update_design | Edits one of the organization's designs |
widgetry_delete_design | Deletes a design no widget wears |
Every widget the tools return carries an editorUrl and a previewUrl, which you open in the browser while signed in. Each published widget also becomes its own tool on the same endpoint, show_<slug>, so the agent can draw it in the conversation right away.
#A worked example
Here is a session with invented data. You ask Claude Code: "Make a widget for our support team's weekly figures, in a teal design, and give me the code for my MCP server."
1. Check where it is. The agent calls widgetry_get_organization and sees canWrite: true.
2. Pick a starter. widgetry_list_starters returns the 13 types. A row of figures with their change fits the metrics starter, so the agent reads it with widgetry_get_starter to learn its schema.
3. Create the widget. A fixed slug keeps the tool name stable:
{
"starterId": "metrics",
"name": "Support weekly",
"slug": "support-weekly",
"locale": "en"
}The answer includes "toolName": "show_support_weekly", the editorUrl and the previewUrl.
4. Shape the data. The agent calls widgetry_update_widget with a description the model will read, a schema that now requires the period, and sample data:
{
"slug": "support-weekly",
"description": "Shows the support team's weekly figures: tickets, response time and satisfaction, each with its change against the previous week.",
"dataSchema": {
"type": "object",
"required": ["title", "period", "metrics"],
"properties": {
"title": { "type": "string" },
"period": { "type": "string" },
"metrics": {
"type": "array",
"items": {
"type": "object",
"required": ["label", "value"],
"properties": {
"label": { "type": "string" },
"value": { "type": "number" },
"unit": { "type": "string" },
"change": { "type": "number" },
"lower_is_better": { "type": "boolean" }
}
}
}
}
},
"sampleData": {
"title": "Support this week",
"period": "Week 40",
"metrics": [
{ "label": "Tickets solved", "value": 1284, "change": 6.2 },
{ "label": "First response", "value": 38, "unit": "min", "change": -12.5, "lower_is_better": true },
{ "label": "Satisfaction", "value": 94.1, "unit": "%", "change": 1.3 }
]
}
}If the sample data did not match the schema, the tool would answer with the error and what to fix, and the agent would try again.
5. Make a design. No built-in design is teal, so the agent creates one on top of nordic:
{
"name": "Harbor",
"from": "nordic",
"tokens": { "--wg-accent": "#0f766e" },
"darkTokens": { "--wg-accent": "#2dd4bf" }
}The design gets the key harbor, and one more widgetry_update_widget call sets "design": "harbor" on the widget.
6. Publish. widgetry_set_published with "published": true. Publishing needs a description and sample data that satisfies the schema, both already in place. The widget is now the tool show_support_weekly on the endpoint.
7. Hand over the code. widgetry_export_widget returns the manifest, the live URL and two TypeScript snippets: one that links the published widget with a read-only key (changes you publish later reach your server within a minute, without a deploy), and one that registers downloaded files. The agent pastes the linked one into your server. Add a UI to your MCP server walks through both.
One thing to know when the agent is Claude Code: the terminal does not draw MCP Apps, it calls the tool as text. Open the previewUrl or the editor to see the widget, or, with Widgetry connected in Claude on the web or desktop, ask it to call show_support_weekly.
#Guardrails: what the agent cannot do
Giving an agent write access is safer when the limits are in the server, not in the prompt:
- Read-only credentials stay read-only. A read key, or an OAuth token without
widgets:write, only gets the reading tools. A token without the write scope acts as a reader even if the person is an owner. - Roles still apply. A viewer in the organization cannot write through an agent either. Writing needs an editor or owner behind the token, or a write key.
- No members, invitations or keys. Those stay on the web on purpose: a key created by an agent would end up written in a chat.
- One organization per credential. Every widget and design belongs to one organization, and the credential decides which. Asking for something in another organization gets "not found".
- Live tools are protected. A published widget refuses an edit that would break its tool; the agent has to unpublish first or keep the schema compatible.
- Deleting is marked as destructive, and the tool tells the agent to confirm with you first. It cannot be undone.
#Compared with skills that generate code
There are good skills that teach a coding agent to write MCP Apps. They solve a different problem.
| Option | What it does | Better for |
|---|---|---|
Official ext-apps Agent Skills (create-mcp-app, add-app-to-server, convert-web-app, migrate-oai-app) and the mcp-apps Claude Code plugin | Generate and migrate MCP App code in your repo (ext-apps) | Custom UIs with your own framework, full control of every line, migrating an OpenAI app |
Anthropic build-mcp-app skill, in the mcp-server-dev plugin | Adds UI such as forms, pickers and confirm dialogs while building an MCP server (plugin) | Interactive steps inside a server you are writing |
Microsoft Power Apps generate-mcp-app-ui skill (preview) | Generates self-contained Fluent UI widget HTML from a tool's JSON (docs) | Teams already on Power Apps who want Fluent UI widgets |
| Widgetry's MCP tools | Build, design and publish widgets as data on a hosted service | Data displays (boards, KPIs, charts, tables) without UI code in your repo, shared designs, and changes that reach your server without a deploy |
Code generation wins when the UI is unusual, when it must fetch its own data or when you do not want a dependency on a service. A widget made in Widgetry is a Liquid template with optional scripts limited to a fixed set of libraries, and it shows the data the model passes in: it does not fetch data by itself. If that fits, the agent gets a working widget at every step instead of code to debug. For a wider look at the options, see MCP Apps builders compared.
#Try it
Sign in at widgets.gonzaloverdugo.com, create a write key in Settings (or connect Claude with OAuth), and ask your agent for a widget. You can watch each change land in the editor, polish it yourself, and let the agent publish when you are happy.