diff --git a/docs/apps/interactive-apps.mdx b/docs/apps/interactive-apps.mdx
index bcf82f41a..fb2963114 100644
--- a/docs/apps/interactive-apps.mdx
+++ b/docs/apps/interactive-apps.mdx
@@ -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),
)
):
diff --git a/docs/apps/overview.mdx b/docs/apps/overview.mdx
index e694156be..11c10773a 100644
--- a/docs/apps/overview.mdx
+++ b/docs/apps/overview.mdx
@@ -10,9 +10,11 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
-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.
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
-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
@@ -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
diff --git a/docs/apps/patterns.mdx b/docs/apps/patterns.mdx
index b2b0eeade..b0ff80376 100644
--- a/docs/apps/patterns.mdx
+++ b/docs/apps/patterns.mdx
@@ -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")
diff --git a/docs/apps/prefab.mdx b/docs/apps/prefab.mdx
index 35181808b..b598fb63c 100644
--- a/docs/apps/prefab.mdx
+++ b/docs/apps/prefab.mdx
@@ -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).
-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.
diff --git a/docs/docs.json b/docs/docs.json
index 7231b40a0..6545b240b 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -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"
+ ]
+ }
]
},
{