Fix blocking docs issues: chart imports, Select API, Rx consistency (#3652)

This commit is contained in:
Jeremiah Lowin 2026-03-27 10:32:22 -04:00 committed by GitHub
commit 8ff28c13fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 56 additions and 42 deletions

View file

@ -283,7 +283,7 @@ Forms are the most common way to collect input and send it to the server. When a
Build forms with individual input components:
```python
from prefab_ui.components import Form, Input, Select, Textarea, Button
from prefab_ui.components import Form, Input, Select, SelectOption, Textarea, Button
from prefab_ui.actions.mcp import CallTool
from prefab_ui.actions import ShowToast
@ -294,11 +294,11 @@ with Form(
)
):
Input(name="title", label="Title", required=True)
Select(
name="priority",
label="Priority",
options=["low", "medium", "high", "critical"],
)
with Select(name="priority", label="Priority"):
SelectOption("Low", value="low")
SelectOption("Medium", value="medium")
SelectOption("High", value="high")
SelectOption("Critical", value="critical")
Textarea(name="description", label="Description")
Button("Create Ticket")
```
@ -433,7 +433,7 @@ from prefab_ui.components import (
Badge, Button, Column, ForEach, Form,
Heading, Input, Muted, Row, Separator, Text,
)
from prefab_ui.rx import RESULT
from prefab_ui.rx import RESULT, Rx
from pydantic import BaseModel, Field
from fastmcp import FastMCP, FastMCPApp
@ -512,7 +512,7 @@ def contact_manager() -> PrefabApp:
with Form(
on_submit=CallTool(
"search_contacts",
arguments={"query": "{{ query }}"},
arguments={"query": Rx("query")},
on_success=SetState("contacts", RESULT),
)
):

View file

@ -10,9 +10,11 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.0.0" />
MCP Apps let tools return interactive UIs — charts, sortable tables, forms, dashboards — rendered in a sandboxed iframe inside the host client's conversation.
MCP tools normally return text. That works for answers, but not for data the user wants to *explore* — a revenue chart they can hover over, a sortable employee directory, a form that submits structured input. MCP Apps let your tools return interactive UIs rendered right inside the conversation.
FastMCP implements the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) and gives you two ways to build interactive UIs with [Prefab](https://prefab.prefect.io), depending on how much server-side interaction you need.
{/* TODO: screenshot of a Prefab app rendering in Claude Desktop */}
FastMCP builds on the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) with [Prefab](https://prefab.prefect.io), a Python component library that compiles to interactive UIs. You write Python; the user sees charts, tables, forms, and dashboards.
<Note>
The examples throughout the Apps docs require the `apps` extra:
@ -28,7 +30,7 @@ This installs [Prefab UI](https://prefab.prefect.io), the component library used
<VersionBadge version="3.1.0" />
The quickest way to give a tool a visual UI. You return a [Prefab](https://prefab.prefect.io) component or `PrefabApp` from an otherwise standard MCP tool, and when the host calls it, the app is rendered instead of plain text:
The quickest way to give a tool a visual UI. Add `app=True` to any tool and return a Prefab component — when the host calls it, the user sees an interactive UI instead of a JSON blob:
```python
from prefab_ui.app import PrefabApp
@ -60,7 +62,7 @@ def revenue_chart(year: int) -> PrefabApp:
return PrefabApp(view=view)
```
Prefab apps aren't limited to static displays. Prefab's state system and client-side actions (toggles, tabs, conditionals) all work. You can even call other tools from the UI using `CallTool` with string names. There's no hard wall on what a Prefab app can do.
Prefab apps aren't limited to static displays. Prefab's state system and client-side actions (toggles, tabs, conditionals) all work. You can even call other tools from the UI using `CallTool`. There's no hard wall on what a Prefab app can do.
See [Prefab Apps](/apps/prefab) for the full guide.
@ -75,7 +77,7 @@ When your app has a lot of server-side interaction — forms that save data, sea
- **`@app.ui()`** — entry-point tools the model calls to open the app
- **`@app.tool()`** — backend tools the UI calls via `CallTool`
Backend tools get stable global identifiers that survive namespacing, visibility is managed automatically (the model sees entry points, the UI sees backends), and `CallTool` accepts function references instead of string names:
Backend tools get stable identifiers that survive namespacing, visibility is managed automatically (the model sees entry points, the UI sees backends), and `CallTool` accepts tool names that resolve correctly regardless of how servers are composed:
```python
from prefab_ui.actions import SetState, ShowToast
@ -130,20 +132,6 @@ You *can* build server-interactive UIs without `FastMCPApp` — it's all the sam
See [FastMCPApp](/apps/interactive-apps) for the full guide.
## Which Approach?
| Scenario | Approach |
| -------- | -------- |
| Visual output — charts, tables, status dashboards | [Prefab app](/apps/prefab) — `@mcp.tool(app=True)` |
| Client-side interactivity — toggles, tabs, conditionals | [Prefab app](/apps/prefab) — state + `Rx()` |
| Light server interaction — one or two tool calls | [Prefab app](/apps/prefab) — `CallTool("tool_name")` |
| Heavy server interaction — forms, CRUD, search, multi-step | [FastMCPApp](/apps/interactive-apps) — managed tool binding |
| Composed servers — apps mounted under namespaces | [FastMCPApp](/apps/interactive-apps) — stable global keys |
| LLM-generated UIs — bespoke visualizations per request | [Generative UI](/apps/generative) — `GenerativeUI` provider |
| Custom rendering — maps, 3D, specific JS frameworks | [Custom HTML](/apps/low-level) — raw MCP Apps extension |
The boundary isn't sharp. Start with a Prefab app; graduate to `FastMCPApp` when the tool-management complexity justifies it.
## Generative UI
<VersionBadge version="3.2.0" />
@ -160,9 +148,23 @@ mcp.add_provider(GenerativeUI())
See [Generative UI](/apps/generative) for the full guide.
## Which Approach?
| Scenario | Approach |
| -------- | -------- |
| Visual output — charts, tables, status dashboards | [Prefab app](/apps/prefab) — `@mcp.tool(app=True)` |
| Client-side interactivity — toggles, tabs, conditionals | [Prefab app](/apps/prefab) — state + `Rx()` |
| Light server interaction — one or two tool calls | [Prefab app](/apps/prefab) — `CallTool("tool_name")` |
| Heavy server interaction — forms, CRUD, search, multi-step | [FastMCPApp](/apps/interactive-apps) — managed tool binding |
| Composed servers — apps mounted under namespaces | [FastMCPApp](/apps/interactive-apps) — stable routing |
| LLM-generated UIs — bespoke visualizations per request | [Generative UI](/apps/generative) — `GenerativeUI` provider |
| Custom rendering — maps, 3D, specific JS frameworks | [Custom HTML](/apps/low-level) — raw MCP Apps extension |
The boundary isn't sharp. Start with a Prefab app; graduate to `FastMCPApp` when the tool-management complexity justifies it.
## Custom HTML Apps
Both approaches above use [Prefab UI](https://prefab.prefect.io) to build UIs in pure Python. If you need full control — your own HTML, CSS, JavaScript, a specific framework — you can use the [MCP Apps extension directly](/apps/low-level). You write the HTML yourself and communicate with the host via the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) SDK.
All the approaches above use [Prefab UI](https://prefab.prefect.io) to build UIs in pure Python. If you need full control — your own HTML, CSS, JavaScript, a specific framework — you can use the [MCP Apps extension directly](/apps/low-level). You write the HTML yourself and communicate with the host via the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) SDK.
## Previewing Apps Locally

View file

@ -22,7 +22,8 @@ Prefab includes [bar, line, area, pie, radar, and radial charts](https://prefab.
```python
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading, BarChart, ChartSeries
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import BarChart, ChartSeries
from fastmcp import FastMCP
mcp = FastMCP("Charts")
@ -61,7 +62,8 @@ Multiple `ChartSeries` entries plot different data keys. Add `stacked=True` to s
```python
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading, AreaChart, ChartSeries
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import AreaChart, ChartSeries
from fastmcp import FastMCP
mcp = FastMCP("Charts")
@ -97,7 +99,8 @@ def usage_trend() -> PrefabApp:
```python
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading, PieChart
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import PieChart
from fastmcp import FastMCP
mcp = FastMCP("Charts")
@ -360,7 +363,7 @@ from prefab_ui.actions.mcp import CallTool
from prefab_ui.app import PrefabApp
from prefab_ui.components import (
Badge, Button, Column, ForEach, Form, Heading,
Input, Muted, Row, Select, Separator, Text, Textarea,
Input, Muted, Row, Select, SelectOption, Separator, Text, Textarea,
)
from prefab_ui.rx import RESULT
from fastmcp import FastMCP, FastMCPApp
@ -407,11 +410,11 @@ def contact_form() -> PrefabApp:
):
Input(name="name", label="Full Name", required=True)
Input(name="email", label="Email", input_type="email", required=True)
Select(
name="category",
label="Category",
options=["Customer", "Vendor", "Partner", "Other"],
)
with Select(name="category", label="Category"):
SelectOption("Customer", value="Customer")
SelectOption("Vendor", value="Vendor")
SelectOption("Partner", value="Partner")
SelectOption("Other", value="Other")
Textarea(name="notes", label="Notes", placeholder="Optional notes...")
Button("Save Contact")

View file

@ -14,7 +14,9 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
[Prefab](https://prefab.prefect.io) is in early, active development — its API changes frequently and breaking changes can occur with any release. Always pin `prefab-ui` to a specific version in your dependencies (see below).
</Tip>
The fastest way to give a tool a visual UI: return a [Prefab](https://prefab.prefect.io) component or `PrefabApp` from an otherwise standard MCP tool. FastMCP registers the rendering engine, wires the protocol metadata, and delivers the component tree to the host. You write Python; the user sees an interactive UI.
When a tool returns text, the LLM reads it and relays the information. But some data is better *seen* — a chart communicates trend at a glance, a sortable table lets the user explore without re-prompting, a dashboard with badges and progress bars gives immediate status.
Prefab apps transform your tools from text-returning functions into visual experiences. Add `app=True` to a tool, return a [Prefab](https://prefab.prefect.io) component, and the host renders an interactive UI instead of text. FastMCP handles the rendering engine, protocol metadata, and delivery. You write Python; the user sees charts, tables, and dashboards.
This works for everything from static charts to reactive dashboards with client-side state. When your app has heavy server-side interaction — multiple backend tools, forms, search, CRUD — consider [FastMCPApp](/apps/interactive-apps), which manages tool binding, visibility, and composition safety for you.

View file

@ -194,12 +194,19 @@
"apps/overview",
"apps/prefab",
"apps/interactive-apps",
"apps/components",
"apps/patterns",
"apps/generative",
"apps/development",
"apps/architecture",
"apps/low-level"
{
"collapsed": true,
"group": "Reference",
"icon": "book",
"pages": [
"apps/components",
"apps/patterns",
"apps/architecture",
"apps/low-level"
]
}
]
},
{