# Custom HTML Apps Source: https://gofastmcp.com/apps/low-level Build apps with your own HTML, CSS, and JavaScript using the MCP Apps extension directly. The [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) is an open protocol that lets tools return interactive UIs — an HTML page rendered in a sandboxed iframe inside the host client. [Prefab UI](/apps/prefab) builds on this protocol so you never have to think about it, but when you need full control — custom rendering, a specific JavaScript framework, maps, 3D, video — you can use the MCP Apps extension directly. This page covers how to write custom HTML apps and wire them up in FastMCP. You'll be working with the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) JavaScript SDK for host communication, and FastMCP's `AppConfig` for resource and CSP management. ## How It Works An MCP App has two parts: 1. A **tool** that does the work and returns data 2. A **`ui://` resource** containing the HTML that renders that data The tool declares which resource to use via `AppConfig`. When the host calls the tool, it also fetches the linked resource, renders it in a sandboxed iframe, and pushes the tool result into the app via `postMessage`. The app can also call tools back, enabling interactive workflows. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} import json from fastmcp import FastMCP from fastmcp.server.apps import AppConfig, ResourceCSP mcp = FastMCP("My App Server") # The tool does the work @mcp.tool(app=AppConfig(resource_uri="ui://my-app/view.html")) def generate_chart(data: list[float]) -> str: return json.dumps({"values": data}) # The resource provides the UI @mcp.resource("ui://my-app/view.html") def chart_view() -> str: return "..." ``` ## AppConfig `AppConfig` controls how a tool or resource participates in the Apps extension. Import it from `fastmcp.server.apps`: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.server.apps import AppConfig ``` On **tools**, you'll typically set `resource_uri` to point to the UI resource: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} @mcp.tool(app=AppConfig(resource_uri="ui://my-app/view.html")) def my_tool() -> str: return "result" ``` You can also pass a raw dict with camelCase keys, matching the wire format: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} @mcp.tool(app={"resourceUri": "ui://my-app/view.html"}) def my_tool() -> str: return "result" ``` ### Tool Visibility The `visibility` field controls where a tool appears: * `["model"]` — visible to the LLM (the default behavior) * `["app"]` — only callable from within the app UI, hidden from the LLM * `["model", "app"]` — both This is useful when you have tools that only make sense as part of the app's interactive flow, not as standalone LLM actions. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} @mcp.tool( app=AppConfig( resource_uri="ui://my-app/view.html", visibility=["app"], ) ) def refresh_data() -> str: """Only callable from the app UI, not by the LLM.""" return fetch_latest() ``` ### AppConfig Fields | Field | Type | Description | | ---------------- | --------------------- | ---------------------------------------------------------------- | | `resource_uri` | `str` | URI of the UI resource. Tools only. | | `visibility` | `list[str]` | Where the tool appears: `"model"`, `"app"`, or both. Tools only. | | `csp` | `ResourceCSP` | Content Security Policy for the iframe. | | `permissions` | `ResourcePermissions` | Iframe sandbox permissions. | | `domain` | `str` | Stable sandbox origin for the iframe. | | `prefers_border` | `bool` | Whether the UI prefers a visible border. | On **resources**, `resource_uri` and `visibility` must not be set — the resource *is* the UI. Use `AppConfig` on resources only for `csp`, `permissions`, and other display settings. ## UI Resources Resources using the `ui://` scheme are automatically served with the MIME type `text/html;profile=mcp-app`. You don't need to set this manually. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} @mcp.resource("ui://my-app/view.html") def my_view() -> str: return "..." ``` The HTML can be anything — a full single-page app, a simple display, or a complex interactive tool. The host renders it in a sandboxed iframe and establishes a `postMessage` channel for communication. ### Writing the App HTML Your HTML app communicates with the host using the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) JavaScript SDK. The simplest approach is to load it from a CDN: ```html theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} ``` The `App` object provides: * **`app.ontoolresult`** — callback that receives tool results pushed by the host * **`app.callServerTool({name, arguments})`** — call a tool on the server from within the app * **`app.onhostcontextchanged`** — callback for host context changes (e.g., safe area insets) * **`app.getHostContext()`** — get current host context If your HTML loads external scripts, styles, or makes API calls, you need to declare those domains in the CSP configuration. See [Security](#security) below. ## Security Apps run in sandboxed iframes with a deny-by-default Content Security Policy. By default, only inline scripts and styles are allowed — no external network access. ### Content Security Policy If your app needs to load external resources (CDN scripts, API calls, embedded iframes), declare the allowed domains with `ResourceCSP`: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.server.apps import AppConfig, ResourceCSP @mcp.resource( "ui://my-app/view.html", app=AppConfig( csp=ResourceCSP( resource_domains=["https://unpkg.com", "https://cdn.example.com"], connect_domains=["https://api.example.com"], ) ), ) def my_view() -> str: return "..." ``` | CSP Field | Controls | | ------------------ | --------------------------------------------------- | | `connect_domains` | `fetch`, XHR, WebSocket (`connect-src`) | | `resource_domains` | Scripts, images, styles, fonts (`script-src`, etc.) | | `frame_domains` | Nested iframes (`frame-src`) | | `base_uri_domains` | Document base URI (`base-uri`) | ### Permissions If your app needs browser capabilities like camera or clipboard access, request them via `ResourcePermissions`: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.server.apps import AppConfig, ResourcePermissions @mcp.resource( "ui://my-app/view.html", app=AppConfig( permissions=ResourcePermissions( camera={}, clipboard_write={}, ) ), ) def my_view() -> str: return "..." ``` Hosts may or may not grant these permissions. Your app should use JavaScript feature detection as a fallback. ## Example: QR Code Server This example creates a tool that generates QR codes and an app that renders them as images. It's based on the [official MCP Apps example](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/qr-server). Requires the `qrcode[pil]` package. ```python expandable theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} import base64 import io import qrcode from mcp import types from fastmcp import FastMCP from fastmcp.server.apps import AppConfig, ResourceCSP from fastmcp.tools import ToolResult mcp = FastMCP("QR Code Server") VIEW_URI = "ui://qr-server/view.html" @mcp.tool(app=AppConfig(resource_uri=VIEW_URI)) def generate_qr(text: str = "https://gofastmcp.com") -> ToolResult: """Generate a QR code from text.""" qr = qrcode.QRCode(version=1, box_size=10, border=4) qr.add_data(text) qr.make(fit=True) img = qr.make_image() buffer = io.BytesIO() img.save(buffer, format="PNG") b64 = base64.b64encode(buffer.getvalue()).decode() return ToolResult( content=[types.ImageContent(type="image", data=b64, mimeType="image/png")] ) @mcp.resource( VIEW_URI, app=AppConfig(csp=ResourceCSP(resource_domains=["https://unpkg.com"])), ) def view() -> str: """Interactive QR code viewer.""" return """\
""" ``` The tool generates a QR code as a base64 PNG. The resource loads the MCP Apps JS SDK from unpkg (declared in the CSP), listens for tool results, and renders the image. The host wires them together — when the LLM calls `generate_qr`, the QR code appears in an interactive frame inside the conversation. ## Checking Client Support Not all hosts support the Apps extension. You can check at runtime using the tool's [context](/servers/context): ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Context from fastmcp.server.apps import AppConfig, UI_EXTENSION_ID @mcp.tool(app=AppConfig(resource_uri="ui://my-app/view.html")) async def my_tool(ctx: Context) -> str: if ctx.client_supports_extension(UI_EXTENSION_ID): # Return data optimized for UI rendering return rich_response() else: # Fall back to plain text return plain_text_response() ``` # Apps Source: https://gofastmcp.com/apps/overview Give your tools interactive UIs rendered directly in the conversation. MCP Apps let your tools return interactive UIs — rendered in a sandboxed iframe right inside the host client's conversation. Instead of returning plain text, a tool can show a chart, a sortable table, a form, or anything you can build with HTML. FastMCP implements the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) and provides two approaches: ## Prefab Apps (Recommended) [Prefab](https://prefab.prefect.io) is in extremely early, active development — its API changes frequently and breaking changes can occur with any release. The FastMCP integration is equally new and under rapid development. These docs are included for users who want to work on the cutting edge; production use is not recommended. Always [pin `prefab-ui` to a specific version](/apps/prefab#getting-started) in your dependencies. [Prefab UI](https://prefab.prefect.io) is a declarative UI framework for Python. You describe layouts, charts, tables, forms, and interactive behaviors using a Python DSL — and the framework compiles them to a JSON protocol that a shared renderer interprets. It started as a component library inside FastMCP and grew into its own framework with [comprehensive documentation](https://prefab.prefect.io). ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, BarChart, ChartSeries from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Dashboard") @mcp.tool(app=True) def sales_chart(year: int) -> PrefabApp: """Show sales data as an interactive chart.""" data = get_sales_data(year) with Column(gap=4, css_class="p-6") as view: Heading(f"{year} Sales") BarChart( data=data, series=[ChartSeries(data_key="revenue", label="Revenue")], x_axis="month", ) return PrefabApp(view=view) ``` Install with `pip install "fastmcp[apps]"` and see [Prefab Apps](/apps/prefab) for the integration guide. ## Custom HTML Apps The [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) is an open protocol, and you can use it directly when you need full control. You write your own HTML/CSS/JavaScript and communicate with the host via the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) SDK. This is the right choice for custom rendering (maps, 3D, video), specific JavaScript frameworks, or capabilities beyond what the component library offers. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP from fastmcp.server.apps import AppConfig, ResourceCSP mcp = FastMCP("Custom App") @mcp.tool(app=AppConfig(resource_uri="ui://my-app/view.html")) def my_tool() -> str: return '{"values": [1, 2, 3]}' @mcp.resource( "ui://my-app/view.html", app=AppConfig(csp=ResourceCSP(resource_domains=["https://unpkg.com"])), ) def view() -> str: return "..." ``` See [Custom HTML Apps](/apps/low-level) for the full reference. # Patterns Source: https://gofastmcp.com/apps/patterns Charts, tables, forms, and other common tool UIs. [Prefab](https://prefab.prefect.io) is in extremely early, active development — its API changes frequently and breaking changes can occur with any release. The FastMCP integration is equally new and under rapid development. These docs are included for users who want to work on the cutting edge; production use is not recommended. Always pin `prefab-ui` to a specific version in your dependencies. The most common use of Prefab is giving your tools a visual representation — a chart instead of raw numbers, a sortable table instead of a text dump, a status dashboard instead of a list of booleans. Each pattern below is a complete, copy-pasteable tool. ## Charts Prefab includes [bar, line, area, pie, radar, and radial charts](https://prefab.prefect.io/docs/components/charts). They all render client-side with tooltips, legends, and responsive sizing. ### Bar Chart ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, BarChart, ChartSeries from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Charts") @mcp.tool(app=True) def quarterly_revenue(year: int) -> PrefabApp: """Show quarterly revenue as a bar chart.""" 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}, ] with Column(gap=4, css_class="p-6") as view: Heading(f"{year} Revenue vs Costs") BarChart( data=data, series=[ ChartSeries(data_key="revenue", label="Revenue"), ChartSeries(data_key="costs", label="Costs"), ], x_axis="quarter", show_legend=True, ) return PrefabApp(view=view) ``` Multiple `ChartSeries` entries plot different data keys. Add `stacked=True` to stack bars, or `horizontal=True` to flip the axes. ### Area Chart `LineChart` and `AreaChart` share the same API as `BarChart`, with `curve` for interpolation (`"linear"`, `"smooth"`, `"step"`) and `show_dots` for data points: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, AreaChart, ChartSeries from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Charts") @mcp.tool(app=True) def usage_trend() -> PrefabApp: """Show API usage over time.""" data = [ {"date": "Feb 1", "requests": 1200}, {"date": "Feb 2", "requests": 1350}, {"date": "Feb 3", "requests": 980}, {"date": "Feb 4", "requests": 1500}, {"date": "Feb 5", "requests": 1420}, ] with Column(gap=4, css_class="p-6") as view: Heading("API Usage") AreaChart( data=data, series=[ChartSeries(data_key="requests", label="Requests")], x_axis="date", curve="smooth", height=250, ) return PrefabApp(view=view) ``` ### Pie and Donut Charts `PieChart` uses `data_key` (the numeric value) and `name_key` (the label) instead of series. Set `inner_radius` for a donut: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, PieChart from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Charts") @mcp.tool(app=True) def ticket_breakdown() -> PrefabApp: """Show open tickets by category.""" data = [ {"category": "Bug", "count": 23}, {"category": "Feature", "count": 15}, {"category": "Docs", "count": 8}, {"category": "Infra", "count": 12}, ] with Column(gap=4, css_class="p-6") as view: Heading("Open Tickets") PieChart( data=data, data_key="count", name_key="category", show_legend=True, inner_radius=60, ) return PrefabApp(view=view) ``` ## Data Tables [DataTable](https://prefab.prefect.io/docs/components/data-display/data-table) provides sortable columns, full-text search, and pagination — all running client-side in the browser. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, DataTable, DataTableColumn from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Directory") @mcp.tool(app=True) def employee_directory() -> PrefabApp: """Show a searchable, sortable employee directory.""" employees = [ {"name": "Alice Chen", "department": "Engineering", "role": "Staff Engineer", "location": "SF"}, {"name": "Bob Martinez", "department": "Design", "role": "Lead Designer", "location": "NYC"}, {"name": "Carol Johnson", "department": "Engineering", "role": "Senior Engineer", "location": "London"}, {"name": "David Kim", "department": "Product", "role": "Product Manager", "location": "SF"}, {"name": "Eva Müller", "department": "Engineering", "role": "Engineer", "location": "Berlin"}, ] 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, searchable=True, paginated=True, page_size=15, ) return PrefabApp(view=view) ``` ## Forms A form collects input, but it needs somewhere to send that input. The [`CallTool`](https://prefab.prefect.io/docs/concepts/actions) action connects a form to a tool on your MCP server — so you need two tools: one that renders the form, and one that handles the submission. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import ( Column, Heading, Row, Muted, Badge, Input, Select, Textarea, Button, Form, ForEach, Separator, ) from prefab_ui.actions import ShowToast from prefab_ui.actions.mcp import CallTool from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Contacts") contacts_db: list[dict] = [ {"name": "Zaphod Beeblebrox", "email": "zaphod@galaxy.gov", "category": "Partner"}, ] @mcp.tool(app=True) def contact_form() -> PrefabApp: """Show a contact list with a form to add new contacts.""" with Column(gap=6, css_class="p-6") as view: Heading("Contacts") with ForEach("contacts"): with Row(gap=2, align="center"): Muted("{{ name }}") Muted("{{ email }}") Badge("{{ category }}") Separator() 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_db)}) @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.""" contacts_db.append({"name": name, "email": email, "category": category, "notes": notes}) return list(contacts_db) ``` When the user submits the form, the renderer calls `save_contact` on the server with all named input values as arguments. Because `result_key="contacts"` is set, the returned list replaces the `contacts` state — and the `ForEach` re-renders with the new data automatically. The `save_contact` tool is a regular MCP tool. The LLM can also call it directly in conversation. Your UI actions and your conversational tools are the same thing. ### Pydantic Model Forms For complex forms, `Form.from_model()` generates the entire form from a Pydantic model — inputs, labels, validation, and submit wiring: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from typing import Literal from pydantic import BaseModel, Field from prefab_ui.components import Column, Heading, Form from prefab_ui.actions.mcp import CallTool from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Bug Tracker") class BugReport(BaseModel): title: str = Field(title="Bug Title") severity: Literal["low", "medium", "high", "critical"] = Field( title="Severity", default="medium" ) description: str = Field(title="Description") steps_to_reproduce: str = Field(title="Steps to Reproduce") @mcp.tool(app=True) def report_bug() -> PrefabApp: """Show a bug report form.""" with Column(gap=4, css_class="p-6") as view: Heading("Report a Bug") Form.from_model(BugReport, on_submit=CallTool("create_bug_report")) return PrefabApp(view=view) @mcp.tool def create_bug_report(data: dict) -> str: """Create a bug report from the form submission.""" report = BugReport(**data) # save to database... return f"Created bug report: {report.title}" ``` `str` fields become text inputs, `Literal` becomes a select, `bool` becomes a checkbox. The `on_submit` CallTool receives all field values under a `data` key. ## Status Displays Cards, badges, progress bars, and grids combine naturally for dashboards. See the [Prefab layout](https://prefab.prefect.io/docs/concepts/composition) and [container](https://prefab.prefect.io/docs/components/containers) docs for the full set of layout and display components. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import ( Column, Row, Grid, Heading, Text, Muted, Badge, Card, CardContent, Progress, Separator, ) from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Monitoring") @mcp.tool(app=True) def system_status() -> PrefabApp: """Show current system health.""" 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}, ] 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 [`If`, `Elif`, and `Else`](https://prefab.prefect.io/docs/concepts/composition#conditional-rendering) show or hide content based on state. Changes are instant — no server round-trip. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, Switch, Separator, Alert, If from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Flags") @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("{{ dark_mode }}"): Alert(title="Dark mode enabled", description="UI will use dark theme.") with If("{{ 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 [Tabs](https://prefab.prefect.io/docs/components/containers/tabs) organize content into switchable views. Switching is client-side — no server round-trip. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import ( Column, Heading, Text, Muted, Badge, Row, DataTable, DataTableColumn, Tabs, Tab, ForEach, ) from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Projects") @mcp.tool(app=True) def project_overview(project_id: str) -> PrefabApp: """Show project details organized in tabs.""" 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"}, ], "activity": [ {"timestamp": "2 hours ago", "message": "Merged PR #342"}, {"timestamp": "1 day ago", "message": "Released v3.0.1"}, ], } 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"): with Row(gap=2): Muted("{{ timestamp }}") Text("{{ message }}") return PrefabApp(view=view, state={"activity": project["activity"]}) ``` ## Accordion [Accordion](https://prefab.prefect.io/docs/components/containers/accordion) collapses sections to save space. `multiple=True` lets users expand several items at once: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import ( Column, Heading, Row, Text, Badge, Progress, Accordion, AccordionItem, ) from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("API Monitor") @mcp.tool(app=True) def api_health() -> PrefabApp: """Show health details for each API endpoint.""" 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}, ] 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) ``` ## Next Steps * **[Custom HTML Apps](/apps/low-level)** — When you need your own HTML, CSS, and JavaScript * **[Prefab UI Docs](https://prefab.prefect.io)** — Components, state, expressions, and actions # Prefab Apps Source: https://gofastmcp.com/apps/prefab Build interactive tool UIs in pure Python — no HTML or JavaScript required. [Prefab](https://prefab.prefect.io) is in extremely early, active development — its API changes frequently and breaking changes can occur with any release. The FastMCP integration is equally new and under rapid development. These docs are included for users who want to work on the cutting edge; production use is not recommended. Always pin `prefab-ui` to a specific version in your dependencies (see below). [Prefab UI](https://prefab.prefect.io) is a declarative UI framework for Python. You describe what your interface should look like — a chart, a table, a form — and return it from your tool. FastMCP takes care of everything else: registering the renderer, wiring the protocol metadata, and delivering the component tree to the host. Prefab started as a component library inside FastMCP and grew into a full framework for building interactive applications — with its own state management, reactive expression system, and action model. The [Prefab documentation](https://prefab.prefect.io) covers all of this in depth. This page focuses on the FastMCP integration: what you return from a tool, and what FastMCP does with it. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} pip install "fastmcp[apps]" ``` Prefab UI is in active early development and its API changes frequently. We strongly recommend pinning `prefab-ui` to a specific version in your project's dependencies. Installing `fastmcp[apps]` pulls in `prefab-ui` but won't pin it — so a routine `pip install --upgrade` could introduce breaking changes. ```toml theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # pyproject.toml dependencies = [ "fastmcp[apps]", "prefab-ui==0.8.0", # pin to a known working version ] ``` Here's the simplest possible Prefab App — a tool that returns a bar chart: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, BarChart, ChartSeries from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Dashboard") @mcp.tool(app=True) def revenue_chart(year: int) -> PrefabApp: """Show annual revenue as an interactive bar chart.""" data = [ {"quarter": "Q1", "revenue": 42000}, {"quarter": "Q2", "revenue": 51000}, {"quarter": "Q3", "revenue": 47000}, {"quarter": "Q4", "revenue": 63000}, ] with Column(gap=4, css_class="p-6") as view: Heading(f"{year} Revenue") BarChart( data=data, series=[ChartSeries(data_key="revenue", label="Revenue")], x_axis="quarter", ) return PrefabApp(view=view) ``` That's it — you declare a layout using Python's `with` statement, and return it. When the host calls this tool, the user sees an interactive bar chart instead of a JSON blob. The [Patterns](/apps/patterns) page has more examples: area charts, data tables, forms, status dashboards, and more. ## What You Return ### Components The simplest way to get started. If you're returning a visual representation of data and don't need Prefab's more advanced features like initial state or stylesheets, just return the components directly. FastMCP wraps them in a `PrefabApp` automatically: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, Badge from fastmcp import FastMCP mcp = FastMCP("Status") @mcp.tool(app=True) def status_badge() -> Column: """Show system status.""" with Column(gap=2) as view: Heading("All Systems Operational") Badge("Healthy", variant="success") return view ``` Want a chart? Return a chart. Want a table? Return a table. FastMCP handles the wiring. ### PrefabApp When you need more control — setting initial state values that components can read and react to, or configuring the rendering engine — return a `PrefabApp` explicitly: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, Text, Button, If, Badge from prefab_ui.actions import ToggleState from prefab_ui.app import PrefabApp from fastmcp import FastMCP mcp = FastMCP("Demo") @mcp.tool(app=True) def toggle_demo() -> PrefabApp: """Interactive toggle with state.""" with Column(gap=4, css_class="p-6") as view: Button("Toggle", on_click=ToggleState("show")) with If("{{ show }}"): Badge("Visible!", variant="success") return PrefabApp(view=view, state={"show": False}) ``` The `state` dict provides the initial values. Components reference state with `{{ expression }}` templates. State mutations like `ToggleState` happen entirely in the browser — no server round-trip. The [Prefab state guide](https://prefab.prefect.io/docs/concepts/state) covers this in detail. ### ToolResult Every tool result has two audiences: the renderer (which displays the UI) and the LLM (which reads the text content to understand what happened). By default, Prefab Apps send `"[Rendered Prefab UI]"` as the text content, which tells the LLM almost nothing. If you want the LLM to understand the result — so it can reference the data in conversation, summarize it, or decide what to do next — wrap your return in a `ToolResult` with a meaningful `content` string: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from prefab_ui.components import Column, Heading, BarChart, ChartSeries from prefab_ui.app import PrefabApp from fastmcp import FastMCP from fastmcp.tools import ToolResult mcp = FastMCP("Sales") @mcp.tool(app=True) def sales_overview(year: int) -> ToolResult: """Show sales data visually and summarize for the model.""" data = get_sales_data(year) total = sum(row["revenue"] for row in data) with Column(gap=4, css_class="p-6") as view: Heading("Sales Overview") BarChart(data=data, series=[ChartSeries(data_key="revenue")]) return ToolResult( content=f"Total revenue for {year}: ${total:,} across {len(data)} quarters", structured_content=view, ) ``` The user sees the chart. The LLM sees `"Total revenue for 2025: $203,000 across 4 quarters"` and can reason about it. ## Type Inference If your tool's return type annotation is a Prefab type — `PrefabApp`, `Component`, or their `Optional` variants — FastMCP detects this and enables app rendering automatically: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} @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 is required when the return type doesn't reveal a Prefab type (e.g., `-> ToolResult`). ## How It Works Behind the scenes, when a tool returns a Prefab component or `PrefabApp`, FastMCP: 1. **Registers a shared renderer** — a `ui://prefab/renderer.html` resource containing the JavaScript rendering engine, fetched once by the host and reused across all your Prefab tools. 2. **Wires the tool metadata** — so the host knows to load the renderer iframe when displaying the tool result. 3. **Serializes the component tree** — your Python components become `structuredContent` on the tool result, which the renderer interprets and displays. None of this requires any configuration. The `app=True` flag (or type inference) is the only thing you need. ## Mixing with Custom HTML Apps 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. Both use the same MCP Apps protocol: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.server.apps import AppConfig @mcp.tool(app=True) def team_directory() -> PrefabApp: ... @mcp.tool(app=AppConfig(resource_uri="ui://my-app/map.html")) def map_view() -> str: ... ``` ## Next Steps * **[Patterns](/apps/patterns)** — Charts, tables, forms, and other common tool UIs * **[Custom HTML Apps](/apps/low-level)** — When you need your own HTML, CSS, and JavaScript * **[Prefab UI Docs](https://prefab.prefect.io)** — Components, state, expressions, and actions # Client Commands Source: https://gofastmcp.com/cli/client List tools, call them, and discover configured servers The CLI can act as an MCP client — connecting to any server (local or remote) to list what it exposes and call its tools directly. This is useful for development, debugging, scripting, and giving shell-capable LLM agents access to MCP servers. ## Listing Tools `fastmcp list` connects to a server and prints its tools as function signatures, showing parameter names, types, and descriptions at a glance: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list http://localhost:8000/mcp fastmcp list server.py fastmcp list weather # name-based resolution ``` When you need the full JSON Schema for a tool's inputs or outputs — for understanding nested objects, enum constraints, or complex types — opt in with `--input-schema` or `--output-schema`: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list server.py --input-schema ``` ### Resources and Prompts By default, only tools are shown. Add `--resources` or `--prompts` to include those: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list server.py --resources --prompts ``` ### Machine-Readable Output The `--json` flag switches to structured JSON with full schemas included. This is the format to use when feeding tool definitions to an LLM or building automation: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list server.py --json ``` ### Options | Option | Flag | Description | | ------------- | ------------------- | ----------------------------------------------------- | | Command | `--command` | Connect via stdio (e.g., `'npx -y @mcp/server'`) | | Transport | `--transport`, `-t` | Force `http` or `sse` for URL targets | | Resources | `--resources` | Include resources in output | | Prompts | `--prompts` | Include prompts in output | | Input Schema | `--input-schema` | Show full input schemas | | Output Schema | `--output-schema` | Show full output schemas | | JSON | `--json` | Structured JSON output | | Timeout | `--timeout` | Connection timeout in seconds | | Auth | `--auth` | `oauth` (default for HTTP), a bearer token, or `none` | ## Calling Tools `fastmcp call` invokes a single tool on a server. Pass arguments as `key=value` pairs — the CLI fetches the tool's schema and coerces your string values to the right types automatically: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp call server.py greet name=World fastmcp call http://localhost:8000/mcp search query=hello limit=5 ``` Type coercion is schema-driven: `"5"` becomes the integer `5` when the schema expects an integer. Booleans accept `true`/`false`, `yes`/`no`, and `1`/`0`. Arrays and objects are parsed as JSON. ### Complex Arguments For tools with nested or structured parameters, `key=value` syntax gets awkward. Pass a single JSON object instead: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp call server.py create_item '{"name": "Widget", "tags": ["sale"], "metadata": {"color": "blue"}}' ``` Or use `--input-json` to provide a base dictionary, then override individual keys with `key=value` pairs: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp call server.py search --input-json '{"query": "hello", "limit": 5}' limit=10 ``` ### Error Handling If you misspell a tool name, the CLI suggests corrections via fuzzy matching. Missing required arguments produce a clear message with the tool's signature as a reminder. Tool execution errors are printed with a non-zero exit code, making the CLI straightforward to use in scripts. ### Structured Output `--json` emits the raw result including content blocks, error status, and structured content: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp call server.py get_weather city=London --json ``` ### Interactive Elicitation Some tools request additional input during execution through MCP's elicitation mechanism. When this happens, the CLI prompts you in the terminal — showing each field's name, type, and whether it's required. You can type `decline` to skip a question or `cancel` to abort the call entirely. ### Options | Option | Flag | Description | | ---------- | ------------------- | ------------------------------------------------ | | Command | `--command` | Connect via stdio | | Transport | `--transport`, `-t` | Force `http` or `sse` | | Input JSON | `--input-json` | Base arguments as JSON (merged with `key=value`) | | JSON | `--json` | Raw JSON output | | Timeout | `--timeout` | Connection timeout in seconds | | Auth | `--auth` | `oauth`, a bearer token, or `none` | ## Discovering Configured Servers `fastmcp discover` scans your machine for MCP servers configured in editors and tools. It checks: * **Claude Desktop** — `claude_desktop_config.json` * **Claude Code** — `~/.claude.json` * **Cursor** — `.cursor/mcp.json` (walks up from current directory) * **Gemini CLI** — `~/.gemini/settings.json` * **Goose** — `~/.config/goose/config.yaml` * **Project** — `./mcp.json` in the current directory ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp discover ``` The output groups servers by source, showing each server's name and transport. Filter by source or get machine-readable output: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp discover --source claude-code fastmcp discover --source cursor --source gemini --json ``` Any server that appears here can be used by name with `list`, `call`, and other commands — so you can go from "I have a server in Claude Code" to querying it without copying URLs or paths. ## LLM Agent Integration For LLM agents that can execute shell commands but don't have native MCP support, the CLI provides a clean bridge. The agent calls `fastmcp list --json` to discover available tools with full schemas, then `fastmcp call --json` to invoke them with structured results. Because the CLI handles connection management, transport selection, and type coercion internally, the agent doesn't need to understand MCP protocol details — it just reads JSON and constructs shell commands. # Inspecting Servers Source: https://gofastmcp.com/cli/inspecting View a server's components and metadata `fastmcp inspect` loads a server and reports what it contains — its tools, resources, prompts, version, and metadata. The default output is a human-readable summary: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp inspect server.py ``` ``` Server: MyServer Instructions: A helpful MCP server Version: 1.0.0 Components: Tools: 5 Prompts: 2 Resources: 3 Templates: 1 Environment: FastMCP: 2.0.0 MCP: 1.0.0 Use --format [fastmcp|mcp] for complete JSON output ``` ## JSON Output For programmatic use, two JSON formats are available: **FastMCP format** (`--format fastmcp`) includes everything FastMCP knows about the server — tool tags, enabled status, output schemas, annotations, and custom metadata. Field names use `snake_case`. This is the format for debugging and introspecting FastMCP servers. **MCP protocol format** (`--format mcp`) shows exactly what MCP clients see through the protocol — only standard MCP fields, `camelCase` names, no FastMCP-specific extensions. This is the format for verifying client compatibility and debugging what clients actually receive. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Full FastMCP metadata to stdout fastmcp inspect server.py --format fastmcp # MCP protocol view saved to file fastmcp inspect server.py --format mcp -o manifest.json ``` ## Options | Option | Flag | Description | | ----------- | ---------------- | --------------------------------------------- | | Format | `--format`, `-f` | `fastmcp` or `mcp` (required when using `-o`) | | Output File | `--output`, `-o` | Save to file instead of stdout | ## Entrypoints The `inspect` command supports the same local entrypoints as [`fastmcp run`](/cli/running): inferred instances, explicit entrypoints, factory functions, and `fastmcp.json` configs. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp inspect server.py # inferred instance fastmcp inspect server.py:my_server # explicit entrypoint fastmcp inspect server.py:create_server # factory function fastmcp inspect fastmcp.json # config file ``` `inspect` only works with local files and `fastmcp.json` — it doesn't connect to remote URLs or standard MCP config files. # Install MCP Servers Source: https://gofastmcp.com/cli/install-mcp Install MCP servers into Claude, Cursor, Gemini, and other clients `fastmcp install` registers a server with an MCP client application so the client can launch it automatically. Each MCP client runs servers in its own isolated environment, which means dependencies need to be explicitly declared — you can't rely on whatever happens to be installed locally. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp install claude-desktop server.py fastmcp install claude-code server.py --with pandas --with matplotlib fastmcp install cursor server.py -e . ``` `uv` must be installed and available in your system PATH. Both Claude Desktop and Cursor run servers in isolated environments managed by `uv`. On macOS, install it globally with Homebrew for Claude Desktop compatibility: `brew install uv`. ## Supported Clients | Client | Install method | | ---------------- | ------------------------------------------------------- | | `claude-code` | Claude Code's built-in MCP management | | `claude-desktop` | Direct config file modification | | `cursor` | Deeplink that opens Cursor for confirmation | | `gemini-cli` | Gemini CLI's built-in MCP management | | `goose` | Deeplink that opens Goose for confirmation (uses `uvx`) | | `mcp-json` | Generates standard MCP JSON config for manual use | | `stdio` | Outputs the shell command to run via stdio | ## Declaring Dependencies Because MCP clients run servers in isolation, you need to tell the install command what your server needs. There are two approaches: **Command-line flags** let you specify dependencies directly: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp install claude-desktop server.py --with pandas --with "sqlalchemy>=2.0" fastmcp install cursor server.py -e . --with-requirements requirements.txt ``` **`fastmcp.json`** configuration files declare dependencies alongside the server definition. When you install from a config file, dependencies are picked up automatically: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp install claude-desktop fastmcp.json fastmcp install claude-desktop # auto-detects fastmcp.json in current directory ``` See [Server Configuration](/deployment/server-configuration) for the full config format. ## Options | Option | Flag | Description | | --------------------- | ----------------------- | ------------------------------------ | | Server Name | `--server-name`, `-n` | Custom name for the server | | Editable Package | `--with-editable`, `-e` | Install a directory in editable mode | | Extra Packages | `--with` | Additional packages (repeatable) | | Environment Variables | `--env` | `KEY=VALUE` pairs (repeatable) | | Environment File | `--env-file`, `-f` | Load env vars from a `.env` file | | Python | `--python` | Python version (e.g., `3.11`) | | Project | `--project` | Run within a uv project directory | | Requirements | `--with-requirements` | Install from a requirements file | ## Examples ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Basic install with auto-detected server instance fastmcp install claude-desktop server.py # Install from fastmcp.json with auto-detection fastmcp install claude-desktop # Explicit entrypoint with dependencies fastmcp install claude-desktop server.py:my_server \ --server-name "My Analysis Server" \ --with pandas # With environment variables fastmcp install claude-code server.py \ --env API_KEY=secret \ --env DEBUG=true # With env file fastmcp install cursor server.py --env-file .env # Specific Python version and requirements file fastmcp install claude-desktop server.py \ --python 3.11 \ --with-requirements requirements.txt ``` ## Generating MCP JSON The `mcp-json` target generates standard MCP configuration JSON instead of installing into a specific client. This is useful for clients that FastMCP doesn't directly support, for CI/CD environments, or for sharing server configs: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp install mcp-json server.py ``` The output follows the standard format used by Claude Desktop, Cursor, and other MCP clients: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "server-name": { "command": "uv", "args": ["run", "--with", "fastmcp", "fastmcp", "run", "/path/to/server.py"], "env": { "API_KEY": "value" } } } ``` Use `--copy` to send it to your clipboard instead of stdout. ## Generating Stdio Commands The `stdio` target outputs the shell command an MCP host would use to start your server over stdio: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp install stdio server.py # Output: uv run --with fastmcp fastmcp run /absolute/path/to/server.py ``` When installing from a `fastmcp.json`, dependencies from the config are included automatically: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp install stdio fastmcp.json # Output: uv run --with fastmcp --with pillow --with 'qrcode[pil]>=8.0' fastmcp run /path/to/server.py ``` Use `--copy` to copy to clipboard. `fastmcp install` is designed for local server files with stdio transport. For remote servers running over HTTP, use your client's native configuration — FastMCP's value here is simplifying the complex local setup with `uv`, dependencies, and environment variables. # CLI Source: https://gofastmcp.com/cli/overview The fastmcp command-line interface The `fastmcp` CLI is installed automatically with FastMCP. It's the primary way to run, test, install, and interact with MCP servers from your terminal. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp --help ``` ## Commands at a Glance | Command | What it does | | -------------------------------------------------------------- | ------------------------------------------------------------------------------- | | [`run`](/cli/running) | Run a server (local file, factory function, remote URL, or config file) | | [`dev inspector`](/cli/running#development-with-the-inspector) | Launch a server inside the MCP Inspector for interactive testing | | [`install`](/cli/install-mcp) | Install a server into Claude Code, Claude Desktop, Cursor, Gemini CLI, or Goose | | [`inspect`](/cli/inspecting) | Print a server's tools, resources, and prompts as a summary or JSON report | | [`list`](/cli/client) | List a server's tools (and optionally resources and prompts) | | [`call`](/cli/client#calling-tools) | Call a single tool with arguments | | [`discover`](/cli/client#discovering-configured-servers) | Find MCP servers configured in your editors and tools | | [`generate-cli`](/cli/generate-cli) | Scaffold a standalone typed CLI from a server's tool schemas | | [`project prepare`](/cli/running#pre-building-environments) | Pre-install dependencies into a reusable uv project | | [`auth cimd`](/cli/auth) | Create and validate CIMD documents for OAuth | | `version` | Print version info (`--copy` to copy to clipboard) | ## Server Targets Most commands need to know *which server* to talk to. You pass a "server spec" as the first argument, and FastMCP resolves the right transport automatically. **URLs** connect to a running HTTP server: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list http://localhost:8000/mcp fastmcp call http://localhost:8000/mcp get_forecast city=London ``` **Python files** are loaded directly — no `mcp.run()` boilerplate needed. FastMCP finds a server instance named `mcp`, `server`, or `app` in the file, or you can specify one explicitly: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list server.py fastmcp run server.py:my_custom_server ``` **Config files** work too — both FastMCP's own `fastmcp.json` format and standard MCP config files with an `mcpServers` key: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run fastmcp.json fastmcp list mcp-config.json ``` **Stdio commands** connect to any MCP server that speaks over standard I/O. Use `--command` instead of a positional argument: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list --command 'npx -y @modelcontextprotocol/server-github' ``` ### Name-Based Resolution If your servers are already configured in an editor or tool, you can refer to them by name. FastMCP scans configs from Claude Desktop, Claude Code, Cursor, Gemini CLI, and Goose: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list weather fastmcp call weather get_forecast city=London ``` When the same name appears in multiple configs, use the `source:name` form to be specific: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list claude-code:my-server fastmcp call cursor:weather get_forecast city=London ``` Run [`fastmcp discover`](/cli/client#discovering-configured-servers) to see what's available on your machine. ## Authentication When targeting an HTTP URL, the CLI enables OAuth authentication by default. If the server requires it, you'll be guided through the flow (typically opening a browser). If it doesn't, the setup is a silent no-op. To skip authentication entirely — useful for local development servers — pass `--auth none`: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp call http://localhost:8000/mcp my_tool --auth none ``` You can also pass a bearer token directly: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list http://localhost:8000/mcp --auth "Bearer sk-..." ``` ## Transport Override FastMCP defaults to Streamable HTTP for URL targets. If the server only supports Server-Sent Events (SSE), force the older transport: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp list http://localhost:8000 --transport sse ``` # Running Servers Source: https://gofastmcp.com/cli/running Start, develop, and configure servers from the command line ## Starting a Server `fastmcp run` starts a server. Point it at a Python file, a factory function, a remote URL, or a config file: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run server.py fastmcp run server.py:create_server fastmcp run https://example.com/mcp fastmcp run fastmcp.json ``` By default, the server runs over **stdio** — the transport that MCP clients like Claude Desktop expect. To serve over HTTP instead, specify the transport: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run server.py --transport http fastmcp run server.py --transport http --host 0.0.0.0 --port 9000 ``` ### Entrypoints FastMCP supports several ways to locate and start your server: **Inferred instance** — FastMCP imports the file and looks for a variable named `mcp`, `server`, or `app`: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run server.py ``` **Explicit instance** — point at a specific variable: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run server.py:my_server ``` **Factory function** — FastMCP calls the function and uses the returned server. Useful when your server needs async setup or configuration that runs before startup: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run server.py:create_server ``` **Remote URL** — starts a local proxy that bridges to a remote server. Handy for local development against a deployed server, or for bridging a remote HTTP server to stdio: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run https://example.com/mcp ``` **FastMCP config** — uses a `fastmcp.json` file that declaratively specifies the server, its dependencies, and deployment settings. When you run `fastmcp run` with no arguments, it auto-detects `fastmcp.json` in the current directory: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run fastmcp run my-config.fastmcp.json ``` See [Server Configuration](/deployment/server-configuration) for the full `fastmcp.json` format. **MCP config** — runs servers defined in a standard MCP configuration file (any `.json` with an `mcpServers` key): ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run mcp.json ``` `fastmcp run` completely ignores the `if __name__ == "__main__"` block. Any setup code in that block won't execute. If you need initialization logic to run, use a [factory function](/cli/overview#factory-functions). ### Options | Option | Flag | Description | | -------------- | -------------------------- | ------------------------------------------------------- | | Transport | `--transport`, `-t` | `stdio` (default), `http`, or `sse` | | Host | `--host` | Bind address for HTTP (default: `127.0.0.1`) | | Port | `--port`, `-p` | Bind port for HTTP (default: `8000`) | | Path | `--path` | URL path for HTTP (default: `/mcp/`) | | Log Level | `--log-level`, `-l` | `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` | | No Banner | `--no-banner` | Suppress the startup banner | | Auto-Reload | `--reload` / `--no-reload` | Watch for file changes and restart automatically | | Reload Dirs | `--reload-dir` | Directories to watch (repeatable) | | Skip Env | `--skip-env` | Don't set up a uv environment (use when already in one) | | Python | `--python` | Python version to use (e.g., `3.11`) | | Extra Packages | `--with` | Additional packages to install (repeatable) | | Project | `--project` | Run within a specific uv project directory | | Requirements | `--with-requirements` | Install from a requirements file | ### Dependency Management By default, `fastmcp run` uses your current Python environment directly. When you pass `--python`, `--with`, `--project`, or `--with-requirements`, it switches to running via `uv run` in a subprocess, which handles dependency isolation automatically. The `--skip-env` flag is useful when you're already inside an activated venv, a Docker container with pre-installed dependencies, or a uv-managed project — it prevents uv from trying to set up another environment layer. ## Development with the Inspector `fastmcp dev inspector` launches your server inside the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), a browser-based tool for interactively testing MCP servers. Auto-reload is on by default, so your server restarts when you save changes. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp dev inspector server.py fastmcp dev inspector server.py -e . --with pandas ``` The Inspector always runs your server via `uv run` in a subprocess — it never uses your local environment directly. Specify dependencies with `--with`, `--with-editable`, `--with-requirements`, or through a `fastmcp.json` file. The Inspector connects over **stdio only**. When it launches, you may need to select "STDIO" from the transport dropdown and click connect. To test a server over HTTP, start it separately with `fastmcp run server.py --transport http` and point the Inspector at the URL. | Option | Flag | Description | | ----------------- | -------------------------- | ------------------------------------ | | Editable Package | `--with-editable`, `-e` | Install a directory in editable mode | | Extra Packages | `--with` | Additional packages (repeatable) | | Inspector Version | `--inspector-version` | MCP Inspector version to use | | UI Port | `--ui-port` | Port for the Inspector UI | | Server Port | `--server-port` | Port for the Inspector proxy | | Auto-Reload | `--reload` / `--no-reload` | File watching (default: on) | | Reload Dirs | `--reload-dir` | Directories to watch (repeatable) | | Python | `--python` | Python version | | Project | `--project` | Run within a uv project directory | | Requirements | `--with-requirements` | Install from a requirements file | ## Pre-Building Environments `fastmcp project prepare` creates a persistent uv project from a `fastmcp.json` file, pre-installing all dependencies. This separates environment setup from server execution — install once, run many times. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Step 1: Build the environment (slow, does dependency resolution) fastmcp project prepare fastmcp.json --output-dir ./env # Step 2: Run using the prepared environment (fast, no install step) fastmcp run fastmcp.json --project ./env ``` The prepared directory contains a `pyproject.toml`, a `.venv` with all packages installed, and a `uv.lock` for reproducibility. This is particularly useful in deployment scenarios where you want deterministic, pre-built environments. # Bearer Token Authentication Source: https://gofastmcp.com/clients/auth/bearer Authenticate your FastMCP client with a Bearer token. Bearer Token authentication is only relevant for HTTP-based transports. You can configure your FastMCP client to use **bearer authentication** by supplying a valid access token. This is most appropriate for service accounts, long-lived API keys, CI/CD, applications where authentication is managed separately, or other non-interactive authentication methods. A Bearer token is a JSON Web Token (JWT) that is used to authenticate a request. It is most commonly used in the `Authorization` header of an HTTP request, using the `Bearer` scheme: ```http theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} Authorization: Bearer ``` ## Client Usage The most straightforward way to use a pre-existing Bearer token is to provide it as a string to the `auth` parameter of the `fastmcp.Client` or transport instance. FastMCP will automatically format it correctly for the `Authorization` header and bearer scheme. If you're using a string token, do not include the `Bearer` prefix. FastMCP will add it for you. ```python {5} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client async with Client( "https://your-server.fastmcp.app/mcp", auth="", ) as client: await client.ping() ``` You can also supply a Bearer token to a transport instance, such as `StreamableHttpTransport` or `SSETransport`: ```python {6} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.transports import StreamableHttpTransport transport = StreamableHttpTransport( "http://your-server.fastmcp.app/mcp", auth="", ) async with Client(transport) as client: await client.ping() ``` ## `BearerAuth` Helper If you prefer to be more explicit and not rely on FastMCP to transform your string token, you can use the `BearerAuth` class yourself, which implements the `httpx.Auth` interface. ```python {6} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.auth import BearerAuth async with Client( "https://your-server.fastmcp.app/mcp", auth=BearerAuth(token=""), ) as client: await client.ping() ``` ## Custom Headers If the MCP server expects a custom header or token scheme, you can manually set the client's `headers` instead of using the `auth` parameter by setting them on your transport: ```python {5} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.transports import StreamableHttpTransport async with Client( transport=StreamableHttpTransport( "https://your-server.fastmcp.app/mcp", headers={"X-API-Key": ""}, ), ) as client: await client.ping() ``` # CIMD Authentication Source: https://gofastmcp.com/clients/auth/cimd Use Client ID Metadata Documents for verifiable, domain-based client identity. CIMD authentication is only relevant for HTTP-based transports and requires a server that advertises CIMD support. With standard OAuth, your client registers dynamically with every server it connects to, receiving a fresh `client_id` each time. This works, but the server has no way to verify *who* your client actually is — any client can claim any name during registration. CIMD (Client ID Metadata Documents) flips this around. You host a small JSON document at an HTTPS URL you control, and that URL becomes your `client_id`. When your client connects to a server, the server fetches your metadata document and can verify your identity through your domain ownership. Users see a verified domain badge in the consent screen instead of an unverified client name. ## Client Usage Pass your CIMD document URL to the `client_metadata_url` parameter of `OAuth`: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.auth import OAuth async with Client( "https://mcp-server.example.com/mcp", auth=OAuth( client_metadata_url="https://myapp.example.com/oauth/client.json", ), ) as client: await client.ping() ``` When the server supports CIMD, the client uses your metadata URL as its `client_id` instead of performing Dynamic Client Registration. The server fetches your document, validates it, and proceeds with the standard OAuth authorization flow. You don't need to pass `mcp_url` when using `OAuth` with `Client(auth=...)` — the transport provides the server URL automatically. ## Creating a CIMD Document A CIMD document is a JSON file that describes your client. The most important field is `client_id`, which must exactly match the URL where you host the document. Use the FastMCP CLI to generate one: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp auth cimd create \ --name "My Application" \ --redirect-uri "http://localhost:*/callback" \ --client-id "https://myapp.example.com/oauth/client.json" ``` This produces: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "client_id": "https://myapp.example.com/oauth/client.json", "client_name": "My Application", "redirect_uris": ["http://localhost:*/callback"], "token_endpoint_auth_method": "none", "grant_types": ["authorization_code"], "response_types": ["code"] } ``` If you omit `--client-id`, the CLI generates a placeholder value and reminds you to update it before hosting. ### CLI Options The `create` command accepts these flags: | Flag | Description | | ---------------------- | ------------------------------------------------------------------- | | `--name` | Human-readable client name (required) | | `--redirect-uri`, `-r` | Allowed redirect URIs — can be specified multiple times (required) | | `--client-id` | The URL where you'll host this document (sets `client_id` directly) | | `--output`, `-o` | Write to a file instead of stdout | | `--scope` | Space-separated list of scopes the client may request | | `--client-uri` | URL of the client's home page | | `--logo-uri` | URL of the client's logo image | | `--no-pretty` | Output compact JSON | ### Redirect URIs The `redirect_uris` field supports wildcard port matching for localhost. The pattern `http://localhost:*/callback` matches any port, which is useful for development clients that bind to random available ports (which is what FastMCP's `OAuth` helper does by default). ## Hosting Requirements CIMD documents must be hosted at a publicly accessible HTTPS URL with a non-root path: * **HTTPS required** — HTTP URLs are rejected for security * **Non-root path** — The URL must have a path component (e.g., `/oauth/client.json`, not just `/`) * **Public accessibility** — The server must be able to fetch the document over the internet * **Matching `client_id`** — The `client_id` field in the document must exactly match the hosting URL Common hosting options include static file hosting services like GitHub Pages, Cloudflare Pages, Vercel, or S3 — anywhere you can serve a JSON file over HTTPS. ## Validating Your Document Before deploying, verify your hosted document passes validation: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp auth cimd validate https://myapp.example.com/oauth/client.json ``` The validator fetches the document and checks that: * The URL is valid (HTTPS, non-root path) * The document is well-formed JSON conforming to the CIMD schema * The `client_id` in the document matches the URL it was fetched from ## How It Works When your client connects to a CIMD-enabled server, the flow works like this: Your client sends its `client_metadata_url` as the `client_id` in the OAuth authorization request. The server sees that the `client_id` is an HTTPS URL with a path — the signature of a CIMD client — and skips Dynamic Client Registration. The server fetches your JSON document from the URL, validates that `client_id` matches the URL, and extracts your client metadata (name, redirect URIs, scopes). The standard OAuth flow continues: browser opens for user consent, authorization code exchange, token issuance. The consent screen shows your verified domain. The server caches your CIMD document according to HTTP cache headers, so subsequent requests don't require re-fetching. ## Server Configuration CIMD is a server-side feature that your MCP server must support. FastMCP's OAuth proxy providers (GitHub, Google, Auth0, etc.) support CIMD by default. See the [OAuth Proxy CIMD documentation](/servers/auth/oauth-proxy#cimd-support) for server-side configuration, including private key JWT authentication and security details. # OAuth Authentication Source: https://gofastmcp.com/clients/auth/oauth Authenticate your FastMCP client via OAuth 2.1. OAuth authentication is only relevant for HTTP-based transports and requires user interaction via a web browser. When your FastMCP client needs to access an MCP server protected by OAuth 2.1, and the process requires user interaction (like logging in and granting consent), you should use the Authorization Code Flow. FastMCP provides the `fastmcp.client.auth.OAuth` helper to simplify this entire process. This flow is common for user-facing applications where the application acts on behalf of the user. ## Client Usage ### Default Configuration The simplest way to use OAuth is to pass the string `"oauth"` to the `auth` parameter of the `Client` or transport instance. FastMCP will automatically configure the client to use OAuth with default settings: ```python {4} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client # Uses default OAuth settings async with Client("https://your-server.fastmcp.app/mcp", auth="oauth") as client: await client.ping() ``` ### `OAuth` Helper To fully configure the OAuth flow, use the `OAuth` helper and pass it to the `auth` parameter of the `Client` or transport instance. `OAuth` manages the complexities of the OAuth 2.1 Authorization Code Grant with PKCE (Proof Key for Code Exchange) for enhanced security, and implements the full `httpx.Auth` interface. ```python {2, 4, 6} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.auth import OAuth oauth = OAuth(scopes=["user"]) async with Client("https://your-server.fastmcp.app/mcp", auth=oauth) as client: await client.ping() ``` You don't need to pass `mcp_url` when using `OAuth` with `Client(auth=...)` — the transport provides the server URL automatically. #### `OAuth` Parameters * **`scopes`** (`str | list[str]`, optional): OAuth scopes to request. Can be space-separated string or list of strings * **`client_name`** (`str`, optional): Client name for dynamic registration. Defaults to `"FastMCP Client"` * **`client_id`** (`str`, optional): Pre-registered OAuth client ID. When provided, skips Dynamic Client Registration entirely. See [Pre-Registered Clients](#pre-registered-clients) * **`client_secret`** (`str`, optional): OAuth client secret for pre-registered clients. Optional — public clients that rely on PKCE can omit this * **`client_metadata_url`** (`str`, optional): URL-based client identity (CIMD). See [CIMD Authentication](/clients/auth/cimd) for details * **`token_storage`** (`AsyncKeyValue`, optional): Storage backend for persisting OAuth tokens. Defaults to in-memory storage (tokens lost on restart). See [Token Storage](#token-storage) for encrypted storage options * **`additional_client_metadata`** (`dict[str, Any]`, optional): Extra metadata for client registration * **`callback_port`** (`int`, optional): Fixed port for OAuth callback server. If not specified, uses a random available port * **`httpx_client_factory`** (`McpHttpClientFactory`, optional): Factory for creating httpx clients ## OAuth Flow The OAuth flow is triggered when you use a FastMCP `Client` configured to use OAuth. The client first checks the configured `token_storage` backend for existing, valid tokens for the target server. If one is found, it will be used to authenticate the client. If no valid tokens exist, the client attempts to discover the OAuth server's endpoints using a well-known URI (e.g., `/.well-known/oauth-authorization-server`) based on the `mcp_url`. If a `client_id` is provided, the client uses those pre-registered credentials directly and skips this step entirely. Otherwise, if a `client_metadata_url` is configured and the server supports CIMD, the client uses its metadata URL as its identity. As a fallback, the client performs Dynamic Client Registration (RFC 7591) if the server supports it. A temporary local HTTP server is started on an available port (or the port specified via `callback_port`). This server's address (e.g., `http://127.0.0.1:/callback`) acts as the `redirect_uri` for the OAuth flow. The user's default web browser is automatically opened, directing them to the OAuth server's authorization endpoint. The user logs in and grants (or denies) the requested `scopes`. Upon approval, the OAuth server redirects the user's browser to the local callback server with an `authorization_code`. The client captures this code and exchanges it with the OAuth server's token endpoint for an `access_token` (and often a `refresh_token`) using PKCE for security. The obtained tokens are saved to the configured `token_storage` backend for future use, eliminating the need for repeated browser interactions. The access token is automatically included in the `Authorization` header for requests to the MCP server. If the access token expires, the client will automatically use the refresh token to get a new access token. ## Token Storage By default, tokens are stored in memory and lost when your application restarts. For persistent storage, pass an `AsyncKeyValue`-compatible storage backend to the `token_storage` parameter. **Security Consideration**: Use encrypted storage for production. MCP clients can accumulate OAuth credentials for many servers over time, and a compromised token store could expose access to multiple services. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.auth import OAuth from key_value.aio.stores.disk import DiskStore from key_value.aio.wrappers.encryption import FernetEncryptionWrapper from cryptography.fernet import Fernet import os # Create encrypted disk storage encrypted_storage = FernetEncryptionWrapper( key_value=DiskStore(directory="~/.fastmcp/oauth-tokens"), fernet=Fernet(os.environ["OAUTH_STORAGE_ENCRYPTION_KEY"]) ) oauth = OAuth(token_storage=encrypted_storage) async with Client("https://your-server.fastmcp.app/mcp", auth=oauth) as client: await client.ping() ``` You can use any `AsyncKeyValue`-compatible backend from the [key-value library](https://github.com/strawgate/py-key-value) including Redis, DynamoDB, and more. Wrap your storage in `FernetEncryptionWrapper` for encryption. When selecting a storage backend, review the [py-key-value documentation](https://github.com/strawgate/py-key-value) to understand the maturity level and limitations of your chosen backend. Some backends may be in preview or have constraints that affect production suitability. ## CIMD Authentication Client ID Metadata Documents (CIMD) provide an alternative to Dynamic Client Registration. Instead of registering with each server, your client hosts a static JSON document at an HTTPS URL. That URL becomes your client's identity, and servers can verify who you are through your domain ownership. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.auth import OAuth async with Client( "https://mcp-server.example.com/mcp", auth=OAuth( client_metadata_url="https://myapp.example.com/oauth/client.json", ), ) as client: await client.ping() ``` See the [CIMD Authentication](/clients/auth/cimd) page for complete documentation on creating, hosting, and validating CIMD documents. ## Pre-Registered Clients Some OAuth servers don't support Dynamic Client Registration — the MCP spec explicitly makes DCR optional. If your client has been pre-registered with the server (you already have a `client_id` and optionally a `client_secret`), you can provide them directly to skip DCR entirely. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.auth import OAuth async with Client( "https://mcp-server.example.com/mcp", auth=OAuth( client_id="my-registered-client-id", client_secret="my-client-secret", ), ) as client: await client.ping() ``` Public clients that rely on PKCE for security can omit `client_secret`: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} oauth = OAuth(client_id="my-public-client-id") ``` When using pre-registered credentials, the client will not attempt Dynamic Client Registration. If the server rejects the credentials, the error is surfaced immediately rather than falling back to DCR. # The FastMCP Client Source: https://gofastmcp.com/clients/client Programmatic client for interacting with MCP servers through a well-typed, Pythonic interface. The `fastmcp.Client` class provides a programmatic interface for interacting with any MCP server. It handles protocol details and connection management automatically, letting you focus on the operations you want to perform. The FastMCP Client is designed for deterministic, controlled interactions rather than autonomous behavior, making it ideal for testing MCP servers during development, building deterministic applications that need reliable MCP interactions, and creating the foundation for agentic or LLM-based clients with structured, type-safe operations. This is a programmatic client that requires explicit function calls and provides direct control over all MCP operations. Use it as a building block for higher-level systems. ## Creating a Client You provide a server source and the client automatically infers the appropriate transport mechanism. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} import asyncio from fastmcp import Client, FastMCP # In-memory server (ideal for testing) server = FastMCP("TestServer") client = Client(server) # HTTP server client = Client("https://example.com/mcp") # Local Python script client = Client("my_mcp_server.py") async def main(): async with client: # Basic server interaction await client.ping() # List available operations tools = await client.list_tools() resources = await client.list_resources() prompts = await client.list_prompts() # Execute operations result = await client.call_tool("example_tool", {"param": "value"}) print(result) asyncio.run(main()) ``` All client operations require using the `async with` context manager for proper connection lifecycle management. ## Choosing a Transport The client automatically selects a transport based on what you pass to it, but different transports have different characteristics that matter for your use case. **In-memory transport** connects directly to a FastMCP server instance within the same Python process. Use this for testing and development where you want to eliminate subprocess and network complexity. The server shares your process's environment and memory space. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client, FastMCP server = FastMCP("TestServer") client = Client(server) # In-memory, no network or subprocess ``` **STDIO transport** launches a server as a subprocess and communicates through stdin/stdout pipes. This is the standard mechanism used by desktop clients like Claude Desktop. The subprocess runs in an isolated environment, so you must explicitly pass any environment variables the server needs. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client # Simple inference from file path client = Client("my_server.py") # With explicit environment configuration client = Client("my_server.py", env={"API_KEY": "secret"}) ``` **HTTP transport** connects to servers running as web services. Use this for production deployments where the server runs independently and manages its own lifecycle. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client client = Client("https://api.example.com/mcp") ``` See [Transports](/clients/transports) for detailed configuration options including authentication headers, session persistence, and multi-server configurations. ## Configuration-Based Clients Create clients from MCP configuration dictionaries, which can include multiple servers. While there is no official standard for MCP configuration format, FastMCP follows established conventions used by tools like Claude Desktop. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} config = { "mcpServers": { "weather": { "url": "https://weather-api.example.com/mcp" }, "assistant": { "command": "python", "args": ["./assistant_server.py"] } } } client = Client(config) async with client: # Tools are prefixed with server names weather_data = await client.call_tool("weather_get_forecast", {"city": "London"}) response = await client.call_tool("assistant_answer_question", {"question": "What's the capital of France?"}) # Resources use prefixed URIs icons = await client.read_resource("weather://weather/icons/sunny") ``` ## Connection Lifecycle The client uses context managers for connection management. When you enter the context, the client establishes a connection and performs an MCP initialization handshake with the server. This handshake exchanges capabilities, server metadata, and instructions. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client, FastMCP mcp = FastMCP(name="MyServer", instructions="Use the greet tool to say hello!") @mcp.tool def greet(name: str) -> str: """Greet a user by name.""" return f"Hello, {name}!" async with Client(mcp) as client: # Initialization already happened automatically print(f"Server: {client.initialize_result.serverInfo.name}") print(f"Instructions: {client.initialize_result.instructions}") print(f"Capabilities: {client.initialize_result.capabilities.tools}") ``` For advanced scenarios where you need precise control over when initialization happens, disable automatic initialization and call `initialize()` manually: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client client = Client("my_mcp_server.py", auto_initialize=False) async with client: # Connection established, but not initialized yet print(f"Connected: {client.is_connected()}") print(f"Initialized: {client.initialize_result is not None}") # False # Initialize manually with custom timeout result = await client.initialize(timeout=10.0) print(f"Server: {result.serverInfo.name}") # Now ready for operations tools = await client.list_tools() ``` ## Operations FastMCP clients interact with three types of server components. **Tools** are server-side functions that the client can execute with arguments. Call them with `call_tool()` and receive structured results. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: tools = await client.list_tools() result = await client.call_tool("multiply", {"a": 5, "b": 3}) print(result.data) # 15 ``` See [Tools](/clients/tools) for detailed documentation including version selection, error handling, and structured output. **Resources** are data sources that the client can read, either static or templated. Access them with `read_resource()` using URIs. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: resources = await client.list_resources() content = await client.read_resource("file:///config/settings.json") print(content[0].text) ``` See [Resources](/clients/resources) for detailed documentation including templates and binary content. **Prompts** are reusable message templates that can accept arguments. Retrieve rendered prompts with `get_prompt()`. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: prompts = await client.list_prompts() messages = await client.get_prompt("analyze_data", {"data": [1, 2, 3]}) print(messages.messages) ``` See [Prompts](/clients/prompts) for detailed documentation including argument serialization. ## Callback Handlers The client supports callback handlers for advanced server interactions. These let you respond to server-initiated requests and receive notifications. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.logging import LogMessage async def log_handler(message: LogMessage): print(f"Server log: {message.data}") async def progress_handler(progress: float, total: float | None, message: str | None): print(f"Progress: {progress}/{total} - {message}") async def sampling_handler(messages, params, context): # Integrate with your LLM service here return "Generated response" client = Client( "my_mcp_server.py", log_handler=log_handler, progress_handler=progress_handler, sampling_handler=sampling_handler, timeout=30.0 ) ``` Each handler type has its own documentation: * **[Sampling](/clients/sampling)** - Respond to server LLM requests * **[Elicitation](/clients/elicitation)** - Handle server requests for user input * **[Progress](/clients/progress)** - Monitor long-running operations * **[Logging](/clients/logging)** - Handle server log messages * **[Roots](/clients/roots)** - Provide local context to servers The FastMCP Client is designed as a foundational tool. Use it directly for deterministic operations, or build higher-level agentic systems on top of its reliable, type-safe interface. # User Elicitation Source: https://gofastmcp.com/clients/elicitation Handle server requests for structured user input. Use this when you need to respond to server requests for user input during tool execution. Elicitation allows MCP servers to request structured input from users during operations. Instead of requiring all inputs upfront, servers can interactively ask for missing parameters, request clarification, or gather additional context. ## Handler Template ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.elicitation import ElicitResult, ElicitRequestParams, RequestContext async def elicitation_handler( message: str, response_type: type | None, params: ElicitRequestParams, context: RequestContext ) -> ElicitResult | object: """ Handle server requests for user input. Args: message: The prompt to display to the user response_type: Python dataclass type for the response (None if no data expected) params: Original MCP elicitation parameters including raw JSON schema context: Request context with metadata Returns: - Data directly (implicitly accepts the elicitation) - ElicitResult for explicit control over the action """ # Present the message and collect input user_input = input(f"{message}: ") if not user_input: return ElicitResult(action="decline") # Create response using the provided dataclass type return response_type(value=user_input) client = Client( "my_mcp_server.py", elicitation_handler=elicitation_handler, ) ``` ## How It Works When a server needs user input, it sends an elicitation request with a message prompt and a JSON schema describing the expected response structure. FastMCP automatically converts this schema into a Python dataclass type, making it easy to construct properly typed responses without manually parsing JSON schemas. The handler receives four parameters: The prompt message to display to the user A Python dataclass type that FastMCP created from the server's JSON schema. Use this to construct your response with proper typing. If the server requests an empty object, this will be `None`. The original MCP elicitation parameters, including the raw JSON schema in `params.requestedSchema` Request context containing metadata about the elicitation request ## Response Actions You can return data directly, which implicitly accepts the elicitation: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async def elicitation_handler(message, response_type, params, context): user_input = input(f"{message}: ") return response_type(value=user_input) # Implicit accept ``` Or return an `ElicitResult` for explicit control over the action: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.client.elicitation import ElicitResult async def elicitation_handler(message, response_type, params, context): user_input = input(f"{message}: ") if not user_input: return ElicitResult(action="decline") # User declined if user_input == "cancel": return ElicitResult(action="cancel") # Cancel entire operation return ElicitResult( action="accept", content=response_type(value=user_input) ) ``` **Action types:** * **`accept`**: User provided valid input. Include the data in the `content` field. * **`decline`**: User chose not to provide the requested information. Omit `content`. * **`cancel`**: User cancelled the entire operation. Omit `content`. ## Example A file management tool might ask which directory to create: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.elicitation import ElicitResult async def elicitation_handler(message, response_type, params, context): print(f"Server asks: {message}") user_response = input("Your response: ") if not user_response: return ElicitResult(action="decline") # Use the response_type dataclass to create a properly structured response return response_type(value=user_response) client = Client( "my_mcp_server.py", elicitation_handler=elicitation_handler ) ``` # Server Logging Source: https://gofastmcp.com/clients/logging Receive and handle log messages from MCP servers. Use this when you need to capture or process log messages sent by the server. MCP servers can emit log messages to clients. The client handles these through a log handler callback. ## Log Handler Provide a `log_handler` function when creating the client: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} import logging from fastmcp import Client from fastmcp.client.logging import LogMessage logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) LOGGING_LEVEL_MAP = logging.getLevelNamesMapping() async def log_handler(message: LogMessage): """Forward MCP server logs to Python's logging system.""" msg = message.data.get('msg') extra = message.data.get('extra') level = LOGGING_LEVEL_MAP.get(message.level.upper(), logging.INFO) logger.log(level, msg, extra=extra) client = Client( "my_mcp_server.py", log_handler=log_handler, ) ``` The handler receives a `LogMessage` object: The log level The logger name (may be None) The log payload, containing `msg` and `extra` keys ## Structured Logs The `message.data` attribute is a dictionary containing the log payload. This enables structured logging with rich contextual information. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async def detailed_log_handler(message: LogMessage): msg = message.data.get('msg') extra = message.data.get('extra') if message.level == "error": print(f"ERROR: {msg} | Details: {extra}") elif message.level == "warning": print(f"WARNING: {msg} | Details: {extra}") else: print(f"{message.level.upper()}: {msg}") ``` This structure is preserved even when logs are forwarded through a FastMCP proxy, making it useful for debugging multi-server applications. ## Default Behavior If you do not provide a custom `log_handler`, FastMCP's default handler routes server logs to Python's logging system at the appropriate severity level. The MCP levels map as follows: `notice` becomes INFO; `alert` and `emergency` become CRITICAL. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} client = Client("my_mcp_server.py") async with client: # Server logs are forwarded at proper severity automatically await client.call_tool("some_tool") ``` # Notifications Source: https://gofastmcp.com/clients/notifications Handle server-sent notifications for list changes and other events. Use this when you need to react to server-side changes like tool list updates or resource modifications. MCP servers can send notifications to inform clients about state changes. The message handler provides a unified way to process these notifications. ## Handling Notifications The simplest approach is a function that receives all messages and filters for the notifications you care about: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client async def message_handler(message): """Handle MCP notifications from the server.""" if hasattr(message, 'root'): method = message.root.method if method == "notifications/tools/list_changed": print("Tools have changed - refresh tool cache") elif method == "notifications/resources/list_changed": print("Resources have changed") elif method == "notifications/prompts/list_changed": print("Prompts have changed") client = Client( "my_mcp_server.py", message_handler=message_handler, ) ``` ## MessageHandler Class For fine-grained targeting, subclass `MessageHandler` to use specific hooks: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.messages import MessageHandler import mcp.types class MyMessageHandler(MessageHandler): async def on_tool_list_changed( self, notification: mcp.types.ToolListChangedNotification ) -> None: """Handle tool list changes.""" print("Tool list changed - refreshing available tools") async def on_resource_list_changed( self, notification: mcp.types.ResourceListChangedNotification ) -> None: """Handle resource list changes.""" print("Resource list changed") async def on_prompt_list_changed( self, notification: mcp.types.PromptListChangedNotification ) -> None: """Handle prompt list changes.""" print("Prompt list changed") client = Client( "my_mcp_server.py", message_handler=MyMessageHandler(), ) ``` ### Handler Template ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.client.messages import MessageHandler import mcp.types class MyMessageHandler(MessageHandler): async def on_message(self, message) -> None: """Called for ALL messages (requests and notifications).""" pass async def on_notification( self, notification: mcp.types.ServerNotification ) -> None: """Called for notifications (fire-and-forget).""" pass async def on_tool_list_changed( self, notification: mcp.types.ToolListChangedNotification ) -> None: """Called when the server's tool list changes.""" pass async def on_resource_list_changed( self, notification: mcp.types.ResourceListChangedNotification ) -> None: """Called when the server's resource list changes.""" pass async def on_prompt_list_changed( self, notification: mcp.types.PromptListChangedNotification ) -> None: """Called when the server's prompt list changes.""" pass async def on_progress( self, notification: mcp.types.ProgressNotification ) -> None: """Called for progress updates during long-running operations.""" pass async def on_logging_message( self, notification: mcp.types.LoggingMessageNotification ) -> None: """Called for log messages from the server.""" pass ``` ## List Change Notifications A practical example of maintaining a tool cache that refreshes when tools change: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.messages import MessageHandler import mcp.types class ToolCacheHandler(MessageHandler): def __init__(self): self.cached_tools = [] async def on_tool_list_changed( self, notification: mcp.types.ToolListChangedNotification ) -> None: """Clear tool cache when tools change.""" print("Tools changed - clearing cache") self.cached_tools = [] # Force refresh on next access client = Client("server.py", message_handler=ToolCacheHandler()) ``` ## Server Requests While the message handler receives server-initiated requests, you should use dedicated callback parameters for most interactive scenarios: * **Sampling requests**: Use [`sampling_handler`](/clients/sampling) * **Elicitation requests**: Use [`elicitation_handler`](/clients/elicitation) * **Progress updates**: Use [`progress_handler`](/clients/progress) * **Log messages**: Use [`log_handler`](/clients/logging) The message handler is primarily for monitoring and handling notifications rather than responding to requests. # Progress Monitoring Source: https://gofastmcp.com/clients/progress Handle progress notifications from long-running server operations. Use this when you need to track progress of long-running operations. MCP servers can report progress during operations. The client receives these updates through a progress handler. ## Progress Handler Set a handler when creating the client: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client async def progress_handler( progress: float, total: float | None, message: str | None ) -> None: if total is not None: percentage = (progress / total) * 100 print(f"Progress: {percentage:.1f}% - {message or ''}") else: print(f"Progress: {progress} - {message or ''}") client = Client( "my_mcp_server.py", progress_handler=progress_handler ) ``` The handler receives three parameters: Current progress value Expected total value (may be None if unknown) Optional status message ## Per-Call Handler Override the client-level handler for specific tool calls: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.call_tool( "long_running_task", {"param": "value"}, progress_handler=my_progress_handler ) ``` # Getting Prompts Source: https://gofastmcp.com/clients/prompts Retrieve rendered message templates with automatic argument serialization. Use this when you need to retrieve server-defined message templates for LLM interactions. Prompts are reusable message templates exposed by MCP servers. They can accept arguments to generate personalized message sequences for LLM interactions. ## Basic Usage Request a rendered prompt with `get_prompt()`: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: # Simple prompt without arguments result = await client.get_prompt("welcome_message") # result -> mcp.types.GetPromptResult # Access the generated messages for message in result.messages: print(f"Role: {message.role}") print(f"Content: {message.content}") ``` Pass arguments to customize the prompt: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.get_prompt("user_greeting", { "name": "Alice", "role": "administrator" }) for message in result.messages: print(f"Generated message: {message.content}") ``` ## Argument Serialization FastMCP automatically serializes complex arguments to JSON strings as required by the MCP specification. You can pass typed objects directly: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from dataclasses import dataclass @dataclass class UserData: name: str age: int async with client: result = await client.get_prompt("analyze_user", { "user": UserData(name="Alice", age=30), # Automatically serialized "preferences": {"theme": "dark"}, # Dict serialized "scores": [85, 92, 78], # List serialized "simple_name": "Bob" # Strings unchanged }) ``` The client handles serialization using `pydantic_core.to_json()` for consistent formatting. FastMCP servers automatically deserialize these JSON strings back to the expected types. ## Working with Results The `get_prompt()` method returns a `GetPromptResult` containing a list of messages: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.get_prompt("conversation_starter", {"topic": "climate"}) for i, message in enumerate(result.messages): print(f"Message {i + 1}:") print(f" Role: {message.role}") print(f" Content: {message.content.text if hasattr(message.content, 'text') else message.content}") ``` Prompts can generate different message types. System messages configure LLM behavior: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.get_prompt("system_configuration", { "role": "helpful assistant", "expertise": "python programming" }) # Access the returned messages message = result.messages[0] print(f"Prompt: {message.content}") ``` Conversation templates generate multi-turn flows: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.get_prompt("interview_template", { "candidate_name": "Alice", "position": "Senior Developer" }) # Multiple messages for a conversation flow for message in result.messages: print(f"{message.role}: {message.content}") ``` ## Version Selection When a server exposes multiple versions of a prompt, you can request a specific version: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: # Get the highest version (default) result = await client.get_prompt("summarize", {"text": "..."}) # Get a specific version result_v1 = await client.get_prompt("summarize", {"text": "..."}, version="1.0") ``` See [Metadata](/servers/versioning#version-discovery) for how to discover available versions. ## Multi-Server Clients When using multi-server clients, prompts are accessible directly without prefixing: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: # Multi-server client result1 = await client.get_prompt("weather_prompt", {"city": "London"}) result2 = await client.get_prompt("assistant_prompt", {"query": "help"}) ``` ## Raw Protocol Access For complete control, use `get_prompt_mcp()` which returns the full MCP protocol object: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.get_prompt_mcp("example_prompt", {"arg": "value"}) # result -> mcp.types.GetPromptResult ``` # Reading Resources Source: https://gofastmcp.com/clients/resources Access static and templated data sources from MCP servers. Use this when you need to read data from server-exposed resources like configuration files, generated content, or external data sources. Resources are data sources exposed by MCP servers. They can be static files with fixed content, or dynamic templates that generate content based on parameters in the URI. ## Reading Resources Read a resource using its URI: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: content = await client.read_resource("file:///path/to/README.md") # content -> list[TextResourceContents | BlobResourceContents] # Access text content if hasattr(content[0], 'text'): print(content[0].text) # Access binary content if hasattr(content[0], 'blob'): print(f"Binary data: {len(content[0].blob)} bytes") ``` Resource templates generate content based on URI parameters. The template defines a pattern like `weather://{{city}}/current`, and you fill in the parameters when reading: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: # Read from a resource template weather_content = await client.read_resource("weather://london/current") print(weather_content[0].text) ``` ## Content Types Resources return different content types depending on what they expose. Text resources include configuration files, JSON data, and other human-readable content: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: content = await client.read_resource("resource://config/settings.json") for item in content: if hasattr(item, 'text'): print(f"Text content: {item.text}") print(f"MIME type: {item.mimeType}") ``` Binary resources include images, PDFs, and other non-text data: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: content = await client.read_resource("resource://images/logo.png") for item in content: if hasattr(item, 'blob'): print(f"Binary content: {len(item.blob)} bytes") print(f"MIME type: {item.mimeType}") # Save to file with open("downloaded_logo.png", "wb") as f: f.write(item.blob) ``` ## Multi-Server Clients When using multi-server clients, resource URIs are prefixed with the server name: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: # Multi-server client weather_icons = await client.read_resource("weather://weather/icons/sunny") templates = await client.read_resource("resource://assistant/templates/list") ``` ## Version Selection When a server exposes multiple versions of a resource, you can request a specific version: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: # Read the highest version (default) content = await client.read_resource("data://config") # Read a specific version content_v1 = await client.read_resource("data://config", version="1.0") ``` See [Metadata](/servers/versioning#version-discovery) for how to discover available versions. ## Raw Protocol Access For complete control, use `read_resource_mcp()` which returns the full MCP protocol object: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.read_resource_mcp("resource://example") # result -> mcp.types.ReadResourceResult ``` # Client Roots Source: https://gofastmcp.com/clients/roots Provide local context and resource boundaries to MCP servers. Use this when you need to tell servers what local resources the client has access to. Roots inform servers about resources the client can provide. Servers can use this information to adjust behavior or provide more relevant responses. ## Static Roots Provide a list of roots when creating the client: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client client = Client( "my_mcp_server.py", roots=["/path/to/root1", "/path/to/root2"] ) ``` ## Dynamic Roots Use a callback to compute roots dynamically when the server requests them: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.roots import RequestContext async def roots_callback(context: RequestContext) -> list[str]: print(f"Server requested roots (Request ID: {context.request_id})") return ["/path/to/root1", "/path/to/root2"] client = Client( "my_mcp_server.py", roots=roots_callback ) ``` # LLM Sampling Source: https://gofastmcp.com/clients/sampling Handle server-initiated LLM completion requests. Use this when you need to respond to server requests for LLM completions. MCP servers can request LLM completions from clients during tool execution. This enables servers to delegate AI reasoning to the client, which controls which LLM is used and how requests are made. ## Handler Template ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext async def sampling_handler( messages: list[SamplingMessage], params: SamplingParams, context: RequestContext ) -> str: """ Handle server requests for LLM completions. Args: messages: Conversation messages to send to the LLM params: Sampling parameters (temperature, max_tokens, etc.) context: Request context with metadata Returns: Generated text response from your LLM """ # Extract message content conversation = [] for message in messages: content = message.content.text if hasattr(message.content, 'text') else str(message.content) conversation.append(f"{message.role}: {content}") # Use the system prompt if provided system_prompt = params.systemPrompt or "You are a helpful assistant." # Integrate with your LLM service here return "Generated response based on the messages" client = Client( "my_mcp_server.py", sampling_handler=sampling_handler, ) ``` ## Handler Parameters The role of the message The content of the message. TextContent has a `.text` attribute. Optional system prompt the server wants to use Server preferences for model selection (hints, cost/speed/intelligence priorities) Sampling temperature Maximum tokens to generate Stop sequences for sampling Tools the LLM can use during sampling Tool usage behavior (`auto`, `required`, or `none`) ## Built-in Handlers FastMCP provides built-in handlers for OpenAI, Anthropic, and Google Gemini APIs that support the full sampling API including tool use. ### OpenAI Handler ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.sampling.handlers.openai import OpenAISamplingHandler client = Client( "my_mcp_server.py", sampling_handler=OpenAISamplingHandler(default_model="gpt-4o"), ) ``` For OpenAI-compatible APIs (like local models): ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from openai import AsyncOpenAI client = Client( "my_mcp_server.py", sampling_handler=OpenAISamplingHandler( default_model="llama-3.1-70b", client=AsyncOpenAI(base_url="http://localhost:8000/v1"), ), ) ``` Install the OpenAI handler with `pip install fastmcp[openai]`. ### Anthropic Handler ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.sampling.handlers.anthropic import AnthropicSamplingHandler client = Client( "my_mcp_server.py", sampling_handler=AnthropicSamplingHandler(default_model="claude-sonnet-4-5"), ) ``` Install the Anthropic handler with `pip install fastmcp[anthropic]`. ### Google Gemini Handler ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.sampling.handlers.google_genai import GoogleGenAISamplingHandler client = Client( "my_mcp_server.py", sampling_handler=GoogleGenAISamplingHandler(default_model="gemini-2.0-flash"), ) ``` Install the Google Gemini handler with `pip install fastmcp[gemini]`. ## Sampling Capabilities When you provide a `sampling_handler`, FastMCP automatically advertises full sampling capabilities to the server, including tool support. To disable tool support for simpler handlers: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from mcp.types import SamplingCapability client = Client( "my_mcp_server.py", sampling_handler=basic_handler, sampling_capabilities=SamplingCapability(), # No tool support ) ``` ## Tool Execution Tool execution happens on the server side. The client's role is to pass tools to the LLM and return the LLM's response (which may include tool use requests). The server then executes the tools and may send follow-up sampling requests with tool results. To implement a custom sampling handler, see the [handler source code](https://github.com/PrefectHQ/fastmcp/tree/main/src/fastmcp/client/sampling/handlers) as a reference. # Background Tasks Source: https://gofastmcp.com/clients/tasks Execute operations asynchronously and track their progress. Use this when you need to run long operations asynchronously while doing other work. The MCP task protocol lets you request operations to run in the background. The call returns a Task object immediately, letting you track progress, cancel operations, or await results. ## Requesting Background Execution Pass `task=True` to run an operation as a background task: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client async with Client(server) as client: # Start a background task task = await client.call_tool("slow_computation", {"duration": 10}, task=True) print(f"Task started: {task.task_id}") # Do other work while it runs... # Get the result when ready result = await task.result() ``` This works with tools, resources, and prompts: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} tool_task = await client.call_tool("my_tool", args, task=True) resource_task = await client.read_resource("file://large.txt", task=True) prompt_task = await client.get_prompt("my_prompt", args, task=True) ``` ## Task API All task types share a common interface. ### Getting Results Call `await task.result()` or simply `await task` to block until the task completes: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} task = await client.call_tool("analyze", {"text": "hello"}, task=True) # Wait for result (blocking) result = await task.result() # or: result = await task ``` ### Checking Status Check the current status without blocking: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} status = await task.status() print(f"{status.status}: {status.statusMessage}") # status.status is "working", "completed", "failed", or "cancelled" ``` ### Waiting with Control Use `task.wait()` for more control over waiting: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Wait up to 30 seconds for completion status = await task.wait(timeout=30.0) # Wait for a specific state status = await task.wait(state="completed", timeout=30.0) ``` ### Cancellation Cancel a running task: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} await task.cancel() ``` ## Status Updates Register callbacks to receive real-time status updates as the server reports progress: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} def on_status_change(status): print(f"Task {status.taskId}: {status.status} - {status.statusMessage}") task.on_status_change(on_status_change) # Async callbacks work too async def on_status_async(status): await log_status(status) task.on_status_change(on_status_async) ``` ### Handler Template ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client def status_handler(status): """ Handle task status updates. Args: status: Task status object with: - taskId: Unique task identifier - status: "working", "completed", "failed", or "cancelled" - statusMessage: Optional progress message from server """ if status.status == "working": print(f"Progress: {status.statusMessage}") elif status.status == "completed": print("Task completed") elif status.status == "failed": print(f"Task failed: {status.statusMessage}") task.on_status_change(status_handler) ``` ## Graceful Degradation You can always pass `task=True` regardless of whether the server supports background tasks. Per the MCP specification, servers without task support execute the operation immediately and return the result inline. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} task = await client.call_tool("my_tool", args, task=True) if task.returned_immediately: print("Server executed immediately (no background support)") else: print("Running in background") # Either way, this works result = await task.result() ``` This lets you write task-aware client code without worrying about server capabilities. ## Example ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} import asyncio from fastmcp import Client async def main(): async with Client(server) as client: # Start background task task = await client.call_tool( "slow_computation", {"duration": 10}, task=True, ) # Subscribe to updates def on_update(status): print(f"Progress: {status.statusMessage}") task.on_status_change(on_update) # Do other work while task runs print("Doing other work...") await asyncio.sleep(2) # Wait for completion and get result result = await task.result() print(f"Result: {result.content}") asyncio.run(main()) ``` See [Server Background Tasks](/servers/tasks) for how to enable background task support on the server side. # Calling Tools Source: https://gofastmcp.com/clients/tools Execute server-side tools and handle structured results. Use this when you need to execute server-side functions and process their results. Tools are executable functions exposed by MCP servers. The client's `call_tool()` method executes a tool by name with arguments and returns structured results. ## Basic Execution ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.call_tool("add", {"a": 5, "b": 3}) # result -> CallToolResult with structured and unstructured data # Access structured data (automatically deserialized) print(result.data) # 8 # Access traditional content blocks print(result.content[0].text) # "8" ``` Arguments are passed as a dictionary. For multi-server clients, tool names are automatically prefixed with the server name (e.g., `weather_get_forecast` for a tool named `get_forecast` on the `weather` server). ## Execution Options The `call_tool()` method supports timeout control and progress monitoring: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: # With timeout (aborts if execution takes longer than 2 seconds) result = await client.call_tool( "long_running_task", {"param": "value"}, timeout=2.0 ) # With progress handler result = await client.call_tool( "long_running_task", {"param": "value"}, progress_handler=my_progress_handler ) ``` ## Structured Results Tool execution returns a `CallToolResult` object. The `.data` property provides fully hydrated Python objects including complex types like datetimes and UUIDs, reconstructed from the server's output schema. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from datetime import datetime from uuid import UUID async with client: result = await client.call_tool("get_weather", {"city": "London"}) # FastMCP reconstructs complete Python objects weather = result.data print(f"Temperature: {weather.temperature}C at {weather.timestamp}") # Complex types are properly deserialized assert isinstance(weather.timestamp, datetime) assert isinstance(weather.station_id, UUID) # Raw structured JSON is also available print(f"Raw JSON: {result.structured_content}") ``` Fully hydrated Python objects with complex type support (datetimes, UUIDs, custom classes). FastMCP exclusive. Standard MCP content blocks (`TextContent`, `ImageContent`, `AudioContent`, etc.). Standard MCP structured JSON data as sent by the server. Boolean indicating if the tool execution failed. For tools without output schemas or when deserialization fails, `.data` will be `None`. Fall back to content blocks in that case: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.call_tool("legacy_tool", {"param": "value"}) if result.data is not None: print(f"Structured: {result.data}") else: for content in result.content: if hasattr(content, 'text'): print(f"Text result: {content.text}") ``` FastMCP servers automatically wrap primitive results (like `int`, `str`, `bool`) in a `{"result": value}` structure. FastMCP clients automatically unwrap this, so you get the original value in `.data`. ## Error Handling By default, `call_tool()` raises a `ToolError` if the tool execution fails: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.exceptions import ToolError async with client: try: result = await client.call_tool("potentially_failing_tool", {"param": "value"}) print("Tool succeeded:", result.data) except ToolError as e: print(f"Tool failed: {e}") ``` To handle errors manually instead of catching exceptions, disable automatic error raising: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.call_tool( "potentially_failing_tool", {"param": "value"}, raise_on_error=False ) if result.is_error: print(f"Tool failed: {result.content[0].text}") else: print(f"Tool succeeded: {result.data}") ``` ## Sending Metadata The `meta` parameter sends ancillary information alongside tool calls for observability, debugging, or client identification: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.call_tool( name="send_email", arguments={ "to": "user@example.com", "subject": "Hello", "body": "Welcome!" }, meta={ "trace_id": "abc-123", "request_source": "mobile_app" } ) ``` See [Client Metadata](/servers/context#client-metadata) to learn how servers access this data. ## Raw Protocol Access For complete control, use `call_tool_mcp()` which returns the raw MCP protocol object: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} async with client: result = await client.call_tool_mcp("my_tool", {"param": "value"}) # result -> mcp.types.CallToolResult if result.isError: print(f"Tool failed: {result.content}") else: print(f"Tool succeeded: {result.content}") # Note: No automatic deserialization with call_tool_mcp() ``` # Client Transports Source: https://gofastmcp.com/clients/transports Configure how clients connect to and communicate with MCP servers. Transports handle the underlying connection between your client and MCP servers. While the client can automatically select a transport based on what you pass to it, instantiating transports explicitly gives you full control over configuration. ## STDIO Transport STDIO transport communicates with MCP servers through subprocess pipes. When using STDIO, your client launches and manages the server process, controlling its lifecycle and environment. STDIO servers run in isolated environments by default. They do not inherit your shell's environment variables. You must explicitly pass any configuration the server needs. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.transports import StdioTransport transport = StdioTransport( command="python", args=["my_server.py", "--verbose"], env={"API_KEY": "secret", "LOG_LEVEL": "DEBUG"}, cwd="/path/to/server" ) client = Client(transport) ``` For convenience, the client can infer STDIO transport from file paths, though this limits configuration options: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client client = Client("my_server.py") # Limited - no configuration options ``` ### Environment Variables Since STDIO servers do not inherit your environment, you need strategies for passing configuration. **Selective forwarding** passes only the variables your server needs: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} import os from fastmcp.client.transports import StdioTransport required_vars = ["API_KEY", "DATABASE_URL", "REDIS_HOST"] env = {var: os.environ[var] for var in required_vars if var in os.environ} transport = StdioTransport(command="python", args=["server.py"], env=env) client = Client(transport) ``` **Loading from .env files** keeps configuration separate from code: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from dotenv import dotenv_values from fastmcp.client.transports import StdioTransport env = dotenv_values(".env") transport = StdioTransport(command="python", args=["server.py"], env=env) client = Client(transport) ``` ### Session Persistence STDIO transports maintain sessions across multiple client contexts by default (`keep_alive=True`). This reuses the same subprocess for multiple connections, improving performance. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.client.transports import StdioTransport transport = StdioTransport(command="python", args=["server.py"]) client = Client(transport) async def efficient_multiple_operations(): async with client: await client.ping() async with client: # Reuses the same subprocess await client.call_tool("process_data", {"file": "data.csv"}) ``` For complete isolation between connections, disable session persistence: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} transport = StdioTransport(command="python", args=["server.py"], keep_alive=False) ``` ## HTTP Transport HTTP transport connects to MCP servers running as web services. This is the recommended transport for production deployments. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.transports import StreamableHttpTransport transport = StreamableHttpTransport( url="https://api.example.com/mcp", headers={ "Authorization": "Bearer your-token-here", "X-Custom-Header": "value" } ) client = Client(transport) ``` FastMCP also provides authentication helpers: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client from fastmcp.client.auth import BearerAuth client = Client( "https://api.example.com/mcp", auth=BearerAuth("your-token-here") ) ``` ### SSE Transport Server-Sent Events transport is maintained for backward compatibility. Use Streamable HTTP for new deployments unless you have specific infrastructure requirements. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.client.transports import SSETransport transport = SSETransport( url="https://api.example.com/sse", headers={"Authorization": "Bearer token"} ) client = Client(transport) ``` ## In-Memory Transport In-memory transport connects directly to a FastMCP server instance within the same Python process. This eliminates both subprocess management and network overhead, making it ideal for testing. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP, Client import os mcp = FastMCP("TestServer") @mcp.tool def greet(name: str) -> str: prefix = os.environ.get("GREETING_PREFIX", "Hello") return f"{prefix}, {name}!" client = Client(mcp) async with client: result = await client.call_tool("greet", {"name": "World"}) ``` Unlike STDIO transports, in-memory servers share the same memory space and environment variables as your client code. ## Multi-Server Configuration Connect to multiple servers defined in a configuration dictionary: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import Client config = { "mcpServers": { "weather": { "url": "https://weather.example.com/mcp", "transport": "http" }, "assistant": { "command": "python", "args": ["./assistant.py"], "env": {"LOG_LEVEL": "INFO"} } } } client = Client(config) async with client: # Tools are namespaced by server weather = await client.call_tool("weather_get_forecast", {"city": "NYC"}) answer = await client.call_tool("assistant_ask", {"question": "What?"}) ``` ### Tool Transformations FastMCP supports tool transformations within the configuration. You can change names, descriptions, tags, and arguments for tools from a server. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} config = { "mcpServers": { "weather": { "url": "https://weather.example.com/mcp", "transport": "http", "tools": { "weather_get_forecast": { "name": "miami_weather", "description": "Get the weather for Miami", "arguments": { "city": { "default": "Miami", "hide": True, } } } } } } } ``` To filter tools by tag, use `include_tags` or `exclude_tags` at the server level: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} config = { "mcpServers": { "weather": { "url": "https://weather.example.com/mcp", "include_tags": ["forecast"] # Only tools with this tag } } } ``` # HTTP Deployment Source: https://gofastmcp.com/deployment/http Deploy your FastMCP server over HTTP for remote access STDIO transport is perfect for local development and desktop applications. But to unlock the full potential of MCP—centralized services, multi-client access, and network availability—you need remote HTTP deployment. This guide walks you through deploying your FastMCP server as a remote MCP service that's accessible via a URL. Once deployed, your MCP server will be available over the network, allowing multiple clients to connect simultaneously and enabling integration with cloud-based LLM applications. This guide focuses specifically on remote MCP deployment, not local STDIO servers. ## Choosing Your Approach FastMCP provides two ways to deploy your server as an HTTP service. Understanding the trade-offs helps you choose the right approach for your needs. The **direct HTTP server** approach is simpler and perfect for getting started quickly. You modify your server's `run()` method to use HTTP transport, and FastMCP handles all the web server configuration. This approach works well for standalone deployments where you want your MCP server to be the only service running on a port. The **ASGI application** approach gives you more control and flexibility. Instead of running the server directly, you create an ASGI application that can be served by Uvicorn. This approach is better when you need advanced server features like multiple workers, custom middleware, or when you're integrating with existing web applications. ### Direct HTTP Server The simplest way to get your MCP server online is to use the built-in `run()` method with HTTP transport. This approach handles all the server configuration for you and is ideal when you want a standalone MCP server without additional complexity. ```python server.py theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("My Server") @mcp.tool def process_data(input: str) -> str: """Process data on the server""" return f"Processed: {input}" if __name__ == "__main__": mcp.run(transport="http", host="0.0.0.0", port=8000) ``` Run your server with a simple Python command: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} python server.py ``` Your server is now accessible at `http://localhost:8000/mcp` (or use your server's actual IP address for remote access). This approach is ideal when you want to get online quickly with minimal configuration. It's perfect for internal tools, development environments, or simple deployments where you don't need advanced server features. The built-in server handles all the HTTP details, letting you focus on your MCP implementation. ### ASGI Application For production deployments, you'll often want more control over how your server runs. FastMCP can create a standard ASGI application that works with any ASGI server like Uvicorn, Gunicorn, or Hypercorn. This approach is particularly useful when you need to configure advanced server options, run multiple workers, or integrate with existing infrastructure. ```python app.py theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("My Server") @mcp.tool def process_data(input: str) -> str: """Process data on the server""" return f"Processed: {input}" # Create ASGI application app = mcp.http_app() ``` Run with any ASGI server - here's an example with Uvicorn: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} uvicorn app:app --host 0.0.0.0 --port 8000 ``` Your server is accessible at the same URL: `http://localhost:8000/mcp` (or use your server's actual IP address for remote access). The ASGI approach shines in production environments where you need reliability and performance. You can run multiple worker processes to handle concurrent requests, add custom middleware for logging or monitoring, integrate with existing deployment pipelines, or mount your MCP server as part of a larger application. ## Configuring Your Server ### Custom Path By default, your MCP server is accessible at `/mcp/` on your domain. You can customize this path to fit your URL structure or avoid conflicts with existing endpoints. This is particularly useful when integrating MCP into an existing application or following specific API conventions. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Option 1: With mcp.run() mcp.run(transport="http", host="0.0.0.0", port=8000, path="/api/mcp/") # Option 2: With ASGI app app = mcp.http_app(path="/api/mcp/") ``` Now your server is accessible at `http://localhost:8000/api/mcp/`. ### Authentication Authentication is **highly recommended** for remote MCP servers. Some LLM clients require authentication for remote servers and will refuse to connect without it. FastMCP supports multiple authentication methods to secure your remote server. See the [Authentication Overview](/servers/auth/authentication) for complete configuration options including Bearer tokens, JWT, and OAuth. If you're mounting an authenticated server under a path prefix, see [Mounting Authenticated Servers](#mounting-authenticated-servers) below for important routing considerations. ### Health Checks Health check endpoints are essential for monitoring your deployed server and ensuring it's responding correctly. FastMCP allows you to add custom routes alongside your MCP endpoints, making it easy to implement health checks that work with both deployment approaches. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from starlette.responses import JSONResponse @mcp.custom_route("/health", methods=["GET"]) async def health_check(request): return JSONResponse({"status": "healthy", "service": "mcp-server"}) ``` This health endpoint will be available at `http://localhost:8000/health` and can be used by load balancers, monitoring systems, or deployment platforms to verify your server is running. ### Custom Middleware Add custom Starlette middleware to your FastMCP ASGI apps: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP from starlette.middleware import Middleware from starlette.middleware.cors import CORSMiddleware # Create your FastMCP server mcp = FastMCP("MyServer") # Define middleware middleware = [ Middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) ] # Create ASGI app with middleware http_app = mcp.http_app(middleware=middleware) ``` ### CORS for Browser-Based Clients Most MCP clients, including those that you access through a browser like ChatGPT or Claude, don't need CORS configuration. Only enable CORS if you're working with an MCP client that connects directly from a browser, such as debugging tools or inspectors. CORS (Cross-Origin Resource Sharing) is needed when JavaScript running in a web browser connects directly to your MCP server. This is different from using an LLM through a browser—in that case, the browser connects to the LLM service, and the LLM service connects to your MCP server (no CORS needed). Browser-based MCP clients that need CORS include: * **MCP Inspector** - Browser-based debugging tool for testing MCP servers * **Custom browser-based MCP clients** - If you're building a web app that directly connects to MCP servers For these scenarios, add CORS middleware with the specific headers required for MCP protocol: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP from starlette.middleware import Middleware from starlette.middleware.cors import CORSMiddleware mcp = FastMCP("MyServer") # Configure CORS for browser-based clients middleware = [ Middleware( CORSMiddleware, allow_origins=["*"], # Allow all origins; use specific origins for security allow_methods=["GET", "POST", "DELETE", "OPTIONS"], allow_headers=[ "mcp-protocol-version", "mcp-session-id", "Authorization", "Content-Type", ], expose_headers=["mcp-session-id"], ) ] app = mcp.http_app(middleware=middleware) ``` **Key configuration details:** * **`allow_origins`**: Specify exact origins (e.g., `["http://localhost:3000"]`) rather than `["*"]` for production deployments * **`allow_headers`**: Must include `mcp-protocol-version`, `mcp-session-id`, and `Authorization` (for authenticated servers) * **`expose_headers`**: Must include `mcp-session-id` so JavaScript can read the session ID from responses and send it in subsequent requests Without `expose_headers=["mcp-session-id"]`, browsers will receive the session ID but JavaScript won't be able to access it, causing session management to fail. **Production Security**: Never use `allow_origins=["*"]` in production. Specify the exact origins of your browser-based clients. Using wildcards exposes your server to unauthorized access from any website. ### SSE Polling for Long-Running Operations This feature only applies to the **StreamableHTTP transport** (the default for `http_app()`). It does not apply to the legacy SSE transport (`transport="sse"`). When running tools that take a long time to complete, you may encounter issues with load balancers or proxies terminating connections that stay idle too long. [SEP-1699](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699) introduces SSE polling to solve this by allowing the server to gracefully close connections and have clients automatically reconnect. To enable SSE polling, configure an `EventStore` when creating your HTTP application: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP, Context from fastmcp.server.event_store import EventStore mcp = FastMCP("My Server") @mcp.tool async def long_running_task(ctx: Context) -> str: """A task that takes several minutes to complete.""" for i in range(100): await ctx.report_progress(i, 100) # Periodically close the connection to avoid load balancer timeouts # Client will automatically reconnect and resume receiving progress if i % 30 == 0 and i > 0: await ctx.close_sse_stream() await do_expensive_work() return "Done!" # Configure with EventStore for resumability event_store = EventStore() app = mcp.http_app( event_store=event_store, retry_interval=2000, # Client reconnects after 2 seconds ) ``` **How it works:** 1. When `event_store` is configured, the server stores all events (progress updates, results) with unique IDs 2. Calling `ctx.close_sse_stream()` gracefully closes the HTTP connection 3. The client automatically reconnects with a `Last-Event-ID` header 4. The server replays any events the client missed during the disconnection The `retry_interval` parameter (in milliseconds) controls how long clients wait before reconnecting. Choose a value that balances responsiveness with server load. `close_sse_stream()` is a no-op if called without an `EventStore` configured, so you can safely include it in tools that may run in different deployment configurations. #### Custom Storage Backends By default, `EventStore` uses in-memory storage. For production deployments with multiple server instances, you can provide a custom storage backend using the `key_value` package: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp.server.event_store import EventStore from key_value.aio.stores.redis import RedisStore # Use Redis for distributed deployments redis_store = RedisStore(url="redis://localhost:6379") event_store = EventStore( storage=redis_store, max_events_per_stream=100, # Keep last 100 events per stream ttl=3600, # Events expire after 1 hour ) app = mcp.http_app(event_store=event_store) ``` ## Integration with Web Frameworks If you already have a web application running, you can add MCP capabilities by mounting a FastMCP server as a sub-application. This allows you to expose MCP tools alongside your existing API endpoints, sharing the same domain and infrastructure. The MCP server becomes just another route in your application, making it easy to manage and deploy. ### Mounting in Starlette Mount your FastMCP server in a Starlette application: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP from starlette.applications import Starlette from starlette.routing import Mount # Create your FastMCP server mcp = FastMCP("MyServer") @mcp.tool def analyze(data: str) -> dict: return {"result": f"Analyzed: {data}"} # Create the ASGI app mcp_app = mcp.http_app(path='/mcp') # Create a Starlette app and mount the MCP server app = Starlette( routes=[ Mount("/mcp-server", app=mcp_app), # Add other routes as needed ], lifespan=mcp_app.lifespan, ) ``` The MCP endpoint will be available at `/mcp-server/mcp/` of the resulting Starlette app. For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized. #### Nested Mounts You can create complex routing structures by nesting mounts: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP from starlette.applications import Starlette from starlette.routing import Mount # Create your FastMCP server mcp = FastMCP("MyServer") # Create the ASGI app mcp_app = mcp.http_app(path='/mcp') # Create nested application structure inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)]) app = Starlette( routes=[Mount("/outer", app=inner_app)], lifespan=mcp_app.lifespan, ) ``` In this setup, the MCP server is accessible at the `/outer/inner/mcp/` path. ### FastAPI Integration For FastAPI-specific integration patterns including both mounting MCP servers into FastAPI apps and generating MCP servers from FastAPI apps, see the [FastAPI Integration guide](/integrations/fastapi). Here's a quick example showing how to add MCP to an existing FastAPI application: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastapi import FastAPI from fastmcp import FastMCP # Create your MCP server mcp = FastMCP("API Tools") @mcp.tool def query_database(query: str) -> dict: """Run a database query""" return {"result": "data"} # Create the MCP ASGI app with path="/" since we'll mount at /mcp mcp_app = mcp.http_app(path="/") # Create FastAPI app with MCP lifespan (required for session management) api = FastAPI(lifespan=mcp_app.lifespan) @api.get("/api/status") def status(): return {"status": "ok"} # Mount MCP at /mcp api.mount("/mcp", mcp_app) # Run with: uvicorn app:api --host 0.0.0.0 --port 8000 ``` Your existing API remains at `http://localhost:8000/api` while MCP is available at `http://localhost:8000/mcp`. Just like with Starlette, you **must** pass the lifespan from the MCP app to FastAPI. Without this, the session manager won't initialize properly and requests will fail. ## Mounting Authenticated Servers This section only applies if you're **mounting an OAuth-protected FastMCP server under a path prefix** (like `/api`) inside another application using `Mount()`. If you're deploying your FastMCP server at root level without any `Mount()` prefix, the well-known routes are automatically included in `mcp.http_app()` and you don't need to do anything special. OAuth specifications (RFC 8414 and RFC 9728) require discovery metadata to be accessible at well-known paths under the root level of your domain. When you mount an OAuth-protected FastMCP server under a path prefix like `/api`, this creates a routing challenge: your operational OAuth endpoints move under the prefix, but discovery endpoints must remain at the root. **Common Mistakes to Avoid:** 1. **Forgetting to mount `.well-known` routes at root** - FastMCP cannot do this automatically when your server is mounted under a path prefix. You must explicitly mount well-known routes at the root level. 2. **Including mount prefix in both base\_url AND mcp\_path** - The mount prefix (like `/api`) should only be in `base_url`, not in `mcp_path`. Otherwise you'll get double paths. ✅ **Correct:** ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} base_url = "http://localhost:8000/api" mcp_path = "/mcp" # Result: /api/mcp ``` ❌ **Wrong:** ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} base_url = "http://localhost:8000/api" mcp_path = "/api/mcp" # Result: /api/api/mcp (double prefix!) ``` Follow the configuration instructions below to set up mounting correctly. **CORS Middleware Conflicts:** If you're integrating FastMCP into an existing application with its own CORS middleware, be aware that layering CORS middleware can cause conflicts (such as 404 errors on `.well-known` routes or OPTIONS requests). FastMCP and the MCP SDK already handle CORS for OAuth routes. If you need CORS on your own application routes, consider using the sub-app pattern: mount FastMCP and your routes as separate apps, each with their own middleware, rather than adding application-wide CORS middleware. ### Route Types OAuth-protected MCP servers expose two categories of routes: **Operational routes** handle the OAuth flow and MCP protocol: * `/authorize` - OAuth authorization endpoint * `/token` - Token exchange endpoint * `/auth/callback` - OAuth callback handler * `/mcp` - MCP protocol endpoint **Discovery routes** provide metadata for OAuth clients: * `/.well-known/oauth-authorization-server` - Authorization server metadata * `/.well-known/oauth-protected-resource/*` - Protected resource metadata When you mount your MCP app under a prefix, operational routes move with it, but discovery routes must stay at root level for RFC compliance. ### Configuration Parameters Three parameters control where routes are located and how they combine: **`base_url`** tells clients where to find operational endpoints. This includes any Starlette `Mount()` path prefix (e.g., `/api`): ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} base_url="http://localhost:8000/api" # Includes mount prefix ``` **`mcp_path`** is the internal FastMCP endpoint path, which gets appended to `base_url`: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} mcp_path="/mcp" # Internal MCP path, NOT the mount prefix ``` **`issuer_url`** (optional) controls the authorization server identity for OAuth discovery. Defaults to `base_url`. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Usually not needed - just set base_url and it works issuer_url="http://localhost:8000" # Only if you want root-level discovery ``` When `issuer_url` has a path (either explicitly or by defaulting from `base_url`), FastMCP creates path-aware discovery routes per RFC 8414. For example, if `base_url` is `http://localhost:8000/api`, the authorization server metadata will be at `/.well-known/oauth-authorization-server/api`. **Key Invariant:** `base_url + mcp_path = actual externally-accessible MCP URL` Example: * `base_url`: `http://localhost:8000/api` (mount prefix `/api`) * `mcp_path`: `/mcp` (internal path) * Result: `http://localhost:8000/api/mcp` (final MCP endpoint) Note that the mount prefix (`/api` from `Mount("/api", ...)`) goes in `base_url`, while `mcp_path` is just the internal MCP route. Don't include the mount prefix in both places or you'll get `/api/api/mcp`. ### Mounting Strategy When mounting an OAuth-protected server under a path prefix, declare your URLs upfront to make the relationships clear: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP from fastmcp.server.auth.providers.github import GitHubProvider from starlette.applications import Starlette from starlette.routing import Mount # Define the routing structure ROOT_URL = "http://localhost:8000" MOUNT_PREFIX = "/api" MCP_PATH = "/mcp" ``` Create the auth provider with `base_url`: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} auth = GitHubProvider( client_id="your-client-id", client_secret="your-client-secret", base_url=f"{ROOT_URL}{MOUNT_PREFIX}", # Operational endpoints under prefix # issuer_url defaults to base_url - path-aware discovery works automatically ) ``` Create the MCP app, which generates operational routes at the specified path: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} mcp = FastMCP("Protected Server", auth=auth) mcp_app = mcp.http_app(path=MCP_PATH) ``` Retrieve the discovery routes from the auth provider. The `mcp_path` argument should match the path used when creating the MCP app: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} well_known_routes = auth.get_well_known_routes(mcp_path=MCP_PATH) ``` Finally, mount everything in the Starlette app with discovery routes at root and the MCP app under the prefix: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} app = Starlette( routes=[ *well_known_routes, # Discovery routes at root level Mount(MOUNT_PREFIX, app=mcp_app), # Operational routes under prefix ], lifespan=mcp_app.lifespan, ) ``` This configuration produces the following URL structure: * MCP endpoint: `http://localhost:8000/api/mcp` * OAuth authorization: `http://localhost:8000/api/authorize` * OAuth callback: `http://localhost:8000/api/auth/callback` * Authorization server metadata: `http://localhost:8000/.well-known/oauth-authorization-server/api` * Protected resource metadata: `http://localhost:8000/.well-known/oauth-protected-resource/api/mcp` Both discovery endpoints use path-aware URLs per RFC 8414 and RFC 9728, matching the `base_url` path. ### Complete Example Here's a complete working example showing all the pieces together: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP from fastmcp.server.auth.providers.github import GitHubProvider from starlette.applications import Starlette from starlette.routing import Mount import uvicorn # Define routing structure ROOT_URL = "http://localhost:8000" MOUNT_PREFIX = "/api" MCP_PATH = "/mcp" # Create OAuth provider auth = GitHubProvider( client_id="your-client-id", client_secret="your-client-secret", base_url=f"{ROOT_URL}{MOUNT_PREFIX}", # issuer_url defaults to base_url - path-aware discovery works automatically ) # Create MCP server mcp = FastMCP("Protected Server", auth=auth) @mcp.tool def analyze(data: str) -> dict: return {"result": f"Analyzed: {data}"} # Create MCP app mcp_app = mcp.http_app(path=MCP_PATH) # Get discovery routes for root level well_known_routes = auth.get_well_known_routes(mcp_path=MCP_PATH) # Assemble the application app = Starlette( routes=[ *well_known_routes, Mount(MOUNT_PREFIX, app=mcp_app), ], lifespan=mcp_app.lifespan, ) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000) ``` For more details on OAuth authentication, see the [Authentication guide](/servers/auth). ## Production Deployment ### Running with Uvicorn When deploying to production, you'll want to optimize your server for performance and reliability. Uvicorn provides several options to improve your server's capabilities: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Run with basic configuration uvicorn app:app --host 0.0.0.0 --port 8000 # Run with multiple workers for production (requires stateless mode - see below) uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4 ``` ### Horizontal Scaling When deploying FastMCP behind a load balancer or running multiple server instances, you need to understand how the HTTP transport handles sessions and configure your server appropriately. #### Understanding Sessions By default, FastMCP's Streamable HTTP transport maintains server-side sessions. Sessions enable stateful MCP features like [elicitation](/servers/elicitation) and [sampling](/servers/sampling), where the server needs to maintain context across multiple requests from the same client. This works perfectly for single-instance deployments. However, sessions are stored in memory on each server instance, which creates challenges when scaling horizontally. #### Without Stateless Mode When running multiple server instances behind a load balancer (Traefik, nginx, HAProxy, Kubernetes, etc.), requests from the same client may be routed to different instances: 1. Client connects to Instance A → session created on Instance A 2. Next request routes to Instance B → session doesn't exist → **request fails** You might expect sticky sessions (session affinity) to solve this, but they don't work reliably with MCP clients. **Why sticky sessions don't work:** Most MCP clients—including Cursor and Claude Code—use `fetch()` internally and don't properly forward `Set-Cookie` headers. Without cookies, load balancers can't identify which instance should handle subsequent requests. This is a limitation in how these clients implement HTTP, not something you can fix with load balancer configuration. #### Enabling Stateless Mode For horizontally scaled deployments, enable stateless HTTP mode. In stateless mode, each request creates a fresh transport context, eliminating the need for session affinity entirely. **Option 1: Via constructor** ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("My Server", stateless_http=True) @mcp.tool def process(data: str) -> str: return f"Processed: {data}" app = mcp.http_app() ``` **Option 2: Via `run()`** ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} if __name__ == "__main__": mcp.run(transport="http", stateless_http=True) ``` **Option 3: Via environment variable** ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} FASTMCP_STATELESS_HTTP=true uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4 ``` ### Environment Variables Production deployments should never hardcode sensitive information like API keys or authentication tokens. Instead, use environment variables to configure your server at runtime. This keeps your code secure and makes it easy to deploy the same code to different environments with different configurations. Here's an example using bearer token authentication (though OAuth is recommended for production): ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} import os from fastmcp import FastMCP from fastmcp.server.auth import BearerTokenAuth # Read configuration from environment auth_token = os.environ.get("MCP_AUTH_TOKEN") if auth_token: auth = BearerTokenAuth(token=auth_token) mcp = FastMCP("Production Server", auth=auth) else: mcp = FastMCP("Production Server") app = mcp.http_app() ``` Deploy with your secrets safely stored in environment variables: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} MCP_AUTH_TOKEN=secret uvicorn app:app --host 0.0.0.0 --port 8000 ``` ### OAuth Token Security If you're using the [OAuth Proxy](/servers/auth/oauth-proxy), FastMCP issues its own JWT tokens to clients instead of forwarding upstream provider tokens. This maintains proper OAuth 2.0 token boundaries. **Default Behavior (Development Only):** By default, FastMCP automatically manages cryptographic keys: * **Mac/Windows**: Keys are generated and stored in your system keyring, surviving server restarts. Suitable **only** for development and local testing. * **Linux**: Keys are ephemeral (random salt at startup), so tokens are invalidated on restart. This automatic approach is convenient for development but not suitable for production deployments. **For Production:** Production requires explicit key management to ensure tokens survive restarts and can be shared across multiple server instances. This requires the following two things working together: 1. **Explicit JWT signing key** for signing tokens issued to clients 2. **Persistent network-accessible storage** for upstream tokens (wrapped in `FernetEncryptionWrapper` to encrypt sensitive data at rest) **Configuration:** Add two parameters to your auth provider: ```python {8-12} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from key_value.aio.stores.redis import RedisStore from key_value.aio.wrappers.encryption import FernetEncryptionWrapper from cryptography.fernet import Fernet auth = GitHubProvider( client_id=os.environ["GITHUB_CLIENT_ID"], client_secret=os.environ["GITHUB_CLIENT_SECRET"], jwt_signing_key=os.environ["JWT_SIGNING_KEY"], client_storage=FernetEncryptionWrapper( key_value=RedisStore(host="redis.example.com", port=6379), fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"]) ), base_url="https://your-server.com" # use HTTPS ) ``` Both parameters are required for production. Without an explicit signing key, keys are signed using a key derived from the client\_secret, which will cause invalidation upon rotation of the client secret. Without persistent storage, tokens are local to the server and won't be trusted across hosts. **Wrap your storage backend in `FernetEncryptionWrapper` to encrypt sensitive OAuth tokens at rest** - without encryption, tokens are stored in plaintext. For more details on the token architecture and key management, see [OAuth Proxy Key and Storage Management](/servers/auth/oauth-proxy#key-and-storage-management). ## Reverse Proxy (nginx) In production, you'll typically run your FastMCP server behind a reverse proxy like nginx. A reverse proxy provides TLS termination, domain-based routing, static file serving, and an additional layer of security between the internet and your application. ### Running FastMCP as a Linux Service Before configuring nginx, you need your FastMCP server running as a background service. A systemd unit file ensures your server starts automatically and restarts on failure. Create a file at `/etc/systemd/system/fastmcp.service`: ```ini theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} [Unit] Description=FastMCP Server After=network.target [Service] User=www-data Group=www-data WorkingDirectory=/opt/fastmcp ExecStart=/opt/fastmcp/.venv/bin/uvicorn app:app --host 127.0.0.1 --port 8000 Restart=always RestartSec=5 Environment="PATH=/opt/fastmcp/.venv/bin" [Install] WantedBy=multi-user.target ``` Enable and start the service: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} sudo systemctl daemon-reload sudo systemctl enable fastmcp sudo systemctl start fastmcp ``` This assumes your ASGI application is in `/opt/fastmcp/app.py` with a virtual environment at `/opt/fastmcp/.venv`. Adjust paths to match your deployment layout. ### nginx Configuration FastMCP's Streamable HTTP transport uses Server-Sent Events (SSE) for streaming responses. This requires specific nginx settings to prevent buffering from breaking the event stream. Create a site configuration at `/etc/nginx/sites-available/fastmcp`: ```nginx theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} server { listen 80; server_name mcp.example.com; # Redirect HTTP to HTTPS return 301 https://$host$request_uri; } server { listen 443 ssl; server_name mcp.example.com; ssl_certificate /etc/letsencrypt/live/mcp.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_set_header Connection ''; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Required for SSE (Server-Sent Events) streaming proxy_buffering off; proxy_cache off; # Allow long-lived connections for streaming responses proxy_read_timeout 300s; proxy_send_timeout 300s; } } ``` Enable the site and reload nginx: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} sudo ln -s /etc/nginx/sites-available/fastmcp /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` Your FastMCP server is now accessible at `https://mcp.example.com/mcp`. **SSE buffering is the most common issue.** If clients connect but never receive streaming responses (progress updates, tool results), verify that `proxy_buffering off` is set. Without it, nginx buffers the entire SSE stream and delivers it only when the connection closes, which breaks real-time communication. ### Key Considerations When deploying FastMCP behind a reverse proxy, keep these points in mind: * **Disable buffering**: SSE requires `proxy_buffering off` so events reach clients immediately. This is the single most important setting. * **Increase timeouts**: The default nginx `proxy_read_timeout` is 60 seconds. Long-running MCP tools will cause the connection to drop. Set timeouts to at least 300 seconds, or higher if your tools run longer. For tools that may exceed any timeout, use [SSE Polling](#sse-polling-for-long-running-operations) to gracefully handle proxy disconnections. * **Use HTTP/1.1**: Set `proxy_http_version 1.1` and `proxy_set_header Connection ''` to enable keep-alive connections between nginx and your server. Clearing the `Connection` header prevents clients from sending `Connection: close` to your upstream, which would break SSE streams. Both settings are required for proper SSE support. * **Forward headers**: Pass `X-Forwarded-For` and `X-Forwarded-Proto` so your FastMCP server can determine the real client IP and protocol. This is important for logging and for OAuth redirect URLs. * **TLS termination**: Let nginx handle TLS certificates (e.g., via Let's Encrypt with Certbot). Your FastMCP server can then run on plain HTTP internally. ### Mounting Under a Path Prefix If you want your MCP server available at a subpath like `https://example.com/api/mcp` instead of at the root domain, adjust the nginx `location` block: ```nginx theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} location /api/ { proxy_pass http://127.0.0.1:8000/; proxy_http_version 1.1; proxy_set_header Connection ''; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Required for SSE streaming proxy_buffering off; proxy_cache off; proxy_read_timeout 300s; proxy_send_timeout 300s; } ``` Note the trailing `/` on both `location /api/` and `proxy_pass http://127.0.0.1:8000/` — this ensures nginx strips the `/api` prefix before forwarding to your server. If you're using OAuth authentication with a mount prefix, see [Mounting Authenticated Servers](#mounting-authenticated-servers) for additional configuration. ## Testing Your Deployment Once your server is deployed, you'll need to verify it's accessible and functioning correctly. For comprehensive testing strategies including connectivity tests, client testing, and authentication testing, see the [Testing Your Server](/development/tests) guide. ## Hosting Your Server This guide has shown you how to create an HTTP-accessible MCP server, but you'll still need a hosting provider to make it available on the internet. Your FastMCP server can run anywhere that supports Python web applications: * **Cloud VMs** (AWS EC2, Google Compute Engine, Azure VMs) * **Container platforms** (Cloud Run, Container Instances, ECS) * **Platform-as-a-Service** (Railway, Render, Vercel) * **Edge platforms** (Cloudflare Workers) * **Kubernetes clusters** (self-managed or managed) The key requirements are Python 3.10+ support and the ability to expose an HTTP port. Most providers will require you to package your server (requirements.txt, Dockerfile, etc.) according to their deployment format. For managed, zero-configuration deployment, see [Prefect Horizon](/deployment/prefect-horizon). # Prefect Horizon Source: https://gofastmcp.com/deployment/prefect-horizon The MCP platform from the FastMCP team [Prefect Horizon](https://www.prefect.io/horizon) is a platform for deploying and managing MCP servers. Built by the FastMCP team at [Prefect](https://www.prefect.io), Horizon provides managed hosting, authentication, access control, and a registry of MCP capabilities. Horizon includes a **free personal tier for FastMCP users**, making it the fastest way to get a secure, production-ready server URL with built-in OAuth authentication. Horizon is free for personal projects. Enterprise governance features are available for teams deploying to thousands of users. ## The Platform Horizon is organized into four integrated pillars: * **Deploy**: Managed hosting with CI/CD, scaling, monitoring, and rollbacks. Push code and get a live, governed endpoint in 60 seconds. * **Registry**: A central catalog of MCP servers across your organization—first-party, third-party, and curated remix servers composed from multiple sources. * **Gateway**: Role-based access control, authentication, and audit logs. Define what agents can see and do at the tool level. * **Agents**: A permissioned chat interface for interacting with any MCP server or curated combination of servers. This guide focuses on **Horizon Deploy**, the managed hosting layer that gives you the fastest path from a FastMCP server to a production URL. ## Prerequisites To use Horizon, you'll need a [GitHub](https://github.com) account and a GitHub repo containing a FastMCP server. If you don't have one yet, Horizon can create a starter repo for you during onboarding. Your repo can be public or private, but must include at least a Python file containing a FastMCP server instance. To verify your file is compatible with Horizon, run `fastmcp inspect ` to see what Horizon will see when it runs your server. If you have a `requirements.txt` or `pyproject.toml` in the repo, Horizon will automatically detect your server's dependencies and install them. Your file *can* have an `if __name__ == "__main__"` block, but it will be ignored by Horizon. For example, a minimal server file might look like: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("MyServer") @mcp.tool def hello(name: str) -> str: return f"Hello, {name}!" ``` ## Getting Started There are just three steps to deploying a server to Horizon: ### Step 1: Select a Repository Visit [horizon.prefect.io](https://horizon.prefect.io) and sign in with your GitHub account. Connect your GitHub account to grant Horizon access to your repositories, then select the repo you want to deploy. Horizon repository selection ### Step 2: Configure Your Server Next, you'll configure how Horizon should build and deploy your server. Horizon server configuration The configuration screen lets you specify: * **Server name**: A unique name for your server. This determines your server's URL. * **Description**: A brief description of what your server does. * **Entrypoint**: The Python file containing your FastMCP server (e.g., `main.py`). This field has the same syntax as the `fastmcp run` command—use `main.py:mcp` to specify a specific object in the file. * **Authentication**: When enabled, only authenticated users in your organization can connect. Horizon handles all the OAuth complexity for you. Horizon will automatically detect your server's Python dependencies from either a `requirements.txt` or `pyproject.toml` file. ### Step 3: Deploy and Connect Click **Deploy Server** and Horizon will clone your repository, build your server, and deploy it to a unique URL—typically in under 60 seconds. Horizon deployment view showing live server Once deployed, your server is accessible at a URL like: ``` https://your-server-name.fastmcp.app/mcp ``` Horizon monitors your repo and redeploys automatically whenever you push to `main`. It also builds preview deployments for every PR, so you can test changes before they go live. ## Testing Your Server Horizon provides two ways to verify your server is working before connecting external clients. ### Inspector The Inspector gives you a structured view of everything your server exposes—tools, resources, and prompts. You can click any tool, fill in the inputs, execute it, and see the output. This is useful for systematically validating each capability and debugging specific behaviors. ### ChatMCP For quick end-to-end testing, ChatMCP lets you interact with your server conversationally. It uses a fast model optimized for rapid iteration—you can verify the server works, test tool calls in context, and confirm the overall behavior before sharing it with others. Horizon ChatMCP interface ChatMCP is designed for testing, not as a daily work environment. Once you've confirmed your server works, you can copy connection snippets for Claude Desktop, Cursor, Claude Code, and other MCP clients—or use the FastMCP client library to connect programmatically. ## Horizon Agents Beyond testing individual servers, Horizon lets you create **Agents**—chat interfaces backed by one or more MCP servers. While ChatMCP tests a single server, Agents let you compose capabilities from multiple servers into a unified experience. Horizon Agent configuration To create an agent: 1. Navigate to **Agents** in the sidebar 2. Click **Create Agent** and give it a name and description 3. Add MCP servers to the agent—these can be servers you've deployed to Horizon or external servers in the registry Once configured, you can chat with your agent directly in Horizon: Chatting with a Horizon Agent Agents are useful for creating purpose-built interfaces that combine tools from different servers. For example, you might create an agent that has access to both your company's internal data server and a general-purpose utilities server. # Running Your Server Source: https://gofastmcp.com/deployment/running-server Learn how to run your FastMCP server locally for development and testing FastMCP servers can be run in different ways depending on your needs. This guide focuses on running servers locally for development and testing. For production deployment to a URL, see the [HTTP Deployment](/deployment/http) guide. ## The `run()` Method Every FastMCP server needs to be started to accept connections. The simplest way to run a server is by calling the `run()` method on your FastMCP instance. This method starts the server and blocks until it's stopped, handling all the connection management for you. For maximum compatibility, it's best practice to place the `run()` call within an `if __name__ == "__main__":` block. This ensures the server starts only when the script is executed directly, not when imported as a module. ```python {9-10} my_server.py theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP(name="MyServer") @mcp.tool def hello(name: str) -> str: return f"Hello, {name}!" if __name__ == "__main__": mcp.run() ``` You can now run this MCP server by executing `python my_server.py`. ## Transport Protocols MCP servers communicate with clients through different transport protocols. Think of transports as the "language" your server speaks to communicate with clients. FastMCP supports three main transport protocols, each designed for specific use cases and deployment scenarios. The choice of transport determines how clients connect to your server, what network capabilities are available, and how many clients can connect simultaneously. Understanding these transports helps you choose the right approach for your application. ### STDIO Transport (Default) STDIO (Standard Input/Output) is the default transport for FastMCP servers. When you call `run()` without arguments, your server uses STDIO transport. This transport communicates through standard input and output streams, making it perfect for command-line tools and desktop applications like Claude Desktop. With STDIO transport, the client spawns a new server process for each session and manages its lifecycle. The server reads MCP messages from stdin and writes responses to stdout. This is why STDIO servers don't stay running - they're started on-demand by the client. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("MyServer") @mcp.tool def hello(name: str) -> str: return f"Hello, {name}!" if __name__ == "__main__": mcp.run() # Uses STDIO transport by default ``` STDIO is ideal for: * Local development and testing * Claude Desktop integration * Command-line tools * Single-user applications ### HTTP Transport (Streamable) HTTP transport turns your MCP server into a web service accessible via a URL. This transport uses the Streamable HTTP protocol, which allows clients to connect over the network. Unlike STDIO where each client gets its own process, an HTTP server can handle multiple clients simultaneously. The Streamable HTTP protocol provides full bidirectional communication between client and server, supporting all MCP operations including streaming responses. This makes it the recommended choice for network-based deployments. To use HTTP transport, specify it in the `run()` method along with networking options: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("MyServer") @mcp.tool def hello(name: str) -> str: return f"Hello, {name}!" if __name__ == "__main__": # Start an HTTP server on port 8000 mcp.run(transport="http", host="127.0.0.1", port=8000) ``` Your server is now accessible at `http://localhost:8000/mcp`. This URL is the MCP endpoint that clients will connect to. HTTP transport enables: * Network accessibility * Multiple concurrent clients * Integration with web infrastructure * Remote deployment capabilities For production HTTP deployment with authentication and advanced configuration, see the [HTTP Deployment](/deployment/http) guide. ### SSE Transport (Legacy) Server-Sent Events (SSE) transport was the original HTTP-based transport for MCP. While still supported for backward compatibility, it has limitations compared to the newer Streamable HTTP transport. SSE only supports server-to-client streaming, making it less efficient for bidirectional communication. ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} if __name__ == "__main__": # SSE transport - use HTTP instead for new projects mcp.run(transport="sse", host="127.0.0.1", port=8000) ``` We recommend using HTTP transport instead of SSE for all new projects. SSE remains available only for compatibility with older clients that haven't upgraded to Streamable HTTP. ### Choosing the Right Transport Each transport serves different needs. STDIO is perfect when you need simple, local execution - it's what Claude Desktop and most command-line tools expect. HTTP transport is essential when you need network access, want to serve multiple clients, or plan to deploy your server remotely. SSE exists only for backward compatibility and shouldn't be used in new projects. Consider your deployment scenario: Are you building a tool for local use? STDIO is your best choice. Need a centralized service that multiple clients can access? HTTP transport is the way to go. ## The FastMCP CLI FastMCP provides a powerful command-line interface for running servers without modifying the source code. The CLI can automatically find and run your server with different transports, manage dependencies, and handle development workflows: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run server.py ``` The CLI automatically finds a FastMCP instance in your file (named `mcp`, `server`, or `app`) and runs it with the specified options. This is particularly useful for testing different transports or configurations without changing your code. ### Dependency Management The CLI integrates with `uv` to manage Python environments and dependencies: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Run with a specific Python version fastmcp run server.py --python 3.11 # Run with additional packages fastmcp run server.py --with pandas --with numpy # Run with dependencies from a requirements file fastmcp run server.py --with-requirements requirements.txt # Combine multiple options fastmcp run server.py --python 3.10 --with httpx --transport http # Run within a specific project directory fastmcp run server.py --project /path/to/project ``` When using `--python`, `--with`, `--project`, or `--with-requirements`, the server runs via `uv run` subprocess instead of using your local environment. ### Passing Arguments to Servers When servers accept command line arguments (using argparse, click, or other libraries), you can pass them after `--`: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run config_server.py -- --config config.json fastmcp run database_server.py -- --database-path /tmp/db.sqlite --debug ``` This is useful for servers that need configuration files, database paths, API keys, or other runtime options. For more CLI features including development mode with the MCP Inspector, see the [CLI documentation](/cli/running). ### Auto-Reload for Development During development, you can use the `--reload` flag to automatically restart your server when source files change: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run server.py --reload ``` The server watches for changes to Python files in the current directory and restarts automatically when you save changes. This provides a fast feedback loop during development without manually stopping and starting the server. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Watch specific directories for changes fastmcp run server.py --reload --reload-dir ./src --reload-dir ./lib # Combine with other options fastmcp run server.py --reload --transport http --port 8080 ``` Auto-reload uses stateless mode to enable seamless restarts. For stdio transport, this is fully featured. For HTTP transport, some bidirectional features like elicitation are not available during reload mode. SSE transport does not support auto-reload due to session limitations. Use HTTP transport instead if you need both network access and auto-reload. ### Async Usage FastMCP servers are built on async Python, but the framework provides both synchronous and asynchronous APIs to fit your application's needs. The `run()` method we've been using is actually a synchronous wrapper around the async server implementation. For applications that are already running in an async context, FastMCP provides the `run_async()` method: ```python {10-12} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP import asyncio mcp = FastMCP(name="MyServer") @mcp.tool def hello(name: str) -> str: return f"Hello, {name}!" async def main(): # Use run_async() in async contexts await mcp.run_async(transport="http", port=8000) if __name__ == "__main__": asyncio.run(main()) ``` The `run()` method cannot be called from inside an async function because it creates its own async event loop internally. If you attempt to call `run()` from inside an async function, you'll get an error about the event loop already running. Always use `run_async()` inside async functions and `run()` in synchronous contexts. Both `run()` and `run_async()` accept the same transport arguments, so all the examples above apply to both methods. ## Custom Routes When using HTTP transport, you might want to add custom web endpoints alongside your MCP server. This is useful for health checks, status pages, or simple APIs. FastMCP lets you add custom routes using the `@custom_route` decorator: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP from starlette.requests import Request from starlette.responses import PlainTextResponse mcp = FastMCP("MyServer") @mcp.custom_route("/health", methods=["GET"]) async def health_check(request: Request) -> PlainTextResponse: return PlainTextResponse("OK") @mcp.tool def process(data: str) -> str: return f"Processed: {data}" if __name__ == "__main__": mcp.run(transport="http") # Health check at http://localhost:8000/health ``` Custom routes are served by the same web server as your MCP endpoint. They're available at the root of your domain while the MCP endpoint is at `/mcp/`. For more complex web applications, consider [mounting your MCP server into a FastAPI or Starlette app](/deployment/http#integration-with-web-frameworks). ## Alternative Initialization Patterns The `if __name__ == "__main__"` pattern works well for standalone scripts, but some deployment scenarios require different approaches. FastMCP handles these cases automatically. ### CLI-Only Servers When using the FastMCP CLI, you don't need the `if __name__` block at all. The CLI will find your FastMCP instance and run it: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # server.py from fastmcp import FastMCP mcp = FastMCP("MyServer") # CLI looks for 'mcp', 'server', or 'app' @mcp.tool def process(data: str) -> str: return f"Processed: {data}" # No if __name__ block needed - CLI will find and run 'mcp' ``` ### ASGI Applications For ASGI deployment (running with Uvicorn or similar), you'll want to create an ASGI application object. This approach is common in production deployments where you need more control over the server configuration: ```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # app.py from fastmcp import FastMCP def create_app(): mcp = FastMCP("MyServer") @mcp.tool def process(data: str) -> str: return f"Processed: {data}" return mcp.http_app() app = create_app() # Uvicorn will use this ``` See the [HTTP Deployment](/deployment/http) guide for more ASGI deployment patterns. # Project Configuration Source: https://gofastmcp.com/deployment/server-configuration Use fastmcp.json for portable, declarative project configuration FastMCP supports declarative configuration through `fastmcp.json` files. This is the canonical and preferred way to configure FastMCP projects, providing a single source of truth for server settings, dependencies, and deployment options that replaces complex command-line arguments. The `fastmcp.json` file is designed to be a portable description of your server configuration that can be shared across environments and teams. When running from a `fastmcp.json` file, you can override any configuration values using CLI arguments. ## Overview The `fastmcp.json` configuration file allows you to define all aspects of your FastMCP server in a structured, shareable format. Instead of remembering command-line arguments or writing shell scripts, you declare your server's configuration once and use it everywhere. When you have a `fastmcp.json` file, running your server becomes as simple as: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Run the server using the configuration fastmcp run fastmcp.json # Or if fastmcp.json exists in the current directory fastmcp run ``` This configuration approach ensures reproducible deployments across different environments, from local development to production servers. It works seamlessly with Claude Desktop, VS Code extensions, and any MCP-compatible client. ## File Structure The `fastmcp.json` configuration answers three fundamental questions about your server: * **Source** = WHERE does your server code live? * **Environment** = WHAT environment setup does it require? * **Deployment** = HOW should the server run? This conceptual model helps you understand the purpose of each configuration section and organize your settings effectively. The configuration file maps directly to these three concerns: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", "source": { // WHERE: Location of your server code "type": "filesystem", // Optional, defaults to "filesystem" "path": "server.py", "entrypoint": "mcp" }, "environment": { // WHAT: Environment setup and dependencies "type": "uv", // Optional, defaults to "uv" "python": ">=3.10", "dependencies": ["pandas", "numpy"] }, "deployment": { // HOW: Runtime configuration "transport": "stdio", "log_level": "INFO" } } ``` Only the `source` field is required. The `environment` and `deployment` sections are optional and provide additional configuration when needed. ### JSON Schema Support FastMCP provides JSON schemas for IDE autocomplete and validation. Add the schema reference to your `fastmcp.json` for enhanced developer experience: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", "source": { "path": "server.py", "entrypoint": "mcp" } } ``` Two schema URLs are available: * **Version-specific**: `https://gofastmcp.com/public/schemas/fastmcp.json/v1.json` * **Latest version**: `https://gofastmcp.com/public/schemas/fastmcp.json/latest.json` Modern IDEs like VS Code will automatically provide autocomplete suggestions, validation, and inline documentation when the schema is specified. ### Source Configuration The source configuration determines **WHERE** your server code lives. It tells FastMCP how to find and load your server, whether it's a local Python file, a remote repository, or hosted in the cloud. This section is required and forms the foundation of your configuration. The server source configuration that determines where your server code lives. The source type identifier that determines which implementation to use. Currently supports `"filesystem"` for local files. Future releases will add support for `"git"` and `"cloud"` source types. When `type` is `"filesystem"` (or omitted), the source points to a local Python file containing your FastMCP server: Path to the Python file containing your FastMCP server. Name of the server instance or factory function within the module: * Can be a FastMCP server instance (e.g., `mcp = FastMCP("MyServer")`) * Can be a function with no arguments that returns a FastMCP server * If not specified, FastMCP searches for common names: `mcp`, `server`, or `app` **Example:** ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "source": { "type": "filesystem", "path": "src/server.py", "entrypoint": "mcp" } ``` Note: File paths are resolved relative to the configuration file's location. **Future Source Types** Future releases will support additional source types: * **Git repositories** (`type: "git"`) for loading server code directly from version control * **Prefect Horizon** (`type: "cloud"`) for hosted servers with automatic scaling and management ### Environment Configuration The environment configuration determines **WHAT** environment setup your server requires. It controls the build-time setup of your Python environment, ensuring your server runs with the exact Python version and dependencies it requires. This section creates isolated, reproducible environments across different systems. FastMCP uses an extensible environment system with a base `Environment` class that can be implemented by different environment providers. Currently, FastMCP supports the `UVEnvironment` for Python environment management using `uv`'s powerful dependency resolver. Optional environment configuration. When specified, FastMCP uses the appropriate environment implementation to set up your server's runtime. The environment type identifier that determines which implementation to use. Currently supports `"uv"` for Python environments managed by uv. If omitted, defaults to `"uv"`. When `type` is `"uv"` (or omitted), the environment uses uv to manage Python dependencies: Python version constraint. Examples: * Exact version: `"3.12"` * Minimum version: `">=3.10"` * Version range: `">=3.10,<3.13"` List of pip packages with optional version specifiers (PEP 508 format). ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "dependencies": ["pandas>=2.0", "requests", "httpx"] ``` Path to a requirements.txt file, resolved relative to the config file location. ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "requirements": "requirements.txt" ``` Path to a project directory containing pyproject.toml for uv project management. ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "project": "." ``` List of paths to packages to install in editable/development mode. Useful for local development when you want changes to be reflected immediately. Supports multiple packages for monorepo setups or shared libraries. ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "editable": ["."] ``` Or with multiple packages: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "editable": [".", "../shared-lib", "/path/to/another-package"] ``` **Example:** ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "environment": { "type": "uv", "python": ">=3.10", "dependencies": ["pandas", "numpy"], "editable": ["."] } ``` Note: When any UVEnvironment field is specified, FastMCP automatically creates an isolated environment using `uv` before running your server. When environment configuration is provided, FastMCP: 1. Detects the environment type (defaults to `"uv"` if not specified) 2. Creates an isolated environment using the appropriate provider 3. Installs the specified dependencies 4. Runs your server in this clean environment This build-time setup ensures your server always has the dependencies it needs, without polluting your system Python or conflicting with other projects. **Future Environment Types** Similar to source types, future releases may support additional environment types for different runtime requirements, such as Docker containers or language-specific environments beyond Python. ### Deployment Configuration The deployment configuration controls **HOW** your server runs. It defines the runtime behavior including network settings, environment variables, and execution context. These settings determine how your server operates when it executes, from transport protocols to logging levels. Environment variables are included in this section because they're runtime configuration that affects how your server behaves when it executes, not how its environment is built. The deployment configuration is applied every time your server starts, controlling its operational characteristics. Optional runtime configuration for the server. Protocol for client communication: * `"stdio"`: Standard input/output for desktop clients * `"http"`: Network-accessible HTTP server * `"sse"`: Server-sent events Network interface to bind (HTTP transport only): * `"127.0.0.1"`: Local connections only * `"0.0.0.0"`: All network interfaces Port number for HTTP transport. URL path for the MCP endpoint when using HTTP transport. Server logging verbosity. Options: * `"DEBUG"`: Detailed debugging information * `"INFO"`: General informational messages * `"WARNING"`: Warning messages * `"ERROR"`: Error messages only * `"CRITICAL"`: Critical errors only Environment variables to set when running the server. Supports `${VAR_NAME}` syntax for runtime interpolation. ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "env": { "API_KEY": "secret-key", "DATABASE_URL": "postgres://${DB_USER}@${DB_HOST}/mydb" } ``` Working directory for the server process. Relative paths are resolved from the config file location. Command-line arguments to pass to the server, passed after `--` to the server's argument parser. ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} "args": ["--config", "server-config.json"] ``` #### Environment Variable Interpolation The `env` field in deployment configuration supports runtime interpolation of environment variables using `${VAR_NAME}` syntax. This enables dynamic configuration based on your deployment environment: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "deployment": { "env": { "API_URL": "https://api.${ENVIRONMENT}.example.com", "DATABASE_URL": "postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/myapp", "CACHE_KEY": "myapp_${ENVIRONMENT}_${VERSION}" } } } ``` When the server starts, FastMCP replaces `${ENVIRONMENT}`, `${DB_USER}`, etc. with values from your system's environment variables. If a variable doesn't exist, the placeholder is preserved as-is. **Example**: If your system has `ENVIRONMENT=production` and `DB_HOST=db.example.com`: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} // Configuration { "deployment": { "env": { "API_URL": "https://api.${ENVIRONMENT}.example.com", "DB_HOST": "${DB_HOST}" } } } // Result at runtime { "API_URL": "https://api.production.example.com", "DB_HOST": "db.example.com" } ``` This feature is particularly useful for: * Deploying the same configuration across development, staging, and production * Keeping sensitive values out of configuration files * Building dynamic URLs and connection strings * Creating environment-specific prefixes or suffixes ## Usage with CLI Commands FastMCP automatically detects and uses a file specifically named `fastmcp.json` in the current directory, making server execution simple and consistent. Files with FastMCP configuration format but different names are not auto-detected and must be specified explicitly: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Auto-detect fastmcp.json in current directory cd my-project fastmcp run # No arguments needed! # Or specify a configuration file explicitly fastmcp run prod.fastmcp.json # Skip environment setup when already in a uv environment fastmcp run fastmcp.json --skip-env # Skip source preparation when source is already prepared fastmcp run fastmcp.json --skip-source # Skip both environment and source preparation fastmcp run fastmcp.json --skip-env --skip-source ``` ### Pre-building Environments You can use `fastmcp project prepare` to create a persistent uv project with all dependencies pre-installed: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Create a persistent environment fastmcp project prepare fastmcp.json --output-dir ./env # Use the pre-built environment to run the server fastmcp run fastmcp.json --project ./env ``` This pattern separates environment setup (slow) from server execution (fast), useful for deployment scenarios. ### Using an Existing Environment By default, FastMCP creates an isolated environment with `uv` based on your configuration. When you already have a suitable Python environment, use the `--skip-env` flag to skip environment creation: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run fastmcp.json --skip-env ``` **When you already have an environment:** * You're in an activated virtual environment with all dependencies installed * You're inside a Docker container with pre-installed dependencies * You're in a CI/CD pipeline that pre-builds the environment * You're using a system-wide installation with all required packages * You're in a uv-managed environment (prevents infinite recursion) This flag tells FastMCP: "I already have everything installed, just run the server." ### Using an Existing Source When working with source types that require preparation (future support for git repositories or cloud sources), use the `--skip-source` flag when you already have the source code available: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run fastmcp.json --skip-source ``` **When you already have the source:** * You've previously cloned a git repository and don't need to re-fetch * You have a cached copy of a cloud-hosted server * You're in a CI/CD pipeline where source checkout is a separate step * You're iterating locally on already-downloaded code This flag tells FastMCP: "I already have the source code, skip any download/clone steps." Note: For filesystem sources (local Python files), this flag has no effect since they don't require preparation. The configuration file works with all FastMCP commands: * **`run`** - Start the server in production mode * **`dev`** - Launch with the Inspector UI for development * **`inspect`** - View server capabilities and configuration * **`install`** - Install to Claude Desktop, Cursor, or other MCP clients When no file argument is provided, FastMCP searches the current directory for `fastmcp.json`. This means you can simply navigate to your project directory and run `fastmcp run` to start your server with all its configured settings. ### CLI Override Behavior Command-line arguments take precedence over configuration file values, allowing ad-hoc adjustments without modifying the file: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} # Config specifies port 3000, CLI overrides to 8080 fastmcp run fastmcp.json --port 8080 # Config specifies stdio, CLI overrides to HTTP fastmcp run fastmcp.json --transport http # Add extra dependencies not in config fastmcp run fastmcp.json --with requests --with httpx ``` This precedence order enables: * Quick testing of different settings * Environment-specific overrides in deployment scripts * Debugging with increased log levels * Temporary configuration changes ### Custom Naming Patterns You can use different configuration files for different environments: * `fastmcp.json` - Default configuration * `dev.fastmcp.json` - Development settings * `prod.fastmcp.json` - Production settings * `test_fastmcp.json` - Test configuration Any file with "fastmcp.json" in the name is recognized as a configuration file. ## Examples A minimal configuration for a simple server: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", "source": { "path": "server.py", "entrypoint": "mcp" } } ``` This configuration explicitly specifies the server entrypoint (`mcp`), making it clear which server instance or factory function to use. Uses all defaults: STDIO transport, no special dependencies, standard logging. A configuration optimized for local development: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", // WHERE does the server live? "source": { "path": "src/server.py", "entrypoint": "app" }, // WHAT dependencies does it need? "environment": { "type": "uv", "python": "3.12", "dependencies": ["fastmcp[dev]"], "editable": "." }, // HOW should it run? "deployment": { "transport": "http", "host": "127.0.0.1", "port": 8000, "log_level": "DEBUG", "env": { "DEBUG": "true", "ENV": "development" } } } ``` A production-ready configuration with full dependency management: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", // WHERE does the server live? "source": { "path": "app/main.py", "entrypoint": "mcp_server" }, // WHAT dependencies does it need? "environment": { "python": "3.11", "requirements": "requirements/production.txt", "project": "." }, // HOW should it run? "deployment": { "transport": "http", "host": "0.0.0.0", "port": 3000, "path": "/api/mcp/", "log_level": "INFO", "env": { "ENV": "production", "API_BASE_URL": "https://api.example.com", "DATABASE_URL": "postgresql://user:pass@db.example.com/prod" }, "cwd": "/app", "args": ["--workers", "4"] } } ``` Configuration for a data analysis server with scientific packages: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", "source": { "path": "analysis_server.py", "entrypoint": "mcp" }, "environment": { "python": "3.11", "dependencies": [ "pandas>=2.0", "numpy", "scikit-learn", "matplotlib", "jupyterlab" ] }, "deployment": { "transport": "stdio", "env": { "MATPLOTLIB_BACKEND": "Agg", "DATA_PATH": "./datasets" } } } ``` You can maintain multiple configuration files for different environments: **dev.fastmcp.json**: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", "source": { "path": "server.py", "entrypoint": "mcp" }, "deployment": { "transport": "http", "log_level": "DEBUG" } } ``` **prod.fastmcp.json**: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", "source": { "path": "server.py", "entrypoint": "mcp" }, "environment": { "requirements": "requirements/production.txt" }, "deployment": { "transport": "http", "host": "0.0.0.0", "log_level": "WARNING" } } ``` Run different configurations: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run dev.fastmcp.json # Development fastmcp run prod.fastmcp.json # Production ``` ## Migrating from CLI Arguments If you're currently using command-line arguments or shell scripts, migrating to `fastmcp.json` simplifies your workflow. Here's how common CLI patterns map to configuration: **CLI Command**: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} uv run --with pandas --with requests \ fastmcp run server.py \ --transport http \ --port 8000 \ --log-level INFO ``` **Equivalent fastmcp.json**: ```json theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} { "$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json", "source": { "path": "server.py", "entrypoint": "mcp" }, "environment": { "dependencies": ["pandas", "requests"] }, "deployment": { "transport": "http", "port": 8000, "log_level": "INFO" } } ``` Now simply run: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run # Automatically finds and uses fastmcp.json ``` The configuration file approach provides better documentation, easier sharing, and consistent execution across different environments while maintaining the flexibility to override settings when needed. # Installation Source: https://gofastmcp.com/getting-started/installation Install FastMCP and verify your setup ## Install FastMCP We recommend using [uv](https://docs.astral.sh/uv/getting-started/installation/) to install and manage FastMCP. ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} pip install fastmcp ``` Or with uv: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} uv add fastmcp ``` ### Optional Dependencies FastMCP provides optional extras for specific features. For example, to install the background tasks extra: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} pip install "fastmcp[tasks]" ``` See [Background Tasks](/servers/tasks) for details on the task system. ### Verify Installation To verify that FastMCP is installed correctly, you can run the following command: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp version ``` You should see output like the following: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} $ fastmcp version FastMCP version: 3.0.0 MCP version: 1.25.0 Python version: 3.12.2 Platform: macOS-15.3.1-arm64-arm-64bit FastMCP root path: ~/Developer/fastmcp ``` ### Dependency Licensing FastMCP depends on Cyclopts for CLI functionality. Cyclopts v4 includes docutils as a transitive dependency, which has complex licensing that may trigger compliance reviews in some organizations. If this is a concern, you can install Cyclopts v5 alpha which removes this dependency: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} pip install "cyclopts>=5.0.0a1" ``` Alternatively, wait for the stable v5 release. See [this issue](https://github.com/BrianPugh/cyclopts/issues/672) for details. ## Upgrading ### From FastMCP 2.0 See the [Upgrade Guide](/getting-started/upgrading/from-fastmcp-2) for a complete list of breaking changes and migration steps. ### From the MCP SDK #### From FastMCP 1.0 If you're using FastMCP 1.0 via the `mcp` package (meaning you import FastMCP as `from mcp.server.fastmcp import FastMCP`), upgrading is straightforward — for most servers, it's a single import change. See the [full upgrade guide](/getting-started/upgrading/from-mcp-sdk) for details. #### From the Low-Level Server API If you built your server directly on the `mcp` package's `Server` class — with `list_tools()`/`call_tool()` handlers and hand-written JSON Schema — see the [migration guide](/getting-started/upgrading/from-low-level-sdk) for a full walkthrough. ## Versioning Policy FastMCP follows semantic versioning with pragmatic adaptations for the rapidly evolving MCP ecosystem. Breaking changes may occur in minor versions (e.g., 2.3.x to 2.4.0) when necessary to stay current with the MCP Protocol. For production use, always pin to exact versions: ``` fastmcp==3.0.0 # Good fastmcp>=3.0.0 # Bad - may install breaking changes ``` See the full [versioning and release policy](/development/releases#versioning-policy) for details on our public API, deprecation practices, and breaking change philosophy. ## Contributing to FastMCP Interested in contributing to FastMCP? See the [Contributing Guide](/development/contributing) for details on: * Setting up your development environment * Running tests and pre-commit hooks * Submitting issues and pull requests * Code standards and review process # Quickstart Source: https://gofastmcp.com/getting-started/quickstart Welcome! This guide will help you quickly set up FastMCP, run your first MCP server, and deploy a server to Prefect Horizon. If you haven't already installed FastMCP, follow the [installation instructions](/getting-started/installation). ## Create a FastMCP Server A FastMCP server is a collection of tools, resources, and other MCP components. To create a server, start by instantiating the `FastMCP` class. Create a new file called `my_server.py` and add the following code: ```python my_server.py theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("My MCP Server") ``` That's it! You've created a FastMCP server, albeit a very boring one. Let's add a tool to make it more interesting. ## Add a Tool To add a tool that returns a simple greeting, write a function and decorate it with `@mcp.tool` to register it with the server: ```python my_server.py {5-7} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("My MCP Server") @mcp.tool def greet(name: str) -> str: return f"Hello, {name}!" ``` ## Run the Server The simplest way to run your FastMCP server is to call its `run()` method. You can choose between different transports, like `stdio` for local servers, or `http` for remote access: ```python my_server.py (stdio) {9, 10} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("My MCP Server") @mcp.tool def greet(name: str) -> str: return f"Hello, {name}!" if __name__ == "__main__": mcp.run() ``` ```python my_server.py (HTTP) {9, 10} theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} from fastmcp import FastMCP mcp = FastMCP("My MCP Server") @mcp.tool def greet(name: str) -> str: return f"Hello, {name}!" if __name__ == "__main__": mcp.run(transport="http", port=8000) ``` This lets us run the server with `python my_server.py`. The stdio transport is the traditional way to connect MCP servers to clients, while the HTTP transport enables remote connections. Why do we need the `if __name__ == "__main__":` block? The `__main__` block is recommended for consistency and compatibility, ensuring your server works with all MCP clients that execute your server file as a script. Users who will exclusively run their server with the FastMCP CLI can omit it, as the CLI imports the server object directly. ### Using the FastMCP CLI You can also use the `fastmcp run` command to start your server. Note that the FastMCP CLI **does not** execute the `__main__` block of your server file. Instead, it imports your server object and runs it with whatever transport and options you provide. For example, to run this server with the default stdio transport (no matter how you called `mcp.run()`), you can use the following command: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run my_server.py:mcp ``` To run this server with the HTTP transport, you can use the following command: ```bash theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} fastmcp run my_server.py:mcp --transport http --port 8000 ``` ## Call Your Server Once your server is running with HTTP transport, you can connect to it with a FastMCP client or any LLM client that supports the MCP protocol: ```python my_client.py theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}} import asyncio from fastmcp import Client client = Client("http://localhost:8000/mcp") async def call_tool(name: str): async with client: result = await client.call_tool("greet", {"name": name}) print(result) asyncio.run(call_tool("Ford")) ``` Note that: * FastMCP clients are asynchronous, so we need to use `asyncio.run` to run the client * We must enter a client context (`async with client:`) before using the client * You can make multiple client calls within the same context ## Deploy to Prefect Horizon [Prefect Horizon](https://horizon.prefect.io) is the enterprise MCP platform built by the FastMCP team at [Prefect](https://www.prefect.io). It provides managed hosting, authentication, access control, and observability for MCP servers. Horizon is **free for personal projects** and offers enterprise governance for teams. To deploy your server, you'll need a [GitHub account](https://github.com). Once you have one, you can deploy your server in three steps: 1. Push your `my_server.py` file to a GitHub repository 2. Sign in to [Prefect Horizon](https://horizon.prefect.io) with your GitHub account 3. Create a new project from your repository and enter `my_server.py:mcp` as the server entrypoint That's it! Horizon will build and deploy your server, making it available at a URL like `https://your-project.fastmcp.app/mcp`. You can chat with it to test its functionality, or connect to it from any LLM client that supports the MCP protocol. For more details, see the [Prefect Horizon guide](/deployment/prefect-horizon). # Welcome to FastMCP Source: https://gofastmcp.com/getting-started/welcome The fast, Pythonic way to build MCP servers, clients, and applications.