This guide is the FastMCP entry from our seven-framework deploy comparison. We shipped the same example on Manufact Cloud: an echo tool and a greet_widget that returns a MCP Apps view. Below is the deploy path we used; the reference server code is in the second half if you want to reproduce the example.
If you want to run the same deploy pipeline on your repo, connect it in the dashboard. Open the dashboard.
Deploy to Manufact
Manufact's Python pipeline detects pyproject.toml. With a committed uv.lock it runs uv sync --frozen; otherwise it falls back to pip install . (both honor the [apps] extra). The runtime launches uvicorn against the ASGI app you declared.
Push the repo to GitHub
After you edit pyproject.toml, run uv lock (ideally on Python 3.12 to match Manufact's runtime) and commit pyproject.toml + uv.lock. The cloud uses uv sync --frozen when the lock is present.
If a starter shipped a stale uv.lock (for example fastmcp 3.0.1 without the [apps] extra), regenerate with uv lock before pushing. Removing the lock is only a quick one-off shortcut; for production, keep the lock in sync with pyproject.toml.
Open the new-server flow
Go to manufact.com/cloud/<your-org>/servers/new and pick Deploy from GitHub. The probe labels the repo fastmcp.
Set port and start command
- Port:
8000 - Build command: (leave empty: the cloud auto-runs
uv sync --frozenwhenuv.lockis present, otherwisepip install .frompyproject.toml) - Start command:
uvicorn src.server:app --host 0.0.0.0 --port 8000
Manufact reads requires-python from your pyproject.toml and picks a matching base image (supported: 3.11, 3.12, 3.13). Pin a version in that range — requires-python = ">=3.13" deploys on Python 3.13. Anything above the supported range (for example >=3.14) isn't available yet, so commit a Dockerfile if you need it.
Click Deploy
First install pulls FastMCP + Prefab UI + transitive deps (~60–120s). Subsequent deploys reuse Depot's layer cache.
Smoke-test the URL
The response includes structuredContent.$prefab: the serialized component tree the renderer interprets. The view URI in _meta.ui.resourceUri will be something like ui://prefab/tool/<hash>/renderer.html.
Example repo: manufacts/mcp-detect-fastmcp
Live /mcp: warm-wave-cqtmi.run.mcp-use.com/mcp — serverInfo.name is mcp-detect-fastmcp (Manufact server fastmcp-demo, repo mcp-detect-fastmcp; status: running).
Keep `uv.lock` in sync with `pyproject.toml`
The official FastMCP starter once shipped a uv.lock pinned to fastmcp 3.0.1 (without the [apps] extra). uv sync --frozen honours the lock no matter what pyproject.toml says. The fix for production is uv lock on your target Python, then commit both files. Deleting uv.lock only forces the pip fallback — we used that once to unblock the comparison deploy, not as a long-term pattern.
The steps above are what we used for the live demo. Your repo can use the same GitHub deploy flow. Open the dashboard.
Reference server
Use this section if you are following along with the same example. If you already have an MCP app to deploy, the GitHub steps above are enough.
What we deployed
- A FastMCP server with the
[apps]extra installed - An
echotool (text-only) - A
greet_widgettool withapp=Truethat returns aPrefabApprendered as an interactive UI
Project setup
pyproject.toml:
Project layout:
src/__init__.py: empty.
src/config.py:
The server
src/server.py:
Three things happen when you write @mcp.tool(app=True):
- FastMCP infers the return-type annotation.
PrefabApptriggers app-rendering automatically (you can omitapp=Trueif the type is right, but explicit is clearer). - The framework registers a
text/html;profile=mcp-appresource hosting the Prefab renderer at a URI likeui://prefab/tool/<hash>/renderer.html. The renderer is a static JS bundle fromcdn.jsdelivr.netthat interprets the JSON component tree the tool returns. - The tool's
_meta.ui.resourceUriis set to that renderer URI. The host fetches both, calls the tool, sends the structured Prefab tree to the renderer, and the user sees the UI.
The model still sees text: by default, the placeholder string [Rendered Prefab UI]. To give the model a real summary, wrap your return in ToolResult(content="...", structured_content=view).
Run it
Hit it:
You should see both tools and the auto-injected _meta.ui.resourceUri on greet_widget.
When to reach for it
Pick this when your team is Python-only and the widget is data-shaped: charts, tables, dashboards, forms: so Prefab's component palette covers what you need. You'd rather write BarChart(data=…, series=[…]) than wire up Recharts, and you're OK with the model seeing a placeholder string for the tool result unless you wrap it explicitly.
For raw protocol-level control without the Prefab DSL, see mcp-python. If you're not strictly Python, mcp-use gives you the same widget-from-React-component pattern in TypeScript without baking you into a single component palette. The full comparison is in Deploying Seven MCP Frameworks.









