diff --git a/docs/apps/generative.mdx b/docs/apps/generative.mdx
new file mode 100644
index 000000000..71d7e111d
--- /dev/null
+++ b/docs/apps/generative.mdx
@@ -0,0 +1,128 @@
+---
+title: Generative UI
+sidebarTitle: Generative UI
+description: Let the LLM build custom Prefab UIs on the fly.
+icon: wand-magic-sparkles
+tag: NEW
+---
+
+import { VersionBadge } from '/snippets/version-badge.mdx'
+
+
+
+Generative UI means the LLM writes the UI code at runtime. Instead of calling a pre-built tool with a fixed interface, the model writes Prefab Python code tailored to the current data and request. The user watches the UI build up in real time as the model generates code.
+
+```python
+from fastmcp import FastMCP
+from fastmcp.apps.generative import GenerativeUI
+
+mcp = FastMCP("Prefab Studio")
+mcp.add_provider(GenerativeUI())
+```
+
+That's it. The `GenerativeUI` provider registers everything:
+
+- **`generate_prefab_ui`** — a tool that accepts Python code, executes it in a Pyodide sandbox, and renders the result as a Prefab app
+- **`search_prefab_components`** — a tool that lets the LLM search the Prefab component library to discover what's available
+- **The generative renderer** — a `ui://` resource with browser-side Pyodide for streaming progressive rendering
+
+## How It Works
+
+When the LLM decides to call `generate_prefab_ui`, it writes Prefab Python code into the `code` argument. The MCP Apps protocol creates the renderer iframe in parallel with the tool call, so the app is already running when partial arguments start flowing.
+
+As the LLM generates each token:
+
+1. The host forwards partial arguments to the app via `ontoolinputpartial`
+2. The renderer extracts the growing `code` string
+3. Browser-side Pyodide executes whatever compiles successfully
+4. The user sees components appear as they're written
+
+When the LLM finishes, the server runs the complete code in a server-side Pyodide sandbox for validation, and the renderer replaces the streaming preview with the final server-validated result.
+
+## What the LLM Writes
+
+The tool description includes code examples that teach the LLM the Prefab patterns. A typical generation looks like:
+
+```python
+from prefab_ui.components import Column, Row, Heading, Text, Badge, Card, CardContent
+from prefab_ui.components.charts import BarChart, ChartSeries
+from prefab_ui.app import PrefabApp
+
+with PrefabApp() as app:
+ with Column(gap=6, css_class="p-6"):
+ Heading("Q3 Revenue Report")
+
+ BarChart(
+ data=[
+ {"month": "Jul", "revenue": 42000},
+ {"month": "Aug", "revenue": 51000},
+ {"month": "Sep", "revenue": 63000},
+ ],
+ series=[ChartSeries(data_key="revenue", label="Revenue")],
+ x_axis="month",
+ )
+
+ with Row(gap=4):
+ with Card():
+ with CardContent():
+ Text("Total", css_class="text-sm text-muted-foreground")
+ Heading("$156,000")
+ with Card():
+ with CardContent():
+ Text("Growth", css_class="text-sm text-muted-foreground")
+ Badge("+18%", variant="success")
+```
+
+The model writes real Python — loops, f-strings, computation, helper functions. Prefab's component library gives it charts, tables, forms, cards, badges, and layout primitives to work with.
+
+## The Component Search Tool
+
+Before writing code, the LLM can call `search_prefab_components` to discover what's available:
+
+```
+search_prefab_components("Chart")
+→ 7 components matching 'Chart':
+ AreaChart — from prefab_ui.components.charts import AreaChart
+ BarChart — from prefab_ui.components.charts import BarChart
+ ...
+```
+
+Passing `detail=True` returns full field descriptions and docstrings. The search tool introspects the actual Prefab classes at runtime, so it's always up to date with the installed version.
+
+## Passing Data
+
+The `generate_prefab_ui` tool accepts a `data` parameter. Values passed here become global variables in the sandbox:
+
+```python
+# The LLM can reference 'sales_data' directly in its code
+result = await generate_prefab_ui(
+ code="...",
+ data={"sales_data": [{"month": "Jan", "revenue": 42000}, ...]}
+)
+```
+
+This lets the model use real data from earlier in the conversation to build visualizations.
+
+## Configuration
+
+`GenerativeUI` accepts options for customizing tool names:
+
+```python
+GenerativeUI(
+ tool_name="generate_prefab_ui", # default
+ components_tool_name="search_prefab_components", # default
+ include_components_tool=True, # default
+)
+```
+
+## Requirements
+
+Generative UI requires `fastmcp[apps]` which installs `prefab-ui`. The Pyodide sandbox (for server-side validation) requires Deno — it installs automatically on first use.
+
+The streaming renderer loads Pyodide from CDN in the browser. The CSP is configured automatically by the provider — no manual setup needed.
+
+## Next Steps
+
+- **[Prefab Apps](/apps/prefab)** — The component library and state system the LLM writes code against
+- **[Components](/apps/components)** — Quick reference for the most-used components
+- **[Development](/apps/development)** — Preview generative UI tools locally with `fastmcp dev apps`
diff --git a/docs/apps/low-level.mdx b/docs/apps/low-level.mdx
index 944e48498..9ce86e3dd 100644
--- a/docs/apps/low-level.mdx
+++ b/docs/apps/low-level.mdx
@@ -27,7 +27,7 @@ The tool declares which resource to use via `AppConfig`. When the host calls the
import json
from fastmcp import FastMCP
-from fastmcp.server.apps import AppConfig, ResourceCSP
+from fastmcp.apps import AppConfig, ResourceCSP
mcp = FastMCP("My App Server")
@@ -47,7 +47,7 @@ def chart_view() -> str:
`AppConfig` controls how a tool or resource participates in the Apps extension. Import it from `fastmcp.server.apps`:
```python
-from fastmcp.server.apps import AppConfig
+from fastmcp.apps import AppConfig
```
On **tools**, you'll typically set `resource_uri` to point to the UI resource:
@@ -158,7 +158,7 @@ Apps run in sandboxed iframes with a deny-by-default Content Security Policy. By
If your app needs to load external resources (CDN scripts, API calls, embedded iframes), declare the allowed domains with `ResourceCSP`:
```python
-from fastmcp.server.apps import AppConfig, ResourceCSP
+from fastmcp.apps import AppConfig, ResourceCSP
@mcp.resource(
"ui://my-app/view.html",
@@ -185,7 +185,7 @@ def my_view() -> str:
If your app needs browser capabilities like camera or clipboard access, request them via `ResourcePermissions`:
```python
-from fastmcp.server.apps import AppConfig, ResourcePermissions
+from fastmcp.apps import AppConfig, ResourcePermissions
@mcp.resource(
"ui://my-app/view.html",
@@ -214,7 +214,7 @@ import qrcode
from mcp import types
from fastmcp import FastMCP
-from fastmcp.server.apps import AppConfig, ResourceCSP
+from fastmcp.apps import AppConfig, ResourceCSP
from fastmcp.tools import ToolResult
mcp = FastMCP("QR Code Server")
@@ -290,7 +290,7 @@ Not all hosts support the Apps extension. You can check at runtime using the too
```python
from fastmcp import Context
-from fastmcp.server.apps import AppConfig, UI_EXTENSION_ID
+from fastmcp.apps import AppConfig, UI_EXTENSION_ID
@mcp.tool(app=AppConfig(resource_uri="ui://my-app/view.html"))
async def my_tool(ctx: Context) -> str:
diff --git a/docs/apps/overview.mdx b/docs/apps/overview.mdx
index 760ca9c4f..e694156be 100644
--- a/docs/apps/overview.mdx
+++ b/docs/apps/overview.mdx
@@ -32,7 +32,8 @@ The quickest way to give a tool a visual UI. You return a [Prefab](https://prefa
```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("Dashboard")
@@ -138,10 +139,27 @@ See [FastMCPApp](/apps/interactive-apps) for the full guide.
| 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
+
+
+
+Instead of pre-building a UI, the LLM can write one from scratch. The `GenerativeUI` provider registers tools that let the model write Prefab Python code, execute it in a sandbox, and render the result — with streaming so the user watches the UI build up in real time.
+
+```python
+from fastmcp import FastMCP
+from fastmcp.apps.generative import GenerativeUI
+
+mcp = FastMCP("Prefab Studio")
+mcp.add_provider(GenerativeUI())
+```
+
+See [Generative UI](/apps/generative) for the full guide.
+
## 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.
diff --git a/docs/apps/prefab.mdx b/docs/apps/prefab.mdx
index 50a44a8c8..35181808b 100644
--- a/docs/apps/prefab.mdx
+++ b/docs/apps/prefab.mdx
@@ -24,7 +24,8 @@ Here's a tool that returns a bar chart:
```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("Dashboard")
@@ -312,7 +313,8 @@ If the model needs to reason about the data — reference it in conversation, su
```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
from fastmcp.tools import ToolResult
@@ -361,12 +363,28 @@ The **component tree** is serialized as `structuredContent` on the tool result.
None of this requires configuration. The `app=True` flag (or type inference) is the only thing you need.
+## Customizing CSP
+
+`app=True` auto-wires the Prefab renderer with default CSP settings. If your app loads external resources — embedding iframes, fetching from APIs, loading scripts — use `PrefabAppConfig` to add the required domains while keeping all the auto-wiring:
+
+```python
+from fastmcp.apps import PrefabAppConfig, ResourceCSP
+
+@mcp.tool(app=PrefabAppConfig(
+ csp=ResourceCSP(frame_domains=["https://example.com"]),
+))
+def dashboard_with_embed() -> PrefabApp:
+ ...
+```
+
+`PrefabAppConfig()` with no arguments is equivalent to `app=True`. It auto-sets the renderer URI and merges the renderer's CSP with any additional domains you provide.
+
## Mixing with Custom HTML
Prefab tools and [custom HTML tools](/apps/low-level) coexist in the same server. Prefab tools share a single renderer resource; custom tools point to their own:
```python
-from fastmcp.server.apps import AppConfig
+from fastmcp.apps import AppConfig
@mcp.tool(app=True)
def team_directory() -> PrefabApp:
diff --git a/docs/docs.json b/docs/docs.json
index 1770ff0a1..7231b40a0 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -196,6 +196,7 @@
"apps/interactive-apps",
"apps/components",
"apps/patterns",
+ "apps/generative",
"apps/development",
"apps/architecture",
"apps/low-level"