mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-17 02:59:11 +02:00
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com> Co-authored-by: Marvin Context Protocol <41898282+Marvin Context Protocol@users.noreply.github.com> Co-authored-by: voidborne-d <voidborne-d@users.noreply.github.com> Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: d 🔹 <258577966+voidborne-d@users.noreply.github.com> Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Co-authored-by: nightcityblade <nightcityblade@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Bill Easton <strawgate@users.noreply.github.com> Co-authored-by: Sumanshu Nankana <sumanshunankana@gmail.com> Co-authored-by: Eric Robinson <ericrobinson@indeed.com> Co-authored-by: Martim Santos <martimfasantos@gmail.com> Co-authored-by: d 🔹 <liusway405@gmail.com> Co-authored-by: Matthieu B <66959271+mtthidoteu@users.noreply.github.com> Co-authored-by: Sascha Buehrle <47737812+saschabuehrle@users.noreply.github.com> Co-authored-by: Hakancan <142545736+hkc5@users.noreply.github.com> Co-authored-by: nightcityblade <jackchen@haloailabs.com> Co-authored-by: Matt Hallowell <17804673+mhallo@users.noreply.github.com> Co-authored-by: nate nowack <thrast36@gmail.com> Co-authored-by: Bill Easton <williamseaston@gmail.com> Co-authored-by: Marcus Shu <46469249+shulkx@users.noreply.github.com> Co-authored-by: Rushabh Doshi <radoshi@gmail.com> Co-authored-by: AIKAWA Shigechika <shige@aikawa.jp> Co-authored-by: Jeremy Simon <simonjer805@gmail.com> Co-authored-by: Miguel Miranda Dias <7780875+pandego@users.noreply.github.com> Co-authored-by: Anthony James Padavano <padavano.anthony@gmail.com> Co-authored-by: Mostafa Kamal <hiremostafa@gmail.com> Fix auto-close MRE script posting comment without closing (#3386) Fix WorkOS token scope verification bypass 🤖 Generated with Codex (#3407) Fix initialize McpError fallthrough 🤖 Generated with Codex (#3413) Fix transform arg collisions with passthrough params (#3431) Fix get_* returning None when latest version is disabled (#3439) Fix get_* returning None when latest version is disabled (#3421) Fix server lifespan overlap teardown (#3415) Fix $ref output schema object detection regression (#3420) resolved annotations (#3429) Fix async partial callables rejected by iscoroutinefunction (#3438) Fix async partial callables rejected by iscoroutinefunction (#3423) fix: add version to components (#3458) fix: use intent-based flag for OIDC scope patch in load_access_token (#3465) Fixes #3461 fix: normalize Google scope shorthands and surface valid_scopes (#3477) fix: resolve ty 0.0.23 type-checking errors and bump pin (#3481) fix: shield lifespan teardown from cancellation (#3480) fix: forward custom_route endpoints from mounted servers (#3462) fix updates _get_additional_http_routes() to traverse providers, Fixes #3457 fix: remove hardcoded version from CLI help text (#3456) fix: monty 0.0.8 compatibility, drop external_functions from constructor (#3468) fix: task test teardown hanging 5s per test (#3499) Closes #3498 fix: validate workspace path is a directory before cursor install (#3440) Fixes #3426 fix: handle re.error from malformed URI templates in build_regex (#3501) fix: reject empty/OIDC-only required_scopes in AzureProvider (#3503) fix: restrict $ref resolution to local refs only (SSRF/LFI) (#3502) fix warnings and timeouts (#3504) close upgrade check issue when build passes (#3505) Closes #3484 fix: URL-encode path params to prevent SSRF/path traversal (GHSA-vv7q-7jx5-f767) (#3507) fix: prevent path traversal in skill download (#3493) fix: prefer IdP-granted scopes over client-requested scopes in OAuthProxy (#3492) fix: remove unrelated transform and http.py changes from PR scope fix: remove forced follow_redirects from httpx_client_factory calls (#3496) fix: stop passing follow_redirects to httpx_client_factory fix: restore follow_redirects=True for custom httpx client factories Closes #3509 fix: CSRF double-submit cookie check in consent flow (#3519) fix: validate server names in install commands (#3522) fix: use raw strings for regex in pytest.raises match (#3523) fix: reject refresh tokens used as Bearer access tokens (#3524) fix: route ResourcesAsTools/PromptsAsTools through server middleware (#3495) fix: resolve Pyright "Module is not callable" on @tool, @resource, @prompt decorators (#3540) fix: filter warnings by message in KEY_PREFIX test (#3549) fix: suppress output schema for ToolResult subclass annotations (#3548) fix: increase sleep duration in proxy cache tests (#3567) fix: store absolute token expiry to prevent stale expires_in on reload (#3572) fix: preserve tool properties named 'title' during schema compression (#3582) Fix loopback redirect URI port matching per RFC 8252 §7.3 (#3589) Fix app tool routing: visibility check and middleware propagation (#3591) Fix query parameter serialization to respect OpenAPI explode/style settings (#3595) Fix dev apps form: union types, textarea support, JSON parsing (#3597) fix(google): replace deprecated /oauth2/v1/tokeninfo with /oauth2/v3/userinfo (#3603) fix: resolve EntraOBOToken dependency injection through MultiAuth (#3609) fix(docs): correct misleading stateless_http header (#3622) fix: filesystem provider import machinery (#3626) Closes #3625 (issues 2, 3, 6) fix: recover StdioTransport after subprocess exits (#3630) fix(server): preserve mounted tool task metadata (#3632) fix: scope deprecation warning filter to FastMCPDeprecationWarning (#3649) fix imports, add PrefabAppConfig (#3650) fix: resolve CurrentFastMCP/ctx.fastmcp to child server in mounted background tasks (#3651) Fix blocking docs issues: chart imports, Select API, Rx consistency (#3652) closed by default (#3657) Fix prompt caching middleware missing wrap/unwrap round-trip (#3666) fix: serialize object query params per OpenAPI style/explode rules (#3662) Fixes #2857 fix: HTTP request headers not accessible in background task workers (#3631) fix: restore HTTP headers in worker execution path for background tasks (#3681) fix: strip discriminator after dereferencing schemas (#3682) fix: remove stale ty:ignore directives for ty 0.0.26 (#3684) Fix docs gaps in app provider pages (#3690) fix: dev apps log panel UX improvements (#3698) fix dev server empty string args (#3700)
345 lines
17 KiB
Python
345 lines
17 KiB
Python
# 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()
|