diff --git a/docs/apps/architecture.mdx b/docs/apps/architecture.mdx
index 8fe5c3868..ee666568f 100644
--- a/docs/apps/architecture.mdx
+++ b/docs/apps/architecture.mdx
@@ -1,13 +1,14 @@
---
-title: Architecture
+title: App Architecture
sidebarTitle: Architecture
-description: How Prefab apps work under the hood — from Python to pixels.
+description: How FastMCP apps work under the hood — from Python to pixels.
icon: sitemap
+tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
-
+
This page explains the internal architecture of Prefab apps — how your Python code becomes an interactive UI inside a host client's conversation. If you're building [custom HTML apps](/apps/low-level), the pipeline is simpler and covered on that page. You don't need to understand any of this to build Prefab apps, but the mental model is useful when you're debugging, extending, or contributing.
diff --git a/docs/apps/components.mdx b/docs/apps/components.mdx
deleted file mode 100644
index 5283a7903..000000000
--- a/docs/apps/components.mdx
+++ /dev/null
@@ -1,601 +0,0 @@
----
-title: Component Reference
-sidebarTitle: Components
-description: Quick reference for the most-used Prefab components.
-icon: shapes
-tag: NEW
----
-
-import { VersionBadge } from '/snippets/version-badge.mdx'
-
-
-
-This page is a scannable reference for the Prefab components you'll use most often in MCP Apps. Each entry shows the component, what it does, a minimal code example, and the props that matter.
-
-For the full component library — every prop, variant, and advanced pattern — see the [Prefab component reference](https://prefab.prefect.io/docs/components).
-
-
-All components below are imported from `prefab_ui.components` unless otherwise noted. Charts must be imported from `prefab_ui.components.charts`.
-
-
-## Layout
-
-Layout components control how children are arranged. They all use Python's `with` statement to collect their children.
-
-### Column
-
-Stacks children vertically. The most common top-level container for an app view.
-
-```python
-from prefab_ui.components import Column, Text
-
-with Column(gap=4, css_class="p-6") as view:
- Text("First")
- Text("Second")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `gap` | `int` | Space between children (Tailwind units) |
-| `align` | `str` | Cross-axis alignment: `"start"`, `"center"`, `"end"`, `"stretch"` |
-| `justify` | `str` | Main-axis alignment: `"start"`, `"center"`, `"end"`, `"between"` |
-| `css_class` | `str` | Tailwind CSS classes |
-
-### Row
-
-Arranges children horizontally.
-
-```python
-from prefab_ui.components import Row, Badge, Text
-
-with Row(gap=2, align="center"):
- Text("Status")
- Badge("Online", variant="success")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `gap` | `int` | Space between children |
-| `align` | `str` | Cross-axis alignment |
-| `justify` | `str` | Main-axis alignment |
-| `wrap` | `bool` | Wrap children to next line |
-
-### Grid
-
-Lays out children in a CSS grid with a fixed number of columns.
-
-```python
-from prefab_ui.components import Grid, Card, CardContent, Text
-
-with Grid(columns=3, gap=4):
- for label in ["API", "Cache", "DB"]:
- with Card():
- with CardContent():
- Text(label)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `columns` | `int` | Number of grid columns |
-| `gap` | `int` | Space between cells |
-
-### Card / CardContent
-
-A bordered container with padding. `Card` provides the outer border and shadow; `CardContent` adds standard inner padding. Cards are commonly used inside grids for dashboard-style layouts.
-
-```python
-from prefab_ui.components import Card, CardContent, Text, Badge
-
-with Card():
- with CardContent():
- Text("API Gateway")
- Badge("healthy", variant="success")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `css_class` | `str` | Additional Tailwind classes |
-
-### Separator
-
-Renders a horizontal rule between sections. Takes no required props.
-
-```python
-from prefab_ui.components import Column, Heading, Separator, Text
-
-with Column(gap=4):
- Heading("Section A")
- Separator()
- Text("Content below the line")
-```
-
-## Typography
-
-### Heading
-
-Renders a heading element. Defaults to `level=2` (an `
`).
-
-```python
-from prefab_ui.components import Heading
-
-Heading("Dashboard")
-Heading("Subsection", level=3)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `level` | `int` | Heading level: `1`-`4` |
-
-### Text
-
-General-purpose text element. Accepts reactive expressions (`Rx`) as content so the text can update with state changes.
-
-```python
-from prefab_ui.components import Text
-
-Text("Hello, world")
-Text("Styled text", css_class="font-medium text-blue-500")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `css_class` | `str` | Tailwind CSS classes |
-
-### Muted
-
-Renders text in a subdued color. Useful for secondary information like timestamps, metadata, or helper text.
-
-```python
-from prefab_ui.components import Muted
-
-Muted("Last updated 5 minutes ago")
-```
-
-### Badge
-
-A small label for status indicators, tags, or categories. Supports color variants to convey meaning at a glance.
-
-```python
-from prefab_ui.components import Badge
-
-Badge("Active", variant="success")
-Badge("Pending", variant="warning")
-Badge("Failed", variant="destructive")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `variant` | `str` | `"default"`, `"success"`, `"warning"`, `"destructive"`, `"outline"` |
-
-## Data
-
-### DataTable
-
-A fully interactive table with client-side sorting, searching, and pagination. You define columns and pass row data as a list of dicts.
-
-```python
-from prefab_ui.components import DataTable, DataTableColumn
-
-DataTable(
- columns=[
- DataTableColumn(key="name", header="Name", sortable=True),
- DataTableColumn(key="role", header="Role"),
- ],
- rows=[
- {"name": "Alice", "role": "Engineer"},
- {"name": "Bob", "role": "Designer"},
- ],
- search=True,
- paginated=True,
- page_size=15,
-)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `columns` | `list[DataTableColumn]` | Column definitions |
-| `rows` | `list[dict]` | Row data |
-| `search` | `bool` | Enable full-text search |
-| `paginated` | `bool` | Enable pagination |
-| `page_size` | `int` | Rows per page (default `10`) |
-
-`DataTableColumn` takes `key` (the dict key), `header` (display name), and `sortable` (enable sorting on that column).
-
-### BarChart
-
-Renders vertical or horizontal bar charts. Each `ChartSeries` maps a key from your data to a colored bar group. Multiple series produce grouped (or stacked) bars.
-
-```python
-from prefab_ui.components.charts import BarChart, ChartSeries
-
-BarChart(
- data=[
- {"month": "Jan", "revenue": 4200},
- {"month": "Feb", "revenue": 5100},
- ],
- series=[ChartSeries(data_key="revenue", label="Revenue")],
- x_axis="month",
- show_legend=True,
-)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `data` | `list[dict]` | Chart data |
-| `series` | `list[ChartSeries]` | Data series to plot |
-| `x_axis` | `str` | Key for the x-axis labels |
-| `stacked` | `bool` | Stack bars instead of grouping |
-| `horizontal` | `bool` | Flip axes for horizontal bars |
-| `show_legend` | `bool` | Display the legend |
-| `height` | `int` | Chart height in pixels |
-
-### PieChart
-
-Displays proportional data as slices. Set `inner_radius` for a donut chart. Unlike bar/line charts, `PieChart` uses `data_key` for the numeric value and `name_key` for the label — it doesn't use `ChartSeries`.
-
-```python
-from prefab_ui.components.charts import PieChart
-
-PieChart(
- data=[
- {"category": "Bug", "count": 23},
- {"category": "Feature", "count": 15},
- ],
- data_key="count",
- name_key="category",
- inner_radius=60,
- show_legend=True,
-)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `data` | `list[dict]` | Chart data |
-| `data_key` | `str` | Key for the numeric value |
-| `name_key` | `str` | Key for the label |
-| `inner_radius` | `int` | Inner radius for donut charts (0 = full pie) |
-| `show_legend` | `bool` | Display the legend |
-
-### LineChart
-
-Plots data points connected by lines. Shares the same API as `BarChart` — use `series`, `x_axis`, and optionally `curve` to control interpolation.
-
-```python
-from prefab_ui.components.charts import LineChart, ChartSeries
-
-LineChart(
- data=[
- {"day": "Mon", "requests": 120},
- {"day": "Tue", "requests": 185},
- ],
- series=[ChartSeries(data_key="requests", label="Requests")],
- x_axis="day",
- curve="smooth",
- show_dots=True,
-)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `data` | `list[dict]` | Chart data |
-| `series` | `list[ChartSeries]` | Data series to plot |
-| `x_axis` | `str` | Key for x-axis labels |
-| `curve` | `str` | `"linear"` or `"smooth"` |
-| `show_dots` | `bool` | Show data point markers |
-| `height` | `int` | Chart height in pixels |
-
-## Forms
-
-Form components collect user input. Each has a `name` prop that determines the key in the submitted data. When used inside a `Form`, their values are gathered automatically on submit.
-
-### Input
-
-A single-line text field. Set `input_type` to `"email"`, `"password"`, `"number"`, etc. for browser-native validation.
-
-```python
-from prefab_ui.components import Input
-
-Input(name="email", label="Email", input_type="email", required=True)
-Input(name="search", placeholder="Search...")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `name` | `str` | State/form key |
-| `label` | `str` | Label text |
-| `placeholder` | `str` | Placeholder text |
-| `input_type` | `str` | HTML input type |
-| `required` | `bool` | Mark as required |
-| `disabled` | `bool` | Disable input |
-
-### Select
-
-A dropdown for choosing from a list of options. Pass a flat list of strings, or structured `SelectOption` objects for custom labels.
-
-```python
-from prefab_ui.components import Select, SelectOption
-
-with Select(name="priority", label="Priority"):
- SelectOption("Low", value="low")
- SelectOption("Medium", value="medium")
- SelectOption("High", value="high")
-```
-
-Options are defined as `SelectOption` children, not as a prop.
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `name` | `str` | State/form key |
-| `label` | `str` | Label text |
-| `placeholder` | `str` | Placeholder text |
-
-### Textarea
-
-A multi-line text area. Works the same as `Input` but renders as a resizable text box.
-
-```python
-from prefab_ui.components import Textarea
-
-Textarea(name="notes", label="Notes", placeholder="Add details...")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `name` | `str` | State/form key |
-| `label` | `str` | Label text |
-| `placeholder` | `str` | Placeholder text |
-| `rows` | `int` | Visible height in rows |
-
-### Checkbox
-
-A boolean toggle rendered as a checkbox. Binds to state as `True`/`False`.
-
-```python
-from prefab_ui.components import Checkbox
-
-Checkbox(name="agree", label="I agree to the terms")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `name` | `str` | State/form key |
-| `label` | `str` | Label text |
-
-### Switch
-
-A toggle switch. Functionally identical to `Checkbox` but rendered as a sliding toggle, better suited for settings and feature flags.
-
-```python
-from prefab_ui.components import Switch
-
-Switch(name="dark_mode", label="Dark Mode")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `name` | `str` | State/form key |
-| `label` | `str` | Label text |
-
-### Slider
-
-A range input for numeric values. The user drags a handle between `min` and `max`.
-
-```python
-from prefab_ui.components import Slider
-
-Slider(name="volume", label="Volume", min=0, max=100, step=1)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `name` | `str` | State/form key |
-| `label` | `str` | Label text |
-| `min` | `float` | Minimum value |
-| `max` | `float` | Maximum value |
-| `step` | `float` | Step increment |
-
-### Form
-
-Wraps input components and gathers their values on submit. Attach a `CallTool` action to `on_submit` to send the data to the server. Every named input inside the form becomes a key in the arguments dict.
-
-```python
-from prefab_ui.components import Form, Input, Button
-from prefab_ui.actions.mcp import CallTool
-
-with Form(on_submit=CallTool("save_contact")):
- Input(name="name", label="Name", required=True)
- Input(name="email", label="Email", required=True)
- Button("Save")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `on_submit` | `Action` | Action to run when the form is submitted |
-
-### Button
-
-A clickable button. Inside a `Form`, a button triggers form submission by default. Outside a form, attach actions to `on_click`.
-
-```python
-from prefab_ui.components import Button
-from prefab_ui.actions import SetState
-
-Button("Reset", on_click=SetState("count", 0))
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `variant` | `str` | `"default"`, `"outline"`, `"ghost"`, `"destructive"` |
-| `on_click` | `Action` | Action to run on click |
-| `disabled` | `bool` | Disable the button |
-
-## Containers
-
-### Tabs / Tab
-
-Organizes content into switchable panels. Each `Tab` becomes a panel with a label in the tab bar. Switching tabs is instant — all panels are rendered, only one is visible.
-
-```python
-from prefab_ui.components import Tabs, Tab, Text
-
-with Tabs():
- with Tab("Overview"):
- Text("Overview content here")
- with Tab("Details"):
- Text("Detail content here")
-```
-
-| Prop (Tabs) | Type | Description |
-|-------------|------|-------------|
-| `value` | `str` | Label of the initially active tab |
-
-### Accordion / AccordionItem
-
-Collapsible sections. Each `AccordionItem` has a title that toggles its content open and closed. By default, only one item is open at a time.
-
-```python
-from prefab_ui.components import Accordion, AccordionItem, Text
-
-with Accordion(multiple=True):
- with AccordionItem("Section A"):
- Text("Content for section A")
- with AccordionItem("Section B"):
- Text("Content for section B")
-```
-
-| Prop (Accordion) | Type | Description |
-|------------------|------|-------------|
-| `multiple` | `bool` | Allow multiple items open simultaneously |
-
-### Dialog
-
-A modal overlay that appears above the page content. Pair it with a trigger (like a `Button`) to open and close it.
-
-```python
-from prefab_ui.components import Dialog, Column, Heading, Text
-
-with Dialog(title="Confirm Delete"):
- with Column(gap=2):
- Text("Are you sure you want to delete this item?")
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `title` | `str` | Dialog title in the header |
-| `description` | `str` | Subtitle below the title |
-
-### Pages / Page
-
-Multi-page navigation within a single app. `Pages` renders one `Page` at a time, controlled by state. Useful for multi-step workflows and wizards.
-
-```python
-from prefab_ui.components import Pages, Page, Text, Button
-from prefab_ui.actions import SetState
-from prefab_ui.rx import Rx
-
-with Pages(active_page=Rx("page")):
- with Page("welcome"):
- Text("Welcome!")
- Button("Next", on_click=SetState("page", "setup"))
- with Page("setup"):
- Text("Configure your settings")
-
-# Pass state={"page": "welcome"} to PrefabApp when returning
-```
-
-| Prop (Pages) | Type | Description |
-|-------------|------|-------------|
-| `active_page` | `str \| Rx` | The label of the currently visible page |
-
-## Control Flow
-
-Control flow components conditionally show or iterate over children based on reactive state. They evaluate in the browser, so changes are instant.
-
-### If / Elif / Else
-
-Conditionally render content based on state values. `If` evaluates an `Rx` expression; `Elif` and `Else` follow the same pattern as Python's branching.
-
-```python
-from prefab_ui.components import If, Elif, Else, Text, Select
-from prefab_ui.rx import Rx
-
-role = Rx("role")
-
-Select(name="role", options=["viewer", "editor", "admin"])
-with If(role == "admin"):
- Text("Full access")
-with Elif(role == "editor"):
- Text("Edit access")
-with Else():
- Text("Read-only access")
-
-# Pass state={"role": "viewer"} to PrefabApp when returning
-```
-
-### ForEach
-
-Iterates over a state array and renders children for each item. The loop variable is an `Rx` proxy scoped to the current item, so `item.name` resolves at render time.
-
-```python
-from prefab_ui.components import ForEach, Row, Text, Badge
-
-with ForEach("users") as user:
- with Row(gap=2):
- Text(user.name)
- Badge(user.role)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| first arg | `str` | The state key containing the array |
-
-## Feedback
-
-### Alert
-
-A callout box for important messages. Supports variants to signal severity.
-
-```python
-from prefab_ui.components import Alert
-
-Alert(title="Deployment complete", variant="success")
-Alert(
- title="Rate limit approaching",
- description="Current usage is at 85% of your plan limit.",
- variant="warning",
-)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `title` | `str` | Alert heading |
-| `description` | `str` | Body text |
-| `variant` | `str` | `"default"`, `"success"`, `"warning"`, `"destructive"` |
-
-### Progress
-
-A horizontal progress bar. Pass a `value` between 0 and 100. Supports reactive values so the bar updates as state changes.
-
-```python
-from prefab_ui.components import Progress
-
-Progress(value=75)
-```
-
-| Prop | Type | Description |
-|------|------|-------------|
-| `value` | `int \| Rx` | Progress percentage (0-100) |
-
-### Loader
-
-A spinning indicator for loading states. Takes no required props — just drop it in and it spins.
-
-```python
-from prefab_ui.components import Loader
-
-Loader()
-```
-
----
-
-For the complete API — including additional components like `Metric`, `Calendar`, `Markdown`, `Embed`, and advanced chart types — see the [Prefab component reference](https://prefab.prefect.io/docs/components).
diff --git a/docs/apps/generative.mdx b/docs/apps/generative.mdx
index 71d7e111d..4046acd0f 100644
--- a/docs/apps/generative.mdx
+++ b/docs/apps/generative.mdx
@@ -123,6 +123,6 @@ The streaming renderer loads Pyodide from CDN in the browser. The CSP is configu
## 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
+- **[Prefab UI](/apps/prefab)** — The component library and state system the LLM writes code against
+- **[Prefab Component Reference](https://prefab.prefect.io/docs/components)** — Full component library documentation
- **[Development](/apps/development)** — Preview generative UI tools locally with `fastmcp dev apps`
diff --git a/docs/apps/images/app-chart.png b/docs/apps/images/app-chart.png
new file mode 100644
index 000000000..cfc816d0e
Binary files /dev/null and b/docs/apps/images/app-chart.png differ
diff --git a/docs/apps/images/app-contacts.png b/docs/apps/images/app-contacts.png
new file mode 100644
index 000000000..5d74f7cb9
Binary files /dev/null and b/docs/apps/images/app-contacts.png differ
diff --git a/docs/apps/images/app-datatable.png b/docs/apps/images/app-datatable.png
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/apps/images/app-greet.png b/docs/apps/images/app-greet.png
new file mode 100644
index 000000000..70a0e4412
Binary files /dev/null and b/docs/apps/images/app-greet.png differ
diff --git a/docs/apps/images/app-overview.png b/docs/apps/images/app-overview.png
new file mode 100644
index 000000000..35f68fd58
Binary files /dev/null and b/docs/apps/images/app-overview.png differ
diff --git a/docs/apps/images/app-showcase.png b/docs/apps/images/app-showcase.png
new file mode 100644
index 000000000..c03294bdb
Binary files /dev/null and b/docs/apps/images/app-showcase.png differ
diff --git a/docs/apps/overview.mdx b/docs/apps/overview.mdx
index 11c10773a..c74482bd7 100644
--- a/docs/apps/overview.mdx
+++ b/docs/apps/overview.mdx
@@ -12,7 +12,9 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
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.
-{/* 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.
@@ -150,17 +152,13 @@ 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 |
+Most apps start with **[Prefab Apps](/apps/prefab)** — add `app=True` to a tool and return components. That covers charts, tables, dashboards, and client-side interactivity.
-The boundary isn't sharp. Start with a Prefab app; graduate to `FastMCPApp` when the tool-management complexity justifies it.
+When your UI needs multiple backend tools with managed visibility and composition safety, use **[FastMCPApp](/apps/interactive-apps)**.
+
+When you want the LLM to design the UI at runtime, use **[Generative UI](/apps/generative)**.
+
+When you need your own HTML/JS (maps, 3D, video), use **[Custom HTML](/apps/low-level)**.
## Custom HTML Apps
diff --git a/docs/apps/prefab.mdx b/docs/apps/prefab.mdx
index b598fb63c..0ee703f5e 100644
--- a/docs/apps/prefab.mdx
+++ b/docs/apps/prefab.mdx
@@ -1,7 +1,7 @@
---
-title: Prefab Apps
-sidebarTitle: Prefab Apps
-description: Build interactive tool UIs in pure Python — charts, tables, dashboards, forms, and reactive displays.
+title: Prefab UI
+sidebarTitle: Prefab UI
+description: The component library behind FastMCP apps — charts, tables, dashboards, forms, and reactive displays.
icon: palette
tag: NEW
---
@@ -14,11 +14,11 @@ 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).
-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 UI](https://prefab.prefect.io) is the component library behind all FastMCP app features. You describe layouts, charts, tables, and forms in Python, and Prefab compiles them to interactive UIs that render in the host's conversation.
-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.
+The simplest way to use it: add `app=True` to a tool and return Prefab components. The host renders an interactive UI instead of text. This works for everything from static charts to reactive dashboards with client-side state — no server round-trips needed.
-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.
+For apps that need server interaction (forms, search, CRUD), see [FastMCPApp](/apps/interactive-apps) which adds managed tool binding on top of Prefab UI. For LLM-generated UIs, see [Generative UI](/apps/generative).
## Getting Started
@@ -258,17 +258,16 @@ def team_list() -> PrefabApp:
```python
from prefab_ui.app import PrefabApp
-from prefab_ui.components import Column, Select, If, Elif, Else, Text
+from prefab_ui.components import Column, Select, SelectOption, If, Elif, Else, Text
from prefab_ui.rx import Rx
tier = Rx("tier")
with Column(gap=4) as view:
- Select(
- name="tier",
- label="Plan",
- options=["free", "pro", "enterprise"],
- )
+ with Select(name="tier", label="Plan"):
+ SelectOption("Free", value="free")
+ SelectOption("Pro", value="pro")
+ SelectOption("Enterprise", value="enterprise")
with If(tier == "enterprise"):
Text("Full access to all features")
with Elif(tier == "pro"):
@@ -281,37 +280,9 @@ with Column(gap=4) as view:
Changes are instant — switching the dropdown re-evaluates the conditions in the browser.
-## What You Return
+## Giving the LLM Context
-### Components
-
-The simplest approach. Return a component directly and FastMCP wraps it in a `PrefabApp` automatically:
-
-```python
-@mcp.tool(app=True)
-def status() -> Column:
- with Column(gap=2) as view:
- Heading("All Systems Operational")
- Badge("Healthy", variant="success")
- return view
-```
-
-### PrefabApp
-
-When you need initial state or other configuration, return a `PrefabApp` explicitly with a `state` dict:
-
-```python
-@mcp.tool(app=True)
-def dashboard() -> PrefabApp:
- # ... build view ...
- return PrefabApp(view=view, state={"tab": "overview"})
-```
-
-### ToolResult
-
-Every tool result has two audiences: the renderer (which displays the UI) and the LLM (which reads text content to understand what happened). By default, Prefab sends `"[Rendered Prefab UI]"` as the text — which tells the LLM nothing useful.
-
-If the model needs to reason about the data — reference it in conversation, summarize it, decide what to do next — wrap your return in a `ToolResult`:
+By default, Prefab sends `"[Rendered Prefab UI]"` as the text content for the LLM. If the model needs to reason about the data, wrap your return in a `ToolResult` with a meaningful summary:
```python
from prefab_ui.app import PrefabApp
@@ -339,35 +310,12 @@ def sales_overview(year: int) -> ToolResult:
)
```
-The user sees the chart. The LLM sees the summary string and can reason about it.
+The user sees the chart. The LLM sees the summary string.
-## Type Inference
+## Advanced
-If your tool's return type annotation is a Prefab type — `PrefabApp`, `Component`, or unions/optionals containing them — FastMCP detects this and enables app rendering automatically:
-
-```python
-@mcp.tool
-def greet(name: str) -> PrefabApp:
- return PrefabApp(view=Heading(f"Hello, {name}!"))
-```
-
-This is equivalent to `@mcp.tool(app=True)`. Explicit `app=True` is recommended for clarity, and required when the return type doesn't reveal a Prefab type (e.g., `-> ToolResult`).
-
-## How It Works
-
-When a tool returns a Prefab component or `PrefabApp`, three things happen behind the scenes:
-
-A **shared renderer** is registered as a `ui://prefab/renderer.html` resource. This is a JavaScript rendering engine that interprets Prefab's JSON component protocol. The host fetches it once and reuses it for all Prefab tools on the server.
-
-The **tool metadata** is wired so the host knows to load the renderer iframe when displaying the result. This includes Content Security Policy headers that the renderer needs for its dependencies.
-
-The **component tree** is serialized as `structuredContent` on the tool result. The renderer receives this JSON and renders the UI.
-
-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:
+
+`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:
```python
from fastmcp.apps import PrefabAppConfig, ResourceCSP
@@ -380,10 +328,22 @@ 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
+
+If your return type annotation is a Prefab type — `PrefabApp`, `Component`, or unions containing them — FastMCP enables app rendering automatically, even without `app=True`:
-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
+@mcp.tool
+def greet(name: str) -> PrefabApp:
+ return PrefabApp(view=Heading(f"Hello, {name}!"))
+```
+
+Explicit `app=True` is recommended for clarity.
+
+
+
+Prefab tools and [custom HTML tools](/apps/low-level) coexist on the same server:
```python
from fastmcp.apps import AppConfig
@@ -396,6 +356,7 @@ def team_directory() -> PrefabApp:
def map_view() -> str:
...
```
+
## Next Steps
diff --git a/docs/docs.json b/docs/docs.json
index 6545b240b..5e08f3069 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -140,8 +140,7 @@
"servers/providers/proxy",
"servers/providers/skills",
"servers/providers/custom"
- ],
- "tag": "NEW"
+ ]
},
{
"collapsed": true,
@@ -156,8 +155,7 @@
"servers/transforms/tool-search",
"servers/transforms/resources-as-tools",
"servers/transforms/prompts-as-tools"
- ],
- "tag": "NEW"
+ ]
},
{
"collapsed": true,
@@ -171,8 +169,7 @@
"servers/auth/oidc-proxy",
"servers/auth/full-oauth-server",
"servers/auth/multi-auth"
- ],
- "tag": "UPDATED"
+ ]
},
"servers/authorization",
{
@@ -201,11 +198,11 @@
"group": "Reference",
"icon": "book",
"pages": [
- "apps/components",
"apps/patterns",
"apps/architecture",
"apps/low-level"
- ]
+ ],
+ "tag": "NEW"
}
]
},
@@ -381,7 +378,8 @@
"pages": [
"python-sdk/fastmcp-apps-__init__",
"python-sdk/fastmcp-apps-app",
- "python-sdk/fastmcp-apps-config"
+ "python-sdk/fastmcp-apps-config",
+ "python-sdk/fastmcp-apps-generative"
]
},
{
diff --git a/docs/python-sdk/fastmcp-apps-app.mdx b/docs/python-sdk/fastmcp-apps-app.mdx
index 93f93f3e3..759e487ae 100644
--- a/docs/python-sdk/fastmcp-apps-app.mdx
+++ b/docs/python-sdk/fastmcp-apps-app.mdx
@@ -26,8 +26,8 @@ Usage::
return Column(...)
@app.tool()
- def save_contact(name: str, email: str) -> dict:
- return {"name": name, "email": email}
+ def save_contact(name: str, email: str) -> str:
+ return name
server = FastMCP("Platform")
server.add_provider(app)
diff --git a/docs/python-sdk/fastmcp-apps-config.mdx b/docs/python-sdk/fastmcp-apps-config.mdx
index aba84b288..d7c5edf2f 100644
--- a/docs/python-sdk/fastmcp-apps-config.mdx
+++ b/docs/python-sdk/fastmcp-apps-config.mdx
@@ -15,7 +15,7 @@ UI metadata for clients that support interactive app rendering.
## Functions
-### `app_config_to_meta_dict`
+### `app_config_to_meta_dict`
```python
app_config_to_meta_dict(app: AppConfig | dict[str, Any]) -> dict[str, Any]
@@ -62,3 +62,29 @@ All fields use ``exclude_none`` serialization so only explicitly-set
values appear on the wire. Aliases match the MCP Apps wire format
(camelCase).
+
+### `PrefabAppConfig`
+
+
+App configuration for Prefab tools with sensible defaults.
+
+Like ``app=True`` but customizable. Auto-wires the Prefab renderer
+URI and merges the renderer's CSP with any additional domains you
+specify. The renderer resource is registered automatically.
+
+Example::
+
+ @mcp.tool(app=PrefabAppConfig()) # same as app=True
+
+ @mcp.tool(app=PrefabAppConfig(
+ csp=ResourceCSP(frame_domains=["https://example.com"]),
+ ))
+
+
+**Methods:**
+
+#### `model_post_init`
+
+```python
+model_post_init(self, __context: Any) -> None
+```
diff --git a/docs/python-sdk/fastmcp-apps-generative.mdx b/docs/python-sdk/fastmcp-apps-generative.mdx
new file mode 100644
index 000000000..336d4f353
--- /dev/null
+++ b/docs/python-sdk/fastmcp-apps-generative.mdx
@@ -0,0 +1,56 @@
+---
+title: generative
+sidebarTitle: generative
+---
+
+# `fastmcp.apps.generative`
+
+
+GenerativeUI — a Provider that adds LLM-generated UI capabilities.
+
+Registers tools and resources from ``prefab_ui.generative`` so that an
+LLM can write Prefab Python code, execute it in a sandbox, and render
+the result as a streaming interactive UI.
+
+Requires ``fastmcp[apps]`` (prefab-ui).
+
+Usage::
+
+ from fastmcp import FastMCP
+ from fastmcp.apps.generative import GenerativeUI
+
+ mcp = FastMCP("My Server")
+ mcp.add_provider(GenerativeUI())
+
+
+## Classes
+
+### `GenerativeUI`
+
+
+A Provider that adds generative UI capabilities to a server.
+
+Registers:
+
+- A ``generate_ui`` tool that accepts Prefab Python code, executes
+ it in a Pyodide sandbox, and returns the rendered PrefabApp.
+ Supports streaming via ``ontoolinputpartial``.
+- A ``components`` tool that searches the Prefab component library.
+- The generative renderer resource with CSP for Pyodide CDN access.
+
+Example::
+
+ from fastmcp import FastMCP
+ from fastmcp.apps.generative import GenerativeUI
+
+ mcp = FastMCP("My Server")
+ mcp.add_provider(GenerativeUI())
+
+
+**Methods:**
+
+#### `lifespan`
+
+```python
+lifespan(self) -> AsyncIterator[None]
+```
diff --git a/docs/python-sdk/fastmcp-cli-apps_dev.mdx b/docs/python-sdk/fastmcp-cli-apps_dev.mdx
index 059a40494..5ca1aa6ec 100644
--- a/docs/python-sdk/fastmcp-cli-apps_dev.mdx
+++ b/docs/python-sdk/fastmcp-cli-apps_dev.mdx
@@ -32,7 +32,7 @@ Startup sequence
## Functions
-### `run_dev_apps`
+### `run_dev_apps`
```python
run_dev_apps(server_spec: str) -> None
diff --git a/docs/python-sdk/fastmcp-client-transports-http.mdx b/docs/python-sdk/fastmcp-client-transports-http.mdx
index 6f819138c..a0db1401d 100644
--- a/docs/python-sdk/fastmcp-client-transports-http.mdx
+++ b/docs/python-sdk/fastmcp-client-transports-http.mdx
@@ -10,7 +10,7 @@ Streamable HTTP transport for FastMCP Client.
## Classes
-### `StreamableHttpTransport`
+### `StreamableHttpTransport`
Transport implementation that connects to an MCP server via Streamable HTTP Requests.
@@ -18,19 +18,19 @@ Transport implementation that connects to an MCP server via Streamable HTTP Requ
**Methods:**
-#### `connect_session`
+#### `connect_session`
```python
connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession]
```
-#### `get_session_id`
+#### `get_session_id`
```python
get_session_id(self) -> str | None
```
-#### `close`
+#### `close`
```python
close(self)
diff --git a/docs/python-sdk/fastmcp-client-transports-stdio.mdx b/docs/python-sdk/fastmcp-client-transports-stdio.mdx
index eb7d98eb2..ac317bfc3 100644
--- a/docs/python-sdk/fastmcp-client-transports-stdio.mdx
+++ b/docs/python-sdk/fastmcp-client-transports-stdio.mdx
@@ -30,49 +30,49 @@ connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[
connect(self, **session_kwargs: Unpack[SessionKwargs]) -> ClientSession | None
```
-#### `disconnect`
+#### `disconnect`
```python
disconnect(self)
```
-#### `close`
+#### `close`
```python
close(self)
```
-### `PythonStdioTransport`
+### `PythonStdioTransport`
Transport for running Python scripts.
-### `FastMCPStdioTransport`
+### `FastMCPStdioTransport`
Transport for running FastMCP servers using the FastMCP CLI.
-### `NodeStdioTransport`
+### `NodeStdioTransport`
Transport for running Node.js scripts.
-### `UvStdioTransport`
+### `UvStdioTransport`
Transport for running commands via the uv tool.
-### `UvxStdioTransport`
+### `UvxStdioTransport`
Transport for running commands via the uvx tool.
-### `NpxStdioTransport`
+### `NpxStdioTransport`
Transport for running commands via the npx tool.
diff --git a/docs/python-sdk/fastmcp-exceptions.mdx b/docs/python-sdk/fastmcp-exceptions.mdx
index d8f5a6871..3494751f7 100644
--- a/docs/python-sdk/fastmcp-exceptions.mdx
+++ b/docs/python-sdk/fastmcp-exceptions.mdx
@@ -10,61 +10,71 @@ Custom exceptions for FastMCP.
## Classes
-### `FastMCPError`
+### `FastMCPDeprecationWarning`
+
+
+Deprecation warning for FastMCP APIs.
+
+Subclass of DeprecationWarning so that standard warning filters
+still apply, but FastMCP can selectively enable its own warnings
+without affecting other libraries in the process.
+
+
+### `FastMCPError`
Base error for FastMCP.
-### `ValidationError`
+### `ValidationError`
Error in validating parameters or return values.
-### `ResourceError`
+### `ResourceError`
Error in resource operations.
-### `ToolError`
+### `ToolError`
Error in tool operations.
-### `PromptError`
+### `PromptError`
Error in prompt operations.
-### `InvalidSignature`
+### `InvalidSignature`
Invalid signature for use with FastMCP.
-### `ClientError`
+### `ClientError`
Error in client operations.
-### `NotFoundError`
+### `NotFoundError`
Object not found.
-### `DisabledError`
+### `DisabledError`
Object is disabled.
-### `AuthorizationError`
+### `AuthorizationError`
Error when authorization check fails.
diff --git a/docs/python-sdk/fastmcp-prompts-base.mdx b/docs/python-sdk/fastmcp-prompts-base.mdx
index d016286b4..1c57fbea2 100644
--- a/docs/python-sdk/fastmcp-prompts-base.mdx
+++ b/docs/python-sdk/fastmcp-prompts-base.mdx
@@ -10,7 +10,7 @@ Base classes for FastMCP prompts.
## Classes
-### `Message`
+### `Message`
Wrapper for prompt message with auto-serialization.
@@ -21,7 +21,7 @@ Accepts any content - strings pass through, other types
**Methods:**
-#### `to_mcp_prompt_message`
+#### `to_mcp_prompt_message`
```python
to_mcp_prompt_message(self) -> PromptMessage
@@ -30,13 +30,13 @@ to_mcp_prompt_message(self) -> PromptMessage
Convert to MCP PromptMessage.
-### `PromptArgument`
+### `PromptArgument`
An argument that can be passed to a prompt.
-### `PromptResult`
+### `PromptResult`
Canonical result type for prompt rendering.
@@ -47,7 +47,7 @@ roles, and metadata at both the message and result level.
**Methods:**
-#### `to_mcp_prompt_result`
+#### `to_mcp_prompt_result`
```python
to_mcp_prompt_result(self) -> GetPromptResult
@@ -56,7 +56,7 @@ to_mcp_prompt_result(self) -> GetPromptResult
Convert to MCP GetPromptResult.
-### `Prompt`
+### `Prompt`
A prompt template that can be rendered with parameters.
@@ -64,7 +64,7 @@ A prompt template that can be rendered with parameters.
**Methods:**
-#### `to_mcp_prompt`
+#### `to_mcp_prompt`
```python
to_mcp_prompt(self, **overrides: Any) -> SDKPrompt
@@ -73,7 +73,7 @@ to_mcp_prompt(self, **overrides: Any) -> SDKPrompt
Convert the prompt to an MCP prompt.
-#### `from_function`
+#### `from_function`
```python
from_function(cls, fn: Callable[..., Any]) -> FunctionPrompt
@@ -87,7 +87,7 @@ The function can return:
- PromptResult: used directly
-#### `render`
+#### `render`
```python
render(self, arguments: dict[str, Any] | None = None) -> str | list[Message | str] | PromptResult
@@ -101,7 +101,7 @@ Subclasses must implement this method. Return one of:
- PromptResult: Used directly
-#### `convert_result`
+#### `convert_result`
```python
convert_result(self, raw_value: Any) -> PromptResult
@@ -113,7 +113,7 @@ Convert a raw return value to PromptResult.
- `TypeError`: for unsupported types
-#### `register_with_docket`
+#### `register_with_docket`
```python
register_with_docket(self, docket: Docket) -> None
@@ -122,7 +122,7 @@ register_with_docket(self, docket: Docket) -> None
Register this prompt with docket for background execution.
-#### `add_to_docket`
+#### `add_to_docket`
```python
add_to_docket(self, docket: Docket, arguments: dict[str, Any] | None, **kwargs: Any) -> Execution
@@ -138,7 +138,7 @@ Schedule this prompt for background execution via docket.
- `**kwargs`: Additional kwargs passed to docket.add()
-#### `get_span_attributes`
+#### `get_span_attributes`
```python
get_span_attributes(self) -> dict[str, Any]
diff --git a/docs/python-sdk/fastmcp-resources-base.mdx b/docs/python-sdk/fastmcp-resources-base.mdx
index 23735cff5..aab4a1dd7 100644
--- a/docs/python-sdk/fastmcp-resources-base.mdx
+++ b/docs/python-sdk/fastmcp-resources-base.mdx
@@ -10,7 +10,7 @@ Base classes and interfaces for FastMCP resources.
## Classes
-### `ResourceContent`
+### `ResourceContent`
Wrapper for resource content with optional MIME type and metadata.
@@ -21,7 +21,7 @@ other types (dict, list, BaseModel, etc.) are automatically JSON-serialized.
**Methods:**
-#### `to_mcp_resource_contents`
+#### `to_mcp_resource_contents`
```python
to_mcp_resource_contents(self, uri: AnyUrl | str) -> mcp.types.TextResourceContents | mcp.types.BlobResourceContents
@@ -36,7 +36,7 @@ Convert to MCP resource contents type.
- TextResourceContents for str content, BlobResourceContents for bytes
-### `ResourceResult`
+### `ResourceResult`
Canonical result type for resource reads.
@@ -47,7 +47,7 @@ per-item MIME types, and metadata at both the item and result level.
**Methods:**
-#### `to_mcp_result`
+#### `to_mcp_result`
```python
to_mcp_result(self, uri: AnyUrl | str) -> mcp.types.ReadResourceResult
@@ -62,7 +62,7 @@ Convert to MCP ReadResourceResult.
- MCP ReadResourceResult with converted contents
-### `Resource`
+### `Resource`
Base class for all resources.
@@ -70,13 +70,13 @@ Base class for all resources.
**Methods:**
-#### `from_function`
+#### `from_function`
```python
from_function(cls, fn: Callable[..., Any], uri: str | AnyUrl) -> FunctionResource
```
-#### `set_default_mime_type`
+#### `set_default_mime_type`
```python
set_default_mime_type(cls, mime_type: str | None) -> str
@@ -85,7 +85,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str
Set default MIME type if not provided.
-#### `set_default_name`
+#### `set_default_name`
```python
set_default_name(self) -> Self
@@ -94,7 +94,7 @@ set_default_name(self) -> Self
Set default name from URI if not provided.
-#### `read`
+#### `read`
```python
read(self) -> str | bytes | ResourceResult
@@ -108,7 +108,7 @@ Subclasses implement this to return resource data. Supported return types:
- ResourceResult: Full control over contents and result-level meta
-#### `convert_result`
+#### `convert_result`
```python
convert_result(self, raw_value: Any) -> ResourceResult
@@ -131,7 +131,7 @@ MCP Apps CSP/permissions) is propagated to each content item so
that hosts can read it from the ``resources/read`` response.
-#### `to_mcp_resource`
+#### `to_mcp_resource`
```python
to_mcp_resource(self, **overrides: Any) -> SDKResource
@@ -140,7 +140,7 @@ to_mcp_resource(self, **overrides: Any) -> SDKResource
Convert the resource to an SDKResource.
-#### `key`
+#### `key`
```python
key(self) -> str
@@ -149,7 +149,7 @@ key(self) -> str
The globally unique lookup key for this resource.
-#### `register_with_docket`
+#### `register_with_docket`
```python
register_with_docket(self, docket: Docket) -> None
@@ -158,7 +158,7 @@ register_with_docket(self, docket: Docket) -> None
Register this resource with docket for background execution.
-#### `add_to_docket`
+#### `add_to_docket`
```python
add_to_docket(self, docket: Docket, **kwargs: Any) -> Execution
@@ -173,7 +173,7 @@ Schedule this resource for background execution via docket.
- `**kwargs`: Additional kwargs passed to docket.add()
-#### `get_span_attributes`
+#### `get_span_attributes`
```python
get_span_attributes(self) -> dict[str, Any]
diff --git a/docs/python-sdk/fastmcp-resources-function_resource.mdx b/docs/python-sdk/fastmcp-resources-function_resource.mdx
index ebdad0fd9..4977f0d58 100644
--- a/docs/python-sdk/fastmcp-resources-function_resource.mdx
+++ b/docs/python-sdk/fastmcp-resources-function_resource.mdx
@@ -10,7 +10,7 @@ Standalone @resource decorator for FastMCP.
## Functions
-### `resource`
+### `resource`
```python
resource(uri: str) -> Callable[[F], F]
@@ -25,19 +25,19 @@ using mcp.add_resource().
## Classes
-### `DecoratedResource`
+### `DecoratedResource`
Protocol for functions decorated with @resource.
-### `ResourceMeta`
+### `ResourceMeta`
Metadata attached to functions by the @resource decorator.
-### `FunctionResource`
+### `FunctionResource`
A resource that defers data loading by wrapping a function.
@@ -54,7 +54,7 @@ The function can return:
**Methods:**
-#### `from_function`
+#### `from_function`
```python
from_function(cls, fn: Callable[..., Any], uri: str | AnyUrl | None = None) -> FunctionResource
@@ -71,7 +71,7 @@ individual parameters must not be passed.
Cannot be used together with metadata parameter.
-#### `read`
+#### `read`
```python
read(self) -> str | bytes | ResourceResult
@@ -80,7 +80,7 @@ read(self) -> str | bytes | ResourceResult
Read the resource by calling the wrapped function.
-#### `register_with_docket`
+#### `register_with_docket`
```python
register_with_docket(self, docket: Docket) -> None
diff --git a/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx b/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx
index 19b6a9a01..6301e4c28 100644
--- a/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx
+++ b/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx
@@ -14,7 +14,7 @@ using the OAuth Proxy pattern for non-DCR OAuth flows.
## Functions
-### `EntraOBOToken`
+### `EntraOBOToken`
```python
EntraOBOToken(scopes: list[str]) -> str
@@ -43,7 +43,7 @@ or OBO exchange fails
## Classes
-### `AzureProvider`
+### `AzureProvider`
Azure (Microsoft Entra) OAuth provider for FastMCP.
@@ -78,7 +78,7 @@ Setup:
**Methods:**
-#### `authorize`
+#### `authorize`
```python
authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str
@@ -98,7 +98,7 @@ scopes to determine the resource/audience instead of a separate parameter.
- Authorization URL to redirect the user to Azure AD
-#### `get_obo_credential`
+#### `get_obo_credential`
```python
get_obo_credential(self, user_assertion: str) -> OnBehalfOfCredential
@@ -120,7 +120,7 @@ calls multiple tools with the same scopes.
- `ImportError`: If azure-identity is not installed (requires fastmcp[azure]).
-#### `close_obo_credentials`
+#### `close_obo_credentials`
```python
close_obo_credentials(self) -> None
@@ -129,7 +129,7 @@ close_obo_credentials(self) -> None
Close all cached OBO credentials.
-### `AzureJWTVerifier`
+### `AzureJWTVerifier`
JWT verifier pre-configured for Azure AD / Microsoft Entra ID.
@@ -166,7 +166,7 @@ Example::
**Methods:**
-#### `scopes_supported`
+#### `scopes_supported`
```python
scopes_supported(self) -> list[str]
diff --git a/docs/python-sdk/fastmcp-server-dependencies.mdx b/docs/python-sdk/fastmcp-server-dependencies.mdx
index 6f35aba31..f23d5ec59 100644
--- a/docs/python-sdk/fastmcp-server-dependencies.mdx
+++ b/docs/python-sdk/fastmcp-server-dependencies.mdx
@@ -15,7 +15,7 @@ CurrentWorker) and background task execution require fastmcp[tasks].
## Functions
-### `get_task_context`
+### `get_task_context`
```python
get_task_context() -> TaskContextInfo | None
@@ -31,7 +31,7 @@ Returns None if not running in a task context (e.g., foreground execution).
- TaskContextInfo with task_id and session_id, or None if not in a task.
-### `register_task_session`
+### `register_task_session`
```python
register_task_session(session_id: str, session: ServerSession) -> None
@@ -49,7 +49,7 @@ client disconnects.
- `session`: The ServerSession instance
-### `get_task_session`
+### `get_task_session`
```python
get_task_session(session_id: str) -> ServerSession | None
@@ -65,7 +65,24 @@ Get a registered session by ID if still alive.
- The ServerSession if found and alive, None otherwise
-### `is_docket_available`
+### `register_task_server`
+
+```python
+register_task_server(task_id: str, server: FastMCP) -> None
+```
+
+
+Register the server for a background task.
+
+Called at task-submission time (inside the child server's call_tool
+context) so that background workers can resolve CurrentFastMCP() and
+ctx.fastmcp to the child server for mounted tasks.
+
+The map is bounded to avoid unbounded growth in long-lived servers.
+Evicted entries fall back to the ContextVar (parent server).
+
+
+### `is_docket_available`
```python
is_docket_available() -> bool
@@ -75,7 +92,7 @@ is_docket_available() -> bool
Check if pydocket is installed.
-### `require_docket`
+### `require_docket`
```python
require_docket(feature: str) -> None
@@ -89,7 +106,7 @@ Raise ImportError with install instructions if docket not available.
"CurrentDocket()"). Will be included in the error message.
-### `transform_context_annotations`
+### `transform_context_annotations`
```python
transform_context_annotations(fn: Callable[..., Any]) -> Callable[..., Any]
@@ -115,7 +132,7 @@ allows them to have defaults in any order.
- Function with modified signature (same function object, updated __signature__)
-### `get_context`
+### `get_context`
```python
get_context() -> Context
@@ -125,7 +142,7 @@ get_context() -> Context
Get the current FastMCP Context instance directly.
-### `get_server`
+### `get_server`
```python
get_server() -> FastMCP
@@ -134,6 +151,10 @@ get_server() -> FastMCP
Get the current FastMCP server instance directly.
+In a background-task worker, checks the task-server map first so that
+mounted-child tasks resolve to the child server (not the parent that
+started the worker).
+
**Returns:**
- The active FastMCP server
@@ -141,7 +162,7 @@ Get the current FastMCP server instance directly.
- `RuntimeError`: If no server in context
-### `get_http_request`
+### `get_http_request`
```python
get_http_request() -> Request
@@ -153,7 +174,7 @@ Get the current HTTP request.
Tries MCP SDK's request_ctx first, then falls back to FastMCP's HTTP context.
-### `get_http_headers`
+### `get_http_headers`
```python
get_http_headers(include_all: bool = False, include: set[str] | None = None) -> dict[str, str]
@@ -174,7 +195,7 @@ normally be excluded. This is useful for proxy transports that need to forward
authorization headers to upstream MCP servers.
-### `get_access_token`
+### `get_access_token`
```python
get_access_token() -> AccessToken | None
@@ -193,7 +214,7 @@ token snapshot stored in Redis at task submission time.
- The access token if an authenticated user is available, None otherwise.
-### `without_injected_parameters`
+### `without_injected_parameters`
```python
without_injected_parameters(fn: Callable[..., Any]) -> Callable[..., Any]
@@ -218,7 +239,7 @@ Handles:
- Async wrapper function without injected parameters
-### `resolve_dependencies`
+### `resolve_dependencies`
```python
resolve_dependencies(fn: Callable[..., Any], arguments: dict[str, Any]) -> AsyncGenerator[dict[str, Any], None]
@@ -244,7 +265,7 @@ time, so all injection goes through the unified DI system.
which will be filtered out)
-### `CurrentContext`
+### `CurrentContext`
```python
CurrentContext() -> Context
@@ -263,7 +284,7 @@ current MCP operation (tool/resource/prompt call).
- `RuntimeError`: If no active context found (during resolution)
-### `OptionalCurrentContext`
+### `OptionalCurrentContext`
```python
OptionalCurrentContext() -> Context | None
@@ -273,7 +294,7 @@ OptionalCurrentContext() -> Context | None
Get the current FastMCP Context, or None when no context is active.
-### `CurrentDocket`
+### `CurrentDocket`
```python
CurrentDocket() -> Docket
@@ -293,7 +314,7 @@ automatically creates for background task scheduling.
- `ImportError`: If fastmcp[tasks] not installed
-### `CurrentWorker`
+### `CurrentWorker`
```python
CurrentWorker() -> Worker
@@ -313,7 +334,7 @@ automatically creates for background task processing.
- `ImportError`: If fastmcp[tasks] not installed
-### `CurrentFastMCP`
+### `CurrentFastMCP`
```python
CurrentFastMCP() -> FastMCP
@@ -331,7 +352,7 @@ This dependency provides access to the active FastMCP server.
- `RuntimeError`: If no server in context (during resolution)
-### `CurrentRequest`
+### `CurrentRequest`
```python
CurrentRequest() -> Request
@@ -351,7 +372,7 @@ current HTTP request. Only available when running over HTTP transports
- `RuntimeError`: If no HTTP request in context (e.g., STDIO transport)
-### `CurrentHeaders`
+### `CurrentHeaders`
```python
CurrentHeaders() -> dict[str, str]
@@ -369,7 +390,7 @@ transport.
- A dependency that resolves to a dictionary of header name -> value
-### `CurrentAccessToken`
+### `CurrentAccessToken`
```python
CurrentAccessToken() -> AccessToken
@@ -388,7 +409,7 @@ authenticated request. Raises an error if no authentication is present.
- `RuntimeError`: If no authenticated user (use get_access_token() for optional)
-### `TokenClaim`
+### `TokenClaim`
```python
TokenClaim(name: str) -> str
@@ -413,7 +434,7 @@ without needing the full token object.
## Classes
-### `TaskContextInfo`
+### `TaskContextInfo`
Information about the current background task context.
@@ -422,7 +443,7 @@ Returned by ``get_task_context()`` when running inside a Docket worker.
Contains identifiers needed to communicate with the MCP session.
-### `ProgressLike`
+### `ProgressLike`
Protocol for progress tracking interface.
@@ -433,7 +454,7 @@ and Docket's Progress (worker context).
**Methods:**
-#### `current`
+#### `current`
```python
current(self) -> int | None
@@ -442,7 +463,7 @@ current(self) -> int | None
Current progress value.
-#### `total`
+#### `total`
```python
total(self) -> int
@@ -451,7 +472,7 @@ total(self) -> int
Total/target progress value.
-#### `message`
+#### `message`
```python
message(self) -> str | None
@@ -460,7 +481,7 @@ message(self) -> str | None
Current progress message.
-#### `set_total`
+#### `set_total`
```python
set_total(self, total: int) -> None
@@ -469,7 +490,7 @@ set_total(self, total: int) -> None
Set the total/target value for progress tracking.
-#### `increment`
+#### `increment`
```python
increment(self, amount: int = 1) -> None
@@ -478,7 +499,7 @@ increment(self, amount: int = 1) -> None
Atomically increment the current progress value.
-#### `set_message`
+#### `set_message`
```python
set_message(self, message: str | None) -> None
@@ -487,7 +508,7 @@ set_message(self, message: str | None) -> None
Update the progress status message.
-### `InMemoryProgress`
+### `InMemoryProgress`
In-memory progress tracker for immediate tool execution.
@@ -499,25 +520,25 @@ progress doesn't need to be observable across processes.
**Methods:**
-#### `current`
+#### `current`
```python
current(self) -> int | None
```
-#### `total`
+#### `total`
```python
total(self) -> int
```
-#### `message`
+#### `message`
```python
message(self) -> str | None
```
-#### `set_total`
+#### `set_total`
```python
set_total(self, total: int) -> None
@@ -526,7 +547,7 @@ set_total(self, total: int) -> None
Set the total/target value for progress tracking.
-#### `increment`
+#### `increment`
```python
increment(self, amount: int = 1) -> None
@@ -535,7 +556,7 @@ increment(self, amount: int = 1) -> None
Atomically increment the current progress value.
-#### `set_message`
+#### `set_message`
```python
set_message(self, message: str | None) -> None
@@ -544,7 +565,7 @@ set_message(self, message: str | None) -> None
Update the progress status message.
-### `Progress`
+### `Progress`
FastMCP Progress dependency that works in both server and worker contexts.
@@ -561,7 +582,7 @@ is installed.
**Methods:**
-#### `current`
+#### `current`
```python
current(self) -> int | None
@@ -570,7 +591,7 @@ current(self) -> int | None
Current progress value.
-#### `total`
+#### `total`
```python
total(self) -> int
@@ -579,7 +600,7 @@ total(self) -> int
Total/target progress value.
-#### `message`
+#### `message`
```python
message(self) -> str | None
@@ -588,7 +609,7 @@ message(self) -> str | None
Current progress message.
-#### `set_total`
+#### `set_total`
```python
set_total(self, total: int) -> None
@@ -597,7 +618,7 @@ set_total(self, total: int) -> None
Set the total/target value for progress tracking.
-#### `increment`
+#### `increment`
```python
increment(self, amount: int = 1) -> None
@@ -606,7 +627,7 @@ increment(self, amount: int = 1) -> None
Atomically increment the current progress value.
-#### `set_message`
+#### `set_message`
```python
set_message(self, message: str | None) -> None
diff --git a/docs/python-sdk/fastmcp-server-middleware-tool_injection.mdx b/docs/python-sdk/fastmcp-server-middleware-tool_injection.mdx
index 1f7b25ebd..549150689 100644
--- a/docs/python-sdk/fastmcp-server-middleware-tool_injection.mdx
+++ b/docs/python-sdk/fastmcp-server-middleware-tool_injection.mdx
@@ -10,7 +10,7 @@ A middleware for injecting tools into the MCP server context.
## Functions
-### `list_prompts`
+### `list_prompts`
```python
list_prompts(context: Context) -> list[Prompt]
@@ -20,7 +20,7 @@ list_prompts(context: Context) -> list[Prompt]
List prompts available on the server.
-### `get_prompt`
+### `get_prompt`
```python
get_prompt(context: Context, name: Annotated[str, 'The name of the prompt to render.'], arguments: Annotated[dict[str, Any] | None, 'The arguments to pass to the prompt.'] = None) -> mcp.types.GetPromptResult
@@ -30,7 +30,7 @@ get_prompt(context: Context, name: Annotated[str, 'The name of the prompt to ren
Render a prompt available on the server.
-### `list_resources`
+### `list_resources`
```python
list_resources(context: Context) -> list[mcp.types.Resource]
@@ -40,7 +40,7 @@ list_resources(context: Context) -> list[mcp.types.Resource]
List resources available on the server.
-### `read_resource`
+### `read_resource`
```python
read_resource(context: Context, uri: Annotated[AnyUrl | str, 'The URI of the resource to read.']) -> ResourceResult
@@ -52,7 +52,7 @@ Read a resource available on the server.
## Classes
-### `ToolInjectionMiddleware`
+### `ToolInjectionMiddleware`
A middleware for injecting tools into the context.
@@ -60,7 +60,7 @@ A middleware for injecting tools into the context.
**Methods:**
-#### `on_list_tools`
+#### `on_list_tools`
```python
on_list_tools(self, context: MiddlewareContext[mcp.types.ListToolsRequest], call_next: CallNext[mcp.types.ListToolsRequest, Sequence[Tool]]) -> Sequence[Tool]
@@ -69,7 +69,7 @@ on_list_tools(self, context: MiddlewareContext[mcp.types.ListToolsRequest], call
Inject tools into the response.
-#### `on_call_tool`
+#### `on_call_tool`
```python
on_call_tool(self, context: MiddlewareContext[mcp.types.CallToolRequestParams], call_next: CallNext[mcp.types.CallToolRequestParams, ToolResult]) -> ToolResult
@@ -78,7 +78,7 @@ on_call_tool(self, context: MiddlewareContext[mcp.types.CallToolRequestParams],
Intercept tool calls to injected tools.
-### `PromptToolMiddleware`
+### `PromptToolMiddleware`
A middleware for injecting prompts as tools into the context.
@@ -87,7 +87,7 @@ A middleware for injecting prompts as tools into the context.
Use ``fastmcp.server.transforms.PromptsAsTools`` instead.
-### `ResourceToolMiddleware`
+### `ResourceToolMiddleware`
A middleware for injecting resources as tools into the context.
diff --git a/docs/python-sdk/fastmcp-server-openapi-server.mdx b/docs/python-sdk/fastmcp-server-openapi-server.mdx
index 4f751e090..374eac2ba 100644
--- a/docs/python-sdk/fastmcp-server-openapi-server.mdx
+++ b/docs/python-sdk/fastmcp-server-openapi-server.mdx
@@ -21,7 +21,7 @@ This class is deprecated. Use FastMCP with OpenAPIProvider instead:
## Classes
-### `FastMCPOpenAPI`
+### `FastMCPOpenAPI`
FastMCP server implementation that creates components from an OpenAPI schema.
diff --git a/docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx b/docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx
index c30b79175..e3e70093e 100644
--- a/docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx
+++ b/docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx
@@ -39,7 +39,7 @@ wrap(cls, server: Any, tool: Tool) -> FastMCPProviderTool
Wrap a Tool to delegate execution to the server's middleware.
-#### `run`
+#### `run`
```python
run(self, arguments: dict[str, Any]) -> ToolResult
@@ -51,13 +51,13 @@ This is called when the tool is used within a TransformedTool
forwarding function or other contexts where task_meta is not available.
-#### `get_span_attributes`
+#### `get_span_attributes`
```python
get_span_attributes(self) -> dict[str, Any]
```
-### `FastMCPProviderResource`
+### `FastMCPProviderResource`
Resource that delegates reading to a wrapped server's read_resource().
@@ -68,7 +68,7 @@ When `read()` is called, this resource invokes the wrapped server's
**Methods:**
-#### `wrap`
+#### `wrap`
```python
wrap(cls, server: Any, resource: Resource) -> FastMCPProviderResource
@@ -77,13 +77,13 @@ wrap(cls, server: Any, resource: Resource) -> FastMCPProviderResource
Wrap a Resource to delegate reading to the server's middleware.
-#### `get_span_attributes`
+#### `get_span_attributes`
```python
get_span_attributes(self) -> dict[str, Any]
```
-### `FastMCPProviderPrompt`
+### `FastMCPProviderPrompt`
Prompt that delegates rendering to a wrapped server's render_prompt().
@@ -94,7 +94,7 @@ When `render()` is called, this prompt invokes the wrapped server's
**Methods:**
-#### `wrap`
+#### `wrap`
```python
wrap(cls, server: Any, prompt: Prompt) -> FastMCPProviderPrompt
@@ -103,7 +103,7 @@ wrap(cls, server: Any, prompt: Prompt) -> FastMCPProviderPrompt
Wrap a Prompt to delegate rendering to the server's middleware.
-#### `render`
+#### `render`
```python
render(self, arguments: dict[str, Any] | None = None) -> PromptResult
@@ -115,13 +115,13 @@ This is called when the prompt is used within a transformed context
or other contexts where task_meta is not available.
-#### `get_span_attributes`
+#### `get_span_attributes`
```python
get_span_attributes(self) -> dict[str, Any]
```
-### `FastMCPProviderResourceTemplate`
+### `FastMCPProviderResourceTemplate`
Resource template that creates FastMCPProviderResources.
@@ -133,7 +133,7 @@ when read.
**Methods:**
-#### `wrap`
+#### `wrap`
```python
wrap(cls, server: Any, template: ResourceTemplate) -> FastMCPProviderResourceTemplate
@@ -142,7 +142,7 @@ wrap(cls, server: Any, template: ResourceTemplate) -> FastMCPProviderResourceTem
Wrap a ResourceTemplate to create FastMCPProviderResources.
-#### `create_resource`
+#### `create_resource`
```python
create_resource(self, uri: str, params: dict[str, Any]) -> Resource
@@ -155,7 +155,7 @@ We use `_original_uri_template` with `params` to construct the internal
URI that the nested server understands.
-#### `read`
+#### `read`
```python
read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult
@@ -167,7 +167,7 @@ Reads the resource via the wrapped server and returns the ResourceResult.
This method is called by Docket during background task execution.
-#### `register_with_docket`
+#### `register_with_docket`
```python
register_with_docket(self, docket: Docket) -> None
@@ -176,7 +176,7 @@ register_with_docket(self, docket: Docket) -> None
No-op: the child's actual template is registered via get_tasks().
-#### `add_to_docket`
+#### `add_to_docket`
```python
add_to_docket(self, docket: Docket, params: dict[str, Any], **kwargs: Any) -> Execution
@@ -188,13 +188,13 @@ The child's FunctionResourceTemplate.fn is registered (via get_tasks),
and it expects splatted **kwargs, so we splat params here.
-#### `get_span_attributes`
+#### `get_span_attributes`
```python
get_span_attributes(self) -> dict[str, Any]
```
-### `FastMCPProvider`
+### `FastMCPProvider`
Provider that wraps a FastMCP server.
@@ -210,7 +210,7 @@ This ensures middleware runs when components are executed.
**Methods:**
-#### `get_app_tool`
+#### `get_app_tool`
```python
get_app_tool(self, app_name: str, tool_name: str) -> Tool | None
@@ -219,7 +219,7 @@ get_app_tool(self, app_name: str, tool_name: str) -> Tool | None
Delegate to nested server's get_app_tool, wrapping for middleware.
-#### `get_tasks`
+#### `get_tasks`
```python
get_tasks(self) -> Sequence[FastMCPComponent]
@@ -233,7 +233,7 @@ server's transforms applied, then applies this provider's transforms
for correct registration keys.
-#### `lifespan`
+#### `lifespan`
```python
lifespan(self) -> AsyncIterator[None]
diff --git a/docs/python-sdk/fastmcp-server-providers-filesystem_discovery.mdx b/docs/python-sdk/fastmcp-server-providers-filesystem_discovery.mdx
index 8f37fb1eb..2a4595b89 100644
--- a/docs/python-sdk/fastmcp-server-providers-filesystem_discovery.mdx
+++ b/docs/python-sdk/fastmcp-server-providers-filesystem_discovery.mdx
@@ -16,7 +16,7 @@ This module provides functions to:
## Functions
-### `discover_files`
+### `discover_files`
```python
discover_files(root: Path) -> list[Path]
@@ -34,10 +34,10 @@ Excludes __init__.py files (they're for package structure, not components).
- List of .py file paths, sorted for deterministic order.
-### `import_module_from_file`
+### `import_module_from_file`
```python
-import_module_from_file(file_path: Path) -> ModuleType
+import_module_from_file(file_path: Path, provider_root: Path | None = None) -> ModuleType
```
@@ -47,8 +47,13 @@ If the file is part of a package (directory has __init__.py), imports
it as a proper package member (relative imports work). Otherwise,
imports directly using spec_from_file_location.
+sys.path is modified only for the duration of the import and restored
+immediately after, so no permanent pollution occurs.
+
**Args:**
- `file_path`: Path to the Python file.
+- `provider_root`: The provider's root directory. Prevents package root
+discovery from walking above this boundary into ancestor packages.
**Returns:**
- The imported module.
@@ -57,7 +62,7 @@ imports directly using spec_from_file_location.
- `ImportError`: If the module cannot be imported.
-### `extract_components`
+### `extract_components`
```python
extract_components(module: ModuleType) -> list[FastMCPComponent]
@@ -77,7 +82,7 @@ or functions decorated with @tool/@resource/@prompt that have __fastmcp__ metada
- List of component objects (Tool, Resource, ResourceTemplate, Prompt).
-### `discover_and_import`
+### `discover_and_import`
```python
discover_and_import(root: Path) -> DiscoveryResult
@@ -97,7 +102,7 @@ This is the main entry point for filesystem-based discovery.
## Classes
-### `DiscoveryResult`
+### `DiscoveryResult`
Result of filesystem discovery.
diff --git a/docs/python-sdk/fastmcp-server-providers-local_provider-decorators-tools.mdx b/docs/python-sdk/fastmcp-server-providers-local_provider-decorators-tools.mdx
index efeae3661..802adb6e0 100644
--- a/docs/python-sdk/fastmcp-server-providers-local_provider-decorators-tools.mdx
+++ b/docs/python-sdk/fastmcp-server-providers-local_provider-decorators-tools.mdx
@@ -14,7 +14,7 @@ registration functionality to LocalProvider.
## Classes
-### `ToolDecoratorMixin`
+### `ToolDecoratorMixin`
Mixin class providing tool decorator functionality for LocalProvider.
@@ -26,7 +26,7 @@ This mixin contains all methods related to:
**Methods:**
-#### `add_tool`
+#### `add_tool`
```python
add_tool(self: LocalProvider, tool: Tool | Callable[..., Any]) -> Tool
@@ -37,19 +37,19 @@ Add a tool to this provider's storage.
Accepts either a Tool object or a decorated function with __fastmcp__ metadata.
-#### `tool`
+#### `tool`
```python
tool(self: LocalProvider, name_or_fn: F) -> F
```
-#### `tool`
+#### `tool`
```python
tool(self: LocalProvider, name_or_fn: str | None = None) -> Callable[[F], F]
```
-#### `tool`
+#### `tool`
```python
tool(self: LocalProvider, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool | partial[Callable[[AnyFunction], FunctionTool] | FunctionTool]
diff --git a/docs/python-sdk/fastmcp-server-providers-openapi-components.mdx b/docs/python-sdk/fastmcp-server-providers-openapi-components.mdx
index 951ff9a7a..f16ecaaeb 100644
--- a/docs/python-sdk/fastmcp-server-providers-openapi-components.mdx
+++ b/docs/python-sdk/fastmcp-server-providers-openapi-components.mdx
@@ -10,7 +10,7 @@ OpenAPI component classes: Tool, Resource, and ResourceTemplate.
## Classes
-### `OpenAPITool`
+### `OpenAPITool`
Tool implementation for OpenAPI endpoints.
@@ -18,7 +18,7 @@ Tool implementation for OpenAPI endpoints.
**Methods:**
-#### `run`
+#### `run`
```python
run(self, arguments: dict[str, Any]) -> ToolResult
@@ -27,7 +27,7 @@ run(self, arguments: dict[str, Any]) -> ToolResult
Execute the HTTP request using RequestDirector.
-### `OpenAPIResource`
+### `OpenAPIResource`
Resource implementation for OpenAPI endpoints.
@@ -35,7 +35,7 @@ Resource implementation for OpenAPI endpoints.
**Methods:**
-#### `read`
+#### `read`
```python
read(self) -> ResourceResult
@@ -44,7 +44,7 @@ read(self) -> ResourceResult
Fetch the resource data by making an HTTP request.
-### `OpenAPIResourceTemplate`
+### `OpenAPIResourceTemplate`
Resource template implementation for OpenAPI endpoints.
@@ -52,7 +52,7 @@ Resource template implementation for OpenAPI endpoints.
**Methods:**
-#### `create_resource`
+#### `create_resource`
```python
create_resource(self, uri: str, params: dict[str, Any], context: Context | None = None) -> Resource
diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx
index 287480c9c..2e7832961 100644
--- a/docs/python-sdk/fastmcp-server-server.mdx
+++ b/docs/python-sdk/fastmcp-server-server.mdx
@@ -10,7 +10,7 @@ FastMCP - A more ergonomic interface for MCP servers.
## Functions
-### `default_lifespan`
+### `default_lifespan`
```python
default_lifespan(server: FastMCP[LifespanResultT]) -> AsyncIterator[Any]
@@ -26,7 +26,7 @@ Default lifespan context manager that does nothing.
- An empty dictionary as the lifespan result.
-### `create_proxy`
+### `create_proxy`
```python
create_proxy(target: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy
@@ -54,53 +54,53 @@ use `FastMCPProxy` or `ProxyProvider` directly from `fastmcp.server.providers.pr
## Classes
-### `StateValue`
+### `StateValue`
Wrapper for stored context state values.
-### `FastMCP`
+### `FastMCP`
**Methods:**
-#### `name`
+#### `name`
```python
name(self) -> str
```
-#### `instructions`
+#### `instructions`
```python
instructions(self) -> str | None
```
-#### `instructions`
+#### `instructions`
```python
instructions(self, value: str | None) -> None
```
-#### `version`
+#### `version`
```python
version(self) -> str | None
```
-#### `website_url`
+#### `website_url`
```python
website_url(self) -> str | None
```
-#### `icons`
+#### `icons`
```python
icons(self) -> list[mcp.types.Icon]
```
-#### `local_provider`
+#### `local_provider`
```python
local_provider(self) -> LocalProvider
@@ -115,13 +115,13 @@ Use this to remove components:
mcp.local_provider.remove_prompt("my_prompt")
-#### `add_middleware`
+#### `add_middleware`
```python
add_middleware(self, middleware: Middleware) -> None
```
-#### `add_provider`
+#### `add_provider`
```python
add_provider(self, provider: Provider) -> None
@@ -141,7 +141,7 @@ always take precedence over providers.
- Prompts become "namespace_promptname"
-#### `get_tasks`
+#### `get_tasks`
```python
get_tasks(self) -> Sequence[FastMCPComponent]
@@ -153,7 +153,7 @@ Overrides AggregateProvider.get_tasks() to apply server-level transforms
after aggregation. AggregateProvider handles provider-level namespacing.
-#### `add_transform`
+#### `add_transform`
```python
add_transform(self, transform: Transform) -> None
@@ -168,7 +168,7 @@ They transform tools, resources, and prompts from ALL providers.
- `transform`: The transform to add.
-#### `add_tool_transformation`
+#### `add_tool_transformation`
```python
add_tool_transformation(self, tool_name: str, transformation: ToolTransformConfig) -> None
@@ -180,7 +180,7 @@ Add a tool transformation.
Use ``add_transform(ToolTransform({...}))`` instead.
-#### `remove_tool_transformation`
+#### `remove_tool_transformation`
```python
remove_tool_transformation(self, _tool_name: str) -> None
@@ -192,7 +192,7 @@ Remove a tool transformation.
Tool transformations are now immutable. Use enable/disable controls instead.
-#### `list_tools`
+#### `list_tools`
```python
list_tools(self) -> Sequence[Tool]
@@ -205,7 +205,7 @@ and middleware execution. Returns all versions (no deduplication).
Protocol handlers deduplicate for MCP wire format.
-#### `get_tool`
+#### `get_tool`
```python
get_tool(self, name: str, version: VersionSpec | None = None) -> Tool | None
@@ -228,7 +228,7 @@ requested, falls back to the next-highest enabled version.
- The tool if found and enabled, None otherwise.
-#### `list_resources`
+#### `list_resources`
```python
list_resources(self) -> Sequence[Resource]
@@ -241,7 +241,7 @@ and middleware execution. Returns all versions (no deduplication).
Protocol handlers deduplicate for MCP wire format.
-#### `get_resource`
+#### `get_resource`
```python
get_resource(self, uri: str, version: VersionSpec | None = None) -> Resource | None
@@ -263,7 +263,7 @@ requested, falls back to the next-highest enabled version.
- The resource if found and enabled, None otherwise.
-#### `list_resource_templates`
+#### `list_resource_templates`
```python
list_resource_templates(self) -> Sequence[ResourceTemplate]
@@ -276,7 +276,7 @@ auth filtering, and middleware execution. Returns all versions (no deduplication
Protocol handlers deduplicate for MCP wire format.
-#### `get_resource_template`
+#### `get_resource_template`
```python
get_resource_template(self, uri: str, version: VersionSpec | None = None) -> ResourceTemplate | None
@@ -298,7 +298,7 @@ requested, falls back to the next-highest enabled version.
- The template if found and enabled, None otherwise.
-#### `list_prompts`
+#### `list_prompts`
```python
list_prompts(self) -> Sequence[Prompt]
@@ -311,7 +311,7 @@ and middleware execution. Returns all versions (no deduplication).
Protocol handlers deduplicate for MCP wire format.
-#### `get_prompt`
+#### `get_prompt`
```python
get_prompt(self, name: str, version: VersionSpec | None = None) -> Prompt | None
@@ -333,19 +333,19 @@ requested, falls back to the next-highest enabled version.
- The prompt if found and enabled, None otherwise.
-#### `call_tool`
+#### `call_tool`
```python
call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult
```
-#### `call_tool`
+#### `call_tool`
```python
call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.CreateTaskResult
```
-#### `call_tool`
+#### `call_tool`
```python
call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult | mcp.types.CreateTaskResult
@@ -378,19 +378,19 @@ tool registry, bypassing transforms.
- `ValidationError`: If arguments fail validation
-#### `read_resource`
+#### `read_resource`
```python
read_resource(self, uri: str) -> ResourceResult
```
-#### `read_resource`
+#### `read_resource`
```python
read_resource(self, uri: str) -> mcp.types.CreateTaskResult
```
-#### `read_resource`
+#### `read_resource`
```python
read_resource(self, uri: str) -> ResourceResult | mcp.types.CreateTaskResult
@@ -419,19 +419,19 @@ return ResourceResult.
- `ResourceError`: If resource read fails
-#### `render_prompt`
+#### `render_prompt`
```python
render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult
```
-#### `render_prompt`
+#### `render_prompt`
```python
render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.CreateTaskResult
```
-#### `render_prompt`
+#### `render_prompt`
```python
render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult | mcp.types.CreateTaskResult
@@ -461,7 +461,7 @@ return PromptResult.
- `PromptError`: If prompt rendering fails
-#### `add_tool`
+#### `add_tool`
```python
add_tool(self, tool: Tool | Callable[..., Any]) -> Tool
@@ -479,7 +479,7 @@ with the Context type annotation. See the @tool decorator for examples.
- The tool instance that was added to the server.
-#### `remove_tool`
+#### `remove_tool`
```python
remove_tool(self, name: str, version: str | None = None) -> None
@@ -498,19 +498,19 @@ Remove tool(s) from the server.
- `NotFoundError`: If no matching tool is found.
-#### `tool`
+#### `tool`
```python
tool(self, name_or_fn: F) -> F
```
-#### `tool`
+#### `tool`
```python
tool(self, name_or_fn: str | None = None) -> Callable[[F], F]
```
-#### `tool`
+#### `tool`
```python
tool(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool | partial[Callable[[AnyFunction], FunctionTool] | FunctionTool]
@@ -566,7 +566,7 @@ server.tool(my_function, name="custom_name")
```
-#### `add_resource`
+#### `add_resource`
```python
add_resource(self, resource: Resource | Callable[..., Any]) -> Resource | ResourceTemplate
@@ -581,7 +581,7 @@ Add a resource to the server.
- The resource instance that was added to the server.
-#### `add_template`
+#### `add_template`
```python
add_template(self, template: ResourceTemplate) -> ResourceTemplate
@@ -596,7 +596,7 @@ Add a resource template to the server.
- The template instance that was added to the server.
-#### `resource`
+#### `resource`
```python
resource(self, uri: str) -> Callable[[F], F]
@@ -655,7 +655,7 @@ async def get_weather(city: str) -> str:
```
-#### `add_prompt`
+#### `add_prompt`
```python
add_prompt(self, prompt: Prompt | Callable[..., Any]) -> Prompt
@@ -670,19 +670,19 @@ Add a prompt to the server.
- The prompt instance that was added to the server.
-#### `prompt`
+#### `prompt`
```python
prompt(self, name_or_fn: F) -> F
```
-#### `prompt`
+#### `prompt`
```python
prompt(self, name_or_fn: str | None = None) -> Callable[[F], F]
```
-#### `prompt`
+#### `prompt`
```python
prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt | partial[Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt]
@@ -759,7 +759,7 @@ Decorator to register a prompt.
```
-#### `mount`
+#### `mount`
```python
mount(self, server: FastMCP[LifespanResultT], namespace: str | None = None, as_proxy: bool | None = None, tool_names: dict[str, str] | None = None, prefix: str | None = None) -> None
@@ -806,7 +806,7 @@ mounted server.
- `prefix`: Deprecated. Use namespace instead.
-#### `import_server`
+#### `import_server`
```python
import_server(self, server: FastMCP[LifespanResultT], prefix: str | None = None) -> None
@@ -847,7 +847,7 @@ templates, and prompts are imported with their original names.
objects are imported with their original names.
-#### `from_openapi`
+#### `from_openapi`
```python
from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient | None = None, name: str = 'OpenAPI Server', route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, tags: set[str] | None = None, validate_output: bool = True, **settings: Any) -> Self
@@ -876,7 +876,7 @@ response structure while still returning structured JSON.
- A FastMCP server with an OpenAPIProvider attached.
-#### `from_fastapi`
+#### `from_fastapi`
```python
from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, httpx_client_kwargs: dict[str, Any] | None = None, tags: set[str] | None = None, **settings: Any) -> Self
@@ -900,7 +900,7 @@ Use this to configure timeout and other client settings.
- A FastMCP server with an OpenAPIProvider attached.
-#### `as_proxy`
+#### `as_proxy`
```python
as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy
@@ -918,7 +918,7 @@ instance or any value accepted as the `transport` argument of
`fastmcp.client.Client` constructor.
-#### `generate_name`
+#### `generate_name`
```python
generate_name(cls, name: str | None = None) -> str
diff --git a/docs/python-sdk/fastmcp-server-tasks-handlers.mdx b/docs/python-sdk/fastmcp-server-tasks-handlers.mdx
index 31f228f14..3493b752e 100644
--- a/docs/python-sdk/fastmcp-server-tasks-handlers.mdx
+++ b/docs/python-sdk/fastmcp-server-tasks-handlers.mdx
@@ -13,7 +13,7 @@ Handles queuing tool/prompt/resource executions to Docket as background tasks.
## Functions
-### `submit_to_docket`
+### `submit_to_docket`
```python
submit_to_docket(task_type: Literal['tool', 'resource', 'template', 'prompt'], key: str, component: Tool | Resource | ResourceTemplate | Prompt, arguments: dict[str, Any] | None = None, task_meta: TaskMeta | None = None) -> mcp.types.CreateTaskResult
diff --git a/docs/python-sdk/fastmcp-tools-base.mdx b/docs/python-sdk/fastmcp-tools-base.mdx
index 264b74677..4f8d362ea 100644
--- a/docs/python-sdk/fastmcp-tools-base.mdx
+++ b/docs/python-sdk/fastmcp-tools-base.mdx
@@ -7,7 +7,7 @@ sidebarTitle: base
## Functions
-### `default_serializer`
+### `default_serializer`
```python
default_serializer(data: Any) -> str
@@ -15,17 +15,17 @@ default_serializer(data: Any) -> str
## Classes
-### `ToolResult`
+### `ToolResult`
**Methods:**
-#### `to_mcp_result`
+#### `to_mcp_result`
```python
to_mcp_result(self) -> list[ContentBlock] | tuple[list[ContentBlock], dict[str, Any]] | CallToolResult
```
-### `Tool`
+### `Tool`
Internal tool registration info.
@@ -33,7 +33,7 @@ Internal tool registration info.
**Methods:**
-#### `to_mcp_tool`
+#### `to_mcp_tool`
```python
to_mcp_tool(self, **overrides: Any) -> MCPTool
@@ -42,7 +42,7 @@ to_mcp_tool(self, **overrides: Any) -> MCPTool
Convert the FastMCP tool to an MCP tool.
-#### `from_function`
+#### `from_function`
```python
from_function(cls, fn: Callable[..., Any]) -> FunctionTool
@@ -51,7 +51,7 @@ from_function(cls, fn: Callable[..., Any]) -> FunctionTool
Create a Tool from a function.
-#### `run`
+#### `run`
```python
run(self, arguments: dict[str, Any]) -> ToolResult
@@ -66,7 +66,7 @@ implemented by subclasses.
(list of ContentBlocks, dict of structured output).
-#### `convert_result`
+#### `convert_result`
```python
convert_result(self, raw_value: Any) -> ToolResult
@@ -78,7 +78,7 @@ Handles ToolResult passthrough and converts raw values using the tool's
attributes (serializer, output_schema) for proper conversion.
-#### `register_with_docket`
+#### `register_with_docket`
```python
register_with_docket(self, docket: Docket) -> None
@@ -87,7 +87,7 @@ register_with_docket(self, docket: Docket) -> None
Register this tool with docket for background execution.
-#### `add_to_docket`
+#### `add_to_docket`
```python
add_to_docket(self, docket: Docket, arguments: dict[str, Any], **kwargs: Any) -> Execution
@@ -103,13 +103,13 @@ Schedule this tool for background execution via docket.
- `**kwargs`: Additional kwargs passed to docket.add()
-#### `from_tool`
+#### `from_tool`
```python
from_tool(cls, tool: Tool | Callable[..., Any]) -> TransformedTool
```
-#### `get_span_attributes`
+#### `get_span_attributes`
```python
get_span_attributes(self) -> dict[str, Any]
diff --git a/docs/python-sdk/fastmcp-tools-function_tool.mdx b/docs/python-sdk/fastmcp-tools-function_tool.mdx
index 88d2646d6..9bb157d22 100644
--- a/docs/python-sdk/fastmcp-tools-function_tool.mdx
+++ b/docs/python-sdk/fastmcp-tools-function_tool.mdx
@@ -10,7 +10,7 @@ Standalone @tool decorator for FastMCP.
## Functions
-### `tool`
+### `tool`
```python
tool(name_or_fn: str | Callable[..., Any] | None = None) -> Any
@@ -41,18 +41,7 @@ Metadata attached to functions by the @tool decorator.
**Methods:**
-#### `to_mcp_tool`
-
-```python
-to_mcp_tool(self, **overrides: Any) -> mcp.types.Tool
-```
-
-Convert the FastMCP tool to an MCP tool.
-
-Extends the base implementation to add task execution mode if enabled.
-
-
-#### `from_function`
+#### `from_function`
```python
from_function(cls, fn: Callable[..., Any]) -> FunctionTool
@@ -68,7 +57,7 @@ individual parameters must not be passed.
Cannot be used together with metadata parameter.
-#### `run`
+#### `run`
```python
run(self, arguments: dict[str, Any]) -> ToolResult
@@ -77,7 +66,7 @@ run(self, arguments: dict[str, Any]) -> ToolResult
Run the tool with arguments.
-#### `register_with_docket`
+#### `register_with_docket`
```python
register_with_docket(self, docket: Docket) -> None
@@ -89,7 +78,7 @@ FunctionTool registers the underlying function, which has the user's
Depends parameters for docket to resolve.
-#### `add_to_docket`
+#### `add_to_docket`
```python
add_to_docket(self, docket: Docket, arguments: dict[str, Any], **kwargs: Any) -> Execution
diff --git a/docs/python-sdk/fastmcp-tools-tool_transform.mdx b/docs/python-sdk/fastmcp-tools-tool_transform.mdx
index 321818b27..24f6270aa 100644
--- a/docs/python-sdk/fastmcp-tools-tool_transform.mdx
+++ b/docs/python-sdk/fastmcp-tools-tool_transform.mdx
@@ -7,7 +7,7 @@ sidebarTitle: tool_transform
## Functions
-### `forward`
+### `forward`
```python
forward(**kwargs: Any) -> ToolResult
@@ -36,7 +36,7 @@ tool has args `a` and `b`, and an `transform_args` was provided that maps `x` to
- `TypeError`: If provided arguments don't match the transformed schema.
-### `forward_raw`
+### `forward_raw`
```python
forward_raw(**kwargs: Any) -> ToolResult
@@ -62,7 +62,7 @@ y=2)` will call the parent tool with `x=1` and `y=2`.
- `RuntimeError`: If called outside a transformed tool context.
-### `apply_transformations_to_tools`
+### `apply_transformations_to_tools`
```python
apply_transformations_to_tools(tools: dict[str, Tool], transformations: dict[str, ToolTransformConfig]) -> dict[str, Tool]
@@ -78,7 +78,7 @@ but transformations are keyed by tool name (e.g., "my_tool").
## Classes
-### `ArgTransform`
+### `ArgTransform`
Configuration for transforming a parent tool's argument.
@@ -150,7 +150,7 @@ ArgTransform(name="new_name", description="New desc", default=None, type=int)
```
-### `ArgTransformConfig`
+### `ArgTransformConfig`
A model for requesting a single argument transform.
@@ -158,7 +158,7 @@ A model for requesting a single argument transform.
**Methods:**
-#### `to_arg_transform`
+#### `to_arg_transform`
```python
to_arg_transform(self) -> ArgTransform
@@ -167,7 +167,7 @@ to_arg_transform(self) -> ArgTransform
Convert the argument transform to a FastMCP argument transform.
-### `TransformedTool`
+### `TransformedTool`
A tool that is transformed from another tool.
@@ -191,7 +191,7 @@ validation when forward() is called from custom functions.
**Methods:**
-#### `run`
+#### `run`
```python
run(self, arguments: dict[str, Any]) -> ToolResult
@@ -210,7 +210,7 @@ functions.
- ToolResult object containing content and optional structured output.
-#### `from_tool`
+#### `from_tool`
```python
from_tool(cls, tool: Tool | Callable[..., Any], name: str | None = None, version: str | NotSetT | None = NotSet, title: str | NotSetT | None = NotSet, description: str | NotSetT | None = NotSet, tags: set[str] | None = None, transform_fn: Callable[..., Any] | None = None, transform_args: dict[str, ArgTransform] | None = None, annotations: ToolAnnotations | NotSetT | None = NotSet, output_schema: dict[str, Any] | NotSetT | None = NotSet, serializer: Callable[[Any], str] | NotSetT | None = NotSet, meta: dict[str, Any] | NotSetT | None = NotSet) -> TransformedTool
@@ -293,7 +293,7 @@ async def custom_output(**kwargs) -> ToolResult:
```
-### `ToolTransformConfig`
+### `ToolTransformConfig`
Provides a way to transform a tool.
@@ -301,7 +301,7 @@ Provides a way to transform a tool.
**Methods:**
-#### `apply`
+#### `apply`
```python
apply(self, tool: Tool) -> TransformedTool
diff --git a/docs/servers/auth/oauth-proxy.mdx b/docs/servers/auth/oauth-proxy.mdx
index d554490b8..19f5c2cee 100644
--- a/docs/servers/auth/oauth-proxy.mdx
+++ b/docs/servers/auth/oauth-proxy.mdx
@@ -3,7 +3,6 @@ title: OAuth Proxy
sidebarTitle: OAuth Proxy
description: Bridge traditional OAuth providers to work seamlessly with MCP's authentication flow.
icon: share
-tag: NEW
---
import { VersionBadge } from "/snippets/version-badge.mdx";
diff --git a/examples/apps/datatable_server.py b/examples/apps/datatable_server.py
index 35776eeee..dc78b4984 100644
--- a/examples/apps/datatable_server.py
+++ b/examples/apps/datatable_server.py
@@ -1,4 +1,18 @@
-from prefab_ui.components import Column, Heading, Muted
+from collections import Counter
+
+from prefab_ui.app import PrefabApp
+from prefab_ui.components import (
+ Badge,
+ Card,
+ CardContent,
+ Column,
+ Grid,
+ Heading,
+ Row,
+ Separator,
+ Text,
+)
+from prefab_ui.components.charts import BarChart, ChartSeries, PieChart
from prefab_ui.components.data_table import DataTable, DataTableColumn
from fastmcp import FastMCP
@@ -43,12 +57,53 @@ TEAM = [
@mcp.tool(app=True)
-def team_directory(department: str | None = None) -> Column:
- """Browse the team directory — sortable, searchable, paginated."""
+def team_directory(department: str | None = None) -> PrefabApp:
+ """Browse the team directory — sortable, searchable, with department breakdown."""
rows = [p for p in TEAM if not department or p["role"] == department]
- with Column(gap=4, css_class="p-6") as view:
- Heading("Team Directory")
- Muted(f"{len(rows)} people")
+
+ dept_counts = Counter(p["role"] for p in rows)
+ chart_data = [{"department": k, "count": v} for k, v in dept_counts.items()]
+
+ level_counts = Counter(p["level"] for p in rows)
+ level_data = [{"level": k, "count": v} for k, v in level_counts.items()]
+
+ with Column(gap=6, css_class="p-6") as view:
+ with Row(gap=2, align="center"):
+ Heading("Team Directory")
+ Badge(f"{len(rows)} people", variant="secondary")
+
+ with Grid(columns=2, gap=6):
+ with Card():
+ with CardContent():
+ Text(
+ "By Department",
+ css_class="text-sm font-medium text-muted-foreground mb-2",
+ )
+ PieChart(
+ data=chart_data,
+ data_key="count",
+ name_key="department",
+ show_legend=True,
+ inner_radius=40,
+ height=200,
+ )
+
+ with Card():
+ with CardContent():
+ Text(
+ "By Level",
+ css_class="text-sm font-medium text-muted-foreground mb-2",
+ )
+ BarChart(
+ data=level_data,
+ series=[ChartSeries(data_key="count", label="People")],
+ x_axis="level",
+ height=200,
+ horizontal=True,
+ )
+
+ Separator()
+
DataTable(
columns=[
DataTableColumn(key="name", header="Name", sortable=True),
@@ -60,7 +115,8 @@ def team_directory(department: str | None = None) -> Column:
search=True,
paginated=True,
)
- return view
+
+ return PrefabApp(view=view)
if __name__ == "__main__":
diff --git a/examples/apps/showcase_server.py b/examples/apps/showcase_server.py
new file mode 100644
index 000000000..4368a7f9c
--- /dev/null
+++ b/examples/apps/showcase_server.py
@@ -0,0 +1,345 @@
+# ruff: noqa: F405
+"""Component showcase — demonstrates the breadth of Prefab UI components.
+
+Usage:
+ uv run python showcase_server.py
+"""
+
+from prefab_ui.actions import SetState, ShowToast
+from prefab_ui.app import PrefabApp
+from prefab_ui.components import * # noqa: F403, F405
+from prefab_ui.components.charts import * # noqa: F403, F405
+from prefab_ui.components.control_flow import Else, If
+
+from fastmcp import FastMCP
+
+mcp = FastMCP("Showcase")
+
+
+@mcp.tool(app=True)
+def showcase() -> PrefabApp:
+ """Prefab UI component showcase."""
+ with Grid(columns={"default": 1, "md": 2, "lg": 4}, gap=4, css_class="p-4") as view:
+ # ── Col 1 ─────────────────────────────────────────────────────
+ with Column(gap=4):
+ with Card():
+ with CardHeader():
+ CardTitle("Register Towel")
+ CardDescription("The most important item in the galaxy")
+ with CardContent():
+ with Column(gap=3):
+ owner_input = Input(placeholder="Owner name...", name="owner")
+ with Combobox(
+ placeholder="Type...", search_placeholder="Search types..."
+ ):
+ ComboboxOption("Bath", value="bath")
+ ComboboxOption("Beach", value="beach")
+ ComboboxOption("Interstellar", value="interstellar")
+ ComboboxOption("Microfiber", value="micro")
+ DatePicker(placeholder="Registration date")
+ with CardFooter():
+ with Row(gap=2):
+ with Dialog(
+ title="Towel Registered!",
+ description="Your towel has been added to the galactic registry.",
+ ):
+ Button("Register")
+ with If("{{ owner }}"):
+ Text(
+ f"Thanks, {owner_input.rx}. Don't forget to bring it."
+ )
+ with Else():
+ Text("Anonymous, I see? Don't forget to bring it.")
+ Button("Cancel", variant="outline")
+ with Card():
+ with CardContent():
+ with Row(gap=2, align="center"):
+ Loader(variant="dots", size="sm")
+ Muted("Marvin is thinking...")
+
+ with Card():
+ with CardHeader():
+ CardTitle("Ship Status")
+ with CardContent():
+ with Column(gap=3):
+ with Row(align="center", css_class="justify-between"):
+ Text("heart-of-gold")
+ with HoverCard(open_delay=0, close_delay=200):
+ Badge("In Orbit", variant="default")
+ with Column(gap=2):
+ Text("heart-of-gold")
+ Muted("Deployed 2h ago")
+ Progress(value=100, max=100, variant="success")
+ Progress(value=100, max=100, indicator_class="bg-yellow-400")
+ with Row(align="center", css_class="justify-between"):
+ Text("vogon-poetry")
+ with Tooltip("64% — ETA 12 min", delay=0):
+ with Badge(variant="secondary"):
+ Loader(size="sm")
+ Text("Deploying")
+ Progress(value=64, max=100)
+ with Row(align="center", css_class="justify-between"):
+ Text("deep-thought")
+ with Tooltip(
+ "Computing... 7.5 million years remaining", delay=0
+ ):
+ with Badge(variant="outline"):
+ Loader(size="sm", variant="ios")
+ Text("Soon...")
+ Progress(value=12, max=100)
+ with Card():
+ with CardHeader():
+ CardTitle("Planet Ratings")
+ with CardContent():
+ RadarChart(
+ data=[
+ {"axis": "Views", "earth": 30, "mag": 95},
+ {"axis": "Fjords", "earth": 65, "mag": 100},
+ {"axis": "Pubs", "earth": 90, "mag": 10},
+ {"axis": "Mice", "earth": 40, "mag": 85},
+ {"axis": "Tea", "earth": 95, "mag": 15},
+ {"axis": "Safety", "earth": 45, "mag": 70},
+ ],
+ series=[
+ ChartSeries(dataKey="earth", label="Earth"),
+ ChartSeries(dataKey="mag", label="Magrathea"),
+ ],
+ axis_key="axis",
+ height=200,
+ show_legend=True,
+ show_tooltip=True,
+ )
+
+ # ── Col 2 ─────────────────────────────────────────────────────
+ with Column(gap=4):
+ with Card():
+ with CardHeader():
+ CardTitle("Survival Odds")
+ with CardContent(css_class="w-fit mx-auto"):
+ Ring(
+ value=42,
+ label="42%",
+ variant="info",
+ size="lg",
+ thickness=12,
+ indicator_class="group-hover:drop-shadow-[0_0_24px_rgba(59,130,246,0.9)]",
+ )
+ with Card():
+ with CardHeader():
+ with Row(gap=2, align="center"):
+ CardTitle("Improbability Drive")
+ Loader(variant="pulse", size="sm", css_class="text-blue-500")
+ with CardContent():
+ with Column(gap=2):
+ Slider(min=0, max=100, value=42, name="improbability")
+ with Row(align="center", css_class="justify-between"):
+ Muted("Probable")
+ Muted("Infinite")
+ with Alert(variant="success", icon="circle-check"):
+ AlertTitle("Don't Panic")
+ AlertDescription("Normality achieved.")
+ with Card():
+ with CardHeader():
+ CardTitle("Prefect Horizon Config")
+ with CardContent():
+ with Column(gap=3):
+ Switch(label="Auto-scale agents", value=True, name="autoscale")
+ Separator()
+ Switch(label="Code Mode", value=True, name="code_mode")
+ Separator()
+ Switch(label="Tool call caching", value=False, name="cache")
+ with CardFooter():
+ Button("Save Preferences", on_click=ShowToast("Preferences saved!"))
+ with Card():
+ with CardHeader():
+ CardTitle("Travel Class")
+ with CardContent():
+ with RadioGroup(name="travel_class"):
+ Radio(option="economy", label="Economy")
+ Radio(option="business", label="Business Class")
+ Radio(
+ option="improbability",
+ label="Infinite Improbability",
+ value=True,
+ )
+
+ # ── Cols 3–4 ──────────────────────────────────────────────────
+ with GridItem(css_class="md:col-span-2"):
+ with Column(gap=4):
+ with Grid(columns=2, gap=4, css_class="h-32"):
+ with Card():
+ with CardHeader():
+ CardTitle("Context Window")
+ with CardContent():
+ with Column(gap=6, justify="center", css_class="h-full"):
+ with Row(align="center", css_class="justify-between"):
+ Text("45% used")
+ Muted("90k / 200k tokens")
+ with Tooltip("Auto-compact buffer: 12%", delay=0):
+ Progress(value=45, max=100)
+ with Card(css_class="pb-0 gap-0"):
+ with CardContent():
+ Metric(
+ label="Fjords designed",
+ value="1,847",
+ delta="+3 coastlines",
+ )
+ Sparkline(
+ data=[
+ 820,
+ 950,
+ 1100,
+ 980,
+ 1250,
+ 1400,
+ 1350,
+ 1500,
+ 1680,
+ 1847,
+ ],
+ variant="success",
+ fill=True,
+ css_class="h-16",
+ )
+ with Card():
+ with CardHeader():
+ CardTitle("Towel Incidents")
+ with CardContent():
+ BarChart(
+ data=[
+ {"month": "Jan", "lost": 8, "found": 5},
+ {"month": "Feb", "lost": 24, "found": 15},
+ {"month": "Mar", "lost": 12, "found": 28},
+ {"month": "Apr", "lost": 35, "found": 19},
+ {"month": "May", "lost": 18, "found": 38},
+ {"month": "Jun", "lost": 42, "found": 30},
+ ],
+ series=[
+ ChartSeries(dataKey="lost", label="Lost"),
+ ChartSeries(dataKey="found", label="Found"),
+ ],
+ x_axis="month",
+ height=200,
+ bar_radius=4,
+ show_legend=True,
+ show_tooltip=True,
+ show_grid=True,
+ )
+
+ with Grid(columns=2, gap=4):
+ with Column(gap=4):
+ with Card():
+ with CardContent():
+ with Column(gap=2):
+ Checkbox(label="Towel packed", value=True)
+ Checkbox(label="Guide charged", value=True)
+ Checkbox(label="Babel fish inserted", value=False)
+ with If("{{ !pressed }}"):
+ Button(
+ "This is probably the best button to press.",
+ variant="success",
+ on_click=SetState("pressed", True),
+ )
+ with Else():
+ Button(
+ "Please do not press this button again.",
+ variant="destructive",
+ on_click=SetState("pressed", False),
+ )
+ with Card():
+ with CardHeader():
+ CardTitle("Marvin's Mood")
+ with CardContent():
+ with Column(gap=3):
+ P("How's life?")
+ with Column(gap=2):
+ Button(
+ "Meh",
+ on_click=ShowToast(
+ "Noted. Enthusiasm levels nominal."
+ ),
+ )
+ Button(
+ "Depressed",
+ variant="info",
+ on_click=ShowToast(
+ "I think you ought to know I'm feeling very depressed."
+ ),
+ )
+ Button(
+ "Don't talk to me about life",
+ variant="warning",
+ on_click=ShowToast(
+ "Brain the size of a planet and they ask me to pick up a piece of paper."
+ ),
+ )
+
+ with Column(gap=4):
+ with Alert(variant="destructive", icon="triangle-alert"):
+ AlertTitle("Beware of the Leopard")
+ with Card():
+ with CardContent():
+ DataTable(
+ columns=[
+ DataTableColumn(
+ key="crew", header="Crew", sortable=True
+ ),
+ DataTableColumn(
+ key="species",
+ header="Species",
+ sortable=True,
+ ),
+ DataTableColumn(
+ key="towel", header="Towel?", sortable=True
+ ),
+ DataTableColumn(
+ key="status", header="Status", sortable=True
+ ),
+ ],
+ rows=[
+ {
+ "crew": "Arthur Dent",
+ "species": "Human",
+ "towel": "Yes",
+ "status": "Confused",
+ },
+ {
+ "crew": "Ford Prefect",
+ "species": "Betelgeusian",
+ "towel": "Always",
+ "status": "Drinking",
+ },
+ {
+ "crew": "Zaphod",
+ "species": "Betelgeusian",
+ "towel": "Lost it",
+ "status": "Presidential",
+ },
+ {
+ "crew": "Trillian",
+ "species": "Human",
+ "towel": "Yes",
+ "status": "Navigating",
+ },
+ {
+ "crew": "Marvin",
+ "species": "Android",
+ "towel": "No point",
+ "status": "Depressed",
+ },
+ {
+ "crew": "Slartibartfast",
+ "species": "Magrathean",
+ "towel": "Somewhere",
+ "status": "Designing",
+ },
+ ],
+ search=True,
+ paginated=False,
+ )
+
+ return PrefabApp(view=view, state={"pressed": False, "improbability": 42})
+
+
+if __name__ == "__main__":
+ mcp.run()
diff --git a/src/fastmcp/apps/app.py b/src/fastmcp/apps/app.py
index b53e7028f..eaba588df 100644
--- a/src/fastmcp/apps/app.py
+++ b/src/fastmcp/apps/app.py
@@ -18,8 +18,8 @@ Usage::
return Column(...)
@app.tool()
- def save_contact(name: str, email: str) -> dict:
- return {"name": name, "email": email}
+ def save_contact(name: str, email: str) -> str:
+ return name
server = FastMCP("Platform")
server.add_provider(app)
diff --git a/src/fastmcp/cli/apps_dev.py b/src/fastmcp/cli/apps_dev.py
index d1e2be811..b074e60b8 100644
--- a/src/fastmcp/cli/apps_dev.py
+++ b/src/fastmcp/cli/apps_dev.py
@@ -372,6 +372,8 @@ _HOST_HTML_TEMPLATE = """\
await bridge.sendToolResult(result);
status.style.display = "none";
iframe.style.display = "block";
+ // Prevent horizontal scrollbar when vertical scrollbar appears
+ try {{ iframe.contentDocument.documentElement.style.overflowX = "hidden"; }} catch(e) {{}}
}};
// Start listening before the iframe loads
@@ -415,8 +417,8 @@ _LOG_PANEL_HTML = """\
}
#mcp-log-panel.hidden { display: none; }
#app-frame {
- width: calc(100% - 360px) !important; height: 100% !important;
- margin-left: 360px !important;
+ width: 100% !important; height: 100% !important;
+ margin-left: 0 !important;
}
#mcp-log-resize {
position: absolute; right: -3px; top: 0; bottom: 0; width: 6px;
@@ -507,7 +509,7 @@ _LOG_PANEL_HTML = """\
background: #181825; color: #cdd6f4; border: 1px solid #45475a;
padding: 6px 12px; border-radius: 6px; cursor: pointer;
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
- font-size: 11px; display: none;
+ font-size: 11px; display: block;
}
#mcp-log-open:hover { background: #313244; }
#mcp-log-filters {
@@ -553,7 +555,7 @@ _LOG_PANEL_HTML = """\
.log-level-alert { background: rgba(243, 139, 168, 0.25); color: #f38ba8; }
.log-level-emergency { background: rgba(243, 139, 168, 0.3); color: #f38ba8; }
-