Compare commits

...

13 commits

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

View file

@ -19,6 +19,13 @@ Be friendly and welcoming while maintaining high standards. Call out what works
Even perfect code for unwanted features should be rejected.
### Dependency version compatibility
When a PR adapts code to a new version of a dependency (e.g., removing a parameter that was dropped upstream, using a new API):
- **The version pin in `pyproject.toml` must match.** If the change breaks compatibility with the previously-pinned minimum version, the minimum version must be bumped. Otherwise users on the old version get a regression.
- **If backwards compatibility with the old version is desired**, the code must handle both versions (e.g., try/except, version check). Simply deleting the old API usage without bumping the pin is always wrong — it silently breaks users on the old version.
- **Lock file (`uv.lock`) changes should be scoped to the PR's purpose.** A PR fixing a ty compatibility issue should not also include unrelated dependency version bumps (anthropic, google-auth, etc.) from running `uv sync --upgrade`. These create noise and make the diff harder to review.
### API design and naming
Identify confusing patterns or non-idiomatic code:

View file

@ -0,0 +1,102 @@
---
name: review-pr
description: Monitor and respond to automated PR reviews (Codex bot). Use when pushing a PR, checking review status, or responding to bot feedback. Handles the full cycle of push -> wait for review -> evaluate comments -> fix -> re-push.
---
# PR Review Workflow
This repo has `chatgpt-codex-connector[bot]` configured as an automated reviewer. After every push to a PR branch, Codex reviews the diff and either:
- Reacts with a thumbs-up on its review body (no suggestions — PR is clean)
- Posts inline comments with suggestions (each tagged with a priority badge)
## Checking review status
After pushing, check whether Codex has reviewed the latest commit:
```bash
# Get the latest commit SHA on the branch
LATEST=$(git rev-parse HEAD)
# Check if Codex has reviewed that specific commit
gh api repos/PrefectHQ/fastmcp/pulls/{PR_NUMBER}/reviews \
| jq "[.[] | select(.user.login == \"chatgpt-codex-connector[bot]\" and .commit_id == \"$LATEST\")] | length"
```
If the count is 0, Codex hasn't reviewed the latest push yet. Wait and check again.
If the count is > 0, check for inline comments on the latest review:
```bash
# Get the review body to check for thumbs-up
gh api repos/PrefectHQ/fastmcp/pulls/{PR_NUMBER}/reviews \
| jq '[.[] | select(.user.login == "chatgpt-codex-connector[bot]") | {state, body: .body[:300], commit_id: .commit_id}] | last'
```
A clean review from Codex looks like a review body that contains a thumbs-up reaction or says "no suggestions." If the body contains "Here are some automated review suggestions," there are inline comments to evaluate.
## Evaluating Codex comments
Fetch all inline comments from Codex:
```bash
gh api repos/PrefectHQ/fastmcp/pulls/{PR_NUMBER}/comments \
| jq '[.[] | select(.user.login == "chatgpt-codex-connector[bot]") | {body, path, line, created_at}]'
```
Codex comments include priority badges:
- `P0` (red) — Critical issue, likely a real bug
- `P1` (orange) — Important, worth fixing
- `P2` (yellow) — Moderate, evaluate on merit
**How to evaluate Codex comments:**
1. **Treat Codex as a competent but sometimes overzealous reviewer.** It catches real bugs (cache eviction ordering, silent data loss, missing validation) but also suggests scope expansions and hypothetical improvements.
2. **Fix real bugs** — issues in code you actually changed where behavior is incorrect or data is silently lost.
3. **Dismiss scope expansion** — if a comment points out a pre-existing limitation unrelated to your diff, note it as a potential follow-up but don't block the PR.
4. **Dismiss speculative concerns** — if a comment describes a scenario that requires very specific conditions and the existing behavior is acceptable, dismiss it.
5. **When fixing, be proactive** — if Codex found one instance of a pattern bug (e.g., missing role validation in one handler), check all similar code paths before pushing. Codex will find the next instance on the next review cycle, so get ahead of it.
## Responding to every comment
**Every Codex comment must get a visible response** — either a fix or a reply explaining why it was dismissed. The maintainer can't see your reasoning otherwise.
- **If fixing**: The fix itself is the response. No reply needed unless the fix is non-obvious.
- **If dismissing**: Reply to the comment thread with a brief explanation of why. Keep it to 1-2 sentences. Examples:
- "This is pre-existing behavior unrelated to this diff — the scope lookup fallback existed before caching was added. Worth a follow-up issue but not blocking this PR."
- "The AsyncExitStack handles cleanup when the session exits, so the subprocess isn't leaked — just kept alive slightly longer than necessary in this edge case."
- "Gemini supports a much wider range of media types than OpenAI/Anthropic, so a restrictive allowlist would be inaccurate here."
Use `gh api` to reply (note: use `in_reply_to`, not a `/replies` sub-path):
```bash
# Reply to a specific review comment
gh api repos/PrefectHQ/fastmcp/pulls/{PR_NUMBER}/comments \
-f body="Your reply here" \
-F in_reply_to={COMMENT_ID}
```
## The fix-push-review cycle
After evaluating comments:
1. Fix all real issues in one batch
2. Reply to all dismissed comments with reasoning
3. Think about what patterns Codex might flag next — check similar code paths proactively
4. Commit and push
5. Check that Codex reviews the new commit
6. Repeat until Codex gives a clean review (thumbs-up) or only has dismissible comments
## Responding to stale comments
Codex sometimes re-posts old comments that reference code you've already fixed (they appear on the old commit's diff). These are stale — verify the fix is in the latest commit and reply noting the fix is already in place.
## When a PR is ready
A PR is ready for human review when:
- All Codex comments are either fixed or replied to with dismissal reasoning
- CI checks pass
- The diff is clean and focused on the stated purpose

View file

@ -3,33 +3,30 @@ description: Report a bug or unexpected behavior in FastMCP
labels: [bug, pending]
body:
- type: markdown
attributes:
value: Thanks for contributing to FastMCP! 🙏
- type: markdown
attributes:
value: |
Thanks for reporting a bug!
A good bug report is one of the most valuable contributions you can make — see [CONTRIBUTING.md](../../CONTRIBUTING.md). If the fix is straightforward, a PR is also welcome.
### Before you submit
To help us help you, please:
- 🔄 **Make sure you're testing on the latest version of FastMCP** - many issues are already fixed in newer versions
- 🔍 **Check if someone else has already reported this issue** or if it's been fixed on the main branch
- 📋 **You MUST include a copy/pasteable and properly formatted MRE** (minimal reproducible example) below or your issue may be closed without response
- 💡 **The ideal issue is a clear problem description and an MRE — that's it.** If you've done a genuine investigation and have a non-obvious insight into the root cause, include it. But please don't speculate or ask an LLM to generate a diagnosis or proposed fix. We have LLMs too, and an incorrect analysis is harder to work with than none at all.
- ✂️ **Keep it short.** A one-paragraph description and a working MRE is the ideal bug report. Issues that are difficult to parse — due to length, speculation, or generated content — may be closed without response.
Thanks for helping to make FastMCP better! 🚀
- Make sure you're testing on the **latest version** of FastMCP — many issues are already fixed in newer releases
- Check if someone else has **already reported this** or if it's been fixed on the main branch
- You **must** include a copy/pasteable, properly formatted MRE (minimal reproducible example) or your issue may be closed without response
- **The ideal issue is a clear problem description and an MRE — that's it.** If you've done genuine investigation and have a non-obvious insight into the root cause, include it. But please don't speculate or ask an LLM to generate a diagnosis. We have LLMs too, and an incorrect analysis is harder to work with than none at all.
- **Keep it short.** A clear description plus a concise MRE is ideal — aim to fit in a single screen. Issues that include unsolicited root cause analysis, proposed fixes, or multi-section diagnostic writeups will be labeled `too-long` and not triaged until condensed.
- **Using an LLM?** Great — but it must follow these guidelines. Generic LLM output that ignores our contributing conventions will be closed. See [CONTRIBUTING.md](../../CONTRIBUTING.md).
- type: textarea
id: description
attributes:
label: Description
label: What happened?
description: |
Please explain what you're experiencing and what you would expect to happen instead.
Describe the bug in a few sentences. What did you do, what happened, and what did you expect instead?
Provide as much detail as possible to help us understand and solve your problem quickly.
Do NOT include root cause analysis, proposed fixes, or diagnostic writeups — just describe the problem.
validations:
required: true

View file

@ -3,33 +3,27 @@ description: Suggest an idea or improvement for FastMCP
labels: [enhancement, pending]
body:
- type: markdown
attributes:
value: Thanks for contributing to FastMCP! 🙏
- type: markdown
attributes:
value: |
Thanks for suggesting an improvement to FastMCP!
Enhancement issues are the **primary way** features and improvements get into FastMCP. Maintainers use well-written issues to implement changes that fit the codebase's patterns and ship quickly. A clear issue here is more impactful than a PR — see [CONTRIBUTING.md](../../CONTRIBUTING.md) for why.
### Before you submit
To help us evaluate your enhancement request:
- 🔍 **Check if this has already been requested** - search existing issues first
- 💭 **Think about the broader impact** - how would this affect other users?
- 📋 **Consider implementation complexity** - is this a small change or a major feature?
- ✂️ **Keep it short.** Describe the problem you're trying to solve and why existing behavior falls short. Skip proposed implementations unless you have a specific, well-considered suggestion — we don't need LLM-generated API designs. Requests that are difficult to parse may be closed without response.
Thanks for helping to make FastMCP better! 🚀
- 🔍 **Check if this has already been requested** — search existing issues first
- 🎯 **Describe the problem you're trying to solve**, not the solution you want — we'll figure out the best implementation
- ✂️ **Keep it short.** A motivating description and a concrete use case is the ideal request — aim to fit in a single screen. Skip proposed implementations, API designs, or multi-option analyses — maintainers will figure out the approach. Requests that are difficult to parse will be labeled `too-long` and not triaged until condensed.
- 🤖 **Using an LLM?** Great — but it must follow these guidelines. Generic LLM output that ignores our contributing conventions will be closed. See [CONTRIBUTING.md](../../CONTRIBUTING.md).
- type: textarea
id: description
attributes:
label: Enhancement
description: |
Please describe the enhancement:
What problem or use case does this solve? How does current behavior fall short?
- What problem or use case would it solve?
- How would it improve your workflow or experience with FastMCP?
- Are there any alternative solutions you've considered?
Focus on the *what* and *why* — the motivating scenario. You don't need to propose an API or implementation.
validations:
required: true

View file

@ -3,7 +3,7 @@ description: "Run pytest with appropriate flags for the test type and platform"
inputs:
test-type:
description: "Type of tests to run: unit, integration, or client_process"
description: "Type of tests to run: unit, integration, client_process, or conformance"
required: false
default: "unit"
@ -23,8 +23,13 @@ runs:
TIMEOUT="5"
MAX_PROCS="0"
EXTRA_FLAGS="-x"
elif [ "${{ inputs.test-type }}" == "conformance" ]; then
MARKER="conformance"
TIMEOUT="120"
MAX_PROCS="0"
EXTRA_FLAGS="-x"
else
MARKER="not integration and not client_process"
MARKER="not integration and not client_process and not conformance"
TIMEOUT="5"
MAX_PROCS="4"
EXTRA_FLAGS=""
@ -38,6 +43,7 @@ runs:
uv run --no-sync pytest \
--inline-snapshot=disable \
--timeout=$TIMEOUT \
--durations=50 \
-m "$MARKER" \
$PARALLEL_FLAGS \
$EXTRA_FLAGS \

View file

@ -1,28 +1,22 @@
## Description
<!--
Please provide a clear and concise description of the changes made in this pull request.
Using AI to generate code? Please include a note in the description with which AI tool you used.
-->
<!-- What does this PR do? Link to the issue it addresses. -->
**Contributors Checklist**
<!--
NOTE:
1. You must create an issue in the repository before making a Pull Request.
2. You must not create a Pull Request for an issue that is already assigned to someone else.
Closes #
If you do not follow these steps, your Pull Request will be closed without review.
-->
## Contribution type
- [ ] My change closes #(issue number)
- [ ] I have followed the repository's development workflow
- [ ] I have tested my changes manually and by adding relevant tests
- [ ] I have performed all required documentation updates
<!-- Check the one that applies. If you're unsure whether your change is welcome, please open an issue first — see CONTRIBUTING.md. -->
**Review Checklist**
<!-- Your Pull Request will not be reviewed if tests are failing, you have not self-reviewed your changes, or you have not checked all of the following: -->
- [ ] Bug fix (simple, well-scoped fix for a clearly broken behavior)
- [ ] Documentation improvement
- [ ] Enhancement (maintainers typically implement enhancements — see [CONTRIBUTING.md](../CONTRIBUTING.md))
## Checklist
- [ ] This PR addresses an existing issue (or fixes a self-evident bug)
- [ ] I have read [CONTRIBUTING.md](../CONTRIBUTING.md)
- [ ] I have added tests that cover my changes
- [ ] I have run `uv run prek run --all-files` and all checks pass
- [ ] I have self-reviewed my changes
- [ ] My Pull Request is ready for review
---
- [ ] If I used an LLM, it followed the repo's contributing conventions (not generic output)

26
.github/release.yml vendored
View file

@ -8,12 +8,25 @@ changelog:
labels:
- feature
- title: Enhancements 🔧
- title: Breaking Changes ⚠️
labels:
- breaking change
exclude:
labels:
- contrib
- security
- title: Enhancements ✨
labels:
- enhancement
exclude:
labels:
- breaking change
- security
- title: Security 🔒
labels:
- security
- title: Fixes 🐞
labels:
@ -21,13 +34,7 @@ changelog:
exclude:
labels:
- contrib
- title: Breaking Changes 🛫
labels:
- breaking change
exclude:
labels:
- contrib
- security
- title: Docs 📚
labels:
@ -41,6 +48,9 @@ changelog:
- title: Dependencies 📦
labels:
- dependencies
exclude:
labels:
- security
- title: Other Changes 🦾
labels:

View file

@ -20,7 +20,7 @@ jobs:
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

View file

@ -20,7 +20,7 @@ jobs:
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

View file

@ -29,7 +29,7 @@ jobs:
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

View file

@ -33,7 +33,7 @@ jobs:
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

View file

@ -36,14 +36,9 @@ jobs:
- name: Install dependencies
run: uv sync --python 3.12
- name: Run prek
uses: j178/prek-action@v1
env:
SKIP: no-commit-to-branch
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

View file

@ -38,14 +38,9 @@ jobs:
- name: Install dependencies
run: uv sync --python 3.12
- name: Run prek
uses: j178/prek-action@v1
env:
SKIP: no-commit-to-branch
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

View file

@ -25,7 +25,7 @@ jobs:
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

View file

@ -36,7 +36,7 @@ jobs:
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}
@ -49,7 +49,7 @@ jobs:
PROMPT<<PROMPT_END
You're an issue triage assistant for FastMCP, a Python framework for building Model Context Protocol servers and clients. Your task is to analyze issues/PRs and apply appropriate labels.
IMPORTANT: Your ONLY action should be to apply labels using mcp__github__update_issue. DO NOT post any comments.
IMPORTANT: Your primary action should be to apply labels using mcp__github__update_issue. DO NOT post comments EXCEPT when applying the too-long label (see below).
Issue/PR Information:
- REPO: ${{ github.repository }}
@ -68,7 +68,7 @@ jobs:
3. Analyze and apply labels based on these guidelines:
CORE CATEGORIES (apply EXACTLY ONE - these are mutually exclusive):
CORE CATEGORIES (apply EXACTLY ONE - these are mutually exclusive; skip if applying too-long):
- bug: Reports of broken functionality OR PRs that fix bugs
- enhancement: New functions/endpoints, improvements to existing features, internal tooling, workflow improvements, minor new capabilities
- feature: ONLY for major headline functionality worthy of a blog post announcement (2-4 per release, never for issues)
@ -96,8 +96,12 @@ jobs:
STATUS (apply if applicable):
- needs more info: Issue lacks reproduction steps, error messages, or clear description
- good first issue: ONLY if it's clearly scoped, has obvious solution, and touches limited files
- invalid: Spam, completely off-topic, or nonsensical (often LLM-generated)
- too-long: Apply when an issue or PR doesn't conform to CONTRIBUTING.md. Issues should be a short problem description, an MRE, and expected vs. actual behavior — not a design document. PRs should have a focused description of the change — not a report. We don't need proposed solutions or design alternatives (the issue should describe the problem and let maintainers architect the fix), summaries of what tests cover, explanations of code we can read ourselves, or speculative root-cause analysis. Common LLM failure modes to watch for: verbose "diagnostic" writeups, large proposed patches in issue bodies, multi-section reports restating what's visible in the diff, numbered lists of possible approaches or solutions, "suggested" schemas/shapes/APIs, generic analysis that doesn't reference specific code, and "Notes" sections. But these are heuristics, not rules — a complex PR may legitimately need more context, and a brief submission can still be low-quality. Judge by whether the content helps a reviewer or just adds noise. When applying, do not apply other triage labels. The author needs to condense before triage is worthwhile.
WHEN APPLYING too-long: After labeling, post a brief comment using mcp__github__add_issue_comment:
"Thanks for the report. This issue goes beyond what our contributor guidelines ask for — we just need a short problem description and an MRE. Please see our [contributing guidelines](https://github.com/PrefectHQ/fastmcp/blob/main/CONTRIBUTING.md) and condense this issue. We'll triage it once it's trimmed down."
Use this exact text (or very close to it). Do not editorialize or add details.
AREA LABELS (apply ONLY when thematically central to the issue):
- cli: Issues primarily about FastMCP CLI commands (run, dev, install)
@ -108,6 +112,7 @@ jobs:
- http: HTTP transport or networking is the main issue
- contrib: Specifically about community contributions in src/contrib/
- tests: Issues primarily about testing infrastructure, CI/CD workflows, or test coverage
- security: Apply ONLY when the issue/PR addresses an exploitable vulnerability or hardens against one. Examples: SSRF, LFI, path traversal, injection, auth bypass allowing unauthorized access, scope escalation, open redirects. Do NOT apply for ordinary auth bugs (wrong scopes returned, token refresh logic, OAuth flow correctness) unless an attacker could exploit the bug to bypass access controls or escalate privileges. The key question: "Could a malicious actor exploit this?" If the answer is just "it breaks for legitimate users," that's a bug, not a security issue.
IMPORTANT LABELING RULES:
- Be selective - only apply labels that are clearly relevant
@ -122,7 +127,7 @@ jobs:
4. Apply selected labels:
Use mcp__github__update_issue to apply your selected labels
DO NOT post any comments
DO NOT post any comments unless applying too-long (see above)
PROMPT_END
EOF
@ -139,7 +144,7 @@ jobs:
allowed_non_write_users: "*"
allowed_bots: "marvin-context-protocol"
claude_args: |
--allowedTools Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__get_pull_request_files
--allowedTools Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__add_issue_comment,mcp__github__get_pull_request_files
settings: |
{
"model": "claude-sonnet-4-6",

View file

@ -35,6 +35,6 @@ jobs:
resolution: locked
- name: Run prek
uses: j178/prek-action@v1
uses: j178/prek-action@v2
env:
SKIP: no-commit-to-branch

View file

@ -73,6 +73,29 @@ jobs:
with:
test-type: client_process
run_conformance_tests:
name: "MCP conformance tests"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- name: Setup uv
uses: ./.github/actions/setup-uv
with:
resolution: locked
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "22"
- name: Run conformance tests
uses: ./.github/actions/run-pytest
with:
test-type: conformance
run_integration_tests:
name: "Integration tests"
runs-on: ubuntu-latest

View file

@ -38,7 +38,7 @@ jobs:
resolution: upgrade
- name: Run prek
uses: j178/prek-action@v1
uses: j178/prek-action@v2
env:
SKIP: no-commit-to-branch
@ -105,7 +105,7 @@ jobs:
uses: jayqi/failed-build-issue-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
label: "build-failure"
label: "build failed"
title-template: "Upgrade checks failing on main branch"
body-template: |
## Upgrade Checks Failure on Main Branch
@ -131,3 +131,27 @@ jobs:
---
*This issue was automatically created by a GitHub Action.*
close-on-success:
name: Close issue on success
needs: [static_analysis, run_tests, run_integration_tests]
if: success() && github.event.pull_request == null && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Close resolved failure issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
issue=$(gh issue list \
--repo "$GITHUB_REPOSITORY" \
--label "build failed" \
--state open \
--json number \
--jq '.[0].number // empty')
if [ -n "$issue" ]; then
gh issue close "$issue" \
--repo "$GITHUB_REPOSITORY" \
--comment "Upgrade checks are passing again as of [\`${GITHUB_SHA::7}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA})."
fi

View file

@ -23,7 +23,7 @@ jobs:
steps:
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

View file

@ -23,7 +23,7 @@ jobs:
steps:
- name: Generate Marvin App token
id: marvin-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.MARVIN_APP_ID }}
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}

1
.gitignore vendored
View file

@ -65,6 +65,7 @@ dmypy.json
# Claude worktree management
.claude-wt/worktrees
.claude/worktrees/
# Agents
/PLAN.md

View file

@ -52,6 +52,8 @@ When modifying MCP functionality, changes typically need to be applied across al
## Development Rules
**Read `CONTRIBUTING.md` before opening issues or PRs.** It describes when PRs are appropriate, what we expect from enhancement proposals, and what we'll close without review.
### Git & CI
- Prek hooks are required (run automatically on commits)
@ -62,6 +64,28 @@ When modifying MCP functionality, changes typically need to be applied across al
- **ALWAYS** run prek before PRs
- **NEVER** create a release, comment on an issue, or open a PR unless specifically instructed to do so.
### Releases
Only cut releases when the maintainer explicitly asks. Tags follow `v<version>` (e.g., `v3.2.0`). Always pass `--generate-notes` so the auto-generated changelog appears at the bottom.
**The title pun is critical.** Titles follow `v<version>: <pun>` where the pun relates to the most important theme of the release. Propose multiple options and let the maintainer choose — never pick one yourself. Look at recent releases for tone (e.g., "Code to Joy" for the code mode release, "Three at Last" for 3.0).
Write the maintainer-approved handwritten notes to a temporary file, then create the release. `--generate-notes` appends the auto-generated changelog after the handwritten content.
```bash
gh release create v3.2.0 --target main --title "v3.2.0: Theme Here" --generate-notes --notes-file /tmp/release-notes.md
```
Most releases target `main`, but maintenance or backport releases may target a different branch (e.g., `release/2.x`). Confirm the target with the maintainer if there's any ambiguity.
The handwritten notes are prepended above the auto-generated changelog and are the part that matters. Do not include a title in the notes body — the release title (`v{version}: {pun}`) already serves as the heading. Work with the maintainer to draft the notes — propose a draft, get feedback, iterate. Do not publish without the maintainer's sign-off.
**Before drafting, always read recent existing releases** (`gh release list` then `gh release view <tag>`) to absorb the voice, structure, and level of detail. Each release builds on the tone of previous ones — don't guess at the style from these instructions alone.
**Point releases** (3.0, 3.1, 3.2) get narrative prose: open with the theme of the release, then walk through headline features conceptually — what they enable, why they matter, how they fit together. Write it the way a blog post reads, not a changelog. Multiple paragraphs, code examples where they clarify.
**Patch releases** (3.1.1, 3.0.2) get 1-2 sentences explaining what broke and what the fix does. Keep it minimal — the auto-generated changelog has the details.
### Commit Messages and Agent Attribution
- **Agents NOT acting on behalf of @jlowin MUST identify themselves** (e.g., "🤖 Generated with Claude Code" in commits/PRs)
@ -79,6 +103,14 @@ When modifying MCP functionality, changes typically need to be applied across al
- Minor fixes: keep body short and concise
- No "test plan" sections or testing summaries
### Code Review Guidelines
- **Fix causes, not symptoms.** When a PR works around a problem instead of addressing why it occurs, that's a red flag. A side-channel that compensates for a missing step adds permanent complexity. If the fix doesn't change the code path where the bug actually happens, ask why not.
- Focus on API design and naming clarity
- Identify confusing patterns (e.g., parameter values that contradict defaults) or non-idiomatic code (mutable defaults, etc.). Contributed code will need to be maintained indefinitely, and by someone other than the author (unless the author is a maintainer).
- Suggest specific improvements, not generic "add more tests" comments
- Think about API ergonomics from a user perspective
### Code Standards
- Python ≥ 3.10 with full type annotations
@ -102,6 +134,7 @@ When modifying MCP functionality, changes typically need to be applied across al
- Do not manually modify `docs/python-sdk/**` — these files are auto-generated from source code by a bot and maintained via a long-lived PR. Do not include changes to these files in contributor PRs.
- Do not manually modify `docs/public/schemas/**` or `src/fastmcp/utilities/mcp_server_config/v1/schema.json` — these are auto-generated and maintained via a long-lived PR.
- **Core Principle:** A feature doesn't exist unless it is documented!
- When adding or modifying settings in `src/fastmcp/settings.py`, update `docs/more/settings.mdx` to match.
### Documentation Guidelines
@ -110,6 +143,7 @@ When modifying MCP functionality, changes typically need to be applied across al
- **Structure:** Headers form navigation guide, logical H2/H3 hierarchy
- **Content:** User-focused sections, motivate features (why) before mechanics (how)
- **Style:** Prose over code comments for important information
- **Docstrings:** FastMCP docstrings are automatically compiled into MDX documents. Use markdown (single backticks, fenced code blocks), not RST (no double backticks). Bare `{}` in examples will be interpreted as JSX — wrap in backticks instead.
## Critical Patterns

53
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,53 @@
# Contributing to FastMCP
FastMCP is an actively maintained, high-traffic project. We welcome contributions — but the most impactful way to contribute might not be what you expect.
## The best contribution is a great issue
FastMCP is an opinionated framework, and its maintainers use AI-assisted tooling that is deeply tuned to those opinions — the design philosophy, the API patterns, the way the framework is meant to evolve. A well-written issue with a clear problem description is often more valuable than a pull request, because it lets maintainers produce a solution that isn't just correct, but consistent with how the framework wants to work. That matters more than speed, though it's faster too.
**A great issue looks like this:**
1. A short, motivating description of the problem or gap
2. A minimal reproducible example (for bugs) or a concrete use case (for enhancements)
3. A brief note on expected vs. actual behavior
That's it. No need to diagnose root causes, propose API designs, or suggest implementations. If you've done genuine investigation and have a non-obvious insight, include it.
## Using AI to contribute
We encourage you to use LLMs to help identify bugs, write MREs, and prepare contributions. But if you do, your LLM must take into account the conventions and contributing guidelines of this repo — including how we want issues formatted and when it's appropriate to open a PR. Generic LLM output that ignores these guidelines tells us the contribution wasn't made thoughtfully, and we will close it. A good AI-assisted contribution is indistinguishable from a good human one. A bad one is obvious.
## When to open a pull request
An open issue is not an invitation to submit a PR. Issues track problems; whether and how to solve them is a separate decision. If you want to work on something, propose your approach in the issue first — especially for anything beyond a trivial fix.
**Bug fixes** — PRs are welcome for simple, well-scoped bug fixes where the problem and solution are both straightforward. "The function raises `TypeError` when passed `None` because of a missing guard" is a good candidate. If the fix requires design decisions or touches multiple subsystems, open an issue with a design proposal instead.
**Documentation** — Typo fixes, clarifications, and improvements to examples are always welcome as PRs.
**Enhancements and features** — We welcome enhancement PRs, but our experience is that most contributors — even when using LLMs — implement fixes that address the one instance of a problem they encountered rather than understanding why the framework produces that problem and fixing it at the right layer. This creates branching, patch-style code that's difficult to maintain and makes it impossible to reason about the framework as a coherent system. For this reason, enhancements need a design proposal in the issue before code is written. The proposal doesn't need to be long — just enough to show you've thought about how the change fits into the framework, not just how it solves your immediate case.
**Integrations** — FastMCP generally does not accept PRs that add third-party integrations (custom middleware, provider-specific adapters, etc.). If you're building something for your users, ship it as a standalone package — that's a feature, not a limitation. Authentication providers are an exception, since auth is tightly coupled to the framework.
## PR guidelines
If you do open a PR:
- **Reference an issue.** Every PR should address a tracked issue. If there isn't one, open an issue first. This isn't a permission step — you don't need to wait for a response. But the issue gives us context on the problem, and if a maintainer is already working on it, we can let you know before you invest time in code.
- **Keep it focused.** One logical change per PR. Don't bundle unrelated fixes or refactors.
- **Match existing patterns.** Follow the code style, type annotation conventions, and test patterns you see in the codebase. Run `uv run prek run --all-files` before submitting.
- **Write tests.** Bug fixes should include a test that fails without the fix. Enhancements should include tests for the new behavior.
- **Fix the cause, not the symptom.** If the bug is that a code path skips a step, the fix should make it stop skipping that step — not add compensation elsewhere. Workaround-style fixes will be sent back for revision.
- **Don't submit generated boilerplate.** We review every line. PRs that read like unedited LLM output — verbose descriptions, speculative changes, shotgun-style fixes — will be closed.
## What we'll close without review
To keep the project maintainable, we will close PRs that:
- Don't reference an issue or address a clearly self-evident bug
- Make sweeping changes without prior discussion
- Add third-party integrations that belong in a separate package
- Are difficult to review due to size, scope, or generated content
This isn't personal — contributing to a framework is different from contributing to an application. In an application, a fix that works is a good fix. In a framework, a fix that works but doesn't fit the framework's design creates maintenance burden that compounds over time. Every patch that works around a problem instead of solving it at the right layer makes the system harder for *everyone* to reason about — maintainers, contributors, and users. We hold contributions to this standard because the alternative is a codebase that's a series of patches rather than a coherent system. A good issue is often the best thing you can do for the project.

View file

@ -77,7 +77,13 @@ FastMCP has three pillars:
**[Servers](https://gofastmcp.com/servers/server)** wrap your Python functions into MCP-compliant tools, resources, and prompts. **[Clients](https://gofastmcp.com/clients/client)** connect to any server with full protocol support. And **[Apps](https://gofastmcp.com/apps/overview)** give your tools interactive UIs rendered directly in the conversation.
Ready to build? Start with the [installation guide](https://gofastmcp.com/getting-started/installation) or jump straight to the [quickstart](https://gofastmcp.com/getting-started/quickstart). When you're ready to deploy, [Prefect Horizon](https://www.prefect.io/horizon) offers free hosting for FastMCP users.
Ready to build? Start with the [installation guide](https://gofastmcp.com/getting-started/installation) or jump straight to the [quickstart](https://gofastmcp.com/getting-started/quickstart).
## Run FastMCP in production with Horizon
FastMCP is how teams build MCP servers. **[Prefect Horizon](https://www.prefect.io/horizon)** is how enterprises run them in production. Register any MCP server behind a managed gateway with SSO, tool-level RBAC, audit logs, and observability. Deploy FastMCP servers and go from PR to preview in 60 seconds, then remix tools from across your registry into use-case-specific, permissioned endpoints. Horizon is everything we've learned about MCP at scale from building the world's most popular MCP framework. Free for individuals, built for teams.
[Deploy FastMCP with Horizon →](https://www.prefect.io/horizon)
## Installation

View file

@ -2,15 +2,33 @@
## Supported Versions
FastMCP v2.x receives security updates. Earlier versions are no longer supported.
| Version | Supported |
| ------- | ------------------ |
| 2.x | :white_check_mark: |
| < 2.0 | :x: |
| 3.x | :white_check_mark: |
| 2.x | :x: |
| 1.x | :x: |
| 0.x | :x: |
## Reporting a Vulnerability
Please report security vulnerabilities privately using [GitHub's security advisory feature](https://github.com/PrefectHQ/fastmcp/security/advisories/new).
Please report security vulnerabilities privately using [GitHub's security advisory feature](https://github.com/PrefectHQ/fastmcp/security/advisories/new). Do not open public issues for security concerns.
Do not open public issues for security concerns.
## Scope
We accept reports for vulnerabilities in FastMCP itself — the library code in this repository.
The following are **out of scope**:
- Vulnerabilities in third-party dependencies or the MCP SDK itself. We'll bump version floors for known CVEs, but the fix belongs upstream.
- Limitations of upstream identity providers that FastMCP cannot control.
- Issues that require the attacker to already have server-side access or control of the MCP server configuration.
## Disclosure Process
When we receive a valid report:
1. We triage the report and determine whether it affects FastMCP directly.
2. We develop and test a fix on a private branch.
3. We coordinate CVE assignment through GitHub's advisory process when warranted.
4. We publish the advisory and release a patched version.
5. We credit the reporter in the advisory (unless they prefer otherwise).

119
docs/apps/architecture.mdx Normal file
View file

@ -0,0 +1,119 @@
---
title: App Architecture
sidebarTitle: Architecture
description: How FastMCP apps work under the hood — from Python to pixels.
icon: sitemap
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
This page explains how Prefab apps work under the hood — how your Python code becomes an interactive UI inside a host client's conversation. You don't need any of this to build apps, but the mental model is useful when something isn't rendering the way you expect, when tool calls from the UI aren't reaching your server, or when you're building [custom HTML apps](/apps/low-level) and need to understand the protocol directly.
## The Pipeline
An MCP App moves through five stages from Python to pixels:
```
Python components → JSON tree → structuredContent → Renderer iframe → Host UI
```
You write Prefab components in Python. FastMCP serializes them to a JSON component tree and delivers it as `structuredContent` on the tool result. The host loads the Prefab renderer in a sandboxed iframe, pushes the JSON into it, and the renderer paints the UI. If the UI needs to call server tools, it talks back through the same `postMessage` channel.
The following sections walk through each stage.
## Tool Registration
When you mark a tool with `app=True` or `@app.ui()`, FastMCP wires up the metadata and renderer resource that the protocol requires.
### The `app=True` Flag
The `app` parameter on `@mcp.tool` accepts `True`, an `AppConfig` object, or a dict. When you pass `True`, FastMCP checks whether the tool's return type is a Prefab type (`PrefabApp`, `Component`, or unions containing them). If the tool qualifies, FastMCP expands `True` into a full `AppConfig` — setting the renderer URI, CSP headers, and visibility — and stores it in the tool's `meta["ui"]` dict.
This expansion also triggers registration of the shared Prefab renderer resource (discussed below). The tool and the renderer are linked through a `resourceUri` field in the metadata: the tool says "render me with `ui://prefab/renderer.html`", and the host fetches that resource when it needs to display the result.
Type inference works the same way. If your return type annotation is a Prefab type and you haven't set `app` explicitly, FastMCP auto-wires the metadata as if you'd written `app=True`.
### FastMCPApp Registration
`FastMCPApp` uses the same underlying mechanism but adds two things. First, it tags every tool — both `@app.ui()` entry points and `@app.tool()` backends — with `meta["fastmcp"]["app"]` set to the app's name. This tag is how the server identifies which app a tool belongs to when routing calls from the UI.
Second, it sets `meta["ui"]["visibility"]` to control who can see each tool. Entry points default to `["model"]` (visible to the LLM). Backend tools default to `["app"]` (visible only to the UI). Hosts use this to filter the tool list — the model sees entry points, and the UI sees backends.
## Serialization
When a Prefab tool runs, its return value — a `PrefabApp` or a raw `Component` — needs to become a JSON blob that the renderer can interpret.
### PrefabApp.to_json()
The serialization entry point is `PrefabApp.to_json()`. This method walks the component tree and produces a JSON object with three top-level keys: `view` (the component tree), `state` (initial state values), and `_meta` (routing metadata).
FastMCP passes a `tool_resolver` callback to `to_json()`. Whenever the component tree contains a `CallTool` action that references a function (not a string), the resolver converts it to a `ResolvedTool` with the function's registered name. This is how `CallTool(save_contact)` becomes `CallTool("save_contact")` in the wire format. The resolver also handles `unwrap_result` — a flag that tells the renderer to unwrap single-value results from the `{"result": value}` envelope that FastMCP uses for schema compliance.
### The _meta.fastmcp.app Tag
After `to_json()` produces the JSON tree, FastMCP injects `_meta.fastmcp.app` with the app's name (if the tool belongs to a `FastMCPApp`). This tag rides along inside `structuredContent` all the way to the renderer.
When the renderer calls a backend tool, it includes `_meta.fastmcp.app` in the `CallTool` request. The server sees this tag and routes the call through a special path that bypasses transforms — more on this in the next section.
### ToolResult Assembly
The final tool result has two parts: `content` (a list of `TextContent` blocks for the LLM) and `structuredContent` (the JSON tree for the renderer). By default, Prefab tools send `"[Rendered Prefab UI]"` as the text content — just enough for the LLM to know something was rendered. If you return a `ToolResult` explicitly, you control both halves.
## Tool Call Routing
When a host calls a tool, the server needs to find it. Normal tool calls go through the provider chain, which applies transforms (namespace prefixes, visibility filters, etc.) before resolving the tool by name. But app UI calls need a different path.
### The get_app_tool Bypass
Backend tools registered with `@app.tool()` are typically hidden from the model (`visibility=["app"]`). Visibility transforms would filter them out of normal resolution. And namespace transforms might rename them — `save_contact` becomes `contacts_save_contact` — but the renderer still uses the original name.
`get_app_tool` solves both problems. When the server sees `_meta.fastmcp.app` on an incoming `CallTool` request, it calls `get_app_tool(app_name, tool_name)` instead of the normal `get_tool(name)`. This method walks the provider tree directly, skipping the transform chain entirely. It finds the tool by its original registered name and verifies that its `meta["fastmcp"]["app"]` matches the expected app identity.
This is why `CallTool("save_contact")` keeps working even when the server is mounted under a namespace prefix. The renderer sends the original name plus the app identity; the server uses `get_app_tool` to find the tool without transforms getting in the way.
Authorization checks still apply — `get_app_tool` bypasses transforms, but it runs auth checks against the tool's `auth` configuration before executing.
### Provider Delegation
The `get_app_tool` method is defined on the `Provider` base class and overridden by aggregate and wrapped providers. Aggregate providers fan out the lookup across all child providers in parallel. Wrapped providers (like `FastMCPProvider`, which wraps a nested `FastMCP` server) delegate to the inner server's `get_app_tool`. This means backend tools are reachable through any depth of server composition.
## The Renderer
The Prefab renderer is a self-contained JavaScript application that interprets the JSON component tree and renders it as a React UI.
### The Shared Resource
FastMCP registers the renderer as a `ui://prefab/renderer.html` resource with MIME type `text/html;profile=mcp-app`. The renderer HTML is bundled inside the `prefab-ui` Python package — `get_renderer_html()` returns it as a string. All Prefab tools on a server share this single resource, regardless of how many tools or apps are registered.
The resource also carries CSP metadata (via `get_renderer_csp()`) declaring which CDN domains the renderer needs to load its JavaScript dependencies. Hosts use this to configure the iframe's Content Security Policy.
### postMessage Communication
The renderer lives in a sandboxed iframe. It communicates with the host using `postMessage` — the standard browser API for cross-origin iframe communication. The protocol follows the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) specification:
The host pushes the tool result (including `structuredContent`) into the iframe. The renderer parses the JSON component tree, initializes state, and renders the UI. When the user interacts with the UI — submitting a form, clicking a button — and that interaction triggers a `CallTool` action, the renderer sends a `callServerTool` message back to the host via `postMessage`. The host forwards this as a regular MCP `tools/call` request to the server, including `_meta.fastmcp.app` for routing.
The response flows back the same way: server to host, host to iframe via `postMessage`, renderer updates state with the result.
### AppBridge
The `@modelcontextprotocol/ext-apps` JavaScript SDK provides the `App` class (sometimes called AppBridge) that manages the `postMessage` handshake. It handles connection negotiation, tool result delivery, server tool calls, and host context (like safe area insets and theme preferences). The Prefab renderer uses this SDK internally — you only interact with it directly when building [custom HTML apps](/apps/low-level).
## The Dev Server
`fastmcp dev apps` provides a local preview environment that simulates the host-side behavior without requiring a real MCP host client.
### Proxy Architecture
The dev server runs two HTTP servers. Your MCP server starts on port 8000 (configurable) with the Streamable HTTP transport. The dev UI runs on port 8080 and serves a picker page that lists your app tools.
A reverse proxy at `/mcp` on the dev server forwards requests to your MCP server. This is important because the renderer iframe runs on `localhost:8080`, and your MCP server runs on `localhost:8000`. Without the proxy, the renderer's `callServerTool` requests would be cross-origin and blocked by the browser. The proxy makes everything same-origin from the iframe's perspective.
### The Launch Flow
When you select a tool and click launch, the dev UI calls the tool through the proxy, receives the `structuredContent` response, and opens a new tab. That tab loads the tool's renderer resource (fetched from the proxy) in an iframe, creates an AppBridge instance, and pushes the tool result into the renderer. From this point forward, the experience matches what a real host would provide — the renderer displays the UI, and any `CallTool` actions route back through the proxy to your MCP server.
Auto-reload is enabled by default, so changes to your server code restart the MCP server automatically. The dev UI stays running — just re-launch the tool to see your changes.

66
docs/apps/development.mdx Normal file
View file

@ -0,0 +1,66 @@
---
title: Development
sidebarTitle: Development
description: Preview and test your app tools locally without a full MCP host.
icon: flask
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
<Frame>
<img src="/apps/images/dev-app.png" alt="The dev UI showing a rendered Prefab app with the MCP inspector panel" />
</Frame>
`fastmcp dev apps` launches a browser-based preview for your app tools. It starts your MCP server and a local dev UI side by side — you pick a tool, fill in its arguments, and see the rendered result in a new tab. No MCP host client needed.
This works with both [Prefab apps](/apps/prefab) and [custom HTML apps](/apps/low-level).
## Quick Start
```bash
fastmcp dev apps server.py
```
The dev UI opens at `http://localhost:8080`. Your MCP server runs on port 8000 with auto-reload enabled by default — save a file and the server restarts automatically.
## How It Works
The dev server does three things:
The **picker page** connects to your MCP server, finds all tools with UI metadata, and renders a form for each one. The forms are auto-generated from the tool's input schema — text fields, dropdowns, checkboxes, all wired up.
When you submit a form, the dev server **calls your tool** via the MCP protocol and opens the result in a new tab. The result page loads the tool's UI resource (the Prefab renderer or your custom HTML) inside an AppBridge — the same protocol that real MCP hosts use.
A **reverse proxy** on `/mcp` forwards requests from the browser to your MCP server, avoiding CORS issues that would otherwise block the iframe-based renderer from talking to a different port.
## MCP Inspector
The dev UI includes an inspector panel on the left side that captures MCP traffic in real time. It shows JSON-RPC messages flowing between the browser and your server — requests, responses, and AppBridge `postMessage` traffic.
Each entry shows direction, method, timing, and a smart summary. Click any entry to expand the full JSON-RPC body. The panel auto-scrolls to new messages unless you've scrolled up to inspect older ones.
The inspector is useful for debugging: you can see exactly what arguments your tool received, what it returned, and how the AppBridge communicated with the renderer.
## Options
```bash
fastmcp dev apps server.py:mcp --mcp-port 9000 --dev-port 9090 --no-reload
```
| Option | Flag | Default | Description |
| ------ | ---- | ------- | ----------- |
| MCP Port | `--mcp-port` | `8000` | Port for your MCP server |
| Dev Port | `--dev-port` | `8080` | Port for the dev UI |
| Auto-Reload | `--reload` / `--no-reload` | On | Watch files and restart the server on changes |
## Multiple Tools
If your server has multiple app tools, the picker shows a dropdown. Each tool gets its own form and launch button. The tool's `title` is displayed when available, falling back to the tool name.
```bash
# Server with multiple app tools
fastmcp dev apps examples/apps/contacts/contacts_server.py
```

140
docs/apps/examples.mdx Normal file
View file

@ -0,0 +1,140 @@
---
title: Examples
sidebarTitle: Examples
description: Example apps you can run right now.
icon: images
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
Every example below is a working FastMCP server you can run with `fastmcp dev apps` or connect to from any MCP host. The source is in `examples/apps/` in the repository.
<Columns cols={2}>
<Tile href="#sales-dashboard" title="Sales Dashboard" description="Metrics, charts, and deal pipeline">
<div style={{overflow: "hidden", width: "100%"}}>
<img src="/apps/images/app-example-sales-dashboard.png" />
</div>
</Tile>
<Tile href="#system-monitor" title="System Monitor" description="Live CPU, memory, disk with auto-refresh">
<img src="/apps/images/app-example-system-dashboard.png" />
</Tile>
<Tile href="#quiz" title="Quiz" description="LLM-generated trivia with scoring">
<img src="/apps/images/app-example-quiz.png" />
</Tile>
<Tile href="#interactive-map" title="Interactive Map" description="Geocoded addresses on Leaflet">
<img src="/apps/images/app-example-map.png" />
</Tile>
<Tile href="/apps/providers/file-upload" title="File Upload" description="Drag-and-drop upload provider">
<img src="/apps/images/app-file-upload.png" />
</Tile>
<Tile href="/apps/providers/approval" title="Approval" description="Human-in-the-loop confirmation">
<img src="/apps/images/app-approval.png" />
</Tile>
<Tile href="/apps/providers/choice" title="Choice" description="Clickable option selection">
<img src="/apps/images/app-choice.png" />
</Tile>
<Tile href="/apps/providers/form" title="Form Input" description="Pydantic model forms">
<img src="/apps/images/app-form.png" />
</Tile>
<Tile href="/apps/generative" title="Generative UI" description="LLM writes the UI at runtime">
<img src="/apps/images/app-showcase.png" />
</Tile>
</Columns>
## Running Examples
Preview any example in your browser with the dev server:
```bash
pip install "fastmcp[apps]"
fastmcp dev apps examples/apps/sales_dashboard/sales_dashboard_server.py
```
The dev server opens an interactive browser UI where you can select a tool and provide arguments. In a real deployment, the LLM provides these arguments on the fly based on the conversation. For example, the quiz example works best when connected to an MCP host like Goose or Claude Desktop, where the LLM generates the questions itself.
## Standalone Examples
### Sales Dashboard
A full dashboard with KPI metrics, revenue trends, segment breakdown, and a deal pipeline table. Shows what you can build with a single `app=True` tool and Prefab's chart and data components.
```bash
fastmcp dev apps examples/apps/sales_dashboard/sales_dashboard_server.py
```
### System Monitor
Reads live CPU, memory, and disk stats from your machine using `psutil`. Auto-refreshes via `SetInterval` calling a backend tool, with a dropdown to control the refresh rate. The chart accumulates 100 data points over time.
```bash
pip install psutil
fastmcp dev apps examples/apps/system_monitor/system_monitor_server.py
```
### Quiz
The LLM generates trivia questions and passes them to the tool. The user answers via buttons, sees correct/incorrect feedback, and tracks score across questions. Demonstrates multi-turn client-side state with FastMCPApp.
```bash
fastmcp dev apps examples/apps/quiz/quiz_server.py
```
### Interactive Map
Accepts addresses or place names, geocodes them via OpenStreetMap Nominatim (free, no API key), and renders an interactive Leaflet map using Prefab's `Embed` component with inline HTML. Proves that Prefab apps aren't limited to built-in components.
```bash
fastmcp dev apps examples/apps/map/map_server.py
```
## Built-in Providers
These are ready-made capabilities you add with a single `add_provider()` call.
### [File Upload](/apps/providers/file-upload)
Drag-and-drop file upload. The user drops files, clicks Upload, and the server stores them. The LLM can list and read uploaded files through model-visible tools.
```python
from fastmcp.apps.file_upload import FileUpload
mcp.add_provider(FileUpload())
```
### [Approval](/apps/providers/approval)
Human-in-the-loop confirmation. The LLM presents what it's about to do, the user clicks Approve or Reject, and the decision flows back as a message.
```python
from fastmcp.apps.approval import Approval
mcp.add_provider(Approval())
```
### [Choice](/apps/providers/choice)
Present clickable options instead of asking users to type. Clean structured input without parsing free text.
```python
from fastmcp.apps.choice import Choice
mcp.add_provider(Choice())
```
### [Form Input](/apps/providers/form)
Generate a validated form from a Pydantic model. Submission is validated against the model before being returned.
```python
from fastmcp.apps.form import FormInput
mcp.add_provider(FormInput(model=MyModel))
```
### [Generative UI](/apps/providers/generative)
The LLM writes Prefab Python code at runtime and the result renders as a streaming interactive UI. Tailored visualizations for any data. See the [full guide](/apps/generative) for details.
```python
from fastmcp.apps.generative import GenerativeUI
mcp.add_provider(GenerativeUI())
```

133
docs/apps/generative.mdx Normal file
View file

@ -0,0 +1,133 @@
---
title: Generative UI
sidebarTitle: Generative UI
description: Let the LLM build custom Prefab UIs on the fly.
icon: wand-magic-sparkles
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
Generative UI means the LLM writes the UI code at runtime. Instead of calling a pre-built tool with a fixed interface, the model writes Prefab Python code tailored to the current data and request. The user watches the UI build up in real time as the model generates code.
```python
from fastmcp import FastMCP
from fastmcp.apps.generative import GenerativeUI
mcp = FastMCP("Prefab Studio")
mcp.add_provider(GenerativeUI())
```
That's it. The `GenerativeUI` provider registers everything:
- **`generate_prefab_ui`** — a tool that accepts Python code, executes it in a Pyodide sandbox, and renders the result as a Prefab app
- **`search_prefab_components`** — a tool that lets the LLM search the Prefab component library to discover what's available
- **The generative renderer** — a `ui://` resource with browser-side Pyodide for streaming progressive rendering
## How It Works
When the LLM decides to call `generate_prefab_ui`, it writes Prefab Python code into the `code` argument. The MCP Apps protocol creates the renderer iframe in parallel with the tool call, so the app is already running when partial arguments start flowing.
As the LLM generates each token:
1. The host forwards partial arguments to the app via `ontoolinputpartial`
2. The renderer extracts the growing `code` string
3. Browser-side Pyodide executes whatever compiles successfully
4. The user sees components appear as they're written
When the LLM finishes, the server runs the complete code in a server-side Pyodide sandbox for validation, and the renderer replaces the streaming preview with the final server-validated result.
## What the LLM Writes
The tool description includes code examples that teach the LLM the Prefab patterns. A typical generation looks like:
```python
from prefab_ui.components import Column, Row, Heading, Text, Badge, Card, CardContent
from prefab_ui.components.charts import BarChart, ChartSeries
from prefab_ui.app import PrefabApp
with PrefabApp() as app:
with Column(gap=6, css_class="p-6"):
Heading("Q3 Revenue Report")
BarChart(
data=[
{"month": "Jul", "revenue": 42000},
{"month": "Aug", "revenue": 51000},
{"month": "Sep", "revenue": 63000},
],
series=[ChartSeries(data_key="revenue", label="Revenue")],
x_axis="month",
)
with Row(gap=4):
with Card():
with CardContent():
Text("Total", css_class="text-sm text-muted-foreground")
Heading("$156,000")
with Card():
with CardContent():
Text("Growth", css_class="text-sm text-muted-foreground")
Badge("+18%", variant="success")
```
The model writes real Python — loops, f-strings, computation, helper functions. Prefab's component library gives it charts, tables, forms, cards, badges, and layout primitives to work with.
## The Component Search Tool
Before writing code, the LLM can call `search_prefab_components` to discover what's available:
```
search_prefab_components("Chart")
→ 7 components matching 'Chart':
AreaChart — from prefab_ui.components.charts import AreaChart
BarChart — from prefab_ui.components.charts import BarChart
...
```
Passing `detail=True` returns full field descriptions and docstrings. The search tool introspects the actual Prefab classes at runtime, so it's always up to date with the installed version.
## Passing Data
The `generate_prefab_ui` tool accepts a `data` parameter. Values passed here become global variables in the sandbox:
```python
# The LLM can reference 'sales_data' directly in its code
result = await generate_prefab_ui(
code="...",
data={"sales_data": [{"month": "Jan", "revenue": 42000}, ...]}
)
```
This lets the model use real data from earlier in the conversation to build visualizations.
## Configuration
`GenerativeUI` accepts options for customizing tool names:
```python
GenerativeUI(
tool_name="generate_prefab_ui", # default
components_tool_name="search_prefab_components", # default
include_components_tool=True, # default
)
```
## Requirements
Generative UI requires `fastmcp[apps]` which installs `prefab-ui`. The Pyodide sandbox (for server-side validation) requires Deno — it installs automatically on first use.
The streaming renderer loads Pyodide from CDN in the browser. The CSP is configured automatically by the provider — no manual setup needed.
## Sandbox Limitations
The Pyodide sandbox includes the Python standard library and Prefab. External packages (NumPy, pandas, requests, etc.) are **not available** — the LLM's code must work with only built-in Python and Prefab components. If the LLM tries to import an unavailable package, the sandbox will raise an `ImportError`.
## Next Steps
- **[GenerativeUI Provider Reference](/apps/providers/generative)** — Configuration options and quick setup
- **[Prefab UI](/apps/prefab)** — The component library and state system the LLM writes code against
- **[Prefab Component Reference](https://prefab.prefect.io/docs/components)** — Full component library documentation
- **[Development](/apps/development)** — Preview generative UI tools locally with `fastmcp dev apps`

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,001 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

View file

@ -0,0 +1,538 @@
---
title: FastMCPApp
sidebarTitle: FastMCPApp
description: Managed tool binding, visibility, and composition for apps with heavy server interaction.
icon: puzzle-piece
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
<Tip>
[Prefab](https://prefab.prefect.io) is in early, active development — its API changes frequently and breaking changes can occur with any release. Always pin `prefab-ui` to a specific version in your dependencies.
</Tip>
Any [Prefab app](/apps/prefab) can call server tools — there's nothing stopping you from using `CallTool("tool_name")` in a regular `@mcp.tool(app=True)`. But once you have multiple backend tools, the management overhead adds up: Which tools should the model see vs. only the UI? What happens to string-based tool references when servers are composed under namespaces? How do you keep things wired correctly as the app grows?
`FastMCPApp` is a class that solves these problems. It gives you two decorators that work together:
- **`@app.ui()`** — entry-point tools the model calls to open the app. These return a Prefab UI.
- **`@app.tool()`** — backend tools the UI calls via `CallTool`. These do the work.
Backend tools get globally stable identifiers that survive namespacing. Visibility is managed automatically — the model sees entry points, the UI sees backends. And `CallTool` accepts function references instead of strings, so references are refactorable and composition-safe.
## Your First Interactive App
Here's a minimal app with a form that saves data:
```python
from prefab_ui.actions import SetState, ShowToast
from prefab_ui.actions.mcp import CallTool
from prefab_ui.app import PrefabApp
from prefab_ui.components import (
Badge, Button, Column, ForEach, Form,
Heading, Input, Row, Separator, Text,
)
from prefab_ui.rx import RESULT
from fastmcp import FastMCP, FastMCPApp
app = FastMCPApp("Notes")
notes_db: list[dict] = []
@app.tool()
def add_note(title: str, body: str) -> list[dict]:
"""Save a note and return all notes."""
notes_db.append({"title": title, "body": body})
return list(notes_db)
@app.ui()
def notes_app() -> PrefabApp:
"""Open the notes app."""
with Column(gap=6, css_class="p-6") as view:
Heading("Notes")
with ForEach("notes") as note:
with Row(gap=2, align="center"):
Text(note.title, css_class="font-semibold")
Badge(note.body)
Separator()
with Form(
on_submit=CallTool(
"add_note",
on_success=[
SetState("notes", RESULT),
ShowToast("Note saved!", variant="success"),
],
on_error=ShowToast("Failed to save", variant="error"),
)
):
Input(name="title", label="Title", required=True)
Input(name="body", label="Body", required=True)
Button("Add Note")
return PrefabApp(view=view, state={"notes": list(notes_db)})
mcp = FastMCP("Notes Server", providers=[app])
```
When the model calls `notes_app`, the user sees a form. Submitting it calls `add_note` on the server, updates the state with the result, and shows a toast — all without leaving the UI.
Let's break down the key concepts.
## Entry Points: @app.ui()
Entry points are what the model sees and calls to open your app. They return a Prefab UI, just like display tools:
```python
@app.ui()
def dashboard() -> PrefabApp:
"""The model calls this to open the dashboard."""
with Column(gap=4, css_class="p-6") as view:
Heading("Dashboard")
# ... build UI ...
return PrefabApp(view=view)
```
Entry points default to `visibility=["model"]` — they show up in the tool list for the LLM but aren't callable from within the app UI. They support the same options as `@mcp.tool`: `name`, `description`, `title`, `tags`, `icons`, `auth`, and `timeout`.
```python
@app.ui(title="Contact Manager", description="Open the contact management interface")
def contact_manager() -> PrefabApp:
...
```
## Backend Tools: @app.tool()
Backend tools do the work. The UI calls them via `CallTool`; they run on the server and return data:
```python
@app.tool()
def save_contact(name: str, email: str) -> list[dict]:
"""Save a contact and return the updated list."""
db.append({"name": name, "email": email})
return list(db)
```
By default, backend tools are only visible to the app UI (`visibility=["app"]`). The model doesn't see them in the tool list. If you want a tool callable by both the model and the UI, pass `model=True`:
```python
@app.tool(model=True)
def list_contacts() -> list[dict]:
"""Both the model and the UI can call this."""
return list(db)
```
Backend tools support `name`, `description`, `auth`, and `timeout`:
```python
@app.tool(description="Search contacts by name or email", timeout=10.0)
def search(query: str) -> list[dict]:
...
```
## Connecting UI to Backend: CallTool
`CallTool` is the bridge between the UI and the server. Pass the name of a backend tool registered with `@app.tool()`:
```python
from prefab_ui.actions.mcp import CallTool
# Reference a backend tool by name
CallTool("save_contact", arguments={"name": "Alice", "email": "alice@example.com"})
# Arguments can reference state with Rx
from prefab_ui.rx import STATE
CallTool("search", arguments={"query": STATE.search_term})
```
FastMCPApp resolves the name to the tool's stable global key automatically, so `CallTool("save_contact")` keeps working even when the server is mounted under a namespace.
You can also pass the function directly — `CallTool(save_contact)` — which can be convenient when the tool is defined in the same file. Both forms resolve identically.
### Handling Results
Server calls are asynchronous. Use `on_success` and `on_error` callbacks to handle outcomes:
```python
from prefab_ui.actions import SetState, ShowToast
from prefab_ui.rx import RESULT
CallTool(
"save_contact",
on_success=[
SetState("contacts", RESULT),
ShowToast("Saved!", variant="success"),
],
on_error=ShowToast("Something went wrong", variant="error"),
)
```
`RESULT` is a reactive reference to the value the tool returned — available inside `on_success` callbacks. Similarly, `ERROR` (from `prefab_ui.rx`) is available inside `on_error`.
Callbacks can be a single action or a list of actions. They execute in order, and an error in any action short-circuits the rest.
### result_key Shorthand
When a tool returns data that should replace a state key, `result_key` is a convenient shorthand for `on_success=SetState(key, RESULT)`:
```python
CallTool("list_contacts", result_key="contacts")
# equivalent to:
CallTool(
"list_contacts",
on_success=SetState("contacts", RESULT),
)
```
## Actions
`CallTool` is one of several actions available in Prefab. Actions are events attached to component handlers like `on_click`, `on_submit`, and `on_change`.
### Client Actions
These run instantly in the browser — no server round-trip:
```python
from prefab_ui.actions import SetState, ToggleState, AppendState, PopState, ShowToast
# Set a value
SetState("count", 42)
# Toggle a boolean
ToggleState("expanded")
# Append to a list
AppendState("items", {"name": "New Item"})
# Remove by index
PopState("items", 0)
# Show a notification
ShowToast("Done!", variant="success")
```
### Chaining Actions
Pass a list to execute multiple actions in sequence:
```python
from prefab_ui.components import Button
from prefab_ui.actions import SetState, ShowToast
Button(
"Reset",
on_click=[
SetState("query", ""),
SetState("results", []),
ShowToast("Cleared", variant="default"),
],
)
```
### Loading States
A common pattern: show a loading indicator while a server call is in flight.
```python
from prefab_ui.actions import SetState, ShowToast
from prefab_ui.actions.mcp import CallTool
from prefab_ui.components import Button
from prefab_ui.rx import RESULT, Rx
saving = Rx("saving")
Button(
saving.then("Saving...", "Save"),
disabled=saving,
on_click=[
SetState("saving", True),
CallTool(
"save_data",
on_success=[
SetState("saving", False),
SetState("result", RESULT),
ShowToast("Saved!", variant="success"),
],
on_error=[
SetState("saving", False),
ShowToast("Failed", variant="error"),
],
),
],
)
# Pass state={"saving": False} to PrefabApp when returning
```
## Forms
Forms are the most common way to collect input and send it to the server. When a form submits, all named input values are gathered and passed as arguments to the `CallTool` action.
### Manual Forms
Build forms with individual input components:
```python
from prefab_ui.components import Form, Input, Select, SelectOption, Textarea, Button
from prefab_ui.actions.mcp import CallTool
from prefab_ui.actions import ShowToast
with Form(
on_submit=CallTool(
"create_ticket",
on_success=ShowToast("Ticket created!", variant="success"),
)
):
Input(name="title", label="Title", required=True)
with Select(name="priority", label="Priority"):
SelectOption("Low", value="low")
SelectOption("Medium", value="medium")
SelectOption("High", value="high")
SelectOption("Critical", value="critical")
Textarea(name="description", label="Description")
Button("Create Ticket")
```
When submitted, the CallTool receives `{"title": "...", "priority": "...", "description": "..."}` as arguments to `create_ticket`.
### Pydantic Model Forms
For structured data, `Form.from_model()` generates the entire form from a Pydantic model — inputs, labels, and submit wiring:
```python
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.actions import SetState, ShowToast
from prefab_ui.app import PrefabApp
from prefab_ui.rx import RESULT
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")
@app.ui()
def report_bug() -> PrefabApp:
"""File a bug report."""
with Column(gap=4, css_class="p-6") as view:
Heading("Report a Bug")
Form.from_model(
BugReport,
on_submit=CallTool(
"create_bug",
on_success=ShowToast("Bug filed!", variant="success"),
on_error=ShowToast("Failed to submit", variant="error"),
),
)
return PrefabApp(view=view)
@app.tool()
def create_bug(data: BugReport) -> str:
"""Create a bug report."""
# save to database...
return f"Created: {data.title}"
```
`str` fields become text inputs, `Literal` becomes a select dropdown, `bool` becomes a checkbox. Field titles and defaults are respected.
## Composition and Namespacing
The reason `FastMCPApp` exists — and why you'd use it instead of plain `@mcp.tool(app=True)` with `CallTool("tool_name")` — is composition safety.
When you mount a server under a namespace, tool names get prefixed:
```python
from fastmcp import FastMCP
platform = FastMCP("Platform")
platform.mount("contacts", contacts_server)
# "save_contact" becomes "contacts_save_contact"
```
If your UI used `CallTool("save_contact")`, it would break — the tool is now named `contacts_save_contact`. But `CallTool(save_contact)` with a function reference resolves to a globally stable key (like `save_contact-a1b2c3d4`) that bypasses the namespace entirely.
This is why `FastMCPApp` assigns global keys to backend tools, and why `CallTool` accepts function references. Your app works the same whether it's running standalone or mounted inside a larger platform.
### Mounting an App
`FastMCPApp` is a Provider. Add it to a server with `providers=` or `add_provider`:
```python
from fastmcp import FastMCP, FastMCPApp
app = FastMCPApp("Contacts")
@app.ui()
def contact_manager() -> PrefabApp:
...
@app.tool()
def save_contact(name: str, email: str) -> dict:
...
# Option 1: providers list
mcp = FastMCP("Platform", providers=[app])
# Option 2: add_provider
mcp = FastMCP("Platform")
mcp.add_provider(app)
```
Multiple apps can coexist on the same server:
```python
mcp = FastMCP("Platform", providers=[contacts_app, inventory_app, billing_app])
```
Each app's backend tools have their own global keys, so there's no collision even if two apps have a tool named `save`.
### Running Standalone
For development, `FastMCPApp` has a convenience `run()` method that wraps itself in a temporary `FastMCP` server:
```python
app = FastMCPApp("Contacts")
# ... register tools ...
if __name__ == "__main__":
app.run()
```
## Complete Example: Contact Manager
This pulls together everything — entry points, backend tools, callable references, forms (both manual and Pydantic), state management, and actions:
```python expandable
from __future__ import annotations
from typing import Literal
from prefab_ui.actions import SetState, ShowToast
from prefab_ui.actions.mcp import CallTool
from prefab_ui.app import PrefabApp
from prefab_ui.components import (
Badge, Button, Column, ForEach, Form,
Heading, Input, Muted, Row, Separator, Text,
)
from prefab_ui.rx import RESULT, Rx
from pydantic import BaseModel, Field
from fastmcp import FastMCP, FastMCPApp
# Data
contacts_db: list[dict] = [
{"name": "Arthur Dent", "email": "arthur@earth.com", "category": "Customer"},
{"name": "Ford Prefect", "email": "ford@betelgeuse.org", "category": "Partner"},
]
class ContactModel(BaseModel):
name: str = Field(title="Full Name", min_length=1)
email: str = Field(title="Email")
category: Literal["Customer", "Vendor", "Partner", "Other"] = "Other"
# App
app = FastMCPApp("Contacts")
@app.tool()
def save_contact(data: ContactModel) -> list[dict]:
"""Save a new contact and return the updated list."""
contacts_db.append(data.model_dump())
return list(contacts_db)
@app.tool()
def search_contacts(query: str) -> list[dict]:
"""Filter contacts by name or email."""
q = query.lower()
return [
c for c in contacts_db
if q in c["name"].lower() or q in c["email"].lower()
]
@app.tool(model=True)
def list_contacts() -> list[dict]:
"""Return all contacts. Visible to both the model and the UI."""
return list(contacts_db)
@app.ui()
def contact_manager() -> PrefabApp:
"""Open the contact manager."""
with Column(gap=6, css_class="p-6") as view:
Heading("Contacts")
with ForEach("contacts") as contact:
with Row(gap=2, align="center"):
Text(contact.name, css_class="font-medium")
Muted(contact.email)
Badge(contact.category)
Separator()
Heading("Add Contact", level=3)
Form.from_model(
ContactModel,
on_submit=CallTool(
"save_contact",
on_success=[
SetState("contacts", RESULT),
ShowToast("Contact saved!", variant="success"),
],
on_error=ShowToast("Failed to save", variant="error"),
),
)
Separator()
Heading("Search", level=3)
with Form(
on_submit=CallTool(
"search_contacts",
arguments={"query": Rx("query")},
on_success=SetState("contacts", RESULT),
)
):
Input(name="query", placeholder="Search by name or email...")
Button("Search")
return PrefabApp(view=view, state={"contacts": list(contacts_db)})
mcp = FastMCP("Contacts Server", providers=[app])
if __name__ == "__main__":
mcp.run()
```
This example is also available as a runnable server at `examples/apps/contacts/contacts_server.py`.
## Next Steps
- **[Prefab Apps](/apps/prefab)** — Components, state, and reactive displays (the building blocks)
- **[Patterns](/apps/patterns)** — Copy-paste examples for common UIs
- **[Development](/apps/development)** — Preview and test app tools locally
- **[Prefab UI Docs](https://prefab.prefect.io)** — Full component reference and advanced patterns

View file

@ -27,7 +27,7 @@ The tool declares which resource to use via `AppConfig`. When the host calls the
import json
from fastmcp import FastMCP
from fastmcp.server.apps import AppConfig, ResourceCSP
from fastmcp.apps import AppConfig, ResourceCSP
mcp = FastMCP("My App Server")
@ -47,7 +47,7 @@ def chart_view() -> str:
`AppConfig` controls how a tool or resource participates in the Apps extension. Import it from `fastmcp.server.apps`:
```python
from fastmcp.server.apps import AppConfig
from fastmcp.apps import AppConfig
```
On **tools**, you'll typically set `resource_uri` to point to the UI resource:
@ -145,6 +145,8 @@ The `App` object provides:
- **`app.onhostcontextchanged`** — callback for host context changes (e.g., safe area insets)
- **`app.getHostContext()`** — get current host context
See the full [ext-apps SDK documentation](https://github.com/modelcontextprotocol/ext-apps) for the complete API reference.
<Note>
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.
</Note>
@ -158,7 +160,7 @@ Apps run in sandboxed iframes with a deny-by-default Content Security Policy. By
If your app needs to load external resources (CDN scripts, API calls, embedded iframes), declare the allowed domains with `ResourceCSP`:
```python
from fastmcp.server.apps import AppConfig, ResourceCSP
from fastmcp.apps import AppConfig, ResourceCSP
@mcp.resource(
"ui://my-app/view.html",
@ -185,7 +187,7 @@ def my_view() -> str:
If your app needs browser capabilities like camera or clipboard access, request them via `ResourcePermissions`:
```python
from fastmcp.server.apps import AppConfig, ResourcePermissions
from fastmcp.apps import AppConfig, ResourcePermissions
@mcp.resource(
"ui://my-app/view.html",
@ -214,7 +216,7 @@ import qrcode
from mcp import types
from fastmcp import FastMCP
from fastmcp.server.apps import AppConfig, ResourceCSP
from fastmcp.apps import AppConfig, ResourceCSP
from fastmcp.tools import ToolResult
mcp = FastMCP("QR Code Server")
@ -290,7 +292,7 @@ Not all hosts support the Apps extension. You can check at runtime using the too
```python
from fastmcp import Context
from fastmcp.server.apps import AppConfig, UI_EXTENSION_ID
from fastmcp.apps import AppConfig, UI_EXTENSION_ID
@mcp.tool(app=AppConfig(resource_uri="ui://my-app/view.html"))
async def my_tool(ctx: Context) -> str:

View file

@ -10,67 +10,172 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.0.0" />
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.
MCP tools normally return text. That works for answers, but not for data the user wants to *explore* — a revenue chart they can hover over, a sortable employee directory, a form that submits structured input. MCP Apps let your tools return interactive UIs rendered right inside the conversation.
FastMCP implements the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) and provides two approaches:
<Frame>
<img src="/apps/images/app-showcase.png" alt="A Prefab app showing forms, charts, metrics, progress bars, data tables, and interactive controls — all built in Python" />
</Frame>
## Prefab Apps (Recommended)
FastMCP builds on the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) with [Prefab](https://prefab.prefect.io), a Python component library that compiles to interactive UIs. You write Python; the user sees charts, tables, forms, and dashboards.
<Note>
The examples throughout the Apps docs require the `apps` extra:
```bash
pip install "fastmcp[apps]"
```
This installs [Prefab UI](https://prefab.prefect.io), the component library used to build app UIs.
</Note>
<Warning>
FastMCP pins a **minimum** version of `prefab-ui` for compatibility but intentionally does **not** pin an upper bound. Prefab is a rapidly evolving library with frequent breaking changes. If you are deploying to production, you **must** pin `prefab-ui` to a specific version in your own dependencies. Without a pin, a fresh deploy could pull a newer Prefab version that changes component APIs, breaking your app.
</Warning>
## Which Approach?
Most apps start with **[Prefab Apps](/apps/prefab)** — add `app=True` to a tool and return components. That covers charts, tables, dashboards, and client-side interactivity.
When your UI needs multiple backend tools with managed visibility and composition safety, use **[FastMCPApp](/apps/interactive-apps)**.
When you want the LLM to design the UI at runtime, use **[Generative UI](/apps/generative)**.
When you need your own HTML/JS (maps, 3D, video), use **[Custom HTML](/apps/low-level)**.
FastMCP also includes ready-made **[app providers](/apps/providers/approval)** that add common capabilities with a single `add_provider()` call.
## Building Apps
### Prefab Apps
<VersionBadge version="3.1.0" />
<Tip>
[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.
</Tip>
[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).
The quickest way to give a tool a visual UI. Add `app=True` to any tool and return a Prefab component — when the host calls it, the user sees an interactive UI instead of a JSON blob:
```python
from prefab_ui.components import Column, Heading, BarChart, ChartSeries
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import BarChart, ChartSeries
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)
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} Sales")
Heading(f"{year} Revenue")
BarChart(
data=data,
series=[ChartSeries(data_key="revenue", label="Revenue")],
x_axis="month",
x_axis="quarter",
)
return PrefabApp(view=view)
```
Install with `pip install "fastmcp[apps]"` and see [Prefab Apps](/apps/prefab) for the integration guide.
Prefab apps aren't limited to static displays. Prefab's state system and client-side actions (toggles, tabs, conditionals) all work. You can even call other tools from the UI using `CallTool`. There's no hard wall on what a Prefab app can do.
## Custom HTML Apps
See [Prefab Apps](/apps/prefab) for the full guide.
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.
### FastMCPApp
This is the right choice for custom rendering (maps, 3D, video), specific JavaScript frameworks, or capabilities beyond what the component library offers.
<VersionBadge version="3.2.0" />
When your app has a lot of server-side interaction — forms that save data, search that queries a database, multi-step workflows — managing the connection between UI and backend tools gets complicated fast. Which tools should the model see vs. only the UI? What happens to tool references when servers are composed under namespaces? How do you keep `CallTool("save_contact")` working when the tool name changes?
`FastMCPApp` is a class that solves these problems. It gives you two decorators that work together:
- **`@app.ui()`** — entry-point tools the model calls to open the app
- **`@app.tool()`** — backend tools the UI calls via `CallTool`
Backend tools get stable identifiers that survive namespacing, visibility is managed automatically (the model sees entry points, the UI sees backends), and `CallTool` accepts tool names that resolve correctly regardless of how servers are composed:
```python
from prefab_ui.actions import SetState, ShowToast
from prefab_ui.actions.mcp import CallTool
from prefab_ui.app import PrefabApp
from prefab_ui.components import (
Column, Heading, Form, Input, Button, ForEach, Row, Text, Badge, Separator,
)
from prefab_ui.rx import RESULT
from fastmcp import FastMCP, FastMCPApp
app = FastMCPApp("Contacts")
@app.tool()
def save_contact(name: str, email: str) -> list[dict]:
"""Save a contact and return the updated list."""
db.append({"name": name, "email": email})
return list(db)
@app.ui()
def contact_manager() -> PrefabApp:
"""Open the contact manager."""
with Column(gap=6, css_class="p-6") as view:
Heading("Contacts")
with ForEach("contacts") as contact:
with Row(gap=2):
Text(contact.name)
Badge(contact.email)
Separator()
with Form(
on_submit=CallTool(
"save_contact",
on_success=[
SetState("contacts", RESULT),
ShowToast("Saved!", variant="success"),
],
)
):
Input(name="name", label="Name", required=True)
Input(name="email", label="Email", required=True)
Button("Save")
return PrefabApp(view=view, state={"contacts": list(db)})
mcp = FastMCP("Server", providers=[app])
```
You *can* build server-interactive UIs without `FastMCPApp` — it's all the same protocol underneath. But once you have multiple tools, composition concerns, or visibility requirements, `FastMCPApp` handles the complexity so you don't have to.
See [FastMCPApp](/apps/interactive-apps) for the full guide.
### Generative UI
<VersionBadge version="3.2.0" />
Instead of pre-building a UI, the LLM can write one from scratch. The `GenerativeUI` provider registers tools that let the model write Prefab Python code, execute it in a sandbox, and render the result — with streaming so the user watches the UI build up in real time.
```python
from fastmcp import FastMCP
from fastmcp.server.apps import AppConfig, ResourceCSP
from fastmcp.apps.generative import GenerativeUI
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 "<html>...</html>"
mcp = FastMCP("Prefab Studio")
mcp.add_provider(GenerativeUI())
```
See [Custom HTML Apps](/apps/low-level) for the full reference.
See [Generative UI](/apps/generative) for the full guide, or the [provider reference](/apps/providers/generative) for configuration options.
### Custom HTML
All the approaches above use [Prefab UI](https://prefab.prefect.io) to build UIs in pure Python. If you need full control — your own HTML, CSS, JavaScript, a specific framework — you can use the [MCP Apps extension directly](/apps/low-level). You write the HTML yourself and communicate with the host via the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) SDK.
## Previewing Apps Locally
The `fastmcp dev apps` command launches a browser-based preview for your app tools — no MCP host client needed. See [Development](/apps/development).
```bash
fastmcp dev apps server.py
```

View file

@ -1,30 +1,29 @@
---
title: Patterns
sidebarTitle: Patterns
description: Charts, tables, forms, and other common tool UIs.
description: Copy-paste examples for common tool UIs.
icon: grid-2-plus
tag: SOON
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.1.0" />
<Tip>
[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.
</Tip>
Each pattern below is a complete, copy-pasteable tool. They're organized by what you're building — pick the one closest to your use case, paste it, and adapt.
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.
For the full set of available components — layout containers, form controls, overlays, and more — see the [Prefab component reference](https://prefab.prefect.io/docs/components).
## 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.
Prefab includes [bar, line, area, pie, radar, and radial charts](https://prefab.prefect.io/docs/components/charts). They render client-side with tooltips, legends, and responsive sizing.
### Bar Chart
```python
from prefab_ui.components import Column, Heading, BarChart, ChartSeries
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import BarChart, ChartSeries
from fastmcp import FastMCP
mcp = FastMCP("Charts")
@ -59,11 +58,12 @@ Multiple `ChartSeries` entries plot different data keys. Add `stacked=True` to s
### Area Chart
`LineChart` and `AreaChart` share the same API as `BarChart`, with `curve` for interpolation (`"linear"`, `"smooth"`, `"step"`) and `show_dots` for data points:
`LineChart` and `AreaChart` share the same API as `BarChart`, with `curve` for interpolation and `show_dots` for data points:
```python
from prefab_ui.components import Column, Heading, AreaChart, ChartSeries
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import AreaChart, ChartSeries
from fastmcp import FastMCP
mcp = FastMCP("Charts")
@ -95,11 +95,12 @@ def usage_trend() -> PrefabApp:
### 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:
`PieChart` uses `data_key` (the numeric value) and `name_key` (the label). Set `inner_radius` for a donut:
```python
from prefab_ui.components import Column, Heading, PieChart
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import PieChart
from fastmcp import FastMCP
mcp = FastMCP("Charts")
@ -130,11 +131,11 @@ def ticket_breakdown() -> PrefabApp:
## 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.
[DataTable](https://prefab.prefect.io/docs/components/data-display/data-table) provides sortable columns, full-text search, and pagination — all client-side:
```python
from prefab_ui.components import Column, Heading, DataTable, DataTableColumn
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading, DataTable, DataTableColumn
from fastmcp import FastMCP
mcp = FastMCP("Directory")
@ -161,7 +162,7 @@ def employee_directory() -> PrefabApp:
DataTableColumn(key="location", header="Office", sortable=True),
],
rows=employees,
searchable=True,
search=True,
paginated=True,
page_size=15,
)
@ -169,133 +170,16 @@ def employee_directory() -> PrefabApp:
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
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
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.
Cards, badges, progress bars, and grids combine naturally for dashboards:
```python
from prefab_ui.app import PrefabApp
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")
@ -319,9 +203,7 @@ def system_status() -> PrefabApp:
"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():
@ -338,13 +220,16 @@ def system_status() -> PrefabApp:
return PrefabApp(view=view)
```
## Conditional Content
## Reactive Displays
[`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.
These patterns use state and `Rx()` for client-side interactivity — no server calls needed.
### Feature Toggles
```python
from prefab_ui.components import Column, Heading, Switch, Separator, Alert, If
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading, Switch, Alert, If, Separator
from prefab_ui.rx import Rx
from fastmcp import FastMCP
mcp = FastMCP("Flags")
@ -355,47 +240,41 @@ 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")
Switch(name="beta", label="Beta Features")
Separator()
with If("{{ dark_mode }}"):
with If(Rx("dark_mode")):
Alert(title="Dark mode enabled", description="UI will use dark theme.")
with If("{{ beta_features }}"):
with If(Rx("beta")):
Alert(
title="Beta features active",
description="Experimental features are now visible.",
variant="warning",
)
return PrefabApp(view=view, state={"dark_mode": False, "beta_features": False})
return PrefabApp(view=view, state={"dark_mode": False, "beta": False})
```
## Tabs
[Tabs](https://prefab.prefect.io/docs/components/containers/tabs) organize content into switchable views. Switching is client-side — no server round-trip.
### Tabs
```python
from prefab_ui.app import PrefabApp
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:
def project_overview() -> 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"},
@ -408,13 +287,11 @@ def project_overview(project_id: str) -> PrefabApp:
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(
@ -426,24 +303,22 @@ def project_overview(project_id: str) -> PrefabApp:
)
with Tab("Activity"):
with ForEach("activity"):
with ForEach("activity") as item:
with Row(gap=2):
Muted("{{ timestamp }}")
Text("{{ message }}")
Muted(item.timestamp)
Text(item.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:
### Accordion
```python
from prefab_ui.app import PrefabApp
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")
@ -461,7 +336,6 @@ def api_health() -> PrefabApp:
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"]):
@ -477,7 +351,81 @@ def api_health() -> PrefabApp:
return PrefabApp(view=view)
```
## Interactive Patterns
These patterns call server tools. For context on `FastMCPApp`, `@app.tool()`, and `CallTool`, see [FastMCPApp](/apps/interactive-apps).
### Contact Form
```python
from prefab_ui.actions import SetState, ShowToast
from prefab_ui.actions.mcp import CallTool
from prefab_ui.app import PrefabApp
from prefab_ui.components import (
Badge, Button, Column, ForEach, Form, Heading,
Input, Muted, Row, Select, SelectOption, Separator, Text, Textarea,
)
from prefab_ui.rx import RESULT
from fastmcp import FastMCP, FastMCPApp
app = FastMCPApp("Contacts")
contacts_db: list[dict] = [
{"name": "Zaphod Beeblebrox", "email": "zaphod@galaxy.gov", "category": "Partner"},
]
@app.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})
return list(contacts_db)
@app.ui()
def contact_form() -> PrefabApp:
"""Contact list with an add form."""
with Column(gap=6, css_class="p-6") as view:
Heading("Contacts")
with ForEach("contacts") as contact:
with Row(gap=2, align="center"):
Text(contact.name, css_class="font-medium")
Muted(contact.email)
Badge(contact.category)
Separator()
with Form(
on_submit=CallTool(
"save_contact",
on_success=[
SetState("contacts", RESULT),
ShowToast("Contact saved!", variant="success"),
],
on_error=ShowToast("Failed to save", variant="error"),
)
):
Input(name="name", label="Full Name", required=True)
Input(name="email", label="Email", input_type="email", required=True)
with Select(name="category", label="Category"):
SelectOption("Customer", value="Customer")
SelectOption("Vendor", value="Vendor")
SelectOption("Partner", value="Partner")
SelectOption("Other", value="Other")
Textarea(name="notes", label="Notes", placeholder="Optional notes...")
Button("Save Contact")
return PrefabApp(view=view, state={"contacts": list(contacts_db)})
mcp = FastMCP("Server", providers=[app])
```
## 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
- **[FastMCPApp](/apps/interactive-apps)** — Managed tool binding for server-connected UIs
- **[Development](/apps/development)** — Preview app tools locally with `fastmcp dev apps`
- **[Prefab UI Docs](https://prefab.prefect.io)** — Full component reference, layout guides, and more

View file

@ -1,44 +1,33 @@
---
title: Prefab Apps
sidebarTitle: Prefab Apps
description: Build interactive tool UIs in pure Python — no HTML or JavaScript required.
title: Prefab UI
sidebarTitle: Prefab UI
description: The component library behind FastMCP apps — charts, tables, dashboards, forms, and reactive displays.
icon: palette
tag: SOON
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.1.0" />
<Tip>
[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).
</Tip>
<Warning>
[Prefab](https://prefab.prefect.io) is in early, active development — breaking changes can occur with any release. FastMCP pins a minimum version of `prefab-ui` for compatibility but does not pin an upper bound. If you are deploying to production, **pin `prefab-ui` to a specific version** in your own dependencies.
</Warning>
[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 UI](https://prefab.prefect.io) is the component library behind all FastMCP app features. You describe layouts, charts, tables, and forms in Python, and Prefab compiles them to interactive UIs that render in the host's conversation.
Prefab 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.
The simplest way to use it: add `app=True` to a tool and return Prefab components. The host renders an interactive UI instead of text. This works for everything from static charts to reactive dashboards with client-side state — no server round-trips needed.
```bash
pip install "fastmcp[apps]"
```
For apps that need server interaction (forms, search, CRUD), see [FastMCPApp](/apps/interactive-apps) which adds managed tool binding on top of Prefab UI. For LLM-generated UIs, see [Generative UI](/apps/generative).
<Tip>
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.
## Getting Started
```toml
# pyproject.toml
dependencies = [
"fastmcp[apps]",
"prefab-ui==0.8.0", # pin to a known working version
]
```
</Tip>
Here's the simplest possible Prefab App — a tool that returns a bar chart:
Here's a tool that returns a bar chart:
```python
from prefab_ui.components import Column, Heading, BarChart, ChartSeries
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import BarChart, ChartSeries
from fastmcp import FastMCP
mcp = FastMCP("Dashboard")
@ -65,67 +54,240 @@ def revenue_chart(year: int) -> PrefabApp:
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.
The `app=True` flag tells FastMCP this tool returns a UI. When a host calls the tool, the user sees an interactive chart instead of a JSON blob. The [Patterns](/apps/patterns) page has more examples.
## What You Return
## Layout and Components
### 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:
Prefab uses Python's `with` statement to express nesting. Containers like `Column`, `Row`, and `Grid` collect their children automatically:
```python
from prefab_ui.components import Column, Heading, Badge
from fastmcp import FastMCP
from prefab_ui.components import (
Column, Row, Grid, Heading, Text, Muted, Badge,
Card, CardContent, Separator,
)
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
with Column(gap=4, css_class="p-6") as view:
Heading("Team Status")
Separator()
with Grid(columns=2, gap=4):
with Card():
with CardContent():
Text("API Gateway", css_class="font-medium")
Badge("healthy", variant="success")
with Card():
with CardContent():
Text("Cache", css_class="font-medium")
Badge("degraded", variant="destructive")
```
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:
You can also use Python loops to generate components at build time:
```python
services = [
{"name": "API", "status": "healthy", "ok": True},
{"name": "Cache", "status": "degraded", "ok": False},
]
with Grid(columns=2, gap=4):
for svc in services:
with Card():
with CardContent():
Text(svc["name"])
Badge(
svc["status"],
variant="success" if svc["ok"] else "destructive",
)
```
Build-time loops produce static content — the data is baked into the component tree at construction time. For dynamic iteration over state that changes at render time, use `ForEach` (covered below).
The full component library — layout containers, data display, charts, forms, overlays — is documented in the [Prefab component reference](https://prefab.prefect.io/docs/components).
## State and Reactivity
Display tools can be interactive without calling the server. The key is **state** — a client-side key-value store that lives in the browser. Components read from state, actions mutate it, and the UI re-renders automatically.
### Declaring State
Pass a `state` dict to `PrefabApp` to declare initial state, then use `Rx("key")` to create reactive references:
```python
from prefab_ui.components import Column, Heading, Text, Button, If, Badge
from prefab_ui.actions import ToggleState
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading, Switch, Alert, If
from prefab_ui.rx import Rx
from fastmcp import FastMCP
mcp = FastMCP("Demo")
mcp = FastMCP("Flags")
@mcp.tool(app=True)
def toggle_demo() -> PrefabApp:
"""Interactive toggle with state."""
def feature_flags() -> PrefabApp:
"""Toggle feature flags with live preview."""
with Column(gap=4, css_class="p-6") as view:
Button("Toggle", on_click=ToggleState("show"))
with If("{{ show }}"):
Badge("Visible!", variant="success")
Heading("Feature Flags")
Switch(name="dark_mode", label="Dark Mode")
Switch(name="beta", label="Beta Features")
return PrefabApp(view=view, state={"show": False})
with If(Rx("dark_mode")):
Alert(title="Dark mode enabled")
with If(Rx("beta")):
Alert(title="Beta features active", variant="warning")
return PrefabApp(view=view, state={"dark_mode": False, "beta": 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.
Three things to notice here:
### ToolResult
The `state` dict on `PrefabApp` declares the keys and their starting values. `Rx("dark_mode")` creates a reactive reference that compiles to `{{ dark_mode }}` in the wire protocol.
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.
Interactive components with a `name` prop automatically bind to state. The `Switch(name="dark_mode")` syncs its on/off value to the `dark_mode` state key on every toggle — no event wiring needed.
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:
`If(Rx("dark_mode"))` shows its children only when the state key is truthy. When the switch flips, the condition re-evaluates instantly in the browser.
### Reactive References with Rx
The `Rx` class is how you reference state in component props:
```python
from prefab_ui.rx import Rx
count = Rx("count")
```
Rx objects support arithmetic, comparisons, and formatting — they compile to expressions the renderer evaluates at render time:
```python
from prefab_ui.components import Column, Heading, BarChart, ChartSeries
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Text, Slider
from prefab_ui.rx import Rx
from fastmcp import FastMCP
mcp = FastMCP("Calculator")
@mcp.tool(app=True)
def tip_calculator() -> PrefabApp:
"""Calculate tip with a slider."""
tip_pct = Rx("tip_pct")
bill = Rx("bill")
tip_amount = tip_pct / 100 * bill
total = bill + tip_amount
with Column(gap=4, css_class="p-6") as view:
Slider(name="bill", label="Bill Amount", min=0, max=500, step=0.5)
Slider(name="tip_pct", label="Tip %", min=0, max=50)
Text(f"Tip: {tip_amount.currency()}")
Text(f"Total: {total.currency()}")
return PrefabApp(view=view, state={"bill": 50.00, "tip_pct": 18})
```
`Rx("tip_pct") / 100 * Rx("bill")` builds a compound expression — it doesn't do the math in Python. The renderer evaluates it live as the sliders move. The `.currency()` pipe formats the result as currency.
#### Pipes
Rx objects support formatting pipes that transform values at render time:
```python
from prefab_ui.rx import Rx
price = Rx("price")
ratio = Rx("ratio")
name = Rx("name")
price.currency() # $42.50
price.currency("EUR") # EUR format
ratio.percent() # 85%
name.upper() # ALICE
name.truncate(10) # alice (or truncated if longer)
```
Number pipes include `currency`, `percent`, `number`, `compact`, `round`, and `abs`. String pipes include `upper`, `lower`, and `truncate`. See the [Prefab expression docs](https://prefab.prefect.io/docs/concepts/expressions) for the full list.
#### Conditionals
The `.then()` method creates ternary expressions:
```python
from prefab_ui.rx import Rx
connected = Rx("connected")
Badge(
connected.then("Online", "Offline"),
variant=connected.then("success", "destructive"),
)
```
### Dynamic Iteration with ForEach
Python `for` loops generate static content at build time. When you need to iterate over state that can change — a list that grows, items that get filtered — use `ForEach`:
```python
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading, ForEach, Row, Text, Badge
from fastmcp import FastMCP
mcp = FastMCP("Directory")
@mcp.tool(app=True)
def team_list() -> PrefabApp:
"""Show the current team."""
members = [
{"name": "Alice", "role": "Engineering"},
{"name": "Bob", "role": "Design"},
]
with Column(gap=4, css_class="p-6") as view:
Heading("Team")
with ForEach("members") as member:
with Row(gap=2, align="center"):
Text(member.name, css_class="font-medium")
Badge(member.role)
return PrefabApp(view=view, state={"members": members})
```
`ForEach("members")` iterates over the `members` state key. The `as member` gives you an Rx proxy scoped to each item, so `member.name` resolves to `{{ $item.name }}` in the wire protocol. If the `members` state changes (e.g., through an action), the list re-renders automatically.
### Conditional Rendering
`If`, `Elif`, and `Else` control what's visible based on state:
```python
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Select, SelectOption, If, Elif, Else, Text
from prefab_ui.rx import Rx
tier = Rx("tier")
with Column(gap=4) as view:
with Select(name="tier", label="Plan"):
SelectOption("Free", value="free")
SelectOption("Pro", value="pro")
SelectOption("Enterprise", value="enterprise")
with If(tier == "enterprise"):
Text("Full access to all features")
with Elif(tier == "pro"):
Text("Advanced features unlocked")
with Else():
Text("Basic features only")
# Pass state={"tier": "free"} to PrefabApp when returning
```
Changes are instant — switching the dropdown re-evaluates the conditions in the browser.
## Giving the LLM Context
By default, Prefab sends `"[Rendered Prefab UI]"` as the text content for the LLM. If the model needs to reason about the data, wrap your return in a `ToolResult` with a meaningful summary:
```python
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import BarChart, ChartSeries
from fastmcp import FastMCP
from fastmcp.tools import ToolResult
@ -148,11 +310,28 @@ def sales_overview(year: int) -> ToolResult:
)
```
The user sees the chart. The LLM sees `"Total revenue for 2025: $203,000 across 4 quarters"` and can reason about it.
The user sees the chart. The LLM sees the summary string.
## Type Inference
## Advanced
If your tool's return type annotation is a Prefab type — `PrefabApp`, `Component`, or their `Optional` variants — FastMCP detects this and enables app rendering automatically:
<Accordion title="Customizing CSP">
`app=True` auto-wires the Prefab renderer with default CSP settings. If your app loads external resources — embedding iframes, fetching from APIs, loading scripts — use `PrefabAppConfig` to add the required domains:
```python
from fastmcp.apps import PrefabAppConfig, ResourceCSP
@mcp.tool(app=PrefabAppConfig(
csp=ResourceCSP(frame_domains=["https://example.com"]),
))
def dashboard_with_embed() -> PrefabApp:
...
```
`PrefabAppConfig()` with no arguments is equivalent to `app=True`. It auto-sets the renderer URI and merges the renderer's CSP with any additional domains you provide.
</Accordion>
<Accordion title="Type inference">
If your return type annotation is a Prefab type — `PrefabApp`, `Component`, or unions containing them — FastMCP enables app rendering automatically, even without `app=True`:
```python
@mcp.tool
@ -160,24 +339,14 @@ 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`).
Explicit `app=True` is recommended for clarity.
</Accordion>
## 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:
<Accordion title="Mixing with custom HTML">
Prefab tools and [custom HTML tools](/apps/low-level) coexist on the same server:
```python
from fastmcp.server.apps import AppConfig
from fastmcp.apps import AppConfig
@mcp.tool(app=True)
def team_directory() -> PrefabApp:
@ -187,9 +356,11 @@ def team_directory() -> PrefabApp:
def map_view() -> str:
...
```
</Accordion>
## 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
- **[FastMCPApp](/apps/interactive-apps)** — Managed tool binding for apps with heavy server interaction
- **[Patterns](/apps/patterns)** — Charts, tables, dashboards, and other common examples
- **[Development](/apps/development)** — Preview app tools locally with `fastmcp dev apps`
- **[Prefab UI Docs](https://prefab.prefect.io)** — Full component reference, advanced state patterns, and more

View file

@ -0,0 +1,80 @@
---
title: Approval
sidebarTitle: Approval
description: Human-in-the-loop approval gates for agent actions
icon: shield-check
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
`Approval` adds a human-in-the-loop confirmation step to any server. The LLM presents what it's about to do, the user approves or rejects via buttons, and the decision flows back into the conversation as a message.
<Frame>
<img src="/apps/images/app-approval.png" alt="The Approval provider shown in Goose, with a payment confirmation card and Approve/Cancel buttons" />
</Frame>
```python
from fastmcp import FastMCP
from fastmcp.apps.approval import Approval
mcp = FastMCP("My Server")
mcp.add_provider(Approval())
```
This registers a single tool:
| Tool | Visibility | Purpose |
|------|-----------|---------|
| `request_approval` | Model | Shows an approval card, sends the user's decision back as a message |
The LLM calls `request_approval` with a summary (and optional details) whenever it's about to take a significant action. The user sees a card with Approve and Reject buttons. Clicking either sends a message back into the conversation via `SendMessage`, which triggers the LLM's next turn.
The message looks like it came from the user:
```
"Deploy v3.2 to production" — I selected: Approve
```
<Note>
Approval is an advisory gate, not an enforcement mechanism. The conversation isn't blocked while the card is open — the user can keep typing, and a determined LLM could proceed without waiting. Think of it as a strong UX signal that encourages confirmation, not a security boundary. For hard enforcement, implement approval logic server-side in your tool implementations.
</Note>
## Configuration
The constructor sets defaults; the LLM can override all of these per-call via tool arguments.
```python
Approval(
name="Approval", # App name
title="Approval Required", # Card heading
approve_text="Approve", # Approve button label
reject_text="Reject", # Reject button label
approve_variant="default", # "default", "destructive", "success", "info"
reject_variant="outline", # same options plus "outline"
)
```
The LLM can customize each invocation:
```python
request_approval(
summary="Delete 47 files from /tmp",
details="This cannot be undone.",
title="Destructive Action",
approve_text="Delete",
approve_variant="destructive",
reject_text="Keep files",
)
```
## How It Works
When the user clicks a button, two things happen:
1. `SendMessage` pushes the decision into the conversation as a user message
2. `SetState("decided", True)` replaces the buttons with "Response sent."
The tool description instructs the LLM to stop and wait for the "I selected:" message before proceeding. If approved, it continues. If rejected, it acknowledges and asks how to proceed.

View file

@ -0,0 +1,72 @@
---
title: Choice
sidebarTitle: Choice
description: Present clickable options instead of free-text responses
icon: list-check
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
`Choice` lets the LLM present a set of options as clickable buttons instead of asking the user to type a response. The selection flows back into the conversation as a message, giving the LLM clean structured input.
<Frame>
<img src="/apps/images/app-choice.png" alt="The Choice provider shown in Goose, with four lunch options as clickable buttons" />
</Frame>
```python
from fastmcp import FastMCP
from fastmcp.apps.choice import Choice
mcp = FastMCP("My Server")
mcp.add_provider(Choice())
```
This registers a single tool:
| Tool | Visibility | Purpose |
|------|-----------|---------|
| `choose` | Model | Shows a card with clickable options, sends the selection back as a message |
The LLM calls `choose` with a prompt and a list of options. The user sees a card with one button per option. Clicking one sends a message back into the conversation:
```
"Which deployment strategy?" — I selected: Blue-green
```
<Note>
This is an advisory interaction, not an enforcement mechanism. The conversation isn't blocked while the card is open — the user can keep typing, and the LLM could proceed without waiting. The tool description instructs the LLM to stop and wait for the "I selected:" response, but for hard enforcement, implement selection logic server-side.
</Note>
## Configuration
The constructor sets defaults; the LLM can override `title` per-call.
```python
Choice(
name="Choice", # App name
title="Choose an Option", # Default card heading
variant="outline", # Button style for all options
)
```
The LLM provides the options per-call:
```python
choose(
prompt="What should we have for lunch?",
options=["Pizza", "Tacos", "Ramen", "Salad"],
title="The Important Questions",
)
```
## How It Works
Each option renders as a full-width button in a vertical stack. When the user clicks one:
1. `SendMessage` pushes the selection into the conversation as a user message
2. `SetState("decided", True)` replaces the buttons with "Response sent."
The tool description instructs the LLM to stop and wait for the "I selected:" message before proceeding with whatever the user chose.

View file

@ -0,0 +1,129 @@
---
title: File Upload
sidebarTitle: File Upload
description: Drag-and-drop file upload for any MCP server
icon: upload
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
`FileUpload` adds drag-and-drop file upload to any server. Users upload files through an interactive UI, bypassing the LLM context window entirely. The LLM can then list and read uploaded files through model-visible tools.
<Frame>
<img src="/apps/images/app-file-upload.png" alt="The FileUpload provider shown in Goose, with a drag-and-drop zone for uploading files" />
</Frame>
```python
from fastmcp import FastMCP
from fastmcp.apps.file_upload import FileUpload
mcp = FastMCP("My Server")
mcp.add_provider(FileUpload())
```
This registers four tools:
| Tool | Visibility | Purpose |
|------|-----------|---------|
| `file_manager` | Model | Opens the drag-and-drop upload UI |
| `store_files` | App only | Called by the UI when the user clicks Upload |
| `list_files` | Model | Returns metadata for all uploaded files |
| `read_file` | Model | Returns a file's contents by name |
The LLM sees `file_manager`, `list_files`, and `read_file`. It calls `file_manager` to show the upload interface, then uses `list_files` and `read_file` to work with whatever the user uploaded. `store_files` is app-only — the UI calls it directly and the LLM never needs to know about it.
## Configuration
```python
FileUpload(
name="Files", # App name (used in tool routing)
max_file_size=10 * 1024 * 1024, # 10 MB default, enforced server-side
title="File Upload", # Heading shown in the UI
description="Drop files to...", # Description text below the heading
drop_label="Drop files here", # Label inside the drop zone
)
```
The `max_file_size` limit is enforced both in the UI (the DropZone rejects oversized files) and on the server (the `store_files` tool validates before calling `on_store`).
## Storage Scoping
By default, files are stored in memory and scoped by MCP session ID. Each session gets its own isolated file store — files uploaded in one conversation aren't visible in another.
This works with **stdio**, **SSE**, and **stateful HTTP** transports, where sessions persist across requests.
<Warning>
In **stateless HTTP** mode, each request creates a new session object with a new ID. Files stored during one request (e.g. the UI upload) will be invisible to the next request (e.g. the LLM calling `list_files`). You **must** override `_get_scope_key` to use a stable identifier like a user ID from your auth token.
</Warning>
For stateless deployments, override `_get_scope_key` to return a stable identifier. For example, to scope files by authenticated user:
```python
from fastmcp.apps.file_upload import FileUpload
class UserScopedUpload(FileUpload):
def _get_scope_key(self, ctx):
return ctx.access_token["sub"]
```
For process-wide shared storage (all users see all files):
```python
class SharedUpload(FileUpload):
def _get_scope_key(self, ctx):
return "__shared__"
```
## Custom Storage
The default implementation stores files in memory for the lifetime of the server process. For persistent storage, subclass `FileUpload` and override three methods. Each receives the current `Context`, giving you access to session IDs, auth tokens, and request metadata for partitioning and authorization.
```python
import base64
from fastmcp.apps.file_upload import FileUpload
class S3Upload(FileUpload):
def on_store(self, files, ctx):
user_id = ctx.access_token["sub"]
for f in files:
s3.put_object(
Bucket="uploads",
Key=f"{user_id}/{f['name']}",
Body=base64.b64decode(f["data"]),
)
return self.on_list(ctx)
def on_list(self, ctx):
user_id = ctx.access_token["sub"]
objects = s3.list_objects(Bucket="uploads", Prefix=f"{user_id}/")
return [
{
"name": obj["Key"].split("/", 1)[1],
"type": "application/octet-stream",
"size": obj["Size"],
"size_display": f"{obj['Size']} B",
"uploaded_at": obj["LastModified"].isoformat(),
}
for obj in objects.get("Contents", [])
]
def on_read(self, name, ctx):
user_id = ctx.access_token["sub"]
obj = s3.get_object(Bucket="uploads", Key=f"{user_id}/{name}")
content = obj["Body"].read()
return {
"name": name,
"size": obj["ContentLength"],
"type": obj["ContentType"],
"uploaded_at": obj["LastModified"].isoformat(),
"content": content.decode("utf-8"),
}
```
Each file dict passed to `on_store` contains `name`, `size`, `type`, and `data` (base64-encoded content). The return value from `on_store` and `on_list` should be a list of summary dicts with `name`, `type`, `size`, `size_display`, and `uploaded_at` fields — these populate the file list in the UI.
`on_read` returns a dict with file metadata and either `content` (decoded text) or `content_base64` (a base64 preview for binary files).

View file

@ -0,0 +1,105 @@
---
title: Form Input
sidebarTitle: Form Input
description: Collect structured data from users via Pydantic models
icon: rectangle-list
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
`FormInput` generates a validated form from a Pydantic model. The user fills it out, and the submission is validated against the model before being returned. Structured elicitation that can't be hallucinated.
<Frame>
<img src="/apps/images/app-form.png" alt="The FormInput provider shown in Goose, with a bug report form" />
</Frame>
```python
from typing import Literal
from pydantic import BaseModel, Field
from fastmcp import FastMCP
from fastmcp.apps.form import FormInput
class BugReport(BaseModel):
title: str = Field(description="Brief summary")
severity: Literal["low", "medium", "high", "critical"]
description: str = Field(
description="Detailed description",
json_schema_extra={"ui": {"type": "textarea"}},
)
mcp = FastMCP("My Server")
mcp.add_provider(FormInput(model=BugReport))
```
This registers two tools:
| Tool | Visibility | Purpose |
|------|-----------|---------|
| `collect_bugreport` | Model | Opens the form UI |
| `submit_form` | App only | Validates and processes the submission |
The tool name is derived from the model class name, lowercased: `collect_{modelname}`. So `BugReport` becomes `collect_bugreport`, `ShippingAddress` becomes `collect_shippingaddress`. Use `tool_name` to override if needed. The LLM calls it with a prompt explaining what it needs, and the user gets a form with fields matching the model.
## Field Mapping
`FormInput` uses Prefab's `Form.from_model()`, which maps Pydantic types to form components:
| Python type | Form component |
|------------|---------------|
| `str` | Text input |
| `int`, `float` | Number input |
| `bool` | Checkbox |
| `datetime.date` | Date picker |
| `Literal[...]` | Select dropdown |
| `SecretStr` | Password input |
Use `Field()` metadata to control labels (`title`), placeholders (`description`), and validation (`min_length`, `max_length`, `ge`, `le`). Use `json_schema_extra={"ui": {"type": "textarea"}}` for multiline text.
## Callback
By default, the validated model is returned as JSON. Provide an `on_submit` callback to process the data server-side:
```python
def save_report(report: BugReport) -> str:
db.insert(report.model_dump())
return f"Bug #{db.last_id} filed: {report.title}"
mcp.add_provider(FormInput(model=BugReport, on_submit=save_report))
```
The callback receives a validated model instance and returns a string that becomes the tool result.
## Configuration
```python
FormInput(
model=BugReport, # Required: the Pydantic model
name="BugTracker", # App name (default: model name)
title="File a Bug", # Card heading (default: model name)
tool_name="file_bug", # Tool name (default: collect_{model})
submit_text="Submit Report", # Button label (default: "Submit")
on_submit=save_report, # Optional callback
send_message=True, # Push result as a chat message
)
```
Set `send_message=True` to push the result back into the conversation via `SendMessage`, triggering the LLM's next turn. Without it, the result is just the tool return value.
## Multiple Forms
Add multiple providers for different models — each gets its own tool:
```python
mcp = FastMCP(
"My Server",
providers=[
FormInput(model=ShippingAddress),
FormInput(model=BugReport),
FormInput(model=ContactInfo),
],
)
```

View file

@ -0,0 +1,74 @@
---
title: Generative UI
sidebarTitle: Generative UI
description: Let the LLM generate custom UIs at runtime
icon: wand-magic-sparkles
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
`GenerativeUI` lets the LLM write Prefab Python code at runtime and render it as a streaming interactive UI. Instead of calling pre-built tools with fixed interfaces, the model creates tailored visualizations for whatever data it's working with.
```python
from fastmcp import FastMCP
from fastmcp.apps.generative import GenerativeUI
mcp = FastMCP("My Server")
mcp.add_provider(GenerativeUI())
```
This registers:
| Component | Type | Purpose |
|-----------|------|---------|
| `generate_prefab_ui` | Tool | Accepts Python code, executes in Pyodide sandbox, renders result |
| `search_prefab_components` | Tool | Lets the LLM discover available Prefab components |
| Generative renderer | Resource | `ui://` resource with browser-side Pyodide for streaming |
The LLM writes real Python — loops, f-strings, computation — using Prefab's component library (charts, tables, forms, cards, layout primitives). As the model generates tokens, the host streams partial code to the renderer via `ontoolinputpartial`, so the user watches the UI build up in real time.
## Configuration
```python
GenerativeUI(
tool_name="generate_prefab_ui", # Rename the generation tool
components_tool_name="search_prefab_components", # Rename the search tool
include_components_tool=True, # Set False to omit the search tool
)
```
## What the LLM Sees
The tool description includes code examples that teach the LLM the Prefab patterns. The LLM calls `generate_prefab_ui` with a `code` argument containing Prefab Python, and optionally a `data` argument to pass in real data from the conversation:
```python
# The LLM generates something like:
generate_prefab_ui(
code="""
from prefab_ui.components import Column, Heading
from prefab_ui.components.charts import BarChart, ChartSeries
from prefab_ui.app import PrefabApp
with PrefabApp() as app:
with Column(gap=4):
Heading("Revenue")
BarChart(data=data, series=[ChartSeries(data_key="revenue")], x_axis="quarter")
""",
data={"data": [{"quarter": "Q1", "revenue": 42000}, ...]}
)
```
The component search tool lets the LLM discover what's available before writing code — `search_prefab_components("Chart")` returns matching components with import paths.
## Requirements
Requires `fastmcp[apps]` (installs `prefab-ui`). The Pyodide sandbox for server-side validation requires Deno, which installs automatically on first use. The streaming renderer loads Pyodide from CDN in the browser — CSP is configured automatically.
The sandbox includes the Python standard library and Prefab. External packages (NumPy, pandas, etc.) are not available.
## Learn More
The full **[Generative UI guide](/apps/generative)** covers the streaming mechanics in detail, how to pass data, the component search tool, and sandbox limitations.

208
docs/apps/quickstart.mdx Normal file
View file

@ -0,0 +1,208 @@
---
title: Quickstart
sidebarTitle: Quickstart
description: Build your first MCP app in under a minute.
icon: rocket
tag: NEW
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.2.0" />
MCP tools normally return text. FastMCP apps return interactive UIs rendered directly in the conversation: charts, tables, forms, dashboards. The easiest way to build one is with [Prefab UI](https://prefab.prefect.io), a Python component library designed for exactly this. You describe the UI in Python; Prefab compiles it to something the host can render.
This tutorial builds a working app from scratch. Here's what you'll have in about a minute:
<Frame>
<img src="/apps/images/app-quickstart.png" alt="A team directory app with a pie chart and sortable data table, rendered inside a conversation in Goose" />
</Frame>
## Setup
Install FastMCP with the `apps` extra, which pulls in Prefab UI:
```bash
pip install "fastmcp[apps]"
```
## A Tool That Returns a UI
When your tool has something to *show* (a table of results, a chart, a status dashboard) you can return an interactive UI instead of text. Build the visualization with Prefab components, return it from your tool, and set `app=True` so FastMCP knows to render it. The user sees a live, interactive widget right in the conversation instead of a wall of JSON.
Create `server.py`:
```python server.py expandable
from collections import Counter
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Grid, Heading, DataTable, DataTableColumn
from prefab_ui.components.charts import PieChart
from fastmcp import FastMCP
mcp = FastMCP("My First App")
@mcp.tool(app=True)
def team_directory() -> PrefabApp:
"""Browse the team directory."""
members = [
{"name": "Alice Chen", "role": "Staff Engineer", "office": "San Francisco"},
{"name": "Bob Martinez", "role": "Lead Designer", "office": "New York"},
{"name": "Carol Johnson", "role": "Senior Engineer", "office": "London"},
{"name": "David Kim", "role": "Product Manager", "office": "San Francisco"},
{"name": "Eva Mueller", "role": "Engineer", "office": "Berlin"},
{"name": "Frank Lee", "role": "Data Scientist", "office": "San Francisco"},
{"name": "Grace Park", "role": "Engineering Manager", "office": "New York"},
]
office_counts = [
{"office": office, "count": count}
for office, count in Counter(m["office"] for m in members).items()
]
with PrefabApp() as app:
with Column(gap=4, css_class="p-6"):
Heading("Team Directory")
with Grid(columns=[1, 2], gap=4):
PieChart(
data=office_counts,
data_key="count",
name_key="office",
show_legend=True,
)
DataTable(
columns=[
DataTableColumn(key="name", header="Name", sortable=True),
DataTableColumn(key="role", header="Role", sortable=True),
DataTableColumn(key="office", header="Office", sortable=True),
],
rows=members,
search=True,
)
return app
```
That `app=True` is doing a lot behind the scenes. It tells FastMCP to set up everything the MCP Apps protocol requires: the renderer resource, the content security policy, the metadata that tells the host "this tool returns a UI." Without it, you'd wire all of that up by hand. With it, you just return Prefab components and FastMCP handles the rest. The host (Claude Desktop, Goose, etc.) loads the result in a sandboxed iframe where the user can sort columns, search, and interact, all client-side with no round-trips to your server.
The Prefab code itself reads top-to-bottom like a document. `PrefabApp()` is the root container and everything inside its `with` block becomes the app's UI. `Column` arranges children vertically. `Heading` renders a title. `DataTable` takes rows of data and column definitions, and gives you sorting and search for free. The `with` blocks establish parent-child relationships: nesting components inside each other builds the layout tree.
## Running It
FastMCP includes a dev server that renders your app tools in a browser, no MCP host needed:
```bash
fastmcp dev apps server.py
```
This opens `http://localhost:8080` where you can pick a tool and see the rendered UI. Try sorting the table columns and typing in the search box.
## Making It Interactive
The table above is a static snapshot that renders once from the data your Python code provides. But Prefab apps can also respond to user interaction in real time, without any server round-trips.
The key concept is **state**: a client-side key-value store that components read from and write to. When the user interacts with a component, it updates state. Other components that reference that state re-render instantly. See the [Prefab state docs](https://prefab.prefect.io/docs/concepts/state) for the full guide.
Here's the same directory, but now clicking a row shows that person's details in a card:
<Frame>
<img src="/apps/images/app-quickstart-dev-2.png" alt="The team directory with a detail card showing after clicking Bob Martinez" />
</Frame>
```python expandable server.py
from collections import Counter
from prefab_ui.actions import SetState
from prefab_ui.app import PrefabApp
from prefab_ui.components import (
Card, CardContent, CardHeader, Column, Grid, H3, Heading, Muted,
Row, DataTable, DataTableColumn, Badge, Small, Text,
)
from prefab_ui.components.charts import PieChart
from prefab_ui.components.control_flow import If
from prefab_ui.rx import Rx, STATE
from fastmcp import FastMCP
mcp = FastMCP("My First App")
MEMBERS = [
{"name": "Alice Chen", "role": "Staff Engineer", "office": "San Francisco", "email": "alice@company.com", "projects": 3},
{"name": "Bob Martinez", "role": "Lead Designer", "office": "New York", "email": "bob@company.com", "projects": 5},
{"name": "Carol Johnson", "role": "Senior Engineer", "office": "London", "email": "carol@company.com", "projects": 2},
{"name": "David Kim", "role": "Product Manager", "office": "San Francisco", "email": "david@company.com", "projects": 7},
{"name": "Eva Mueller", "role": "Engineer", "office": "Berlin", "email": "eva@company.com", "projects": 1},
{"name": "Frank Lee", "role": "Data Scientist", "office": "San Francisco", "email": "frank@company.com", "projects": 4},
{"name": "Grace Park", "role": "Engineering Manager", "office": "New York", "email": "grace@company.com", "projects": 6},
]
OFFICE_COUNTS = [
{"office": office, "count": count}
for office, count in Counter(m["office"] for m in MEMBERS).items()
]
@mcp.tool(app=True)
def team_directory() -> PrefabApp:
"""Browse the team directory."""
with PrefabApp(state={"selected": None}) as app:
with Column(gap=4, css_class="p-6"):
Heading("Team Directory")
with Grid(columns=[1, 2], gap=4):
PieChart(
data=OFFICE_COUNTS,
data_key="count",
name_key="office",
show_legend=True,
)
DataTable(
columns=[
DataTableColumn(key="name", header="Name", sortable=True),
DataTableColumn(key="role", header="Role", sortable=True),
DataTableColumn(key="office", header="Office", sortable=True),
],
rows=MEMBERS,
search=True,
on_row_click=SetState("selected", Rx("$event")),
)
with If(STATE.selected):
with Card():
with CardHeader():
with Row(gap=2, align="center"):
H3(Rx("selected.name"))
Badge(Rx("selected.office"))
with CardContent():
with Grid(columns=3, gap=4):
with Column(gap=0):
Small("Role")
Text(Rx("selected.role"))
with Column(gap=0):
Small("Email")
Text(Rx("selected.email"))
with Column(gap=0):
Small("Active Projects")
Text(Rx("selected.projects"))
return app
```
Three new ideas here:
**`SetState` + `on_row_click`** is the interaction. When the user clicks a table row, `SetState("selected", Rx("$event"))` writes the clicked row's data into the `selected` state key. `$event` is a special variable that contains the event payload (in this case, the row dict).
**`Rx("selected.name")`** reads from state reactively. It doesn't hold a Python value. It compiles to a browser-side expression that re-evaluates live whenever `selected` changes. So `Text(Rx("selected.name"))` always shows the name of whoever was last clicked.
**`If(STATE.selected)`** conditionally renders the detail card only when something has been selected. Before any click, `selected` is `None` and the card is hidden.
The `state` dict on `PrefabApp` sets initial values when the app loads. Run `fastmcp dev apps server.py` again and try clicking a row.
## Next Steps
You've built a tool that returns an interactive, reactive UI. This pattern covers a huge range of use cases: build a visualization in Prefab, return it from a tool, and the user gets dashboards, charts, data tables, and status displays right in the conversation.
When you need the UI to talk back to your server (forms that save data, buttons that trigger actions, search that queries a database) you promote the tool to a **[FastMCPApp](/apps/interactive-apps)**. That gives you managed backend tools, automatic visibility control, and stable routing so your UI's button clicks reach the right server-side code.
- **[Prefab UI](/apps/prefab)** covers the full component library: charts, forms, badges, progress bars, and the [reactive state system](https://prefab.prefect.io/docs/concepts/state) in depth.
- **[FastMCPApp](/apps/interactive-apps)** is the next step when your UI needs to interact with backend logic.
- **[App Providers](/apps/providers/approval)** are ready-made capabilities you can add with a single `add_provider()` call.

View file

@ -5,6 +5,86 @@ rss: true
tag: NEW
---
<Update label="v3.1.1" description="2026-03-14">
**[v3.1.1: 'Tis But a Patch](https://github.com/PrefectHQ/fastmcp/releases/tag/v3.1.1)**
Pins `pydantic-monty` below 0.0.8 to fix a breaking change in Monty that affects code mode. Monty 0.0.8 removed the `external_functions` constructor parameter, causing `MontySandboxProvider` to fail. This patch caps the version so existing installs work correctly.
### Fixes 🐞
* Pin pydantic-monty below 0.0.8 to fix code mode by [@jlowin](https://github.com/jlowin) in [#3497](https://github.com/PrefectHQ/fastmcp/pull/3497)
**Full Changelog**: [v3.1.0...v3.1.1](https://github.com/PrefectHQ/fastmcp/compare/v3.1.0...v3.1.1)
</Update>
<Update label="v3.1.0" description="2026-03-03">
**[v3.1.0: Code to Joy](https://github.com/PrefectHQ/fastmcp/releases/tag/v3.1.0)**
FastMCP 3.1 is the Code Mode release. The 3.0 architecture introduced providers and transforms as the extensibility layer — 3.1 puts that architecture to work, shipping the most requested capability since launch: servers that can find and execute code on behalf of agents, without requiring clients to know what tools exist.
### New Features 🎉
* feat: Search transforms for tool discovery by [@jlowin](https://github.com/jlowin) in [#3154](https://github.com/PrefectHQ/fastmcp/pull/3154)
* Add experimental CodeMode transform by [@aaazzam](https://github.com/aaazzam) in [#3297](https://github.com/PrefectHQ/fastmcp/pull/3297)
* Add Prefab Apps integration for MCP tool UIs by [@jlowin](https://github.com/jlowin) in [#3316](https://github.com/PrefectHQ/fastmcp/pull/3316)
### Enhancements 🔧
* Lazy-load heavy imports to reduce import time by [@jlowin](https://github.com/jlowin) in [#3295](https://github.com/PrefectHQ/fastmcp/pull/3295)
* Add http_client parameter to all token verifiers for connection pooling by [@jlowin](https://github.com/jlowin) in [#3300](https://github.com/PrefectHQ/fastmcp/pull/3300)
* Add in-memory caching for token introspection results by [@jlowin](https://github.com/jlowin) in [#3298](https://github.com/PrefectHQ/fastmcp/pull/3298)
* Add SessionStart hook to install gh CLI in cloud sessions by [@jlowin](https://github.com/jlowin) in [#3308](https://github.com/PrefectHQ/fastmcp/pull/3308)
* Fix ty 0.0.19 type errors by [@jlowin](https://github.com/jlowin) in [#3310](https://github.com/PrefectHQ/fastmcp/pull/3310)
* Code Mode: Add resource limits to MontySandboxProvider by [@jlowin](https://github.com/jlowin) in [#3326](https://github.com/PrefectHQ/fastmcp/pull/3326)
* Accept transforms as FastMCP init kwarg by [@jlowin](https://github.com/jlowin) in [#3324](https://github.com/PrefectHQ/fastmcp/pull/3324)
* Split large test files to comply with loq line limit by [@jlowin](https://github.com/jlowin) in [#3328](https://github.com/PrefectHQ/fastmcp/pull/3328)
* Add -m/--module flag to `fastmcp run` and `dev inspector` by [@dgenio](https://github.com/dgenio) in [#3331](https://github.com/PrefectHQ/fastmcp/pull/3331)
* Add search_result_serializer hook and serialize_tools_for_output_markdown by [@MagnusS0](https://github.com/MagnusS0) in [#3337](https://github.com/PrefectHQ/fastmcp/pull/3337)
* Add MultiAuth for composing multiple token verification sources by [@jlowin](https://github.com/jlowin) in [#3335](https://github.com/PrefectHQ/fastmcp/pull/3335)
* Adds PropelAuth as an AuthProvider by [@andrew-propelauth](https://github.com/andrew-propelauth) in [#3358](https://github.com/PrefectHQ/fastmcp/pull/3358)
* Replace vendored DI with uncalled-for by [@chrisguidry](https://github.com/chrisguidry) in [#3301](https://github.com/PrefectHQ/fastmcp/pull/3301)
* Decompose CodeMode into composable discovery tools by [@jlowin](https://github.com/jlowin) in [#3354](https://github.com/PrefectHQ/fastmcp/pull/3354)
* feat(contrib): auto-sync MCPMixin decorators with from_function signatures by [@AnkeshThakur](https://github.com/AnkeshThakur) in [#3323](https://github.com/PrefectHQ/fastmcp/pull/3323)
* Add Google GenAI Sampling Handler by [@strawgate](https://github.com/strawgate) in [#2977](https://github.com/PrefectHQ/fastmcp/pull/2977)
* Add ListTools, search limit, and catalog size annotation to CodeMode by [@jlowin](https://github.com/jlowin) in [#3359](https://github.com/PrefectHQ/fastmcp/pull/3359)
* Allow configuring FastMCP transport setting in the same way as other configuration by [@jvdmr](https://github.com/jvdmr) in [#1796](https://github.com/PrefectHQ/fastmcp/pull/1796)
* Add include_unversioned option to VersionFilter by [@yangbaechu](https://github.com/yangbaechu) in [#3349](https://github.com/PrefectHQ/fastmcp/pull/3349)
### Fixes 🐞
* Fix docs banner pushing nav down by [@jlowin](https://github.com/jlowin) in [#3282](https://github.com/PrefectHQ/fastmcp/pull/3282)
* fix: Replace hardcoded TTL with DEFAULT_TTL_MS - issue #3279 by [@cedric57](https://github.com/cedric57) in [#3280](https://github.com/PrefectHQ/fastmcp/pull/3280)
* fix: stop suppressing server stderr in fastmcp call by [@jlowin](https://github.com/jlowin) in [#3283](https://github.com/PrefectHQ/fastmcp/pull/3283)
* fix: skip max_completion_tokens when maxTokens is None by [@eon01](https://github.com/eon01) in [#3284](https://github.com/PrefectHQ/fastmcp/pull/3284)
* OpenAPI: rewrite $ref under propertyNames and patternProperties in _replace_ref_with_defs; add regression test for dict[StrEnum, Model] by [@manojPal23234](https://github.com/manojPal23234) in [#3306](https://github.com/PrefectHQ/fastmcp/pull/3306)
* Remove stale add_resource() key parameter from docs by [@jlowin](https://github.com/jlowin) in [#3309](https://github.com/PrefectHQ/fastmcp/pull/3309)
* Handle AuthorizationError as exclusion in AuthMiddleware list hooks by [@yangbaechu](https://github.com/yangbaechu) in [#3338](https://github.com/PrefectHQ/fastmcp/pull/3338)
* Fix flaky OpenAPI performance test threshold by [@jlowin](https://github.com/jlowin) in [#3355](https://github.com/PrefectHQ/fastmcp/pull/3355)
* Fix flaky SSE timeout test by [@jlowin](https://github.com/jlowin) in [#3343](https://github.com/PrefectHQ/fastmcp/pull/3343)
* Remove system role references from docs by [@jlowin](https://github.com/jlowin) in [#3356](https://github.com/PrefectHQ/fastmcp/pull/3356)
* Fix session persistence across tool calls in multi-server MCPConfigTransport by [@jer805](https://github.com/jer805) in [#3330](https://github.com/PrefectHQ/fastmcp/pull/3330)
### Docs 📚
* Add v3.0.2 release notes by [@jlowin](https://github.com/jlowin) in [#3276](https://github.com/PrefectHQ/fastmcp/pull/3276)
* Fix "FastMCP Constructor Parameters" in documentation server.mdx (Remove old parameters & Add new parameter) by [@wangyy04](https://github.com/wangyy04) in [#3317](https://github.com/PrefectHQ/fastmcp/pull/3317)
* Fix stale docs: tag filtering API and missing output_schema param by [@jlowin](https://github.com/jlowin) in [#3322](https://github.com/PrefectHQ/fastmcp/pull/3322)
* Narrate search example clients by [@jlowin](https://github.com/jlowin) in [#3321](https://github.com/PrefectHQ/fastmcp/pull/3321)
* Code Mode: Document resource limits and fix docs formatting by [@jlowin](https://github.com/jlowin) in [#3327](https://github.com/PrefectHQ/fastmcp/pull/3327)
* Add reverse proxy (nginx) section to HTTP deployment docs by [@dgenio](https://github.com/dgenio) in [#3344](https://github.com/PrefectHQ/fastmcp/pull/3344)
* Restructure docs navigation: CLI section, Composition, More by [@jlowin](https://github.com/jlowin) in [#3361](https://github.com/PrefectHQ/fastmcp/pull/3361)
### Other Changes 🦾
* Don't advertise sampling.tools capability by default by [@jlowin](https://github.com/jlowin) in [#3334](https://github.com/PrefectHQ/fastmcp/pull/3334)
## New Contributors
* @cedric57 made their first contribution in [#3280](https://github.com/PrefectHQ/fastmcp/pull/3280)
* @eon01 made their first contribution in [#3284](https://github.com/PrefectHQ/fastmcp/pull/3284)
* @manojPal23234 made their first contribution in [#3306](https://github.com/PrefectHQ/fastmcp/pull/3306)
* @wangyy04 made their first contribution in [#3317](https://github.com/PrefectHQ/fastmcp/pull/3317)
* @yangbaechu made their first contribution in [#3338](https://github.com/PrefectHQ/fastmcp/pull/3338)
* @andrew-propelauth made their first contribution in [#3358](https://github.com/PrefectHQ/fastmcp/pull/3358)
* @jer805 made their first contribution in [#3330](https://github.com/PrefectHQ/fastmcp/pull/3330)
* @jvdmr made their first contribution in [#1796](https://github.com/PrefectHQ/fastmcp/pull/1796)
**Full Changelog**: [v3.0.2...v3.1.0](https://github.com/PrefectHQ/fastmcp/compare/v3.0.2...v3.1.0)
</Update>
<Update label="v3.0.2" description="2026-02-22">
**[v3.0.2: Threecovery Mode II](https://github.com/PrefectHQ/fastmcp/releases/tag/v3.0.2)**
@ -663,6 +743,21 @@ Breaking changes are minimal: for most servers, updating the import statement is
</Update>
<Update label="v2.14.6" description="2026-03-27">
**[v2.14.6: $Ref Dead Redemption](https://github.com/PrefectHQ/fastmcp/releases/tag/v2.14.6)**
v2.14.4 backported `dereference_refs()` but never wired it into the tool schema pipeline — `$ref` and `$defs` were still sent to MCP clients. Now fixed: `compress_schema()` dereferences at both tool schema creation sites, so schemas are fully inlined before reaching clients.
### Fixes 🐞
* Updated deprecation URL for V2 by [@SrzStephen](https://github.com/SrzStephen) in [#3109](https://github.com/PrefectHQ/fastmcp/pull/3109)
* Use MemoryStore for OAuth proxy tests by [@SrzStephen](https://github.com/SrzStephen) in [#3111](https://github.com/PrefectHQ/fastmcp/pull/3111)
* fix: wire up dereference_refs() in tool schema pipeline by [@jlowin](https://github.com/jlowin) in [#3170](https://github.com/PrefectHQ/fastmcp/pull/3170)
**Full Changelog**: [v2.14.5...v2.14.6](https://github.com/PrefectHQ/fastmcp/compare/v2.14.5...v2.14.6)
</Update>
<Update label="v2.14.5" description="2026-02-03">
**[v2.14.5: Sealed Docket](https://github.com/PrefectHQ/fastmcp/releases/tag/v2.14.5)**

View file

@ -65,6 +65,7 @@ See [Server Configuration](/deployment/server-configuration) for the full config
| Python | `--python` | Python version (e.g., `3.11`) |
| Project | `--project` | Run within a uv project directory |
| Requirements | `--with-requirements` | Install from a requirements file |
| Config Path | `--config-path` | Custom path to Claude Desktop config directory (`claude-desktop` only) |
## Examples
@ -92,6 +93,10 @@ fastmcp install cursor server.py --env-file .env
fastmcp install claude-desktop server.py \
--python 3.11 \
--with-requirements requirements.txt
# With custom config path (claude-desktop only)
fastmcp install claude-desktop server.py \
--config-path "C:\Users\username\AppData\Local\Packages\Claude_xyz\LocalCache\Roaming\Claude"
```
## Generating MCP JSON

View file

@ -18,6 +18,7 @@ fastmcp --help
| Command | What it does |
| ------- | ------------ |
| [`run`](/cli/running) | Run a server (local file, factory function, remote URL, or config file) |
| [`dev apps`](/cli/running#previewing-apps) | Launch a browser-based preview UI for Prefab App tools |
| [`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 |

View file

@ -96,6 +96,31 @@ By default, `fastmcp run` uses your current Python environment directly. When yo
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.
## Previewing Apps
<VersionBadge version="3.2.0" />
`fastmcp dev apps` launches a browser-based preview UI for servers with [Prefab App tools](/apps/prefab). It starts your MCP server on one port and a local dev UI on another — giving you a live, interactive picker where you can call app tools and see their rendered output without needing a full MCP host client.
```bash
fastmcp dev apps server.py
fastmcp dev apps server.py:mcp --mcp-port 9000 --dev-port 9090
```
The picker auto-generates a form from each tool's input schema. Submit the form and the result opens in a new tab as a rendered Prefab UI.
Auto-reload is on by default — save a file and the MCP server restarts automatically.
<Tip>
`fastmcp dev apps` requires `fastmcp[apps]` — install with `pip install "fastmcp[apps]"`.
</Tip>
| Option | Flag | Description |
| ------ | ---- | ----------- |
| MCP Port | `--mcp-port` | Port for the MCP server (default: `8000`) |
| Dev Port | `--dev-port` | Port for the dev UI (default: `8080`) |
| Auto-Reload | `--reload` / `--no-reload` | Watch for file changes (default: on) |
## 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.

View file

@ -124,6 +124,38 @@ client = Client(
)
```
### SSL Verification
By default, HTTPS connections verify the server's SSL certificate. You can customize this behavior with the `verify` parameter, which accepts the same values as [httpx](https://www.python-httpx.org/advanced/ssl/):
```python
from fastmcp import Client
# Disable SSL verification (e.g., for self-signed certs in development)
client = Client("https://dev-server.internal/mcp", verify=False)
# Use a custom CA bundle
client = Client("https://corp-server.internal/mcp", verify="/path/to/ca-bundle.pem")
# Use a custom SSL context for full control
import ssl
ctx = ssl.create_default_context()
ctx.load_verify_locations("/path/to/internal-ca.pem")
client = Client("https://corp-server.internal/mcp", verify=ctx)
```
The `verify` parameter is also available directly on `StreamableHttpTransport` and `SSETransport`:
```python
from fastmcp.client.transports import StreamableHttpTransport
transport = StreamableHttpTransport(
url="https://dev-server.internal/mcp",
verify=False,
)
client = Client(transport)
```
### SSE Transport
Server-Sent Events transport is maintained for backward compatibility. Use Streamable HTTP for new deployments unless you have specific infrastructure requirements.

View file

@ -115,6 +115,10 @@ async def health_check(request):
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.
<Note>
Custom routes are never protected by the server's authentication middleware, even when an `AuthProvider` is configured. This is by design — the primary use case for custom routes is unauthenticated operational endpoints like health checks and readiness probes. If you need authenticated HTTP endpoints alongside your MCP server, [mount it in a FastAPI app](/integrations/fastapi) and use FastAPI's `Depends()` for auth on your routes.
</Note>
### Custom Middleware
@ -625,18 +629,18 @@ You might expect sticky sessions (session affinity) to solve this, but they don'
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**
**Option 1: Via `http_app()`**
```python
from fastmcp import FastMCP
mcp = FastMCP("My Server", stateless_http=True)
mcp = FastMCP("My Server")
@mcp.tool
def process(data: str) -> str:
return f"Processed: {data}"
app = mcp.http_app()
app = mcp.http_app(stateless_http=True)
```
**Option 2: Via `run()`**

View file

@ -12,7 +12,7 @@
"decoration": "gradient"
},
"banner": {
"content": "Deploy FastMCP servers for free on [Prefect Horizon](https://www.prefect.io/horizon)"
"content": "Meet [Prefect Horizon](https://prefect.io/horizon?utm_source=gofastmcp&utm_medium=docs&utm_campaign=docs_banner&utm_content=sitewide_banner), the enterprise MCP gateway built by the team behind FastMCP"
},
"colors": {
"dark": "#f72585",
@ -140,8 +140,7 @@
"servers/providers/proxy",
"servers/providers/skills",
"servers/providers/custom"
],
"tag": "NEW"
]
},
{
"collapsed": true,
@ -156,25 +155,30 @@
"servers/transforms/tool-search",
"servers/transforms/resources-as-tools",
"servers/transforms/prompts-as-tools"
],
"tag": "NEW"
]
},
{
"collapsed": true,
"group": "Authentication",
"icon": "key",
"group": "Auth",
"icon": "shield-check",
"pages": [
"servers/auth/authentication",
"servers/auth/token-verification",
"servers/auth/remote-oauth",
"servers/auth/oauth-proxy",
"servers/auth/oidc-proxy",
"servers/auth/full-oauth-server",
"servers/auth/multi-auth"
],
"tag": "UPDATED"
{
"collapsed": true,
"group": "Authentication",
"icon": "key",
"pages": [
"servers/auth/authentication",
"servers/auth/token-verification",
"servers/auth/remote-oauth",
"servers/auth/oauth-proxy",
"servers/auth/oidc-proxy",
"servers/auth/full-oauth-server",
"servers/auth/multi-auth"
]
},
"servers/authorization"
]
},
"servers/authorization",
{
"collapsed": true,
"group": "Deployment",
@ -192,9 +196,44 @@
"group": "Apps",
"pages": [
"apps/overview",
"apps/prefab",
"apps/patterns",
"apps/low-level"
"apps/quickstart",
"apps/examples",
{
"collapsed": true,
"group": "Building Apps",
"icon": "hammer",
"pages": [
"apps/prefab",
"apps/interactive-apps",
"apps/generative",
"apps/patterns"
],
"tag": "NEW"
},
{
"collapsed": true,
"group": "Providers",
"icon": "layer-group",
"pages": [
"apps/providers/approval",
"apps/providers/choice",
"apps/providers/file-upload",
"apps/providers/form",
"apps/providers/generative"
],
"tag": "NEW"
},
{
"collapsed": true,
"group": "Advanced",
"icon": "gear",
"pages": [
"apps/development",
"apps/architecture",
"apps/low-level"
],
"tag": "NEW"
}
]
},
{
@ -315,6 +354,7 @@
{
"group": "More",
"pages": [
"more/settings",
{
"collapsed": true,
"group": "Upgrading",
@ -362,10 +402,25 @@
"python-sdk/fastmcp-mcp_config",
"python-sdk/fastmcp-settings",
"python-sdk/fastmcp-telemetry",
"python-sdk/fastmcp-types",
{
"group": "fastmcp.apps",
"pages": [
"python-sdk/fastmcp-apps-__init__",
"python-sdk/fastmcp-apps-app",
"python-sdk/fastmcp-apps-approval",
"python-sdk/fastmcp-apps-choice",
"python-sdk/fastmcp-apps-config",
"python-sdk/fastmcp-apps-file_upload",
"python-sdk/fastmcp-apps-form",
"python-sdk/fastmcp-apps-generative"
]
},
{
"group": "fastmcp.cli",
"pages": [
"python-sdk/fastmcp-cli-__init__",
"python-sdk/fastmcp-cli-apps_dev",
"python-sdk/fastmcp-cli-auth",
"python-sdk/fastmcp-cli-cimd",
"python-sdk/fastmcp-cli-cli",
@ -475,16 +530,16 @@
"group": "fastmcp.prompts",
"pages": [
"python-sdk/fastmcp-prompts-__init__",
"python-sdk/fastmcp-prompts-function_prompt",
"python-sdk/fastmcp-prompts-prompt"
"python-sdk/fastmcp-prompts-base",
"python-sdk/fastmcp-prompts-function_prompt"
]
},
{
"group": "fastmcp.resources",
"pages": [
"python-sdk/fastmcp-resources-__init__",
"python-sdk/fastmcp-resources-base",
"python-sdk/fastmcp-resources-function_resource",
"python-sdk/fastmcp-resources-resource",
"python-sdk/fastmcp-resources-template",
"python-sdk/fastmcp-resources-types"
]
@ -493,6 +548,7 @@
"group": "fastmcp.server",
"pages": [
"python-sdk/fastmcp-server-__init__",
"python-sdk/fastmcp-server-app",
"python-sdk/fastmcp-server-apps",
{
"group": "auth",
@ -521,6 +577,7 @@
"python-sdk/fastmcp-server-auth-providers-auth0",
"python-sdk/fastmcp-server-auth-providers-aws",
"python-sdk/fastmcp-server-auth-providers-azure",
"python-sdk/fastmcp-server-auth-providers-clerk",
"python-sdk/fastmcp-server-auth-providers-debug",
"python-sdk/fastmcp-server-auth-providers-descope",
"python-sdk/fastmcp-server-auth-providers-discord",
@ -684,9 +741,9 @@
"group": "fastmcp.tools",
"pages": [
"python-sdk/fastmcp-tools-__init__",
"python-sdk/fastmcp-tools-base",
"python-sdk/fastmcp-tools-function_parsing",
"python-sdk/fastmcp-tools-function_tool",
"python-sdk/fastmcp-tools-tool",
"python-sdk/fastmcp-tools-tool_transform"
]
},
@ -734,6 +791,7 @@
}
]
},
"python-sdk/fastmcp-utilities-mime",
{
"group": "openapi",
"pages": [
@ -750,6 +808,7 @@
"python-sdk/fastmcp-utilities-skills",
"python-sdk/fastmcp-utilities-tests",
"python-sdk/fastmcp-utilities-timeout",
"python-sdk/fastmcp-utilities-token_cache",
"python-sdk/fastmcp-utilities-types",
"python-sdk/fastmcp-utilities-ui",
"python-sdk/fastmcp-utilities-version_check",
@ -1050,4 +1109,4 @@
"appearance": "light",
"background": "/assets/brand/thumbnail-background-4.jpeg"
}
}
}

257
docs/fastmcp-analytics.js Normal file
View file

@ -0,0 +1,257 @@
(function () {
if (typeof window === "undefined") return;
// Public browser key for the shared Prefect Amplitude project.
// This is intentionally client-side; the secret key must never ship to the browser.
var AMPLITUDE_API_KEY = "c361ed56e7bdc1a48a38773c40120b39";
var AMPLITUDE_SCRIPT_URL =
"https://cdn.amplitude.com/libs/analytics-browser-2.8.1-min.js.gz";
var AMPLITUDE_SERVER_URL = "https://api2.amplitude.com/2/httpapi";
var PAGE_VIEW_EVENT = "Page View: FastMCP Docs";
var OUTBOUND_CLICK_EVENT = "Docs Outbound Clicked";
var SOURCE = "docs";
var SOURCE_DETAIL = "fastmcp";
var SURFACE = "fastmcp_docs";
var DEVICE_ID_PARAM = "deviceId";
var routeListenersInstalled = false;
var amplitudeInitialized = false;
var lastTrackedUrl = null;
var PREFECT_DESTINATION_HOSTNAMES = [
"www.prefect.io",
"prefect.io",
"horizon.prefect.io",
"app.prefect.cloud",
];
var routeChangeCallbacks = [];
function loadScript(src, onload) {
var script = document.createElement("script");
script.src = src;
script.async = true;
if (typeof onload === "function") {
script.addEventListener("load", onload);
}
document.head.appendChild(script);
return script;
}
function getAmplitude() {
return window.amplitude || window.amplitudeAnalytics;
}
function normalizePathname(pathname) {
if (pathname === "/") return pathname;
return pathname.replace(/\/+$/, "");
}
function observeRouteChanges(callback) {
routeChangeCallbacks.push(callback);
if (!routeListenersInstalled) {
var fireCallbacks = function () {
routeChangeCallbacks.forEach(function (cb) {
window.setTimeout(cb, 0);
});
};
var wrapHistoryMethod = function (methodName) {
var original = window.history[methodName];
window.history[methodName] = function () {
var result = original.apply(this, arguments);
fireCallbacks();
return result;
};
};
wrapHistoryMethod("pushState");
wrapHistoryMethod("replaceState");
window.addEventListener("popstate", fireCallbacks);
window.addEventListener("hashchange", fireCallbacks);
routeListenersInstalled = true;
}
callback();
}
function buildPageViewProperties() {
return {
url: window.location.href,
title: document.title,
referrer: document.referrer || null,
path: normalizePathname(window.location.pathname),
source: SOURCE,
source_detail: SOURCE_DETAIL,
surface: SURFACE,
};
}
function trackPageView() {
var amplitude = getAmplitude();
if (!amplitude || typeof amplitude.track !== "function") {
return;
}
var url = window.location.href;
if (url === lastTrackedUrl) {
return;
}
amplitude.track(PAGE_VIEW_EVENT, buildPageViewProperties());
lastTrackedUrl = url;
}
function parseUrl(href) {
try {
return new URL(href, window.location.origin);
} catch (error) {
return null;
}
}
function isPrefectDestination(url) {
return PREFECT_DESTINATION_HOSTNAMES.indexOf(url.hostname) !== -1;
}
function addDeviceIdToLink(event) {
var amplitude = getAmplitude();
if (!amplitude || typeof amplitude.getDeviceId !== "function") {
return;
}
var link = event.currentTarget;
var href = link.getAttribute("href") || "";
var url = parseUrl(href);
if (!url || !isPrefectDestination(url)) {
return;
}
url.searchParams.set(DEVICE_ID_PARAM, amplitude.getDeviceId());
link.href = url.toString();
}
function removeDeviceIdFromLink(event) {
var link = event.currentTarget;
var href = link.getAttribute("href") || "";
var url = parseUrl(href);
if (!url || !isPrefectDestination(url)) {
return;
}
url.searchParams.delete(DEVICE_ID_PARAM);
link.href = url.toString();
}
function attachDeviceIdForwarding() {
var elements = document.querySelectorAll("a[href]");
elements.forEach(function (element) {
if (element.dataset.fastmcpDeviceIdBound === "true") {
return;
}
var url = parseUrl(element.getAttribute("href") || "");
if (!url || !isPrefectDestination(url)) {
return;
}
element.addEventListener("mouseenter", addDeviceIdToLink);
element.addEventListener("mouseleave", removeDeviceIdFromLink);
element.addEventListener("focus", addDeviceIdToLink);
element.addEventListener("blur", removeDeviceIdFromLink);
element.addEventListener("touchstart", addDeviceIdToLink);
element.addEventListener("touchcancel", removeDeviceIdFromLink);
element.dataset.fastmcpDeviceIdBound = "true";
});
}
function trackOutboundClick(event) {
var link = event.target && event.target.closest
? event.target.closest("a[href]")
: null;
if (!link) {
return;
}
var href = link.getAttribute("href");
if (!href || href[0] === "#") {
return;
}
var destination;
destination = parseUrl(href);
if (!destination) {
return;
}
if (destination.hostname === window.location.hostname) {
return;
}
var amplitude = getAmplitude();
if (!amplitude || typeof amplitude.track !== "function") {
return;
}
amplitude.track(OUTBOUND_CLICK_EVENT, {
path: normalizePathname(window.location.pathname),
url: window.location.href,
title: document.title,
source: SOURCE,
source_detail: SOURCE_DETAIL,
surface: SURFACE,
destination: destination.href,
destination_domain: destination.hostname,
link_text: (link.textContent || "").trim().slice(0, 200),
is_prefect_destination: isPrefectDestination(destination),
});
}
function initializeAmplitude() {
var amplitude = getAmplitude();
if (
amplitudeInitialized ||
!amplitude ||
typeof amplitude.init !== "function"
) {
return;
}
amplitude.init(AMPLITUDE_API_KEY, undefined, {
useBatch: true,
serverUrl: AMPLITUDE_SERVER_URL,
attribution: {
disabled: false,
trackNewCampaigns: true,
trackPageViews: true,
resetSessionOnNewCampaign: true,
},
defaultTracking: {
pageViews: false,
sessions: false,
formInteractions: true,
fileDownloads: true,
},
});
amplitudeInitialized = true;
observeRouteChanges(trackPageView);
observeRouteChanges(attachDeviceIdForwarding);
}
function initialize() {
document.addEventListener("click", trackOutboundClick, true);
loadScript(AMPLITUDE_SCRIPT_URL, initializeAmplitude);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initialize);
} else {
initialize();
}
})();

View file

@ -3,7 +3,7 @@ title: Quickstart
icon: rocket-launch
---
Welcome! This guide will help you quickly set up FastMCP, run your first MCP server, and deploy a server to Prefect Horizon.
Welcome! This guide will help you quickly set up FastMCP, run your first MCP server, give it a visual UI, and deploy it to Prefect Horizon.
If you haven't already installed FastMCP, follow the [installation instructions](/getting-started/installation).
@ -117,6 +117,34 @@ Note that:
- We must enter a client context (`async with client:`) before using the client
- You can make multiple client calls within the same context
## Give Your Tool a UI
Tools normally return text, but any tool can return an interactive UI instead. Add `app=True` to your tool decorator and return a [Prefab](https://prefab.prefect.io) component — the host renders it as a chart, table, form, or any other visual element right in the conversation. This requires the `apps` extra (`pip install "fastmcp[apps]"`).
The `app=True` flag tells FastMCP to wire up the renderer and protocol metadata automatically. The tool still works like any other MCP tool — it receives arguments and returns a result — but the result is a component tree that the host displays visually instead of as plain text.
```python my_server.py
from prefab_ui.app import PrefabApp
from prefab_ui.components import Column, Heading, Text, Badge, Row
from fastmcp import FastMCP
mcp = FastMCP("My MCP Server")
@mcp.tool(app=True)
def greet(name: str) -> PrefabApp:
"""Greet someone with a visual card."""
with Column(gap=4, css_class="p-6") as view:
Heading(f"Hello, {name}!")
with Row(gap=2, align="center"):
Text("Status")
Badge("Greeted", variant="success")
return PrefabApp(view=view)
```
You can preview app tools locally with `fastmcp dev apps my_server.py` — no MCP host required. See the [Apps overview](/apps/overview) for the full guide, including state management, forms, charts, and server-connected interactivity.
## 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.

View file

@ -77,7 +77,7 @@ BREAKING CHANGES (will crash at import or runtime):
10. DECORATORS: @mcp.tool, @mcp.resource, @mcp.prompt now return the original function, not a component object. Code that accesses .name, .description, or other component attributes on the decorated result will crash with AttributeError.
Fix: set FASTMCP_DECORATOR_MODE=object for v2 compat (itself deprecated).
11. OAUTH STORAGE: Default OAuth client storage changed from DiskStore to FileTreeStore due to pickle deserialization vulnerability in diskcache (CVE-2025-69872). Clients using default storage will re-register automatically on first connection. If using DiskStore explicitly, switch to FileTreeStore or add pip install 'py-key-value-aio[disk]'.
11. OAUTH STORAGE: Default OAuth client storage changed from DiskStore to FileTreeStore due to pickle deserialization vulnerability in diskcache (CVE-2025-69872). Clients using default storage will re-register automatically on first connection. If using DiskStore explicitly, switch to FileTreeStore (with key/collection sanitization strategies) or add pip install 'py-key-value-aio[disk]'.
12. REPO MOVE: GitHub repository moved from jlowin/fastmcp to PrefectHQ/fastmcp. Update git remotes and dependency URLs that reference the old location.
@ -126,7 +126,11 @@ The default OAuth client storage has moved from `DiskStore` to `FileTreeStore` t
If you were using the default storage (i.e., not passing an explicit `client_storage`), clients will need to re-register on their first connection after upgrading. This happens automatically — no user action required, and it's the same flow that already occurs whenever a server restarts with in-memory storage.
If you were passing a `DiskStore` explicitly, you can either [switch to `FileTreeStore`](/servers/storage-backends) (recommended) or keep using `DiskStore` by adding the dependency yourself:
If you were passing a `DiskStore` explicitly, you can either [switch to `FileTreeStore`](/servers/storage-backends) (recommended) or keep using `DiskStore` by adding the dependency yourself.
<Warning>
When switching to `FileTreeStore`, you **must** configure key and collection sanitization strategies. Without them, keys containing special characters (such as URL-based OAuth client IDs) will cause filesystem errors. See the [File Storage](/servers/storage-backends#file-storage) section for the recommended setup.
</Warning>
<Warning>
Keeping `DiskStore` requires `pip install 'py-key-value-aio[disk]'`, which re-introduces the vulnerable `diskcache` package into your dependency tree.

View file

@ -80,10 +80,16 @@ FastMCP has three pillars:
**[Servers](/servers/server)** wrap your Python functions into MCP-compliant tools, resources, and prompts. **[Clients](/clients/client)** connect to any server with full protocol support. And **[Apps](/apps/overview)** give your tools interactive UIs rendered directly in the conversation.
Ready to build? Start with the [installation guide](/getting-started/installation) or jump straight to the [quickstart](/getting-started/quickstart). When you're ready to deploy, [Prefect Horizon](https://www.prefect.io/horizon) offers free hosting for FastMCP users.
Ready to build? Start with the [installation guide](/getting-started/installation) or jump straight to the [quickstart](/getting-started/quickstart).
FastMCP is made with 💙 by [Prefect](https://www.prefect.io/).
## Run FastMCP in production with Horizon
FastMCP is how teams build MCP servers. **[Prefect Horizon](https://www.prefect.io/horizon)** is how enterprises run them in production. Register any MCP server behind a managed gateway with SSO, tool-level RBAC, audit logs, and observability. Deploy FastMCP servers and go from PR to preview in 60 seconds, then remix tools from across your registry into use-case-specific, permissioned endpoints. Horizon is everything we've learned about MCP at scale from building the world's most popular MCP framework. Free for individuals, built for teams.
[Deploy FastMCP with Horizon →](https://www.prefect.io/horizon)
<Tip>
**This documentation reflects FastMCP's `main` branch**, meaning it always reflects the latest development version. Features are generally marked with version badges (e.g. `New in version: 3.0.0`) to indicate when they were introduced. Note that this may include features that are not yet released.
</Tip>

96
docs/more/settings.mdx Normal file
View file

@ -0,0 +1,96 @@
---
title: Settings
description: Configure FastMCP behavior through environment variables or a .env file.
icon: gear
---
FastMCP uses [pydantic-settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) for configuration. Every setting is available as an environment variable with a `FASTMCP_` prefix. Settings are loaded from environment variables and from a `.env` file (see the [Tasks (Docket)](#tasks-docket) section for a caveat about nested settings in `.env` files).
```bash
# Set via environment
export FASTMCP_LOG_LEVEL=DEBUG
export FASTMCP_PORT=3000
# Or use a .env file (loaded automatically)
echo "FASTMCP_LOG_LEVEL=DEBUG" >> .env
```
You can change which `.env` file is loaded by setting the `FASTMCP_ENV_FILE` environment variable (defaults to `.env`). Because this controls which file is loaded, it must be set as an environment variable — it cannot be set inside a `.env` file itself.
## Logging
| Environment Variable | Type | Default | Description |
|---|---|---|---|
| `FASTMCP_LOG_LEVEL` | `Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]` | `INFO` | Log level for FastMCP's own logging output. Case-insensitive. |
| `FASTMCP_LOG_ENABLED` | `bool` | `true` | Enable or disable FastMCP logging entirely. |
| `FASTMCP_CLIENT_LOG_LEVEL` | `Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]` | None | Default minimum log level for messages sent to MCP clients via `context.log()`. When set, messages below this level are suppressed. Individual clients can override this per-session using the MCP `logging/setLevel` request. |
| `FASTMCP_ENABLE_RICH_LOGGING` | `bool` | `true` | Use rich formatting for log output. Set to `false` for plain Python logging. |
| `FASTMCP_ENABLE_RICH_TRACEBACKS` | `bool` | `true` | Use rich tracebacks for errors. |
| `FASTMCP_DEPRECATION_WARNINGS` | `bool` | `true` | Show deprecation warnings. |
## Transport & HTTP
These control how the server listens when running with an HTTP transport.
| Environment Variable | Type | Default | Description |
|---|---|---|---|
| `FASTMCP_TRANSPORT` | `Literal["stdio", "http", "sse", "streamable-http"]` | `stdio` | Default transport. |
| `FASTMCP_HOST` | `str` | `127.0.0.1` | Host to bind to. |
| `FASTMCP_PORT` | `int` | `8000` | Port to bind to. |
| `FASTMCP_SSE_PATH` | `str` | `/sse` | Path for SSE endpoint. |
| `FASTMCP_MESSAGE_PATH` | `str` | `/messages/` | Path for SSE message endpoint. |
| `FASTMCP_STREAMABLE_HTTP_PATH` | `str` | `/mcp` | Path for Streamable HTTP endpoint. |
| `FASTMCP_STATELESS_HTTP` | `bool` | `false` | Enable stateless HTTP mode (new transport per request). Useful for multi-worker deployments. |
| `FASTMCP_JSON_RESPONSE` | `bool` | `false` | Use JSON responses instead of SSE for Streamable HTTP. |
| `FASTMCP_DEBUG` | `bool` | `false` | Enable debug mode. |
## Error Handling
| Environment Variable | Type | Default | Description |
|---|---|---|---|
| `FASTMCP_MASK_ERROR_DETAILS` | `bool` | `false` | Mask error details before sending to clients. When enabled, only messages from explicitly raised `ToolError`, `ResourceError`, or `PromptError` are included in responses. |
| `FASTMCP_STRICT_INPUT_VALIDATION` | `bool` | `false` | Strictly validate tool inputs against the JSON schema. When disabled, compatible inputs are coerced (e.g., the string `"10"` becomes the integer `10`). |
| `FASTMCP_MOUNTED_COMPONENTS_RAISE_ON_LOAD_ERROR` | `bool` | `false` | Raise errors when loading mounted components instead of logging warnings. |
## Client
| Environment Variable | Type | Default | Description |
|---|---|---|---|
| `FASTMCP_CLIENT_INIT_TIMEOUT` | `float \| None` | None | Timeout in seconds for the client initialization handshake. Set to `0` or leave unset to disable. |
| `FASTMCP_CLIENT_DISCONNECT_TIMEOUT` | `float` | `5` | Maximum time in seconds to wait for a clean disconnect before giving up. |
| `FASTMCP_CLIENT_RAISE_FIRST_EXCEPTIONGROUP_ERROR` | `bool` | `true` | When an `ExceptionGroup` is raised, re-raise the first error directly instead of the group. Simplifies debugging but may mask secondary errors. |
## CLI & Display
| Environment Variable | Type | Default | Description |
|---|---|---|---|
| `FASTMCP_SHOW_SERVER_BANNER` | `bool` | `true` | Show the server banner on startup. Also controllable via `--no-banner` or `server.run(show_banner=False)`. |
| `FASTMCP_CHECK_FOR_UPDATES` | `Literal["stable", "prerelease", "off"]` | `stable` | Update checking on CLI startup. `stable` checks stable releases only, `prerelease` includes pre-releases, `off` disables checking. |
## Tasks (Docket)
These configure the [Docket](https://github.com/prefecthq/docket) task queue used by [server tasks](/servers/tasks). All use the `FASTMCP_DOCKET_` prefix.
<Warning>
When setting Docket values in a `.env` file, use a **double** underscore: `FASTMCP_DOCKET__URL` (not `FASTMCP_DOCKET_URL`). This is because `.env` values are resolved through the parent `Settings` class, which uses `__` as its nested delimiter. As regular environment variables (e.g., `export`), the single-underscore form `FASTMCP_DOCKET_URL` works fine.
</Warning>
| Environment Variable | Type | Default | Description |
|---|---|---|---|
| `FASTMCP_DOCKET_NAME` | `str` | `fastmcp` | Queue name. Servers and workers sharing the same name and backend URL share a task queue. |
| `FASTMCP_DOCKET_URL` | `str` | `memory://` | Backend URL. Use `memory://` for single-process or `redis://host:port/db` for distributed workers. |
| `FASTMCP_DOCKET_WORKER_NAME` | `str \| None` | None | Worker name. Auto-generated if unset. |
| `FASTMCP_DOCKET_CONCURRENCY` | `int` | `10` | Maximum concurrent tasks per worker. |
| `FASTMCP_DOCKET_REDELIVERY_TIMEOUT` | `timedelta` | `300s` | If a worker doesn't complete a task within this time, it's redelivered to another worker. |
| `FASTMCP_DOCKET_RECONNECTION_DELAY` | `timedelta` | `5s` | Delay between reconnection attempts when the worker loses its backend connection. |
| `FASTMCP_DOCKET_MINIMUM_CHECK_INTERVAL` | `timedelta` | `50ms` | How frequently the worker polls for new tasks. Lower values reduce latency at the cost of more CPU usage. |
## Advanced
| Environment Variable | Type | Default | Description |
|---|---|---|---|
| `FASTMCP_HOME` | `Path` | Platform default | Data directory for FastMCP. Defaults to the platform-specific user data directory. |
| `FASTMCP_ENV_FILE` | `str` | `.env` | Path to the `.env` file to load settings from. Must be set as an environment variable (see above). |
| `FASTMCP_SERVER_DEPENDENCIES` | `list[str]` | `[]` | Additional dependencies to install in the server environment. |
| `FASTMCP_DECORATOR_MODE` | `Literal["function", "object"]` | `function` | Controls what `@tool`, `@resource`, and `@prompt` decorators return. `function` returns the original function (default); `object` returns component objects (deprecated, will be removed). |
| `FASTMCP_TEST_MODE` | `bool` | `false` | Enable test mode. |

View file

@ -0,0 +1,16 @@
---
title: __init__
sidebarTitle: __init__
---
# `fastmcp.apps`
FastMCP Apps — interactive UIs for MCP tools.
This package contains the app-related components:
- ``FastMCPApp`` — composable provider for interactive apps with backend tools
- ``AppConfig`` — configuration for MCP App tools and resources
- ``ResourceCSP`` / ``ResourcePermissions`` — security configuration

View file

@ -0,0 +1,146 @@
---
title: app
sidebarTitle: app
---
# `fastmcp.apps.app`
FastMCPApp — a Provider that represents a composable MCP application.
FastMCPApp binds entry-point tools (model calls these) together with backend
tools (the UI calls these via CallTool). Backend tools are tagged with
``meta["fastmcp"]["app"]`` so they can be found through the provider chain
even when transforms (namespace, visibility, etc.) have renamed or hidden
them — the server sets a context var that tells ``Provider.get_tool`` to
fall back to a direct lookup for app-visible tools.
Usage::
from fastmcp import FastMCP, FastMCPApp
app = FastMCPApp("Dashboard")
@app.ui()
def show_dashboard() -> Component:
return Column(...)
@app.tool()
def save_contact(name: str, email: str) -> str:
return name
server = FastMCP("Platform")
server.add_provider(app)
## Classes
### `FastMCPApp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L131" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
A Provider that represents an MCP application.
Binds together entry-point tools (``@app.ui``), backend tools
(``@app.tool``), and the Prefab renderer resource. Backend tools
are tagged with ``meta["fastmcp"]["app"]`` so ``Provider.get_tool``
can find them by original name even when transforms have been applied.
**Methods:**
#### `tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L158" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
tool(self, name_or_fn: F) -> F
```
#### `tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L170" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
tool(self, name_or_fn: str | None = None) -> Callable[[F], F]
```
#### `tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L181" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
tool(self, name_or_fn: str | AnyFunction | None = None) -> Any
```
Register a backend tool that the UI calls via CallTool.
Backend tools default to ``visibility=["app"]``. Pass ``model=True``
to also expose the tool to the model (``visibility=["app", "model"]``).
Supports multiple calling patterns::
@app.tool
def save(name: str): ...
@app.tool()
def save(name: str): ...
@app.tool("custom_name")
def save(name: str): ...
#### `ui` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L242" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
ui(self, name_or_fn: F) -> F
```
#### `ui` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L257" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
ui(self, name_or_fn: str | None = None) -> Callable[[F], F]
```
#### `ui` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L271" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
ui(self, name_or_fn: str | AnyFunction | None = None) -> Any
```
Register a UI entry-point tool that the model calls.
Entry-point tools default to ``visibility=["model"]`` and auto-wire
the Prefab renderer resource and CSP. They are tagged with the app
name so structured content includes ``_meta.fastmcp.app``.
Supports multiple calling patterns::
@app.ui
def dashboard() -> Component: ...
@app.ui()
def dashboard() -> Component: ...
@app.ui("my_dashboard")
def dashboard() -> Component: ...
#### `add_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L360" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
add_tool(self, tool: Tool | Callable[..., Any]) -> Tool
```
Add a tool to this app programmatically.
The tool is tagged with this app's name for routing.
#### `lifespan` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L410" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
lifespan(self) -> AsyncIterator[None]
```
#### `run` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L418" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
run(self, transport: Literal['stdio', 'http', 'sse', 'streamable-http'] | None = None, **kwargs: Any) -> None
```
Create a temporary FastMCP server and run this app standalone.

View file

@ -0,0 +1,58 @@
---
title: approval
sidebarTitle: approval
---
# `fastmcp.apps.approval`
Approval — a Provider that adds human-in-the-loop approval to any server.
The LLM presents a summary of what it's about to do, and the user
approves or rejects via buttons. The result is sent back into the
conversation as a message, prompting the LLM's next turn.
Requires ``fastmcp[apps]`` (prefab-ui).
Usage::
from fastmcp import FastMCP
from fastmcp.apps.approval import Approval
mcp = FastMCP("My Server")
mcp.add_provider(Approval())
## Classes
### `Approval` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/approval.py#L49" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
A Provider that adds human-in-the-loop approval to a server.
The LLM calls the ``request_approval`` tool with a summary and
optional details. The user sees an approval card with Approve and
Reject buttons. Clicking either sends a message back into the
conversation (via ``SendMessage``), triggering the LLM's next turn.
The message appears as if the user sent it, so the LLM sees
something like ``'"Deploy v3.2 to production" is APPROVED'``.
Example::
from fastmcp import FastMCP
from fastmcp.apps.approval import Approval
mcp = FastMCP("My Server")
mcp.add_provider(Approval())
Customized::
Approval(
title="Deploy Gate",
approve_text="Ship it",
approve_variant="default",
reject_text="Abort",
reject_variant="destructive",
)

View file

@ -0,0 +1,44 @@
---
title: choice
sidebarTitle: choice
---
# `fastmcp.apps.choice`
Choice — a Provider that lets the user pick from a set of options.
The LLM presents options, the user clicks one, and the selection
flows back into the conversation as a message.
Requires ``fastmcp[apps]`` (prefab-ui).
Usage::
from fastmcp import FastMCP
from fastmcp.apps.choice import Choice
mcp = FastMCP("My Server")
mcp.add_provider(Choice())
## Classes
### `Choice` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/choice.py#L46" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
A Provider that lets the user choose from a set of options.
The LLM calls ``choose`` with a prompt and a list of options.
The user sees a card with one button per option. Clicking a button
sends the selection back into the conversation via ``SendMessage``,
triggering the LLM's next turn.
Example::
from fastmcp import FastMCP
from fastmcp.apps.choice import Choice
mcp = FastMCP("My Server")
mcp.add_provider(Choice())

View file

@ -0,0 +1,90 @@
---
title: config
sidebarTitle: config
---
# `fastmcp.apps.config`
MCP Apps support — extension negotiation and typed UI metadata models.
Provides constants and Pydantic models for the MCP Apps extension
(io.modelcontextprotocol/ui), enabling tools and resources to carry
UI metadata for clients that support interactive app rendering.
## Functions
### `app_config_to_meta_dict` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/config.py#L173" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
app_config_to_meta_dict(app: AppConfig | dict[str, Any]) -> dict[str, Any]
```
Convert an AppConfig or dict to the wire-format dict for ``meta["ui"]``.
## Classes
### `ResourceCSP` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/config.py#L20" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Content Security Policy for MCP App resources.
Declares which external origins the app is allowed to connect to or
load resources from. Hosts use these declarations to build the
``Content-Security-Policy`` header for the sandboxed iframe.
### `ResourcePermissions` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/config.py#L52" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Iframe sandbox permissions for MCP App resources.
Each field, when set (typically to ``{}``), requests that the host
grant the corresponding Permission Policy feature to the sandboxed
iframe. Hosts MAY honour these; apps should use JS feature detection
as a fallback.
### `AppConfig` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/config.py#L79" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Configuration for MCP App tools and resources.
Controls how a tool or resource participates in the MCP Apps extension.
On tools, ``resource_uri`` and ``visibility`` specify which UI resource
to render and where the tool appears. On resources, those fields must
be left unset (the resource itself is the UI).
All fields use ``exclude_none`` serialization so only explicitly-set
values appear on the wire. Aliases match the MCP Apps wire format
(camelCase).
### `PrefabAppConfig` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/config.py#L117" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
App configuration for Prefab tools with sensible defaults.
Like ``app=True`` but customizable. Auto-wires the Prefab renderer
URI and merges the renderer's CSP with any additional domains you
specify. The renderer resource is registered automatically.
Example::
@mcp.tool(app=PrefabAppConfig()) # same as app=True
@mcp.tool(app=PrefabAppConfig(
csp=ResourceCSP(frame_domains=["https://example.com"]),
))
**Methods:**
#### `model_post_init` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/config.py#L133" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
model_post_init(self, __context: Any) -> None
```

View file

@ -0,0 +1,144 @@
---
title: file_upload
sidebarTitle: file_upload
---
# `fastmcp.apps.file_upload`
FileUpload — a Provider that adds drag-and-drop file upload to any server.
Lets users upload files directly to the server through an interactive UI,
bypassing the LLM context window entirely. The LLM can then read and work
with uploaded files through model-visible tools.
Requires ``fastmcp[apps]`` (prefab-ui).
Usage::
from fastmcp import FastMCP
from fastmcp.apps import FileUpload
mcp = FastMCP("My Server")
mcp.add_provider(FileUpload())
For custom persistence, override the storage methods::
class S3Upload(FileUpload):
def on_store(self, files, ctx):
# write to S3, return summaries
...
def on_list(self, ctx):
# list from S3
...
def on_read(self, name, ctx):
# read from S3
...
## Classes
### `FileUpload` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/file_upload.py#L93" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
A Provider that adds file upload capabilities to a server.
Registers a drag-and-drop UI tool, a backend storage tool, and
model-visible tools for listing and reading uploaded files.
Files are scoped by MCP session and stored in memory by default.
Override ``on_store``, ``on_list``, and ``on_read`` for custom
persistence (filesystem, S3, database, etc.). Each method receives
the current ``Context``, giving access to session ID, auth tokens,
and request metadata for partitioning and authorization.
**Session scoping:** The default storage uses ``ctx.session_id`` to
isolate files by session. This works with stdio, SSE, and stateful
HTTP transports. In **stateless HTTP** mode, each request creates a
new session, so files won't persist across requests. For stateless
deployments, override the storage methods to partition by a stable
identifier from the auth context::
class UserScopedUpload(FileUpload):
def on_store(self, files, ctx):
user_id = ctx.access_token["sub"]
...
Example::
from fastmcp import FastMCP
from fastmcp.apps.file_upload import FileUpload
mcp = FastMCP("My Server")
mcp.add_provider(FileUpload())
**Methods:**
#### `on_store` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/file_upload.py#L174" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
on_store(self, files: list[dict[str, Any]], ctx: Context) -> list[dict[str, Any]]
```
Store uploaded files and return summaries.
**Args:**
- `files`: List of file dicts, each with ``name``, ``size``,
``type``, and ``data`` (base64-encoded content).
- `ctx`: The current request context. Use for session ID,
auth tokens, or any metadata needed for partitioning.
Override this method for custom persistence. The default
implementation stores files in memory, scoped by
``_get_scope_key(ctx)``.
**Returns:**
- List of file summary dicts (``name``, ``type``, ``size``,
- ``size_display``, ``uploaded_at``).
#### `on_list` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/file_upload.py#L207" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
on_list(self, ctx: Context) -> list[dict[str, Any]]
```
List all stored files.
**Args:**
- `ctx`: The current request context.
Override this method for custom persistence. The default
implementation returns files from the current scope.
**Returns:**
- List of file summary dicts.
#### `on_read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/file_upload.py#L223" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
on_read(self, name: str, ctx: Context) -> dict[str, Any]
```
Read a file's contents by name.
**Args:**
- `name`: The filename to read.
- `ctx`: The current request context.
Override this method for custom persistence. The default
implementation reads from the current scope's in-memory store.
Text files are decoded from base64; binary files return a
truncated base64 preview.
**Returns:**
- Dict with file metadata and ``content`` (text) or
- ``content_base64`` (binary preview).
**Raises:**
- `ValueError`: If the file is not found.

View file

@ -0,0 +1,69 @@
---
title: form
sidebarTitle: form
---
# `fastmcp.apps.form`
FormInput — a Provider that collects structured input from the user.
Define a Pydantic model for the data you need, and ``FormInput``
generates a form UI. The user fills it out, the submission is
validated, and an optional callback processes the result.
Requires ``fastmcp[apps]`` (prefab-ui).
Usage::
from pydantic import BaseModel
from fastmcp import FastMCP
from fastmcp.apps.form import FormInput
class ShippingAddress(BaseModel):
street: str
city: str
state: str
zip_code: str
mcp = FastMCP("My Server")
mcp.add_provider(FormInput(model=ShippingAddress))
## Classes
### `FormInput` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/form.py#L57" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
A Provider that collects structured input via a Pydantic model.
Define a model for the data you need, and ``FormInput`` generates
a form from it using ``Form.from_model()``. Field types, labels,
descriptions, and validation are all derived from the model.
Optionally provide an ``on_submit`` callback to process the
validated data. The callback receives a model instance and returns
a string that goes back to the LLM. Without a callback, the
validated JSON is sent directly.
Example::
from pydantic import BaseModel
from fastmcp import FastMCP
from fastmcp.apps.form import FormInput
class Contact(BaseModel):
name: str
email: str
mcp = FastMCP("My Server")
mcp.add_provider(FormInput(model=Contact))
With a callback::
def save_contact(contact: Contact) -> str:
db.insert(contact.model_dump())
return f"Saved {contact.name}"
mcp.add_provider(FormInput(model=Contact, on_submit=save_contact))

View file

@ -0,0 +1,56 @@
---
title: generative
sidebarTitle: generative
---
# `fastmcp.apps.generative`
GenerativeUI — a Provider that adds LLM-generated UI capabilities.
Registers tools and resources from ``prefab_ui.generative`` so that an
LLM can write Prefab Python code, execute it in a sandbox, and render
the result as a streaming interactive UI.
Requires ``fastmcp[apps]`` (prefab-ui).
Usage::
from fastmcp import FastMCP
from fastmcp.apps.generative import GenerativeUI
mcp = FastMCP("My Server")
mcp.add_provider(GenerativeUI())
## Classes
### `GenerativeUI` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/generative.py#L53" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
A Provider that adds generative UI capabilities to a server.
Registers:
- A ``generate_ui`` tool that accepts Prefab Python code, executes
it in a Pyodide sandbox, and returns the rendered PrefabApp.
Supports streaming via ``ontoolinputpartial``.
- A ``components`` tool that searches the Prefab component library.
- The generative renderer resource with CSP for Pyodide CDN access.
Example::
from fastmcp import FastMCP
from fastmcp.apps.generative import GenerativeUI
mcp = FastMCP("My Server")
mcp.add_provider(GenerativeUI())
**Methods:**
#### `lifespan` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/generative.py#L196" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
lifespan(self) -> AsyncIterator[None]
```

View file

@ -0,0 +1,47 @@
---
title: apps_dev
sidebarTitle: apps_dev
---
# `fastmcp.cli.apps_dev`
Dev server for previewing FastMCPApp UIs locally.
Starts the user's MCP server on a configurable port, then starts a lightweight
Starlette dev server that:
- Serves a Prefab-based tool picker at GET /
- Proxies /mcp to the user's server (avoids browser CORS restrictions)
- Serves the AppBridge host page at GET /launch
The host page uses @modelcontextprotocol/ext-apps to connect to the MCP server
and render the selected UI tool inside an iframe.
Startup sequence
----------------
1. Download ext-apps app-bridge.js from npm and patch its bare
``@modelcontextprotocol/sdk/…`` imports to use concrete esm.sh URLs.
2. Detect the exact Zod v4 module URL that esm.sh serves for that SDK version
and build an import-map entry that redirects the broken ``v4.mjs`` (which
only re-exports ``{z, default}``) to ``v4/classic/index.mjs`` (which
correctly exports every named Zod v4 function). Import maps apply to the
full module graph in the document, including cross-origin esm.sh modules.
3. Serve both the patched JS and the import-map JSON from the dev server.
## Functions
### `run_dev_apps` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/apps_dev.py#L1682" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
run_dev_apps(server_spec: str) -> None
```
Start the full dev environment for a FastMCPApp server.
Starts the user's MCP server on *mcp_port*, starts the Prefab dev UI
on *dev_port* (with an /mcp proxy to the user's server), then opens
the browser.

View file

@ -50,7 +50,23 @@ Run an MCP server with the MCP Inspector for development.
- `server_spec`: Python file to run, optionally with \:object suffix, or None to auto-detect fastmcp.json
### `run` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/cli.py#L337" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `apps` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/cli.py#L337" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
apps(server_spec: str) -> None
```
Preview a FastMCPApp UI in the browser.
Starts the MCP server from SERVER_SPEC on --mcp-port, launches a local
dev UI on --dev-port with a tool picker and AppBridge host, then opens
the browser automatically.
Requires fastmcp[apps] to be installed (prefab-ui).
### `run` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/cli.py#L385" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
run(server_spec: str | None = None, *server_args: str) -> None
@ -75,7 +91,7 @@ fastmcp run server.py -- --config config.json --debug
- `server_spec`: Python file, object specification (file\:obj), config file, URL, or None to auto-detect
### `inspect` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/cli.py#L712" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `inspect` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/cli.py#L760" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
inspect(server_spec: str | None = None) -> None
@ -106,7 +122,7 @@ fastmcp inspect # auto-detect fastmcp.json
- `server_spec`: Python file to inspect, optionally with \:object suffix, or fastmcp.json
### `prepare` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/cli.py#L954" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `prepare` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/cli.py#L1002" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
prepare(config_path: Annotated[str | None, cyclopts.Parameter(help='Path to fastmcp.json configuration file')] = None, output_dir: Annotated[str | None, cyclopts.Parameter(help='Directory to create the persistent environment in')] = None, skip_source: Annotated[bool, cyclopts.Parameter(help='Skip source preparation (e.g., git clone)')] = False) -> None

View file

@ -10,7 +10,7 @@ Client-side CLI commands for querying and invoking MCP servers.
## Functions
### `resolve_server_spec` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L42" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `resolve_server_spec` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L43" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
resolve_server_spec(server_spec: str | None) -> str | dict[str, Any] | ClientTransport
@ -32,7 +32,7 @@ When ``command`` is provided, the string is shell-split into a
``StdioTransport(command, args)``.
### `coerce_value` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L263" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `coerce_value` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L264" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
coerce_value(raw: str, schema: dict[str, Any]) -> Any
@ -42,7 +42,7 @@ coerce_value(raw: str, schema: dict[str, Any]) -> Any
Coerce a string CLI value according to a JSON-Schema type hint.
### `parse_tool_arguments` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L297" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `parse_tool_arguments` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L298" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
parse_tool_arguments(raw_args: tuple[str, ...], input_json: str | None, input_schema: dict[str, Any]) -> dict[str, Any]
@ -56,7 +56,7 @@ A single JSON object argument is treated as the full argument dict.
Values are coerced using the tool's ``inputSchema``.
### `format_tool_signature` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L369" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `format_tool_signature` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L370" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
format_tool_signature(tool: mcp.types.Tool) -> str
@ -66,7 +66,7 @@ format_tool_signature(tool: mcp.types.Tool) -> str
Build ``name(param: type, ...) -> return_type`` from a tool's JSON schemas.
### `list_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L625" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `list_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L641" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_command(server_spec: Annotated[str | None, cyclopts.Parameter(help='Server URL, Python file, MCPConfig JSON, or .js file')] = None) -> None
@ -84,7 +84,7 @@ fastmcp list --command 'npx -y @mcp/server' --resources
fastmcp list http://server/mcp --transport sse
### `call_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L774" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `call_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L796" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
call_command(server_spec: Annotated[str | None, cyclopts.Parameter(help='Server URL, Python file, MCPConfig JSON, or .js file')] = None, target: Annotated[str, cyclopts.Parameter(help='Tool name, resource URI, or prompt name (with --prompt)')] = '', *arguments: str) -> None
@ -110,7 +110,7 @@ fastmcp call http://server/mcp create --input-json '{"tags": ["a","b"]}'
```
### `discover_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L875" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `discover_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/client.py#L897" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
discover_command() -> None

View file

@ -57,7 +57,7 @@ Install FastMCP server in Claude Code.
- True if installation was successful, False otherwise
### `claude_code_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/claude_code.py#L153" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `claude_code_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/claude_code.py#L155" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
claude_code_command(server_spec: str) -> None

View file

@ -13,14 +13,17 @@ Claude Desktop integration for FastMCP install using Cyclopts.
### `get_claude_config_path` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/claude_desktop.py#L20" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_claude_config_path() -> Path | None
get_claude_config_path(config_path: Path | None = None) -> Path | None
```
Get the Claude config directory based on platform.
**Args:**
- `config_path`: Optional custom path to the Claude Desktop config directory
### `install_claude_desktop` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/claude_desktop.py#L38" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `install_claude_desktop` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/claude_desktop.py#L49" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
install_claude_desktop(file: Path, server_object: str | None, name: str) -> bool
@ -39,12 +42,13 @@ Install FastMCP server in Claude Desktop.
- `python_version`: Optional Python version to use
- `with_requirements`: Optional requirements file to install from
- `project`: Optional project directory to run within
- `config_path`: Optional custom path to Claude Desktop config directory
**Returns:**
- True if installation was successful, False otherwise
### `claude_desktop_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/claude_desktop.py#L125" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `claude_desktop_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/claude_desktop.py#L139" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
claude_desktop_command(server_spec: str) -> None

View file

@ -68,7 +68,7 @@ Install FastMCP server to workspace-specific Cursor configuration.
- True if installation was successful, False otherwise
### `install_cursor` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/cursor.py#L140" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `install_cursor` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/cursor.py#L143" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
install_cursor(file: Path, server_object: str | None, name: str) -> bool
@ -93,7 +93,7 @@ Install FastMCP server in Cursor.
- True if installation was successful, False otherwise
### `cursor_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/cursor.py#L225" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `cursor_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/cursor.py#L228" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
cursor_command(server_spec: str) -> None

View file

@ -54,7 +54,7 @@ Install FastMCP server in Gemini CLI.
- True if installation was successful, False otherwise
### `gemini_cli_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/gemini_cli.py#L150" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `gemini_cli_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/gemini_cli.py#L152" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
gemini_cli_command(server_spec: str) -> None

View file

@ -10,7 +10,19 @@ Shared utilities for install commands.
## Functions
### `parse_env_var` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/shared.py#L21" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `validate_server_name` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/shared.py#L28" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
validate_server_name(name: str) -> str
```
Validate that a server name is safe for use as a subprocess argument.
Raises SystemExit if the name contains shell metacharacters.
### `parse_env_var` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/shared.py#L42" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
parse_env_var(env_var: str) -> tuple[str, str]
@ -20,7 +32,7 @@ parse_env_var(env_var: str) -> tuple[str, str]
Parse environment variable string in format KEY=VALUE.
### `process_common_args` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/shared.py#L32" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `process_common_args` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/shared.py#L53" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
process_common_args(server_spec: str, server_name: str | None, with_packages: list[str] | None, env_vars: list[str] | None, env_file: Path | None) -> tuple[Path, str | None, str, list[str], dict[str, str] | None]
@ -32,7 +44,7 @@ Process common arguments shared by all install commands.
Handles both fastmcp.json config files and traditional file.py:object syntax.
### `open_deeplink` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/shared.py#L148" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `open_deeplink` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/shared.py#L169" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
open_deeplink(url: str) -> bool

View file

@ -32,37 +32,43 @@ Raised when OAuth client credentials are not found on the server.
**Methods:**
#### `clear` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L99" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `clear` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L102" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
clear(self) -> None
```
#### `get_tokens` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L104" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `get_tokens` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L111" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_tokens(self) -> OAuthToken | None
```
#### `set_tokens` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L108" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `set_tokens` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L115" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
set_tokens(self, tokens: OAuthToken) -> None
```
#### `get_client_info` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L119" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `get_token_expiry` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L135" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_token_expiry(self) -> float | None
```
#### `get_client_info` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L145" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_client_info(self) -> OAuthClientInformationFull | None
```
#### `set_client_info` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L125" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `set_client_info` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L151" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
set_client_info(self, client_info: OAuthClientInformationFull) -> None
```
### `OAuth` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L138" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `OAuth` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L164" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
OAuth client provider for MCP servers with browser-based authentication.
@ -73,7 +79,7 @@ a browser for user authorization and running a local callback server.
**Methods:**
#### `redirect_handler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L290" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `redirect_handler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L320" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
redirect_handler(self, authorization_url: str) -> None
@ -82,7 +88,7 @@ redirect_handler(self, authorization_url: str) -> None
Open browser for authorization, with pre-flight check for invalid client.
#### `callback_handler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L311" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `callback_handler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L341" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
callback_handler(self) -> tuple[str, str | None]
@ -91,7 +97,7 @@ callback_handler(self) -> tuple[str, str | None]
Handle OAuth callback and return (auth_code, state).
#### `async_auth_flow` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L350" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `async_auth_flow` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L380" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
async_auth_flow(self, request: httpx.Request) -> AsyncGenerator[httpx.Request, httpx.Response]

View file

@ -7,7 +7,7 @@ sidebarTitle: client
## Classes
### `ClientSessionState` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L95" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ClientSessionState` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L97" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Holds all session-related state for a Client instance.
@ -16,13 +16,13 @@ This allows clean separation of configuration (which is copied) from
session state (which should be fresh for each new client instance).
### `CallToolResult` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L112" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `CallToolResult` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L114" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Parsed result from a tool call.
### `Client` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L122" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `Client` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L124" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
MCP client that delegates connection management to a Transport instance.
@ -85,7 +85,7 @@ async with client:
**Methods:**
#### `session` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L340" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `session` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L371" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
session(self) -> ClientSession
@ -94,7 +94,7 @@ session(self) -> ClientSession
Get the current active session. Raises RuntimeError if not connected.
#### `initialize_result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L350" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `initialize_result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L381" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
initialize_result(self) -> mcp.types.InitializeResult | None
@ -103,7 +103,7 @@ initialize_result(self) -> mcp.types.InitializeResult | None
Get the result of the initialization request.
#### `set_roots` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L354" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `set_roots` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L385" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
set_roots(self, roots: RootsList | RootsHandler) -> None
@ -112,7 +112,7 @@ set_roots(self, roots: RootsList | RootsHandler) -> None
Set the roots for the client. This does not automatically call `send_roots_list_changed`.
#### `set_sampling_callback` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L358" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `set_sampling_callback` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L389" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
set_sampling_callback(self, sampling_callback: SamplingHandler, sampling_capabilities: mcp.types.SamplingCapability | None = None) -> None
@ -121,7 +121,7 @@ set_sampling_callback(self, sampling_callback: SamplingHandler, sampling_capabil
Set the sampling callback for the client.
#### `set_elicitation_callback` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L373" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `set_elicitation_callback` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L404" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
set_elicitation_callback(self, elicitation_callback: ElicitationHandler) -> None
@ -130,7 +130,7 @@ set_elicitation_callback(self, elicitation_callback: ElicitationHandler) -> None
Set the elicitation callback for the client.
#### `is_connected` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L381" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `is_connected` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L412" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
is_connected(self) -> bool
@ -139,7 +139,7 @@ is_connected(self) -> bool
Check if the client is currently connected.
#### `new` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L385" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `new` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L416" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
new(self) -> Client[ClientTransportT]
@ -155,7 +155,7 @@ share state with the original client.
- A new Client instance with the same configuration but disconnected state.
#### `initialize` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L430" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `initialize` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L461" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
initialize(self, timeout: datetime.timedelta | float | int | None = None) -> mcp.types.InitializeResult
@ -183,13 +183,13 @@ capabilities, protocol version, and optional instructions.
- `RuntimeError`: If the client is not connected or initialization times out.
#### `close` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L731" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `close` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L762" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
close(self)
```
#### `ping` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L737" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `ping` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L768" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
ping(self) -> bool
@ -198,7 +198,7 @@ ping(self) -> bool
Send a ping request.
#### `cancel` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L742" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `cancel` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L773" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
cancel(self, request_id: str | int, reason: str | None = None) -> None
@ -207,7 +207,7 @@ cancel(self, request_id: str | int, reason: str | None = None) -> None
Send a cancellation notification for an in-progress request.
#### `progress` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L759" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `progress` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L790" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
progress(self, progress_token: str | int, progress: float, total: float | None = None, message: str | None = None) -> None
@ -216,7 +216,7 @@ progress(self, progress_token: str | int, progress: float, total: float | None =
Send a progress notification.
#### `set_logging_level` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L771" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `set_logging_level` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L802" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
set_logging_level(self, level: mcp.types.LoggingLevel) -> None
@ -225,7 +225,7 @@ set_logging_level(self, level: mcp.types.LoggingLevel) -> None
Send a logging/setLevel request.
#### `send_roots_list_changed` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L775" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `send_roots_list_changed` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L806" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
send_roots_list_changed(self) -> None
@ -234,7 +234,7 @@ send_roots_list_changed(self) -> None
Send a roots/list_changed notification.
#### `complete_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L781" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `complete_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L812" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
complete_mcp(self, ref: mcp.types.ResourceTemplateReference | mcp.types.PromptReference, argument: dict[str, str], context_arguments: dict[str, Any] | None = None) -> mcp.types.CompleteResult
@ -257,7 +257,7 @@ containing the completion and any additional metadata.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `complete` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L812" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `complete` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L843" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
complete(self, ref: mcp.types.ResourceTemplateReference | mcp.types.PromptReference, argument: dict[str, str], context_arguments: dict[str, Any] | None = None) -> mcp.types.Completion
@ -279,7 +279,7 @@ include with the completion request. Defaults to None.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `generate_name` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L839" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `generate_name` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L870" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
generate_name(cls, name: str | None = None) -> str

View file

@ -10,7 +10,7 @@ Prompt-related methods for FastMCP Client.
## Classes
### `ClientPromptsMixin` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L29" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ClientPromptsMixin` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L31" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Mixin providing prompt-related methods for Client.
@ -18,7 +18,7 @@ Mixin providing prompt-related methods for Client.
**Methods:**
#### `list_prompts_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L34" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `list_prompts_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L36" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_prompts_mcp(self: Client) -> mcp.types.ListPromptsResult
@ -38,10 +38,10 @@ containing the list of prompts and any additional metadata.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `list_prompts` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L57" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `list_prompts` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L59" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_prompts(self: Client) -> list[mcp.types.Prompt]
list_prompts(self: Client, max_pages: int = AUTO_PAGINATION_MAX_PAGES) -> list[mcp.types.Prompt]
```
Retrieve all prompts available on the server.
@ -50,15 +50,18 @@ This method automatically fetches all pages if the server paginates results,
returning the complete list. For manual pagination control (e.g., to handle
large result sets incrementally), use list_prompts_mcp() with the cursor parameter.
**Args:**
- `max_pages`: Maximum number of pages to fetch before raising. Defaults to 250.
**Returns:**
- list\[mcp.types.Prompt]: A list of all Prompt objects.
**Raises:**
- `RuntimeError`: If called while the client is not connected.
- `RuntimeError`: If the page limit is reached before pagination completes.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `get_prompt_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L92" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `get_prompt_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L107" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_prompt_mcp(self: Client, name: str, arguments: dict[str, Any] | None = None, meta: dict[str, Any] | None = None) -> mcp.types.GetPromptResult
@ -80,19 +83,19 @@ containing the prompt messages and any additional metadata.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `get_prompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L161" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `get_prompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L176" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_prompt(self: Client, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.GetPromptResult
```
#### `get_prompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L172" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `get_prompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L187" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_prompt(self: Client, name: str, arguments: dict[str, Any] | None = None) -> PromptTask
```
#### `get_prompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L184" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `get_prompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/prompts.py#L199" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_prompt(self: Client, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.GetPromptResult | PromptTask

View file

@ -10,7 +10,7 @@ Resource-related methods for FastMCP Client.
## Classes
### `ClientResourcesMixin` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L28" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ClientResourcesMixin` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L30" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Mixin providing resource-related methods for Client.
@ -18,7 +18,7 @@ Mixin providing resource-related methods for Client.
**Methods:**
#### `list_resources_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L33" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `list_resources_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L35" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_resources_mcp(self: Client) -> mcp.types.ListResourcesResult
@ -38,10 +38,10 @@ containing the list of resources and any additional metadata.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `list_resources` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L56" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `list_resources` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L58" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_resources(self: Client) -> list[mcp.types.Resource]
list_resources(self: Client, max_pages: int = AUTO_PAGINATION_MAX_PAGES) -> list[mcp.types.Resource]
```
Retrieve all resources available on the server.
@ -50,15 +50,18 @@ This method automatically fetches all pages if the server paginates results,
returning the complete list. For manual pagination control (e.g., to handle
large result sets incrementally), use list_resources_mcp() with the cursor parameter.
**Args:**
- `max_pages`: Maximum number of pages to fetch before raising. Defaults to 250.
**Returns:**
- list\[mcp.types.Resource]: A list of all Resource objects.
**Raises:**
- `RuntimeError`: If called while the client is not connected.
- `RuntimeError`: If the page limit is reached before pagination completes.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `list_resource_templates_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L90" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `list_resource_templates_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L105" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_resource_templates_mcp(self: Client) -> mcp.types.ListResourceTemplatesResult
@ -78,10 +81,10 @@ containing the list of resource templates and any additional metadata.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `list_resource_templates` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L113" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `list_resource_templates` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L128" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_resource_templates(self: Client) -> list[mcp.types.ResourceTemplate]
list_resource_templates(self: Client, max_pages: int = AUTO_PAGINATION_MAX_PAGES) -> list[mcp.types.ResourceTemplate]
```
Retrieve all resource templates available on the server.
@ -91,15 +94,18 @@ returning the complete list. For manual pagination control (e.g., to handle
large result sets incrementally), use list_resource_templates_mcp() with the
cursor parameter.
**Args:**
- `max_pages`: Maximum number of pages to fetch before raising. Defaults to 250.
**Returns:**
- list\[mcp.types.ResourceTemplate]: A list of all ResourceTemplate objects.
**Raises:**
- `RuntimeError`: If called while the client is not connected.
- `RuntimeError`: If the page limit is reached before pagination completes.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `read_resource_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L149" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `read_resource_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L177" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
read_resource_mcp(self: Client, uri: AnyUrl | str, meta: dict[str, Any] | None = None) -> mcp.types.ReadResourceResult
@ -120,19 +126,19 @@ containing the resource contents and any additional metadata.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `read_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L205" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `read_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L233" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
read_resource(self: Client, uri: AnyUrl | str) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
```
#### `read_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L215" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `read_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L243" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
read_resource(self: Client, uri: AnyUrl | str) -> ResourceTask
```
#### `read_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L226" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `read_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/resources.py#L254" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
read_resource(self: Client, uri: AnyUrl | str) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents] | ResourceTask

View file

@ -10,7 +10,7 @@ Tool-related methods for FastMCP Client.
## Classes
### `ClientToolsMixin` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L32" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ClientToolsMixin` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L34" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Mixin providing tool-related methods for Client.
@ -18,7 +18,7 @@ Mixin providing tool-related methods for Client.
**Methods:**
#### `list_tools_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L37" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `list_tools_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L39" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_tools_mcp(self: Client) -> mcp.types.ListToolsResult
@ -38,10 +38,10 @@ containing the list of tools and any additional metadata.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `list_tools` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L60" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `list_tools` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L62" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
list_tools(self: Client) -> list[mcp.types.Tool]
list_tools(self: Client, max_pages: int = AUTO_PAGINATION_MAX_PAGES) -> list[mcp.types.Tool]
```
Retrieve all tools available on the server.
@ -50,15 +50,18 @@ This method automatically fetches all pages if the server paginates results,
returning the complete list. For manual pagination control (e.g., to handle
large result sets incrementally), use list_tools_mcp() with the cursor parameter.
**Args:**
- `max_pages`: Maximum number of pages to fetch before raising. Defaults to 250.
**Returns:**
- list\[mcp.types.Tool]: A list of all Tool objects.
**Raises:**
- `RuntimeError`: If called while the client is not connected.
- `RuntimeError`: If the page limit is reached before pagination completes.
- `McpError`: If the request results in a TimeoutError | JSONRPCError
#### `call_tool_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L96" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `call_tool_mcp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L111" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
call_tool_mcp(self: Client, name: str, arguments: dict[str, Any], progress_handler: ProgressHandler | None = None, timeout: datetime.timedelta | float | int | None = None, meta: dict[str, Any] | None = None) -> mcp.types.CallToolResult
@ -88,19 +91,19 @@ containing the tool result and any additional metadata.
- `McpError`: If the tool call requests results in a TimeoutError | JSONRPCError
#### `call_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L176" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `call_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L191" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
call_tool(self: Client, name: str, arguments: dict[str, Any] | None = None) -> CallToolResult
```
#### `call_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L190" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `call_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L205" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
call_tool(self: Client, name: str, arguments: dict[str, Any] | None = None) -> ToolTask
```
#### `call_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L205" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `call_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/mixins/tools.py#L220" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
call_tool(self: Client, name: str, arguments: dict[str, Any] | None = None) -> CallToolResult | ToolTask

View file

@ -10,7 +10,7 @@ Anthropic sampling handler for FastMCP.
## Classes
### `AnthropicSamplingHandler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/sampling/handlers/anthropic.py#L46" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `AnthropicSamplingHandler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/sampling/handlers/anthropic.py#L72" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Sampling handler that uses the Anthropic API.

View file

@ -10,7 +10,7 @@ Google GenAI sampling handler with tool support for FastMCP 3.0.
## Classes
### `GoogleGenaiSamplingHandler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/sampling/handlers/google_genai.py#L54" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `GoogleGenaiSamplingHandler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/sampling/handlers/google_genai.py#L56" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Sampling handler that uses the Google GenAI API with tool support.

View file

@ -10,7 +10,7 @@ OpenAI sampling handler for FastMCP.
## Classes
### `OpenAISamplingHandler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/sampling/handlers/openai.py#L45" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `OpenAISamplingHandler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/sampling/handlers/openai.py#L95" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Sampling handler that uses the OpenAI API.

View file

@ -7,7 +7,7 @@ sidebarTitle: config
## Classes
### `MCPConfigTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/config.py#L22" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `MCPConfigTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/config.py#L25" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for connecting to one or more MCP servers defined in an MCPConfig.
@ -59,13 +59,13 @@ async with client:
**Methods:**
#### `connect_session` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/config.py#L85" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `connect_session` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/config.py#L88" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession]
```
#### `close` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/config.py#L198" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `close` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/config.py#L205" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
close(self)

View file

@ -10,7 +10,7 @@ Streamable HTTP transport for FastMCP Client.
## Classes
### `StreamableHttpTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/http.py#L25" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `StreamableHttpTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/http.py#L27" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport implementation that connects to an MCP server via Streamable HTTP Requests.
@ -18,19 +18,19 @@ Transport implementation that connects to an MCP server via Streamable HTTP Requ
**Methods:**
#### `connect_session` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/http.py#L92" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `connect_session` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/http.py#L148" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession]
```
#### `get_session_id` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/http.py#L138" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `get_session_id` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/http.py#L201" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_session_id(self) -> str | None
```
#### `close` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/http.py#L146" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `close` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/http.py#L209" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
close(self)

View file

@ -10,7 +10,7 @@ Server-Sent Events (SSE) transport for FastMCP Client.
## Classes
### `SSETransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/sse.py#L24" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `SSETransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/sse.py#L25" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport implementation that connects to an MCP server via Server-Sent Events.
@ -18,7 +18,7 @@ Transport implementation that connects to an MCP server via Server-Sent Events.
**Methods:**
#### `connect_session` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/sse.py#L64" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `connect_session` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/sse.py#L115" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession]

View file

@ -30,49 +30,49 @@ connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[
connect(self, **session_kwargs: Unpack[SessionKwargs]) -> ClientSession | None
```
#### `disconnect` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L120" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `disconnect` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L127" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
disconnect(self)
```
#### `close` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L135" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `close` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L162" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
close(self)
```
### `PythonStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L205" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `PythonStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L232" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running Python scripts.
### `FastMCPStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L258" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `FastMCPStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L285" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running FastMCP servers using the FastMCP CLI.
### `NodeStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L287" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `NodeStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L314" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running Node.js scripts.
### `UvStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L340" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `UvStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L367" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running commands via the uv tool.
### `UvxStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L419" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `UvxStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L446" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running commands via the uvx tool.
### `NpxStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L484" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `NpxStdioTransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/transports/stdio.py#L511" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transport for running commands via the npx tool.

View file

@ -10,61 +10,71 @@ Custom exceptions for FastMCP.
## Classes
### `FastMCPError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L6" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `FastMCPDeprecationWarning` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L6" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Deprecation warning for FastMCP APIs.
Subclass of DeprecationWarning so that standard warning filters
still apply, but FastMCP can selectively enable its own warnings
without affecting other libraries in the process.
### `FastMCPError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L15" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Base error for FastMCP.
### `ValidationError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L10" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ValidationError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L19" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Error in validating parameters or return values.
### `ResourceError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L14" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ResourceError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L23" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Error in resource operations.
### `ToolError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L18" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ToolError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L27" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Error in tool operations.
### `PromptError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L22" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `PromptError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L31" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Error in prompt operations.
### `InvalidSignature` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L26" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `InvalidSignature` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L35" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Invalid signature for use with FastMCP.
### `ClientError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L30" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ClientError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L39" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Error in client operations.
### `NotFoundError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L34" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `NotFoundError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L43" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Object not found.
### `DisabledError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L38" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `DisabledError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L47" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Object is disabled.
### `AuthorizationError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L42" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `AuthorizationError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L51" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Error when authorization check fails.

View file

@ -47,7 +47,7 @@ leave that limit uncapped.
run(self, code: str) -> Any
```
### `Search` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L180" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `Search` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L179" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Discovery tool factory that searches the catalog by query.
@ -64,7 +64,7 @@ Defaults to BM25 ranking.
The LLM can override this per call. ``None`` means no limit.
### `GetSchemas` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L262" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `GetSchemas` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L261" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Discovery tool factory that returns schemas for tools by name.
@ -78,7 +78,7 @@ types, and required markers.
``"full"`` returns the complete JSON schema.
### `GetTags` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L323" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `GetTags` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L322" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Discovery tool factory that lists tool tags from the catalog.
@ -93,7 +93,7 @@ without tags appear under ``"untagged"``.
``"full"`` lists all tools under each tag.
### `ListTools` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L390" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `ListTools` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L389" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Discovery tool factory that lists all tools in the catalog.
@ -106,7 +106,7 @@ Discovery tool factory that lists all tools in the catalog.
``"full"`` returns the complete JSON schema.
### `CodeMode` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L439" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
### `CodeMode` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L438" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
Transform that collapses all tools into discovery + execute meta-tools.
@ -123,13 +123,13 @@ environment with ``call_tool(name, params)`` in scope.
**Methods:**
#### `transform_tools` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L489" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `transform_tools` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L488" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
transform_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]
```
#### `get_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L492" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
#### `get_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L491" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
```python
get_tool(self, name: str, call_next: GetToolNext) -> Tool | None

Some files were not shown because too many files have changed in this diff Show more