mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 15:19:10 +02:00
Docs: generative UI page, fix imports, add PrefabAppConfig (#3650)
This commit is contained in:
parent
b7cb7cea81
commit
426572670e
5 changed files with 175 additions and 10 deletions
128
docs/apps/generative.mdx
Normal file
128
docs/apps/generative.mdx
Normal file
|
|
@ -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'
|
||||
|
||||
<VersionBadge version="3.2.0" />
|
||||
|
||||
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`
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
<VersionBadge version="3.2.0" />
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@
|
|||
"apps/interactive-apps",
|
||||
"apps/components",
|
||||
"apps/patterns",
|
||||
"apps/generative",
|
||||
"apps/development",
|
||||
"apps/architecture",
|
||||
"apps/low-level"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue