Skip to main content
Glama
thecuriousarchitect09

claude-mcp-render-local-widgets

README.md
# Claude.ai UI widgets examples

Two standalone servers, each demonstrating a different Prefab pattern.

## Install

```bash
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
```

## 1. `chat_app.py` — interactive, calls back to the server

Pattern: `FastMCPApp` with two tools —
- `chat_widget` (`@app.ui()`) — the entry point the model calls, visible in
  the model's tool list.
- `send_message` (`@app.tool()`) — UI-only (never shown to the model),
  called by the widget's `Form.on_submit` via `CallTool`.

Use this pattern whenever the UI needs to write data, trigger backend
work, or fetch something fresh — anything beyond just displaying what
the tool returned.

```bash
fastmcp dev apps chat_app.py     # preview in browser
python chat_app.py --http               # run for Claude Desktop (stdio)
```

## 2. `chart_app.py` — data visualization, no callback needed

Pattern: a single `@mcp.tool(app=True)` that returns a `PrefabApp`
wrapping a `BarChart`. No `FastMCPApp`, no second tool — charts render
entirely client-side (tooltips, legend, hover) from the data you pass in
once. Use this pattern whenever the UI's job is just to *display* what
the tool already computed.

```bash
fastmcp dev apps chart_app.py    # preview in browser
python chart_app.py --http             # run for Claude Desktop (stdio)
```

Swap `BarChart`/`ChartSeries` for `LineChart`, `AreaChart`, `PieChart`,
`RadarChart`, or `RadialChart` — same `data` + `series` shape for all of
them (`PieChart` uses `data_key`/`name_key` instead of `series`).

## Connecting to claude.ai (web) instead of Desktop

Follow the ngrok way to tunnel local mcp server
[Read the installation guide](./NGROK-SETUP.md)

## Which pattern for your own tool?

| Your UI needs to... | Use |
|---|---|
| Just display data the tool computed | `@mcp.tool(app=True)` — like `chart_app.py` |
| Submit forms, trigger backend actions, refresh data | `FastMCPApp` with `@app.ui()` + `@app.tool()` — like `chat_app.py` |