60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
488 lines
13 KiB
Python
488 lines
13 KiB
Python
"""Patterns showcase — every Prefab pattern from the docs in one server.
|
|
|
|
A runnable collection of the patterns from https://gofastmcp.com/apps/patterns.
|
|
Each tool demonstrates a different Prefab UI pattern: charts, tables, forms,
|
|
status displays, conditional content, tabs, and accordions.
|
|
|
|
Usage:
|
|
uv run python patterns_server.py # HTTP (port 8000)
|
|
uv run python patterns_server.py --stdio # stdio for MCP clients
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from prefab_ui.actions import ShowToast
|
|
from prefab_ui.actions.mcp import CallTool
|
|
from prefab_ui.app import PrefabApp
|
|
from prefab_ui.components import (
|
|
Accordion,
|
|
AccordionItem,
|
|
Alert,
|
|
Badge,
|
|
Button,
|
|
Card,
|
|
CardContent,
|
|
Column,
|
|
DataTable,
|
|
DataTableColumn,
|
|
ForEach,
|
|
Form,
|
|
Grid,
|
|
Heading,
|
|
If,
|
|
Input,
|
|
Muted,
|
|
Progress,
|
|
Row,
|
|
Select,
|
|
Separator,
|
|
Switch,
|
|
Tab,
|
|
Tabs,
|
|
Text,
|
|
Textarea,
|
|
)
|
|
from prefab_ui.components.charts import AreaChart, BarChart, ChartSeries, PieChart
|
|
from prefab_ui.rx import ERROR, Rx
|
|
|
|
from fastmcp import FastMCP
|
|
|
|
mcp = FastMCP("Patterns Showcase")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Data
|
|
# ---------------------------------------------------------------------------
|
|
|
|
QUARTERLY_DATA = [
|
|
{"quarter": "Q1", "revenue": 42000, "costs": 28000},
|
|
{"quarter": "Q2", "revenue": 51000, "costs": 31000},
|
|
{"quarter": "Q3", "revenue": 47000, "costs": 29000},
|
|
{"quarter": "Q4", "revenue": 63000, "costs": 35000},
|
|
]
|
|
|
|
DAILY_USAGE = [
|
|
{"date": f"Feb {d}", "requests": v}
|
|
for d, v in zip(
|
|
range(1, 11),
|
|
[1200, 1350, 980, 1500, 1420, 1680, 1550, 1700, 1450, 1600],
|
|
)
|
|
]
|
|
|
|
TICKETS = [
|
|
{"category": "Bug", "count": 23},
|
|
{"category": "Feature", "count": 15},
|
|
{"category": "Docs", "count": 8},
|
|
{"category": "Infra", "count": 12},
|
|
]
|
|
|
|
EMPLOYEES = [
|
|
{
|
|
"name": "Alice Chen",
|
|
"department": "Engineering",
|
|
"role": "Staff Engineer",
|
|
"location": "San Francisco",
|
|
},
|
|
{
|
|
"name": "Bob Martinez",
|
|
"department": "Design",
|
|
"role": "Lead Designer",
|
|
"location": "New York",
|
|
},
|
|
{
|
|
"name": "Carol Johnson",
|
|
"department": "Engineering",
|
|
"role": "Senior Engineer",
|
|
"location": "London",
|
|
},
|
|
{
|
|
"name": "David Kim",
|
|
"department": "Product",
|
|
"role": "Product Manager",
|
|
"location": "San Francisco",
|
|
},
|
|
{
|
|
"name": "Eva Müller",
|
|
"department": "Engineering",
|
|
"role": "Engineer",
|
|
"location": "Berlin",
|
|
},
|
|
{
|
|
"name": "Frank Okafor",
|
|
"department": "Data Science",
|
|
"role": "Senior Analyst",
|
|
"location": "Lagos",
|
|
},
|
|
{
|
|
"name": "Grace Liu",
|
|
"department": "Engineering",
|
|
"role": "Junior Engineer",
|
|
"location": "Singapore",
|
|
},
|
|
{
|
|
"name": "Hassan Ali",
|
|
"department": "Design",
|
|
"role": "Senior Designer",
|
|
"location": "Dubai",
|
|
},
|
|
]
|
|
|
|
SERVICES = [
|
|
{
|
|
"name": "API Gateway",
|
|
"status": "healthy",
|
|
"ok": True,
|
|
"latency_ms": 12,
|
|
"uptime_pct": 99.9,
|
|
},
|
|
{
|
|
"name": "Database",
|
|
"status": "healthy",
|
|
"ok": True,
|
|
"latency_ms": 3,
|
|
"uptime_pct": 99.99,
|
|
},
|
|
{
|
|
"name": "Cache",
|
|
"status": "degraded",
|
|
"ok": False,
|
|
"latency_ms": 45,
|
|
"uptime_pct": 98.2,
|
|
},
|
|
{
|
|
"name": "Queue",
|
|
"status": "healthy",
|
|
"ok": True,
|
|
"latency_ms": 8,
|
|
"uptime_pct": 99.8,
|
|
},
|
|
]
|
|
|
|
ENDPOINTS = [
|
|
{
|
|
"path": "/api/users",
|
|
"status": 200,
|
|
"healthy": True,
|
|
"avg_ms": 45,
|
|
"p99_ms": 120,
|
|
"uptime_pct": 99.9,
|
|
},
|
|
{
|
|
"path": "/api/orders",
|
|
"status": 200,
|
|
"healthy": True,
|
|
"avg_ms": 82,
|
|
"p99_ms": 250,
|
|
"uptime_pct": 99.7,
|
|
},
|
|
{
|
|
"path": "/api/search",
|
|
"status": 200,
|
|
"healthy": True,
|
|
"avg_ms": 150,
|
|
"p99_ms": 500,
|
|
"uptime_pct": 99.5,
|
|
},
|
|
{
|
|
"path": "/api/webhooks",
|
|
"status": 503,
|
|
"healthy": False,
|
|
"avg_ms": 2000,
|
|
"p99_ms": 5000,
|
|
"uptime_pct": 95.1,
|
|
},
|
|
]
|
|
|
|
PROJECT = {
|
|
"name": "FastMCP v3",
|
|
"description": "Next generation MCP framework with Apps support.",
|
|
"status": "Active",
|
|
"created_at": "2025-01-15",
|
|
"members": [
|
|
{"name": "Alice Chen", "role": "Lead"},
|
|
{"name": "Bob Martinez", "role": "Design"},
|
|
{"name": "Carol Johnson", "role": "Backend"},
|
|
],
|
|
"activity": [
|
|
{
|
|
"timestamp": "2 hours ago",
|
|
"message": "Merged PR #342: Add Prefab UI integration",
|
|
},
|
|
{
|
|
"timestamp": "5 hours ago",
|
|
"message": "Opened issue #345: CORS convenience API",
|
|
},
|
|
{"timestamp": "1 day ago", "message": "Released v3.0.1"},
|
|
],
|
|
}
|
|
|
|
# In-memory contact store for the form demo
|
|
_contacts: list[dict] = [
|
|
{"name": "Zaphod Beeblebrox", "email": "zaphod@galaxy.gov", "category": "Partner"},
|
|
]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Charts
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def quarterly_revenue(year: int = 2025) -> PrefabApp:
|
|
"""Show quarterly revenue as a bar chart."""
|
|
with Column(gap=4, css_class="p-6") as view:
|
|
Heading(f"{year} Revenue vs Costs")
|
|
BarChart(
|
|
data=QUARTERLY_DATA,
|
|
series=[
|
|
ChartSeries(data_key="revenue", label="Revenue"),
|
|
ChartSeries(data_key="costs", label="Costs"),
|
|
],
|
|
x_axis="quarter",
|
|
show_legend=True,
|
|
)
|
|
|
|
return PrefabApp(view=view)
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def usage_trend() -> PrefabApp:
|
|
"""Show API usage over time as an area chart."""
|
|
with Column(gap=4, css_class="p-6") as view:
|
|
Heading("API Usage (10 Days)")
|
|
AreaChart(
|
|
data=DAILY_USAGE,
|
|
series=[ChartSeries(data_key="requests", label="Requests")],
|
|
x_axis="date",
|
|
curve="smooth",
|
|
height=250,
|
|
)
|
|
|
|
return PrefabApp(view=view)
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def ticket_breakdown() -> PrefabApp:
|
|
"""Show open tickets by category as a donut chart."""
|
|
with Column(gap=4, css_class="p-6") as view:
|
|
Heading("Open Tickets")
|
|
PieChart(
|
|
data=TICKETS,
|
|
data_key="count",
|
|
name_key="category",
|
|
show_legend=True,
|
|
inner_radius=60,
|
|
)
|
|
|
|
return PrefabApp(view=view)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Data Tables
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def employee_directory() -> PrefabApp:
|
|
"""Show a searchable, sortable employee directory."""
|
|
with Column(gap=4, css_class="p-6") as view:
|
|
Heading("Employee Directory")
|
|
DataTable(
|
|
columns=[
|
|
DataTableColumn(key="name", header="Name", sortable=True),
|
|
DataTableColumn(key="department", header="Department", sortable=True),
|
|
DataTableColumn(key="role", header="Role"),
|
|
DataTableColumn(key="location", header="Office", sortable=True),
|
|
],
|
|
rows=EMPLOYEES,
|
|
search=True,
|
|
paginated=True,
|
|
page_size=15,
|
|
)
|
|
|
|
return PrefabApp(view=view)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Forms
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def contact_form() -> PrefabApp:
|
|
"""Show a form to create a new contact, with a live contact list below."""
|
|
with Column(gap=6, css_class="p-6") as view:
|
|
Heading("Contacts")
|
|
|
|
with ForEach("contacts") as item:
|
|
with Row(gap=2, align="center"):
|
|
Text(item.name, css_class="font-medium")
|
|
Muted(item.email)
|
|
Badge(item.category)
|
|
|
|
Separator()
|
|
|
|
Heading("Add Contact", level=3)
|
|
with Form(
|
|
on_submit=CallTool(
|
|
"save_contact",
|
|
result_key="contacts",
|
|
on_success=ShowToast("Contact saved!", variant="success"),
|
|
on_error=ShowToast(ERROR, variant="error"),
|
|
)
|
|
):
|
|
Input(name="name", label="Full Name", required=True)
|
|
Input(name="email", label="Email", input_type="email", required=True)
|
|
Select(
|
|
name="category",
|
|
label="Category",
|
|
options=["Customer", "Vendor", "Partner", "Other"],
|
|
)
|
|
Textarea(name="notes", label="Notes", placeholder="Optional notes...")
|
|
Button("Save Contact")
|
|
|
|
return PrefabApp(view=view, state={"contacts": list(_contacts)})
|
|
|
|
|
|
@mcp.tool
|
|
def save_contact(
|
|
name: str,
|
|
email: str,
|
|
category: str = "Other",
|
|
notes: str = "",
|
|
) -> list[dict]:
|
|
"""Save a new contact and return the updated list."""
|
|
contact = {"name": name, "email": email, "category": category, "notes": notes}
|
|
_contacts.append(contact)
|
|
return list(_contacts)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Status Displays
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def system_status() -> PrefabApp:
|
|
"""Show current system health."""
|
|
all_ok = all(s["ok"] for s in SERVICES)
|
|
|
|
with Column(gap=4, css_class="p-6") as view:
|
|
with Row(gap=2, align="center"):
|
|
Heading("System Status")
|
|
Badge(
|
|
"All Healthy" if all_ok else "Degraded",
|
|
variant="success" if all_ok else "destructive",
|
|
)
|
|
|
|
Separator()
|
|
|
|
with Grid(columns=2, gap=4):
|
|
for svc in SERVICES:
|
|
with Card():
|
|
with CardContent():
|
|
with Row(gap=2, align="center"):
|
|
Text(svc["name"], css_class="font-medium")
|
|
Badge(
|
|
svc["status"],
|
|
variant="success" if svc["ok"] else "destructive",
|
|
)
|
|
Muted(f"Response: {svc['latency_ms']}ms")
|
|
Progress(value=svc["uptime_pct"])
|
|
|
|
return PrefabApp(view=view)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Conditional Content
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def feature_flags() -> PrefabApp:
|
|
"""Toggle feature flags with live preview."""
|
|
with Column(gap=4, css_class="p-6") as view:
|
|
Heading("Feature Flags")
|
|
|
|
Switch(name="dark_mode", label="Dark Mode")
|
|
Switch(name="beta_features", label="Beta Features")
|
|
|
|
Separator()
|
|
|
|
with If(Rx("dark_mode")):
|
|
Alert(title="Dark mode enabled", description="UI will use dark theme.")
|
|
with If(Rx("beta_features")):
|
|
Alert(
|
|
title="Beta features active",
|
|
description="Experimental features are now visible.",
|
|
variant="warning",
|
|
)
|
|
|
|
return PrefabApp(view=view, state={"dark_mode": False, "beta_features": False})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tabs
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def project_overview() -> PrefabApp:
|
|
"""Show project details organized in tabs."""
|
|
with Column(gap=4, css_class="p-6") as view:
|
|
Heading(PROJECT["name"])
|
|
|
|
with Tabs():
|
|
with Tab("Overview"):
|
|
Text(PROJECT["description"])
|
|
with Row(gap=4):
|
|
Badge(PROJECT["status"])
|
|
Muted(f"Created: {PROJECT['created_at']}")
|
|
|
|
with Tab("Members"):
|
|
DataTable(
|
|
columns=[
|
|
DataTableColumn(key="name", header="Name", sortable=True),
|
|
DataTableColumn(key="role", header="Role"),
|
|
],
|
|
rows=PROJECT["members"],
|
|
)
|
|
|
|
with Tab("Activity"):
|
|
with ForEach("activity") as item:
|
|
with Row(gap=2):
|
|
Muted(item.timestamp)
|
|
Text(item.message)
|
|
|
|
return PrefabApp(view=view, state={"activity": PROJECT["activity"]})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Accordion
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@mcp.tool(app=True)
|
|
def api_health() -> PrefabApp:
|
|
"""Show health details for each API endpoint."""
|
|
with Column(gap=4, css_class="p-6") as view:
|
|
Heading("API Health")
|
|
|
|
with Accordion(multiple=True):
|
|
for ep in ENDPOINTS:
|
|
with AccordionItem(ep["path"]):
|
|
with Row(gap=4):
|
|
Badge(
|
|
f"{ep['status']}",
|
|
variant="success" if ep["healthy"] else "destructive",
|
|
)
|
|
Text(f"Avg: {ep['avg_ms']}ms")
|
|
Text(f"P99: {ep['p99_ms']}ms")
|
|
Progress(value=ep["uptime_pct"])
|
|
|
|
return PrefabApp(view=view)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
mcp.run()
|