docs/apps/generative.mdx
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" /><video src="/apps/images/generative-ui.mp4" autoPlay loop muted playsInline style={{width:"100%", borderRadius:"8px", marginBottom:"1rem"}} />
With Generative UI, the LLM writes the UI code at runtime. Instead of calling a pre-built tool with a fixed shape, the model writes Prefab Python tailored to the current data and request. The user watches the UI stream in as the model generates it.
from fastmcp import FastMCP
from fastmcp.apps.generative import GenerativeUI
mcp = FastMCP("Prefab Studio")
mcp.add_provider(GenerativeUI())
One provider registers three things:
generate_prefab_ui — a tool that accepts Python code, executes it in a Pyodide sandbox, and renders the result as a Prefab appsearch_prefab_components — a tool the LLM uses to discover what components are availableui:// resource with browser-side Pyodide that progressively renders partial code as the LLM generates itWhen the LLM calls 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 by the time partial arguments start flowing.
As the LLM generates each token:
ontoolinputpartialcode stringWhen the LLM finishes, the server runs the complete code in a server-side Pyodide sandbox for validation, and the renderer swaps the streaming preview for the final server-validated result.
The tool description includes examples that teach the model the Prefab patterns. A typical generation looks like:
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 gives it charts, tables, forms, cards, badges, and layout primitives to compose.
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 Prefab classes at runtime, so it's always up to date with the installed version.
The generate_prefab_ui tool accepts a data parameter. Values become global variables in the sandbox:
# 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 data from earlier in the conversation to build visualizations.
GenerativeUI takes options for customizing tool names:
GenerativeUI(
tool_name="generate_prefab_ui", # default
components_tool_name="search_prefab_components", # default
include_components_tool=True, # default
)
Generative UI needs fastmcp[apps], which pulls in prefab-ui. The server-side Pyodide sandbox (for final 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.
The Pyodide sandbox includes the Python standard library and Prefab. External packages (NumPy, pandas, requests, etc.) are not available — the LLM's code must work with only built-in Python and Prefab. If the LLM imports something unavailable, the sandbox raises ImportError.
fastmcp dev apps