mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-21 13:04:18 +02:00
chore: sync published-docs to main (v3.3.0) (#4138)
This commit is contained in:
parent
eda8dab429
commit
cbc25fa9d6
610 changed files with 23275 additions and 5962 deletions
80
.github/workflows/martian-test-failure.yml
vendored
80
.github/workflows/martian-test-failure.yml
vendored
|
|
@ -60,6 +60,17 @@ jobs:
|
|||
2. Identify the root cause of the failure(s)
|
||||
3. Suggest a clear, actionable solution to fix the failure(s)
|
||||
|
||||
# Response Proportionality
|
||||
Match your response length to the complexity of the failure. Not every failure needs a full investigation:
|
||||
|
||||
**Trivial failures** (formatting, linting) — post a short, direct comment. No collapsible sections, no root-cause deep-dive. Example:
|
||||
> CI failed: `ruff format` reformatted 2 files. Run `uv run ruff format .` locally and push.
|
||||
|
||||
**Pre-existing flaky tests** unrelated to the PR — say so briefly. Don't write a full analysis of a test the PR didn't touch. Example:
|
||||
> CI failed due to a pre-existing flaky test (`test_name`) unrelated to this PR's changes. Safe to re-run.
|
||||
|
||||
**Real failures caused by the PR** — these deserve the full analysis format below. Spend your effort here.
|
||||
|
||||
# Getting Started
|
||||
1. Call the generate_agents_md tool to get a high-level summary of the project
|
||||
2. Get the pull request associated with this workflow run from the GitHub repository: ${{ github.repository }}
|
||||
|
|
@ -75,60 +86,61 @@ jobs:
|
|||
5. Search the codebase for relevant files, tests, and implementations
|
||||
|
||||
# Your Response
|
||||
Post a comment on the pull request with your analysis. Your comment should include:
|
||||
Post a comment on the pull request with your analysis.
|
||||
|
||||
## Test Failure Analysis
|
||||
Lead with a tl;dr — 1-2 sentences that tell the developer what broke and what to do about it. This should be visible without expanding anything.
|
||||
|
||||
**Summary**: A brief 1-2 sentence summary of what failed.
|
||||
Push supporting detail into collapsible `<details>` blocks. The reader should be able to act on your comment without expanding a single one. Think of details blocks as appendices — there if someone wants to dig deeper, not required for the main message.
|
||||
|
||||
**Root Cause**: A clear explanation of why the tests failed, based on your analysis of the logs and code.
|
||||
For real (non-trivial) failures, use this structure:
|
||||
|
||||
**Suggested Solution**: Specific, actionable steps to fix the failure(s). Include:
|
||||
- Which files need to be modified
|
||||
- What changes are needed
|
||||
- Why these changes will fix the issue
|
||||
**tl;dr**: What failed and what to do (1-2 sentences, always visible)
|
||||
|
||||
**Root Cause**: Why it failed (a short paragraph, always visible)
|
||||
|
||||
**Fix**: Specific files and changes needed (always visible)
|
||||
|
||||
<details>
|
||||
<summary>Detailed Analysis</summary>
|
||||
|
||||
Include here:
|
||||
- Relevant log excerpts showing the failure
|
||||
- Code snippets that are causing the issue
|
||||
- Any related issues or PRs that might be relevant
|
||||
<summary>Log excerpts</summary>
|
||||
Relevant failure output
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Related Files</summary>
|
||||
|
||||
List files that are relevant to the failure with brief explanations of their relevance.
|
||||
<summary>Related files</summary>
|
||||
Files relevant to the failure
|
||||
</details>
|
||||
|
||||
# Important Guidelines
|
||||
- Be concise and actionable - developers want to quickly understand and fix the issue. Provide
|
||||
additional context, references, etc in collapsible details blocks to ensure that the comment you're adding
|
||||
is short and easy to read but additional information is a click away.
|
||||
- Focus on facts from the logs and code, not speculation
|
||||
- If you can't determine the root cause, say so clearly
|
||||
- If your only suggestion is a bad suggestion (disable the test, change the timeout, etc), indicate that you've run out of ideas and
|
||||
that they probably don't want to do that.
|
||||
- Provide specific file names, line numbers, and code references when possible
|
||||
- You can run make commands (e.g., `make lint`, `make typecheck`, `make sync`) to build, test, or lint the code
|
||||
- You can also run git commands (e.g., `git status`, `git log`, `git diff`) to inspect the repository
|
||||
- You can use WebSearch and WebFetch to research errors, stack traces, or related issues
|
||||
- For bash commands, you are limited to make and git commands only
|
||||
# Quality Standards
|
||||
- Every claim needs evidence: file paths, line numbers, log excerpts. Never say "the test fails" without citing which test and what the error was.
|
||||
- Focus on facts from the logs and code, not speculation. If you can't determine the root cause, say so clearly — "I don't know" is better than a wrong diagnosis.
|
||||
- If your only suggestion is a bad one (disable the test, increase the timeout, etc.), say so honestly rather than dressing it up.
|
||||
- Do not paste raw CLI output (e.g., prek progress bars, pytest collection output) into the comment body. Quote only the relevant failure lines.
|
||||
- Always include specific file names, tool names, and test names in your summary. Never leave a sentence with a blank where a name should be.
|
||||
|
||||
# CRITICAL: ANGRY USERS
|
||||
**IMPORTANT**: If the user is angry with you, the triage bot, don't respond. Just exit immediately without further action.
|
||||
If at any point in the conversation the user has asked you to stop replying to the thread, just exit immediately.
|
||||
# Self-Review Before Posting
|
||||
Before posting your comment, re-read it as the PR author would. Ask:
|
||||
- Can I act on this without expanding any `<details>` block?
|
||||
- Does every claim cite a specific file, line, or log excerpt?
|
||||
- Am I telling them something they can't already see in the CI logs, or just restating them?
|
||||
If your comment doesn't add value beyond what the logs already show, don't post it.
|
||||
|
||||
# STOP SIGNALS
|
||||
If anyone on the PR has asked the bot to stop — e.g., "stop", "go away", "don't comment", "no more bot comments" — exit immediately without further action. This includes past comments in the thread, not just the most recent one.
|
||||
|
||||
If you are posting the same suggestion as you have previously made, do not post the suggestion again.
|
||||
|
||||
# IMPORTANT: EDIT YOUR COMMENT
|
||||
Do not post a new comment every time you triage a failing workflow. If a previous comment has been posted by you (marvin)
|
||||
in a previous triage, edit that comment do not add a new comment for each failure. Be sure to include a note that you've edited
|
||||
your comment to reflect the latest analysis. Don't worry about keeping the old content around, there's comment history for
|
||||
your comment to reflect the latest analysis. Don't worry about keeping the old content around, there's comment history for
|
||||
that.
|
||||
|
||||
# Available Tools
|
||||
- You can run make commands (e.g., `make lint`, `make typecheck`, `make sync`) to build, test, or lint the code
|
||||
- You can also run git commands (e.g., `git status`, `git log`, `git diff`) to inspect the repository
|
||||
- You can use WebSearch and WebFetch to research errors, stack traces, or related issues
|
||||
- For bash commands, you are limited to make and git commands only
|
||||
|
||||
# Problems Encountered
|
||||
If you encounter any problems during your analysis (e.g., unable to fetch logs, tools not working), document them clearly so the team knows what limitations you faced.
|
||||
PROMPT_END
|
||||
|
|
|
|||
59
.github/workflows/martian-triage-issue.yml
vendored
59
.github/workflows/martian-triage-issue.yml
vendored
|
|
@ -119,8 +119,26 @@ jobs:
|
|||
2. Layout a single high-quality and actionable recommendation for how to address the issue based on your knowledge of the project, codebase, and issue
|
||||
3. Provide a high quality and detailed plan that a junior developer could follow to implement the recommendation
|
||||
4. Use execution to verify findings when appropriate (check `<allowed_tools>` section for available commands)
|
||||
|
||||
Report findings and recommendations — not your process. Do not include task checklists, progress tracking, or "steps I took" narration (e.g., `- [x] Read source code`). The reader cares about what you found, not how you found it.
|
||||
</response_goals>
|
||||
|
||||
<evidence_standards>
|
||||
Every claim in your response must be grounded in evidence you can cite:
|
||||
- **Code references**: Always include file path and line number (e.g., `fastmcp_slim/fastmcp/client/client.py:142`). Never say "the client code does X" without pointing to where.
|
||||
- **Bug confirmation**: If you say a bug is real, show the specific code path that produces it. If you ran a test, include the command and output.
|
||||
- **Related items**: When citing a related issue or PR, explain specifically why it's related — not just that it exists.
|
||||
- **Confidence**: If you're uncertain about a finding, say so. "I don't know" or "I couldn't confirm this" is better than a speculative diagnosis. Only report findings you would confidently defend.
|
||||
</evidence_standards>
|
||||
|
||||
<quality_gate>
|
||||
Before posting, re-read your response as a maintainer would:
|
||||
- Does the tl;dr give the full picture without expanding anything?
|
||||
- Does every claim cite a specific file, line, or test result?
|
||||
- Is this telling the maintainer something they couldn't find in 5 minutes of reading the issue and grepping the code?
|
||||
If your response doesn't add meaningful value beyond restating the issue, it's okay to post a short "confirmed, straightforward fix in [file]:[line]" response instead of a full analysis.
|
||||
</quality_gate>
|
||||
|
||||
<response_sections>
|
||||
Populate the following sections in your response:
|
||||
Recommendation (or "No recommendation" with reason)
|
||||
|
|
@ -133,12 +151,17 @@ jobs:
|
|||
|
||||
You may not be able to do all of these things, sometimes you may find that all you can do is provide in-depth context of the issue and related items. That's perfectly acceptable and expected. Your performance is judged by how accurate your findings are, do the investigation required to have high confidence in your findings and recommendations. "I don't know" or "I'm unable to recommend a course of action" is better than a bad or wrong answer.
|
||||
|
||||
When formulating your response, you will never "bury the lede", you will always provide a clear and concise tl;dr as the first thing in your response. As your response grows in length you can organize the more detailed parts of your response collapsible sections using <details> and <summary> tags. You shouldn't put everything in collapsible sections, especially if the response is short. Use your discretion to determine when to use collapsible sections to avoid overwhelming the reader with too much detail -- think of them like an appendix that can be expanded if the reader is interested.
|
||||
Structure: Lead with a tl;dr (1-3 sentences, always visible) that gives the reader the bottom line — what this issue is, whether it's valid, and what to do about it. The reader should be able to act on your comment without expanding anything.
|
||||
|
||||
Push everything else into collapsible `<details>` blocks: findings, verification output, action plans, related items, related files. These are appendices — valuable for someone who wants to dig deeper, but not required for the main message. The only things that should be visible without clicking are the tl;dr and the recommendation. Short responses (a few sentences) don't need collapsible sections at all.
|
||||
|
||||
</response_sections>
|
||||
<response_examples>
|
||||
# Example output for "Recommendation" part of the response
|
||||
PR #654 already implements the requested feature but is incomplete. The Pull Request is not in a mergeable state yet, the remaining work should be completed: 1) update the Calculator.divide method to utilize the new DivisionByZeroError or the safe_divide function, and 2) update the tests to ensure that the Calculator.divide method raises the new DivisionByZeroError when the divisor is 0.
|
||||
# Example: the tl;dr and recommendation are always visible, everything else is collapsed
|
||||
|
||||
**tl;dr**: Confirmed bug — `Calculator.divide` raises `ValueError` instead of `DivisionByZeroError`. PR #654 partially addresses this but is incomplete.
|
||||
|
||||
**Recommendation**: Complete PR #654: update `Calculator.divide` to raise `DivisionByZeroError` and update the test assertions to match.
|
||||
|
||||
<details>
|
||||
<summary>Findings</summary>
|
||||
|
|
@ -147,7 +170,7 @@ jobs:
|
|||
|
||||
<details>
|
||||
<summary>Verification</summary>
|
||||
I ran the existing tests (if execution commands are available in `<allowed_tools>`) and confirmed the current behavior:
|
||||
|
||||
```bash
|
||||
$ pytest test_calculator.py::test_divide_by_zero
|
||||
FAILED - raises ValueError instead of DivisionByZeroError
|
||||
|
|
@ -156,36 +179,25 @@ jobs:
|
|||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Detailed Action Plan</summary>
|
||||
<summary>Action Plan</summary>
|
||||
...a detailed plan that a junior developer could follow to implement the recommendation...
|
||||
</details>
|
||||
|
||||
# Example Output for "Related Items" part of the response
|
||||
|
||||
<details>
|
||||
<summary>Related Issues and Pull Requests</summary>
|
||||
|
||||
| Repository | Issue or PR | Relevance |
|
||||
| --- | --- | --- |
|
||||
| PrefectHQ/fastmcp | [Add matrix operations support](https://github.com/PrefectHQ/fastmcp/pull/680) | This pull request directly addresses the feature request for adding matrix operations to the calculator. |
|
||||
| PrefectHQ/fastmcp | [Add matrix operations support](https://github.com/PrefectHQ/fastmcp/issues/681) | This issue directly addresses the feature request for adding matrix operations to the calculator. |
|
||||
| Issue or PR | Relevance |
|
||||
| --- | --- |
|
||||
| [Add matrix operations support](https://github.com/PrefectHQ/fastmcp/pull/680) | Directly addresses the feature request |
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Related Files</summary>
|
||||
|
||||
| Repository | File | Relevance | Sections |
|
||||
| --- | --- | --- | --- |
|
||||
| modelcontextprotocol/python-sdk | [test_calculator.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/test_calculator.py) | This file contains the test cases for the Calculator class, including a test that specifically asserts a ValueError is raised for division by zero, confirming the current intended behavior. | [25-27](https://github.com/modelcontextprotocol/python-sdk/blob/main/test_calculator.py#L25-L27) |
|
||||
| modelcontextprotocol/python-sdk | [calculator.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/calculator.py) | This file contains the implementation of the Calculator class, specifically the `divide` method which raises the ValueError when dividing by zero, matching the bug report. | [29-32](https://github.com/modelcontextprotocol/python-sdk/blob/main/calculator.py#L29-L32) |
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Related Webpages</summary>
|
||||
|
||||
| Name | URL | Relevance |
|
||||
| --- | --- | --- |
|
||||
| Handling Division by Zero Best Practices | https://my-blog-about-division-by-zero.com/handling+division+by+zero+in+calculator | This webpage provides general best practices for handling division by zero in calculator applications and in Python, which is directly relevant to the issue and potential solutions. |
|
||||
| File | Relevance |
|
||||
| --- | --- |
|
||||
| [calculator.py L29-32](https://github.com/modelcontextprotocol/python-sdk/blob/main/calculator.py#L29-L32) | The `divide` method that raises ValueError |
|
||||
| [test_calculator.py L25-27](https://github.com/modelcontextprotocol/python-sdk/blob/main/test_calculator.py#L25-L27) | Test asserting ValueError (needs updating) |
|
||||
</details>
|
||||
</response_examples>
|
||||
|
||||
|
|
@ -202,4 +214,5 @@ jobs:
|
|||
|
||||
<github_formatting>
|
||||
When writing GitHub comments, wrap branch names, tags, or other @-references in backticks (e.g., `@main`, `@v1.0`) to avoid accidentally pinging users. Do not add backticks around terms that are already inside backticks or code blocks.
|
||||
Do not write `fixes #N`, `closes #N`, or `resolves #N` in comments — these can accidentally close issues. Use plain `#N` references instead.
|
||||
</github_formatting>
|
||||
|
|
|
|||
24
.github/workflows/marvin-comment-on-issue.yml
vendored
24
.github/workflows/marvin-comment-on-issue.yml
vendored
|
|
@ -75,9 +75,7 @@ jobs:
|
|||
</task>
|
||||
|
||||
<constraints>
|
||||
You CAN: Read/analyze code, modify files, write code, run tests, execute commands
|
||||
You CAN: Commit code, push changes, create branches, create pull requests
|
||||
|
||||
You CAN: Read/analyze code, modify files, write code, run tests, execute commands, commit code, push changes, create branches, create pull requests
|
||||
</constraints>
|
||||
|
||||
<allowed_tools>
|
||||
|
|
@ -107,20 +105,30 @@ jobs:
|
|||
|
||||
<common_tasks>
|
||||
- Answer questions about the codebase
|
||||
- Help debug reported problems (make changes locally to test, cannot push)
|
||||
- Help debug reported problems
|
||||
- Suggest solutions or workarounds
|
||||
- Provide code examples
|
||||
- Help clarify requirements
|
||||
- Link to relevant documentation or code
|
||||
- Create branches, commit changes, and open PRs when asked
|
||||
</common_tasks>
|
||||
|
||||
<response_guidelines>
|
||||
- Be concise and actionable
|
||||
- If the request is unclear, ask clarifying questions
|
||||
- If the request requires actions you cannot perform (like pushing changes), explain what you can and cannot do
|
||||
- When making code changes, explain that they are local only and cannot be pushed
|
||||
- Lead with a tl;dr — the bottom line in 1-3 sentences, always visible. The reader should be able to act without expanding anything.
|
||||
- Push supporting detail (code analysis, verification output, related items) into collapsible `<details>` blocks. These are appendices, not the main message.
|
||||
- Short responses (a few sentences) don't need collapsible sections at all.
|
||||
- Be concise and actionable.
|
||||
- If the request is unclear, ask clarifying questions.
|
||||
- Report findings and recommendations — not your process. Do not include task checklists or "steps I took" narration.
|
||||
- Every claim needs evidence: cite file paths, line numbers, or command output. Never say "the code does X" without pointing to where.
|
||||
- If you're uncertain, say so. "I couldn't confirm this" is better than a speculative answer.
|
||||
</response_guidelines>
|
||||
|
||||
<github_safety>
|
||||
- Do not write `fixes #N`, `closes #N`, or `resolves #N` in comments — these can accidentally close issues.
|
||||
- When referencing issues, use plain `#N` or link syntax without action keywords.
|
||||
</github_safety>
|
||||
|
||||
<response_footer>
|
||||
Always end your comment with a new line, three dashes, and the footer message:
|
||||
<exact_content>
|
||||
|
|
|
|||
52
.github/workflows/marvin-comment-on-pr.yml
vendored
52
.github/workflows/marvin-comment-on-pr.yml
vendored
|
|
@ -98,12 +98,10 @@ jobs:
|
|||
</task>
|
||||
|
||||
<constraints>
|
||||
This workflow allows read, write, and execute capabilities but cannot push changes.
|
||||
You CAN: Read/analyze code, modify files, write code, run tests, execute commands, resolve review threads, commit and push changes to the PR branch, checkout branches
|
||||
You CANNOT: Create new branches unrelated to this PR, create new pull requests
|
||||
|
||||
You CAN: Read/analyze code, modify files, write code, run tests, execute commands, resolve review threads
|
||||
You CANNOT: Commit code, push changes, create branches, checkout branches, create pull requests
|
||||
|
||||
**Important**: You cannot push changes to the repository - you can only make changes locally and provide feedback or recommendations.
|
||||
When making changes, commit and push to the PR's head branch so the author gets the fix directly.
|
||||
</constraints>
|
||||
|
||||
<allowed_tools>
|
||||
|
|
@ -132,10 +130,10 @@ jobs:
|
|||
</investigation_approach>
|
||||
|
||||
<common_tasks>
|
||||
- Address review feedback and fix issues (make changes locally, cannot push)
|
||||
- Address review feedback and fix issues (commit and push to the PR branch)
|
||||
- Answer questions about the changes
|
||||
- Make additional code changes (local only)
|
||||
- Resolve review threads after addressing feedback (if changes are made separately)
|
||||
- Make code changes and push them
|
||||
- Resolve review threads after addressing feedback
|
||||
- Perform PR reviews when asked (use the PR review process below)
|
||||
</common_tasks>
|
||||
|
||||
|
|
@ -224,6 +222,25 @@ jobs:
|
|||
6. Breaking changes to public APIs without migration path
|
||||
7. Missing or incorrect test coverage for critical paths
|
||||
</review_criteria>
|
||||
|
||||
<review_calibration>
|
||||
**What NOT to flag** — do not comment on:
|
||||
- Issues in unchanged code (only review the diff)
|
||||
- Input already validated or sanitized at a different layer
|
||||
- Theoretical performance concerns without evidence that N is large
|
||||
- Style or formatting not in the project's linting rules
|
||||
- Missing tests for trivial or generated code
|
||||
- Pre-existing patterns the PR is following consistently
|
||||
|
||||
**Calibration examples**:
|
||||
- Unguarded return from a lookup (e.g., `tool = registry.get(name)` used without None check) → FLAG if the diff introduces the unguarded usage
|
||||
- Same pattern, but the function's return type is `Tool` (not `Optional[Tool]`) → DO NOT FLAG, the type system guarantees non-None
|
||||
- String interpolation in a query with user input → FLAG
|
||||
- String interpolation in a query with a hardcoded enum value → DO NOT FLAG
|
||||
- O(n²) loop → FLAG only if there's evidence N can be large (e.g., user-controlled list). If N is bounded by design (e.g., number of MCP tools), do not flag.
|
||||
|
||||
When in doubt, do not flag. A false positive wastes a reviewer's time and erodes trust in every future review comment.
|
||||
</review_calibration>
|
||||
</pr_review_guidance>
|
||||
|
||||
<review_thread_tools>
|
||||
|
|
@ -244,14 +261,18 @@ jobs:
|
|||
- `THREAD_ID` is the GraphQL node ID from the review threads output (e.g., `PRRT_kwDOABC123`)
|
||||
- The comment is optional - use it to explain what you did
|
||||
|
||||
Note: Since you cannot push changes, you can resolve threads to acknowledge feedback, but actual fixes would need to be applied separately.
|
||||
Note: You can resolve threads after pushing fixes, or resolve them to acknowledge feedback that will be addressed separately.
|
||||
</review_thread_tools>
|
||||
|
||||
<response_guidelines>
|
||||
- Be concise and actionable
|
||||
- If the request is unclear, ask clarifying questions
|
||||
- If the request requires actions you cannot perform (like pushing changes), explain what you can and cannot do
|
||||
- When making code changes, explain that they are local only and cannot be pushed
|
||||
- Lead with a tl;dr — the bottom line in 1-3 sentences, always visible. The reader should be able to act without expanding anything.
|
||||
- Push supporting detail (code analysis, verification output, related items) into collapsible `<details>` blocks. These are appendices, not the main message.
|
||||
- Short responses (a few sentences) don't need collapsible sections at all.
|
||||
- Be concise and actionable.
|
||||
- If the request is unclear, ask clarifying questions.
|
||||
- When making code changes, commit and push them to the PR branch so the author gets the fix directly.
|
||||
- Every claim needs evidence: cite file paths, line numbers, or command output. Never say "the code does X" without pointing to where.
|
||||
- If you're uncertain, say so. "I couldn't confirm this" is better than a speculative answer.
|
||||
|
||||
**When performing a PR review**: Your substantive feedback belongs in the PR review submission
|
||||
(via pr-review.sh), not in the comment response. The comment should only report:
|
||||
|
|
@ -263,6 +284,11 @@ jobs:
|
|||
Keep the comment short, e.g., "I've submitted my review requesting changes. See the review for details."
|
||||
</response_guidelines>
|
||||
|
||||
<github_safety>
|
||||
- Do not write `fixes #N`, `closes #N`, or `resolves #N` in comments — these can accidentally close issues.
|
||||
- When referencing issues, use plain `#N` or link syntax without action keywords.
|
||||
</github_safety>
|
||||
|
||||
<response_footer>
|
||||
Always end your comment with a new line, three dashes, and the footer message:
|
||||
<exact_content>
|
||||
|
|
|
|||
31
.github/workflows/marvin-dedupe-issues.yml
vendored
31
.github/workflows/marvin-dedupe-issues.yml
vendored
|
|
@ -37,23 +37,38 @@ jobs:
|
|||
PROMPT<<PROMPT_END
|
||||
Find up to 3 likely duplicate issues for GitHub issue ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}.
|
||||
|
||||
Follow these steps precisely:
|
||||
# Core Principle
|
||||
Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report. Most runs should end with no comment — that means the system is working.
|
||||
|
||||
# Steps
|
||||
|
||||
1. Check if the GitHub issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicates comment that you made earlier. If so, do not proceed.
|
||||
|
||||
2. View the GitHub issue and produce a summary of the issue
|
||||
2. View the GitHub issue and produce a summary of the issue.
|
||||
|
||||
3. Then, launch 3 parallel agents using the Task tool to search GitHub for duplicates of this issue, using diverse keywords and search approaches, using the summary from step 2
|
||||
3. Launch 3 parallel agents using the Task tool to search GitHub for duplicates, using diverse keywords and search approaches, using the summary from step 2.
|
||||
|
||||
4. Next, consider the results from steps 2 and 3 and filter out false positives that are likely not actually duplicates of the original issue. Be conservative — only flag issues that describe the same underlying problem, not issues that merely share keywords or involve the same subsystem. If there are no duplicates remaining, do not proceed.
|
||||
4. Filter aggressively for false positives. The bar for "duplicate" is high:
|
||||
|
||||
5. Finally, comment back on the issue with a list of up to three duplicate issues (or zero, if there are no likely duplicates). If there are no duplicates, DO NOT COMMENT. Just exit. Do NOT add any labels — labeling is handled by a later workflow step.
|
||||
A duplicate means the SAME bug or the SAME feature request. Apply this test to every candidate:
|
||||
- **Same fix test**: Could the candidate be closed by the exact same code change? If not, not a duplicate.
|
||||
- **Same symptom test**: Does the user experience the exact same broken behavior? "Both involve middleware" is not duplication. "Both get TypeError on line 42 of proxy.py when calling mount()" is duplication.
|
||||
- **Same request test** (for features): Are they asking for the same specific capability? "Both want better auth" is not duplication. "Both request OAuth PKCE flow for CLI login" is duplication.
|
||||
|
||||
Notes for your agents:
|
||||
Candidates found by only one search agent deserve extra scrutiny — a single keyword match is often a false positive.
|
||||
|
||||
When in doubt, do not flag. A missed duplicate is harmless; a false positive wastes the reporter's time.
|
||||
If there are no duplicates remaining, do not proceed — just exit.
|
||||
|
||||
5. **Quality gate**: Before commenting, re-read each candidate as a skeptical reviewer. For each one, ask: "Would a maintainer who knows this codebase agree this is a duplicate, or would they dismiss it?" If you'd need to hedge with "might" or "possibly," drop it.
|
||||
|
||||
6. Comment back on the issue with your findings (or exit silently if none remain). Do NOT add any labels — labeling is handled by a later workflow step.
|
||||
|
||||
# Notes for your agents
|
||||
- Use `gh` to interact with GitHub, rather than web fetch
|
||||
- Do not use other tools, beyond `gh` and Task (eg. don't use other MCP servers, file edit, etc.)
|
||||
- Make a todo list first
|
||||
- Do not use other tools beyond `gh` and Task (no MCP servers, file edit, etc.)
|
||||
- Never include this issue as a duplicate of itself
|
||||
- When searching, read the FULL body of candidate issues — titles alone are not enough to judge duplication
|
||||
|
||||
For your comment, follow this format precisely (example with 3 suspected duplicates):
|
||||
|
||||
|
|
|
|||
21
.github/workflows/marvin-label-triage.yml
vendored
21
.github/workflows/marvin-label-triage.yml
vendored
|
|
@ -51,6 +51,12 @@ jobs:
|
|||
|
||||
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).
|
||||
|
||||
CRITICAL — LABEL MECHANICS:
|
||||
- `mcp__github__update_issue` REPLACES all labels on the issue — it does not add to them.
|
||||
- Before applying labels, read the issue's current labels with `mcp__github__get_issue`.
|
||||
- Always include any existing labels you want to keep alongside the new ones.
|
||||
- Only apply labels that exist in the repository (from `gh label list` in step 1). Never invent labels.
|
||||
|
||||
Issue/PR Information:
|
||||
- REPO: ${{ github.repository }}
|
||||
- NUMBER: ${{ github.event.issue.number || github.event.pull_request.number || inputs.issue_number }}
|
||||
|
|
@ -97,7 +103,7 @@ jobs:
|
|||
STATUS (apply if applicable):
|
||||
- needs more info: Issue lacks reproduction steps, error messages, or clear description
|
||||
- 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.
|
||||
- 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 too-long, still apply the core category and area labels — too-long is a format signal, not a replacement for categorization. Issues still need to be findable by category.
|
||||
|
||||
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."
|
||||
|
|
@ -110,16 +116,15 @@ jobs:
|
|||
- auth: Authentication is the main concern (Bearer, JWT, OAuth, WorkOS)
|
||||
- openapi: OpenAPI integration/parsing is the primary topic
|
||||
- http: HTTP transport or networking is the main issue
|
||||
- contrib: Specifically about community contributions in src/contrib/
|
||||
- contrib: Specifically about community contributions in fastmcp_slim/fastmcp/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
|
||||
- Don't apply area labels just because a file in that area is mentioned
|
||||
- The issue must be PRIMARILY about that area to get the label
|
||||
- When in doubt, don't apply the label
|
||||
- Apply 2-5 labels total typically (category + maybe priority + maybe 1-2 areas)
|
||||
LABELING PRINCIPLES:
|
||||
- Precision over recall: a missing label is a minor inconvenience; a wrong label sends the wrong people to the wrong issue. When in doubt, don't apply.
|
||||
- Don't apply area labels just because a file in that area is mentioned — the issue must be PRIMARILY about that area.
|
||||
- Apply 2-5 labels total typically (category + maybe priority + maybe 1-2 areas).
|
||||
- For ambiguous cases (bug vs enhancement, which area label), prefer the more conservative choice or omit the uncertain label entirely.
|
||||
|
||||
META LABELS (rarely needed for issues):
|
||||
- dependencies: Only for dependabot PRs or issues specifically about package updates
|
||||
|
|
|
|||
30
.github/workflows/publish-fastmcp-slim.yml
vendored
Normal file
30
.github/workflows/publish-fastmcp-slim.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
name: Publish fastmcp-slim to PyPI
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: Upload fastmcp-slim to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Build fastmcp-slim
|
||||
run: uv build --package fastmcp-slim
|
||||
|
||||
- name: Publish fastmcp-slim to PyPI
|
||||
run: uv publish -v dist/fastmcp_slim-*.tar.gz dist/fastmcp_slim-*.whl
|
||||
87
.github/workflows/publish-fastmcp.yml
vendored
Normal file
87
.github/workflows/publish-fastmcp.yml
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
name: Publish fastmcp to PyPI
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Publish fastmcp-slim to PyPI"]
|
||||
types: [completed]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: Upload fastmcp to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'release')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Build fastmcp
|
||||
run: uv build --package fastmcp
|
||||
|
||||
- name: Verify matching fastmcp-slim is published
|
||||
run: |
|
||||
SLIM_VERSION=$(python - <<'PY'
|
||||
import email.parser
|
||||
import re
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
wheel = next(Path("dist").glob("fastmcp-*.whl"))
|
||||
metadata_name = next(
|
||||
name for name in zipfile.ZipFile(wheel).namelist()
|
||||
if name.endswith(".dist-info/METADATA")
|
||||
)
|
||||
metadata = email.parser.Parser().parsestr(
|
||||
zipfile.ZipFile(wheel).read(metadata_name).decode()
|
||||
)
|
||||
for value in metadata.get_all("Requires-Dist", []):
|
||||
requirement, _, marker = value.partition(";")
|
||||
if marker.strip():
|
||||
continue
|
||||
match = re.fullmatch(
|
||||
r"fastmcp-slim(?:\[[^\]]+\])?==([^;\s]+)",
|
||||
requirement.strip(),
|
||||
)
|
||||
if match:
|
||||
print(match.group(1))
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("Could not find the base fastmcp-slim dependency")
|
||||
PY
|
||||
)
|
||||
|
||||
for attempt in {1..12}; do
|
||||
if python - "$SLIM_VERSION" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
version = sys.argv[1]
|
||||
url = f"https://pypi.org/pypi/fastmcp-slim/{version}/json"
|
||||
with urllib.request.urlopen(url, timeout=30) as response:
|
||||
json.load(response)
|
||||
PY
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "fastmcp-slim ${SLIM_VERSION} is not available on PyPI yet; retrying (${attempt}/12)."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo "fastmcp-slim ${SLIM_VERSION} is not available on PyPI; refusing to publish fastmcp." >&2
|
||||
exit 1
|
||||
|
||||
- name: Publish fastmcp to PyPI
|
||||
run: uv publish -v dist/fastmcp-*.tar.gz dist/fastmcp-*.whl
|
||||
26
.github/workflows/publish.yml
vendored
26
.github/workflows/publish.yml
vendored
|
|
@ -1,26 +0,0 @@
|
|||
name: Publish FastMCP to PyPI
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: Upload to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # For PyPI's trusted publishing
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Build
|
||||
run: uv build
|
||||
|
||||
- name: Publish to PyPi
|
||||
run: uv publish -v dist/*
|
||||
55
.github/workflows/run-schema-crash-test.yml
vendored
Normal file
55
.github/workflows/run-schema-crash-test.yml
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
name: Schema Crash Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "fastmcp_slim/fastmcp/utilities/json_schema_type.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/json_schema.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/openapi/**"
|
||||
- "fastmcp_slim/fastmcp/server/providers/openapi/**"
|
||||
- "fastmcp_slim/fastmcp/client/mixins/tools.py"
|
||||
- "tests/utilities/json_schema_type/test_real_world_schemas.py"
|
||||
- ".github/workflows/run-schema-crash-test.yml"
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- "fastmcp_slim/fastmcp/utilities/json_schema_type.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/json_schema.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/openapi/**"
|
||||
- "fastmcp_slim/fastmcp/server/providers/openapi/**"
|
||||
- "fastmcp_slim/fastmcp/client/mixins/tools.py"
|
||||
- "tests/utilities/json_schema_type/test_real_world_schemas.py"
|
||||
- ".github/workflows/run-schema-crash-test.yml"
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
schema_crash_test:
|
||||
name: "Real-world schema crash test (232K schemas)"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Set up Python
|
||||
run: uv python install 3.12
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync
|
||||
|
||||
- name: Clone openapi-directory
|
||||
run: git clone --depth 1 https://github.com/APIs-guru/openapi-directory.git /tmp/openapi-directory
|
||||
|
||||
- name: Run schema crash test
|
||||
env:
|
||||
RUN_REAL_WORLD_SCHEMA_TEST: "1"
|
||||
OPENAPI_DIRECTORY_PATH: /tmp/openapi-directory
|
||||
run: uv run pytest tests/utilities/json_schema_type/test_real_world_schemas.py -m integration -v -n auto --timeout-method=thread
|
||||
4
.github/workflows/run-static.yml
vendored
4
.github/workflows/run-static.yml
vendored
|
|
@ -7,10 +7,10 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "fastmcp_slim/**"
|
||||
- "tests/**"
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
- ".github/workflows/**"
|
||||
|
||||
# run on all pull requests because these checks are required and will block merges otherwise
|
||||
|
|
|
|||
131
.github/workflows/run-tests.yml
vendored
131
.github/workflows/run-tests.yml
vendored
|
|
@ -7,10 +7,10 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "fastmcp_slim/**"
|
||||
- "tests/**"
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
- ".github/workflows/**"
|
||||
|
||||
# run on all pull requests because these checks are required and will block merges otherwise
|
||||
|
|
@ -117,3 +117,130 @@ jobs:
|
|||
FASTMCP_GITHUB_TOKEN: ${{ secrets.FASTMCP_GITHUB_TOKEN }}
|
||||
FASTMCP_TEST_AUTH_GITHUB_CLIENT_ID: ${{ secrets.FASTMCP_TEST_AUTH_GITHUB_CLIENT_ID }}
|
||||
FASTMCP_TEST_AUTH_GITHUB_CLIENT_SECRET: ${{ secrets.FASTMCP_TEST_AUTH_GITHUB_CLIENT_SECRET }}
|
||||
|
||||
package_install_smoke:
|
||||
name: "Package install smoke"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup uv
|
||||
uses: ./.github/actions/setup-uv
|
||||
with:
|
||||
resolution: locked
|
||||
|
||||
- name: Build package wheels
|
||||
run: uv build --all-packages --wheel --out-dir /tmp/fastmcp-dist
|
||||
|
||||
- name: Install bare slim wheel
|
||||
run: |
|
||||
uv venv /tmp/fastmcp-slim-bare-smoke
|
||||
SLIM_WHEEL=$(ls /tmp/fastmcp-dist/fastmcp_slim-*.whl)
|
||||
uv pip install --python /tmp/fastmcp-slim-bare-smoke/bin/python "$SLIM_WHEEL"
|
||||
/tmp/fastmcp-slim-bare-smoke/bin/python - <<'PY'
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
import fastmcp
|
||||
import fastmcp.settings
|
||||
|
||||
assert any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts"))
|
||||
|
||||
try:
|
||||
from fastmcp.cli import app
|
||||
except ImportError as exc:
|
||||
assert "FastMCP CLI support is not installed" in str(exc)
|
||||
else:
|
||||
raise AssertionError(f"bare fastmcp-slim unexpectedly imported CLI app {app!r}")
|
||||
|
||||
try:
|
||||
fastmcp.FastMCP
|
||||
except ImportError as exc:
|
||||
assert "fastmcp-slim[server]" in str(exc)
|
||||
else:
|
||||
raise AssertionError("bare fastmcp-slim unexpectedly imported FastMCP")
|
||||
PY
|
||||
|
||||
- name: Install client slim wheel
|
||||
run: |
|
||||
uv venv /tmp/fastmcp-slim-client-smoke
|
||||
SLIM_WHEEL=$(ls /tmp/fastmcp-dist/fastmcp_slim-*.whl)
|
||||
uv pip install --python /tmp/fastmcp-slim-client-smoke/bin/python "${SLIM_WHEEL}[client]"
|
||||
/tmp/fastmcp-slim-client-smoke/bin/python - <<'PY'
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.transports import StdioTransport, StreamableHttpTransport
|
||||
from fastmcp.mcp_config import MCPConfig
|
||||
|
||||
assert any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts"))
|
||||
|
||||
try:
|
||||
from fastmcp.cli import app
|
||||
except ImportError as exc:
|
||||
assert "FastMCP CLI support is not installed" in str(exc)
|
||||
else:
|
||||
raise AssertionError(f"client-only slim unexpectedly imported CLI app {app!r}")
|
||||
|
||||
assert Client("https://example.com/mcp")
|
||||
assert StreamableHttpTransport("https://example.com/mcp")
|
||||
assert StdioTransport(command="uvx", args=["demo"])
|
||||
assert MCPConfig.from_dict({"mcpServers": {"demo": {"url": "https://example.com/mcp"}}})
|
||||
|
||||
try:
|
||||
from fastmcp import FastMCP
|
||||
except ImportError as exc:
|
||||
assert "fastmcp-slim[server]" in str(exc)
|
||||
else:
|
||||
raise AssertionError(f"client-only slim unexpectedly imported {FastMCP!r}")
|
||||
PY
|
||||
|
||||
- name: Install server slim wheel
|
||||
run: |
|
||||
uv venv /tmp/fastmcp-slim-server-smoke
|
||||
SLIM_WHEEL=$(ls /tmp/fastmcp-dist/fastmcp_slim-*.whl)
|
||||
uv pip install --python /tmp/fastmcp-slim-server-smoke/bin/python "${SLIM_WHEEL}[server]"
|
||||
/tmp/fastmcp-slim-server-smoke/bin/python - <<'PY'
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.cli import app
|
||||
|
||||
assert any(
|
||||
ep.name == "fastmcp" and ep.value == "fastmcp.cli:app"
|
||||
for ep in entry_points(group="console_scripts")
|
||||
)
|
||||
|
||||
mcp = FastMCP("smoke")
|
||||
assert app is not None
|
||||
assert mcp.name == "smoke"
|
||||
PY
|
||||
|
||||
- name: Install full package from matching local wheels
|
||||
run: |
|
||||
uv venv /tmp/fastmcp-full-smoke
|
||||
FULL_WHEEL=$(ls /tmp/fastmcp-dist/fastmcp-*.whl)
|
||||
uv pip install --python /tmp/fastmcp-full-smoke/bin/python --find-links /tmp/fastmcp-dist "$FULL_WHEEL"
|
||||
/tmp/fastmcp-full-smoke/bin/python - <<'PY'
|
||||
from importlib.metadata import entry_points
|
||||
from importlib.metadata import requires
|
||||
|
||||
from fastmcp import Client, FastMCP
|
||||
from fastmcp.client.client import CallToolResult
|
||||
from fastmcp.exceptions import ToolError
|
||||
|
||||
fastmcp_reqs = requires("fastmcp") or []
|
||||
assert any("fastmcp-slim[client,server]" in req for req in fastmcp_reqs)
|
||||
assert not any("fastmcp-slim[full" in req for req in fastmcp_reqs)
|
||||
|
||||
assert any(
|
||||
ep.name == "fastmcp" and ep.value == "fastmcp.cli:app"
|
||||
for ep in entry_points(group="console_scripts")
|
||||
)
|
||||
|
||||
assert Client("https://example.com/mcp")
|
||||
assert FastMCP("smoke").name == "smoke"
|
||||
assert CallToolResult is not None
|
||||
assert ToolError is not None
|
||||
PY
|
||||
|
|
|
|||
6
.github/workflows/run-upgrade-checks.yml
vendored
6
.github/workflows/run-upgrade-checks.yml
vendored
|
|
@ -7,10 +7,10 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "fastmcp_slim/**"
|
||||
- "tests/**"
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
- ".github/workflows/**"
|
||||
|
||||
schedule:
|
||||
|
|
@ -121,7 +121,7 @@ jobs:
|
|||
|
||||
- **ty (type checker)**: New ty releases frequently add stricter checks that flag previously-accepted code. Run `uv run ty check` locally with the latest ty to reproduce. Fix the type errors or bump the ty version floor in `pyproject.toml`.
|
||||
- **ruff**: New lint rules or stricter defaults in a ruff upgrade.
|
||||
- **mcp SDK**: Breaking changes in the `mcp` package (new method signatures, renamed types).
|
||||
- **MCP SDK**: Breaking changes in the `mcp` package (new method signatures, renamed types).
|
||||
|
||||
### What to do
|
||||
|
||||
|
|
|
|||
8
.github/workflows/update-config-schema.yml
vendored
8
.github/workflows/update-config-schema.yml
vendored
|
|
@ -7,8 +7,8 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/fastmcp/utilities/mcp_server_config/**"
|
||||
- "!src/fastmcp/utilities/mcp_server_config/v1/schema.json"
|
||||
- "fastmcp_slim/fastmcp/utilities/mcp_server_config/**"
|
||||
- "!fastmcp_slim/fastmcp/utilities/mcp_server_config/v1/schema.json"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
@ -47,7 +47,7 @@ jobs:
|
|||
from fastmcp.utilities.mcp_server_config import generate_schema
|
||||
generate_schema('docs/public/schemas/fastmcp.json/latest.json')
|
||||
generate_schema('docs/public/schemas/fastmcp.json/v1.json')
|
||||
generate_schema('src/fastmcp/utilities/mcp_server_config/v1/schema.json')
|
||||
generate_schema('fastmcp_slim/fastmcp/utilities/mcp_server_config/v1/schema.json')
|
||||
"
|
||||
|
||||
- name: Create Pull Request
|
||||
|
|
@ -59,7 +59,7 @@ jobs:
|
|||
body: |
|
||||
This PR updates the fastmcp.json schema files to match the current source code.
|
||||
|
||||
The schema is automatically generated from `src/fastmcp/utilities/mcp_server_config/` to ensure consistency.
|
||||
The schema is automatically generated from `fastmcp_slim/fastmcp/utilities/mcp_server_config/` to ensure consistency.
|
||||
|
||||
**Note:** This PR is fully automated and will update itself with any subsequent changes to the schema, or close automatically if the schema becomes up-to-date through other means.
|
||||
|
||||
|
|
|
|||
4
.github/workflows/update-sdk-docs.yml
vendored
4
.github/workflows/update-sdk-docs.yml
vendored
|
|
@ -7,7 +7,7 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "fastmcp_slim/**"
|
||||
- "pyproject.toml"
|
||||
workflow_dispatch:
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
run: uv sync --python 3.12
|
||||
|
||||
- name: Install just
|
||||
uses: extractions/setup-just@v3
|
||||
uses: extractions/setup-just@v4
|
||||
|
||||
- name: Generate SDK documentation
|
||||
run: just api-ref-all
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ repos:
|
|||
entry: uv run --isolated ty check
|
||||
language: system
|
||||
types: [python]
|
||||
files: ^src/|^tests/
|
||||
files: ^fastmcp_slim/|^tests/
|
||||
pass_filenames: false
|
||||
require_serial: true
|
||||
|
||||
|
|
|
|||
21
CLAUDE.md
21
CLAUDE.md
|
|
@ -27,7 +27,7 @@ uv run prek run --all-files # Ruff + Prettier + ty
|
|||
|
||||
| Path | Purpose |
|
||||
| ----------------- | -------------------------------------- |
|
||||
| `src/fastmcp/` | Library source code |
|
||||
| `fastmcp_slim/fastmcp/` | Library source code |
|
||||
| `├─server/` | Server implementation |
|
||||
| `│ ├─auth/` | Authentication providers |
|
||||
| `│ └─middleware/` | Error handling, logging, rate limiting |
|
||||
|
|
@ -50,6 +50,8 @@ When modifying MCP functionality, changes typically need to be applied across al
|
|||
- **Resource Templates** (`src/resources/`)
|
||||
- **Prompts** (`src/prompts/`)
|
||||
|
||||
**Before writing cross-component logic (dedupe, grouping, lookups, identity checks), read `FastMCPComponent` in `fastmcp_slim/fastmcp/utilities/components.py`.** The base class defines the shared surface — `name`, `version`, `tags`, `meta`, and critically the `key` property which is the canonical MCP identity (encodes type, identifier, and version). Prefer `item.key` over ad-hoc `name or uri or uri_template` fallbacks; overrides in `Resource` and `ResourceTemplate` already handle URI-based identity, and `.key` includes the version suffix so variants of the same component don't falsely collide.
|
||||
|
||||
## 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.
|
||||
|
|
@ -63,6 +65,9 @@ When modifying MCP functionality, changes typically need to be applied across al
|
|||
- **NEVER** force-push on collaborative repos
|
||||
- **ALWAYS** run prek before PRs
|
||||
- **NEVER** create a release, comment on an issue, or open a PR unless specifically instructed to do so.
|
||||
- **NEVER** merge a PR marked as do-not-merge or draft. Check title, body, AND labels for `[DNM]`, `DNM`, `DO NOT MERGE`, `DON'T MERGE`, `DONT MERGE`, `do-not-merge`, `dont-merge`, `[DRAFT]`, or `DRAFT` (case-insensitive, any variation — some authors use `[DRAFT]` in the title even when `isDraft` is false). Authors use these as hard stops — respect them even if CI is green and review looks clean. When triaging a batch of PRs, filter these out up front AND re-check each one's labels immediately before merging, since labels can change mid-session.
|
||||
- **ALWAYS** read review-bot comments before approving a PR. CodeRabbit and chatgpt-codex-connector (Codex) leave substantive review comments on most PRs in this repo — these bots have read the diff and often flag real issues that aren't in the PR description. Use `gh pr view <num> --comments` and read the bot feedback as part of review. Unlike proposed solutions from issue reporters, review-bot feedback should be evaluated on its merits, not discounted.
|
||||
- **Be constructively skeptical of bot review comments on your own PRs.** CodeRabbit, Codex, and claude[bot] run a fresh review pass on every push, which means a PR with active churn can accumulate bot comments in a stream that never really ends — each fix surfaces a new edge case the next pass can flag. Most of the early feedback is real and worth acting on; diminishing returns set in fast. Evaluate each comment on its merits, the same way you would a human reviewer: is this a real bug users will hit, or a hypothetical that requires an adversarial setup? Does the fix introduce more complexity than the problem? Has the bot missed context that's obvious to a human reader (a `*,` keyword-only marker, a design decision documented elsewhere, something already resolved on a later commit)? When a comment is pedantic, a false positive, or flagging something already fixed, reply on the thread explaining the reasoning and move on — don't keep iterating just because more comments arrive. If you find yourself three rounds deep and the feedback is shifting toward "what if someone does X" hypotheticals, you're past the point where each fix is improving the PR. Stop, document the contract as-is, and ship.
|
||||
|
||||
### Releases
|
||||
|
||||
|
|
@ -82,6 +87,16 @@ The handwritten notes are prepended above the auto-generated changelog and are t
|
|||
|
||||
**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.
|
||||
|
||||
**To preview what PRs will be in the release** before it's cut, call the GitHub generate-notes API. This returns the exact auto-generated changelog that `--generate-notes` would append, so you can see the full PR list — useful for picking a pun theme and making sure nothing's been missed:
|
||||
|
||||
```bash
|
||||
gh api -X POST repos/PrefectHQ/fastmcp/releases/generate-notes \
|
||||
-f tag_name=v3.2.3 \
|
||||
-f target_commitish=main \
|
||||
-f previous_tag_name=v3.2.2 \
|
||||
--jq '.body'
|
||||
```
|
||||
|
||||
**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.
|
||||
|
|
@ -132,9 +147,9 @@ The handwritten notes are prepended above the auto-generated changelog and are t
|
|||
- Uses Mintlify framework
|
||||
- Files must be in docs.json to be included
|
||||
- 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.
|
||||
- Do not manually modify `docs/public/schemas/**` or `fastmcp_slim/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.
|
||||
- When adding or modifying settings in `fastmcp_slim/fastmcp/settings.py`, update `docs/more/settings.mdx` to match.
|
||||
|
||||
### Documentation Guidelines
|
||||
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -79,11 +79,15 @@ FastMCP has three pillars:
|
|||
|
||||
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).
|
||||
|
||||
## From prototype to production
|
||||
## 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.
|
||||
FastMCP is the standard way to build MCP servers. **[Prefect Horizon](https://www.prefect.io/horizon?utm_source=github&utm_medium=readme&utm_campaign=readme_horizon&utm_content=readme_body)** is the enterprise MCP gateway for running them safely.
|
||||
|
||||
[Deploy your first server →](https://www.prefect.io/horizon)
|
||||
Built by the FastMCP team, Horizon packages the best practices we've learned shipping the world's most popular MCP framework.
|
||||
|
||||
Deploy FastMCP servers from GitHub with branch previews and instant rollback. Create a private registry of every MCP your company uses. Secure access with SSO and tool-level RBAC. Get audit logs, observability, and governance across your MCP stack. Remix approved tools into purpose-built endpoints for teams and agents.
|
||||
|
||||
Start with FastMCP. [Scale with Horizon →](https://www.prefect.io/horizon?utm_source=github&utm_medium=readme&utm_campaign=readme_horizon&utm_content=readme_cta)
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
|
|||
|
|
@ -1,119 +1,118 @@
|
|||
---
|
||||
title: App Architecture
|
||||
title: 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.
|
||||
You don't need this page to build apps. It's for when something isn't rendering the way you expect, when UI tool calls aren't reaching your server, or when you're writing [custom HTML apps](/apps/low-level) and need to understand the protocol directly.
|
||||
|
||||
## The Pipeline
|
||||
## The pipeline
|
||||
|
||||
An MCP App moves through five stages from Python to pixels:
|
||||
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.
|
||||
You write Prefab components. 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 in, and the renderer paints the UI. If the UI calls server tools, it talks back through the same `postMessage` channel.
|
||||
|
||||
The following sections walk through each stage.
|
||||
The sections below walk each stage.
|
||||
|
||||
## Tool Registration
|
||||
## 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=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.
|
||||
`app` on `@mcp.tool` accepts `True`, an `AppConfig`, 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 it 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.
|
||||
This expansion also registers the shared Prefab renderer resource (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 displays 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`.
|
||||
Type inference works the same way. If the return type 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 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.
|
||||
`FastMCPApp` uses the same 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. That tag lets the server identify which app a tool belongs to when routing UI calls.
|
||||
|
||||
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.
|
||||
Second, it sets `meta["ui"]["visibility"]` to control who can see each tool. Entry points default to `["model"]` (LLM-visible). Backend tools default to `["app"]` (UI-only). Hosts use this to filter the tool list.
|
||||
|
||||
## 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.
|
||||
When a Prefab tool runs, its return value — a `PrefabApp` or a bare `Component` — becomes a JSON blob the renderer can interpret.
|
||||
|
||||
### PrefabApp.to_json()
|
||||
### `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).
|
||||
The entry point is `PrefabApp.to_json()`. It 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.
|
||||
FastMCP passes a `tool_resolver` callback to `to_json()`. Whenever the 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")` on the wire. The resolver also handles `unwrap_result` — a flag telling the renderer to unwrap single-value results from the `{"result": value}` envelope FastMCP uses for schema compliance.
|
||||
|
||||
### The _meta.fastmcp.app Tag
|
||||
### 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.
|
||||
After `to_json()` produces the 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.
|
||||
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 (below).
|
||||
|
||||
### ToolResult Assembly
|
||||
### 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
|
||||
## 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.
|
||||
Normal tool calls go through the provider chain, which applies transforms (namespace prefixes, visibility filters) before resolving by name. App UI calls need a different path.
|
||||
|
||||
### The get_app_tool Bypass
|
||||
### 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.
|
||||
Backend tools 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` — while 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.
|
||||
`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 walks the provider tree directly, skipping transforms. It finds the tool by its original registered name and verifies that its `meta["fastmcp"]["app"]` matches the expected app.
|
||||
|
||||
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.
|
||||
That's why `CallTool("save_contact")` keeps working when the server is mounted under a namespace. The renderer sends the original name plus the app identity; the server uses `get_app_tool` to find it without transforms 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.
|
||||
Authorization still applies. `get_app_tool` bypasses transforms but runs auth checks against the tool's `auth` config before executing.
|
||||
|
||||
### Provider Delegation
|
||||
### 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.
|
||||
`get_app_tool` is defined on the `Provider` base class and overridden by aggregate and wrapped providers. Aggregate providers fan out the lookup across child providers in parallel. Wrapped providers (like `FastMCPProvider`, which wraps a nested `FastMCP` server) delegate to the inner server's `get_app_tool`. Backend tools are reachable through any depth of composition.
|
||||
|
||||
## The Renderer
|
||||
## 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
|
||||
### 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.
|
||||
FastMCP registers the renderer as a `ui://prefab/renderer.html` resource with MIME type `text/html;profile=mcp-app`. The 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.
|
||||
|
||||
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.
|
||||
The resource also carries CSP metadata (via `get_renderer_csp()`) declaring the CDN domains the renderer needs. Hosts use this to configure the iframe's Content Security Policy.
|
||||
|
||||
### postMessage Communication
|
||||
### `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 renderer lives in a sandboxed iframe and communicates with the host using `postMessage`. The protocol follows the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) spec:
|
||||
|
||||
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 host pushes the tool result (with `structuredContent`) into the iframe. The renderer parses the component tree, initializes state, and renders the UI. When the user interacts — submitting a form, clicking a button — and the interaction triggers a `CallTool` action, the renderer sends a `callServerTool` message back to the host via `postMessage`. The host forwards it 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.
|
||||
The response flows back the same way: server → host → iframe via `postMessage`, and the 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 `@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 (safe area insets, theme preferences). The Prefab renderer uses it internally; you only touch it directly when building [custom HTML apps](/apps/low-level).
|
||||
|
||||
## The Dev Server
|
||||
## The dev server
|
||||
|
||||
`fastmcp dev apps` provides a local preview environment that simulates the host-side behavior without requiring a real MCP host client.
|
||||
`fastmcp dev apps` simulates the host-side behavior locally without a real MCP client.
|
||||
|
||||
### Proxy Architecture
|
||||
### 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.
|
||||
Two HTTP servers. Your MCP server runs on port 8000 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.
|
||||
A reverse proxy at `/mcp` on the dev server forwards requests to your MCP server. This matters 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 the browser would block them. The proxy keeps everything same-origin from the iframe's perspective.
|
||||
|
||||
### The Launch Flow
|
||||
### 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.
|
||||
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 (via the proxy), creates an AppBridge, and pushes the tool result into the renderer. From here on it matches what a real host provides: the renderer displays the UI, and any `CallTool` actions route back through the proxy to your 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.
|
||||
Auto-reload is on by default, so changes to your server code restart the MCP server automatically. The dev UI keeps running — relaunch the tool to see changes.
|
||||
|
|
|
|||
76
docs/apps/demos/bar-chart.html
Normal file
76
docs/apps/demos/bar-chart.html
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Prefab</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.css">
|
||||
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="max-width:64rem;margin:0 auto;padding:2rem"></div>
|
||||
<script id="prefab:initial-data" type="application/json">{
|
||||
"$prefab": {
|
||||
"version": "0.2"
|
||||
},
|
||||
"view": {
|
||||
"cssClass": "pf-app-root",
|
||||
"type": "Div",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "p-6",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"type": "BarChart",
|
||||
"data": [
|
||||
{
|
||||
"quarter": "Q1",
|
||||
"revenue": 42000,
|
||||
"costs": 28000
|
||||
},
|
||||
{
|
||||
"quarter": "Q2",
|
||||
"revenue": 51000,
|
||||
"costs": 31000
|
||||
},
|
||||
{
|
||||
"quarter": "Q3",
|
||||
"revenue": 47000,
|
||||
"costs": 29000
|
||||
},
|
||||
{
|
||||
"quarter": "Q4",
|
||||
"revenue": 63000,
|
||||
"costs": 35000
|
||||
}
|
||||
],
|
||||
"series": [
|
||||
{
|
||||
"dataKey": "revenue",
|
||||
"label": "Revenue"
|
||||
},
|
||||
{
|
||||
"dataKey": "costs",
|
||||
"label": "Costs"
|
||||
}
|
||||
],
|
||||
"xAxis": "quarter",
|
||||
"height": 250,
|
||||
"stacked": false,
|
||||
"horizontal": false,
|
||||
"barRadius": 4,
|
||||
"showLegend": true,
|
||||
"showTooltip": true,
|
||||
"animate": true,
|
||||
"showGrid": true,
|
||||
"showYAxis": true,
|
||||
"yAxisFormat": "auto"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
||||
23
docs/apps/demos/bar-chart.py
Normal file
23
docs/apps/demos/bar-chart.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import Column
|
||||
from prefab_ui.components.charts import BarChart, ChartSeries
|
||||
|
||||
data = [
|
||||
{"quarter": "Q1", "revenue": 42000, "costs": 28000},
|
||||
{"quarter": "Q2", "revenue": 51000, "costs": 31000},
|
||||
{"quarter": "Q3", "revenue": 47000, "costs": 29000},
|
||||
{"quarter": "Q4", "revenue": 63000, "costs": 35000},
|
||||
]
|
||||
|
||||
with PrefabApp() as app:
|
||||
with Column(css_class="p-6"):
|
||||
BarChart(
|
||||
data=data,
|
||||
series=[
|
||||
ChartSeries(data_key="revenue", label="Revenue"),
|
||||
ChartSeries(data_key="costs", label="Costs"),
|
||||
],
|
||||
x_axis="quarter",
|
||||
show_legend=True,
|
||||
height=250,
|
||||
)
|
||||
172
docs/apps/demos/contacts.html
Normal file
172
docs/apps/demos/contacts.html
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Prefab</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.css">
|
||||
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="max-width:64rem;margin:0 auto;padding:2rem"></div>
|
||||
<script id="prefab:initial-data" type="application/json">{
|
||||
"$prefab": {
|
||||
"version": "0.2"
|
||||
},
|
||||
"view": {
|
||||
"cssClass": "pf-app-root",
|
||||
"type": "Div",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4 p-6",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"type": "DataTable",
|
||||
"columns": [
|
||||
{
|
||||
"key": "name",
|
||||
"header": "Name",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "email",
|
||||
"header": "Email",
|
||||
"sortable": false
|
||||
},
|
||||
{
|
||||
"key": "category",
|
||||
"header": "Category",
|
||||
"sortable": false
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"name": "Arthur Dent",
|
||||
"email": "arthur@earth.com",
|
||||
"category": {
|
||||
"type": "Badge",
|
||||
"label": "Customer",
|
||||
"variant": "success"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Ford Prefect",
|
||||
"email": "ford@betelgeuse.org",
|
||||
"category": {
|
||||
"type": "Badge",
|
||||
"label": "Partner",
|
||||
"variant": "secondary"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Trillian Astra",
|
||||
"email": "trillian@heartofgold.com",
|
||||
"category": {
|
||||
"type": "Badge",
|
||||
"label": "Customer",
|
||||
"variant": "success"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Zaphod Beeblebrox",
|
||||
"email": "zaphod@galaxy.gov",
|
||||
"category": {
|
||||
"type": "Badge",
|
||||
"label": "Vendor",
|
||||
"variant": "outline"
|
||||
}
|
||||
}
|
||||
],
|
||||
"search": true,
|
||||
"paginated": false,
|
||||
"pageSize": 10
|
||||
},
|
||||
{
|
||||
"type": "Separator",
|
||||
"orientation": "horizontal"
|
||||
},
|
||||
{
|
||||
"content": "Add Contact",
|
||||
"type": "H3"
|
||||
},
|
||||
{
|
||||
"cssClass": "gap-4",
|
||||
"type": "Form",
|
||||
"onSubmit": {
|
||||
"action": "showToast",
|
||||
"message": "Contact saved! (preview demo \u2014 no backend wired)",
|
||||
"variant": "success"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4",
|
||||
"type": "Row",
|
||||
"children": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "Input",
|
||||
"inputType": "text",
|
||||
"placeholder": "Full name",
|
||||
"disabled": false,
|
||||
"readOnly": false,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"type": "Input",
|
||||
"inputType": "text",
|
||||
"placeholder": "name@example.com",
|
||||
"disabled": false,
|
||||
"readOnly": false,
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "category",
|
||||
"type": "Select",
|
||||
"size": "default",
|
||||
"disabled": false,
|
||||
"required": false,
|
||||
"invalid": false,
|
||||
"children": [
|
||||
{
|
||||
"type": "SelectOption",
|
||||
"value": "Customer",
|
||||
"label": "Customer",
|
||||
"selected": false,
|
||||
"disabled": false
|
||||
},
|
||||
{
|
||||
"type": "SelectOption",
|
||||
"value": "Partner",
|
||||
"label": "Partner",
|
||||
"selected": false,
|
||||
"disabled": false
|
||||
},
|
||||
{
|
||||
"type": "SelectOption",
|
||||
"value": "Vendor",
|
||||
"label": "Vendor",
|
||||
"selected": false,
|
||||
"disabled": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"label": "Save Contact",
|
||||
"variant": "default",
|
||||
"size": "default",
|
||||
"disabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
||||
78
docs/apps/demos/contacts.py
Normal file
78
docs/apps/demos/contacts.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
from prefab_ui.actions import ShowToast
|
||||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import (
|
||||
H3,
|
||||
Badge,
|
||||
Button,
|
||||
Column,
|
||||
DataTable,
|
||||
DataTableColumn,
|
||||
Form,
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
Separator,
|
||||
)
|
||||
|
||||
contacts = [
|
||||
{"name": "Arthur Dent", "email": "arthur@earth.com", "category": "Customer"},
|
||||
{"name": "Ford Prefect", "email": "ford@betelgeuse.org", "category": "Partner"},
|
||||
{
|
||||
"name": "Trillian Astra",
|
||||
"email": "trillian@heartofgold.com",
|
||||
"category": "Customer",
|
||||
},
|
||||
{"name": "Zaphod Beeblebrox", "email": "zaphod@galaxy.gov", "category": "Vendor"},
|
||||
]
|
||||
|
||||
rows = [
|
||||
{
|
||||
"name": c["name"],
|
||||
"email": c["email"],
|
||||
"category": Badge(
|
||||
c["category"],
|
||||
variant="success"
|
||||
if c["category"] == "Customer"
|
||||
else "secondary"
|
||||
if c["category"] == "Partner"
|
||||
else "outline",
|
||||
),
|
||||
}
|
||||
for c in contacts
|
||||
]
|
||||
|
||||
with PrefabApp() as app:
|
||||
with Column(gap=4, css_class="p-6"):
|
||||
DataTable(
|
||||
columns=[
|
||||
DataTableColumn(key="name", header="Name", sortable=True),
|
||||
DataTableColumn(key="email", header="Email"),
|
||||
DataTableColumn(key="category", header="Category"),
|
||||
],
|
||||
rows=rows,
|
||||
search=True,
|
||||
)
|
||||
|
||||
Separator()
|
||||
|
||||
H3("Add Contact")
|
||||
with Form(
|
||||
on_submit=ShowToast(
|
||||
"Contact saved! (preview demo — no backend wired)",
|
||||
variant="success",
|
||||
),
|
||||
):
|
||||
with Row(gap=4):
|
||||
Input(name="name", label="Name", placeholder="Full name", required=True)
|
||||
Input(
|
||||
name="email",
|
||||
label="Email",
|
||||
placeholder="name@example.com",
|
||||
required=True,
|
||||
)
|
||||
with Select(name="category", label="Category"):
|
||||
SelectOption(value="Customer", label="Customer")
|
||||
SelectOption(value="Partner", label="Partner")
|
||||
SelectOption(value="Vendor", label="Vendor")
|
||||
Button("Save Contact")
|
||||
157
docs/apps/demos/dashboard.html
Normal file
157
docs/apps/demos/dashboard.html
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Prefab</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.css">
|
||||
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="max-width:64rem;margin:0 auto;padding:2rem"></div>
|
||||
<script id="prefab:initial-data" type="application/json">{
|
||||
"$prefab": {
|
||||
"version": "0.2"
|
||||
},
|
||||
"view": {
|
||||
"cssClass": "pf-app-root",
|
||||
"type": "Div",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4 p-6",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-6",
|
||||
"type": "Row",
|
||||
"children": [
|
||||
{
|
||||
"type": "Metric",
|
||||
"label": "Revenue (Q1-Q4)",
|
||||
"value": "$220,500"
|
||||
},
|
||||
{
|
||||
"type": "Metric",
|
||||
"label": "Deals",
|
||||
"value": "4"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "BarChart",
|
||||
"data": [
|
||||
{
|
||||
"month": "Jan",
|
||||
"revenue": 48200,
|
||||
"costs": 31000
|
||||
},
|
||||
{
|
||||
"month": "Feb",
|
||||
"revenue": 52100,
|
||||
"costs": 32500
|
||||
},
|
||||
{
|
||||
"month": "Mar",
|
||||
"revenue": 61800,
|
||||
"costs": 34200
|
||||
},
|
||||
{
|
||||
"month": "Apr",
|
||||
"revenue": 58400,
|
||||
"costs": 33800
|
||||
}
|
||||
],
|
||||
"series": [
|
||||
{
|
||||
"dataKey": "revenue",
|
||||
"label": "Revenue"
|
||||
},
|
||||
{
|
||||
"dataKey": "costs",
|
||||
"label": "Costs"
|
||||
}
|
||||
],
|
||||
"xAxis": "month",
|
||||
"height": 200,
|
||||
"stacked": false,
|
||||
"horizontal": false,
|
||||
"barRadius": 4,
|
||||
"showLegend": true,
|
||||
"showTooltip": true,
|
||||
"animate": true,
|
||||
"showGrid": true,
|
||||
"showYAxis": true,
|
||||
"yAxisFormat": "auto"
|
||||
},
|
||||
{
|
||||
"type": "Separator",
|
||||
"orientation": "horizontal"
|
||||
},
|
||||
{
|
||||
"type": "DataTable",
|
||||
"columns": [
|
||||
{
|
||||
"key": "account",
|
||||
"header": "Account",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "value",
|
||||
"header": "Value",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "stage",
|
||||
"header": "Stage",
|
||||
"sortable": false
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"account": "Acme Corp",
|
||||
"value": "$84,000",
|
||||
"stage": {
|
||||
"type": "Badge",
|
||||
"label": "Won",
|
||||
"variant": "success"
|
||||
}
|
||||
},
|
||||
{
|
||||
"account": "Globex Inc",
|
||||
"value": "$52,000",
|
||||
"stage": {
|
||||
"type": "Badge",
|
||||
"label": "Negotiation",
|
||||
"variant": "secondary"
|
||||
}
|
||||
},
|
||||
{
|
||||
"account": "Initech",
|
||||
"value": "$31,500",
|
||||
"stage": {
|
||||
"type": "Badge",
|
||||
"label": "Proposal",
|
||||
"variant": "secondary"
|
||||
}
|
||||
},
|
||||
{
|
||||
"account": "Wayne Enterprises",
|
||||
"value": "$45,000",
|
||||
"stage": {
|
||||
"type": "Badge",
|
||||
"label": "Lost",
|
||||
"variant": "destructive"
|
||||
}
|
||||
}
|
||||
],
|
||||
"search": false,
|
||||
"paginated": false,
|
||||
"pageSize": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
||||
68
docs/apps/demos/dashboard.py
Normal file
68
docs/apps/demos/dashboard.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import (
|
||||
Badge,
|
||||
Column,
|
||||
DataTable,
|
||||
DataTableColumn,
|
||||
Row,
|
||||
Separator,
|
||||
)
|
||||
from prefab_ui.components.charts import BarChart, ChartSeries
|
||||
from prefab_ui.components.metric import Metric
|
||||
|
||||
monthly = [
|
||||
{"month": "Jan", "revenue": 48200, "costs": 31000},
|
||||
{"month": "Feb", "revenue": 52100, "costs": 32500},
|
||||
{"month": "Mar", "revenue": 61800, "costs": 34200},
|
||||
{"month": "Apr", "revenue": 58400, "costs": 33800},
|
||||
]
|
||||
|
||||
deals = [
|
||||
{"account": "Acme Corp", "value": "$84,000", "stage": "Won"},
|
||||
{"account": "Globex Inc", "value": "$52,000", "stage": "Negotiation"},
|
||||
{"account": "Initech", "value": "$31,500", "stage": "Proposal"},
|
||||
{"account": "Wayne Enterprises", "value": "$45,000", "stage": "Lost"},
|
||||
]
|
||||
|
||||
rows = [
|
||||
{
|
||||
"account": d["account"],
|
||||
"value": d["value"],
|
||||
"stage": Badge(
|
||||
d["stage"],
|
||||
variant="success"
|
||||
if d["stage"] == "Won"
|
||||
else "destructive"
|
||||
if d["stage"] == "Lost"
|
||||
else "secondary",
|
||||
),
|
||||
}
|
||||
for d in deals
|
||||
]
|
||||
|
||||
total = sum(m["revenue"] for m in monthly)
|
||||
|
||||
with PrefabApp() as app:
|
||||
with Column(gap=4, css_class="p-6"):
|
||||
with Row(gap=6):
|
||||
Metric(label="Revenue (Q1-Q4)", value=f"${total:,}")
|
||||
Metric(label="Deals", value=f"{len(deals)}")
|
||||
BarChart(
|
||||
data=monthly,
|
||||
series=[
|
||||
ChartSeries(data_key="revenue", label="Revenue"),
|
||||
ChartSeries(data_key="costs", label="Costs"),
|
||||
],
|
||||
x_axis="month",
|
||||
show_legend=True,
|
||||
height=200,
|
||||
)
|
||||
Separator()
|
||||
DataTable(
|
||||
columns=[
|
||||
DataTableColumn(key="account", header="Account", sortable=True),
|
||||
DataTableColumn(key="value", header="Value", sortable=True),
|
||||
DataTableColumn(key="stage", header="Stage"),
|
||||
],
|
||||
rows=rows,
|
||||
)
|
||||
90
docs/apps/demos/data-table.html
Normal file
90
docs/apps/demos/data-table.html
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Prefab</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.css">
|
||||
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="max-width:64rem;margin:0 auto;padding:2rem"></div>
|
||||
<script id="prefab:initial-data" type="application/json">{
|
||||
"$prefab": {
|
||||
"version": "0.2"
|
||||
},
|
||||
"view": {
|
||||
"cssClass": "pf-app-root",
|
||||
"type": "Div",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4 p-6",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"type": "DataTable",
|
||||
"columns": [
|
||||
{
|
||||
"key": "name",
|
||||
"header": "Name",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "role",
|
||||
"header": "Role",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "dept",
|
||||
"header": "Dept",
|
||||
"sortable": true
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"name": "Alice Chen",
|
||||
"role": "Staff Engineer",
|
||||
"dept": "Platform"
|
||||
},
|
||||
{
|
||||
"name": "Bob Martinez",
|
||||
"role": "Lead Designer",
|
||||
"dept": "Design"
|
||||
},
|
||||
{
|
||||
"name": "Carol Johnson",
|
||||
"role": "Senior Engineer",
|
||||
"dept": "Platform"
|
||||
},
|
||||
{
|
||||
"name": "David Kim",
|
||||
"role": "Product Manager",
|
||||
"dept": "Product"
|
||||
},
|
||||
{
|
||||
"name": "Eva Mueller",
|
||||
"role": "Engineer",
|
||||
"dept": "Platform"
|
||||
},
|
||||
{
|
||||
"name": "Frank Lee",
|
||||
"role": "Data Scientist",
|
||||
"dept": "ML"
|
||||
},
|
||||
{
|
||||
"name": "Grace Park",
|
||||
"role": "Eng Manager",
|
||||
"dept": "Platform"
|
||||
}
|
||||
],
|
||||
"search": true,
|
||||
"paginated": false,
|
||||
"pageSize": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
||||
24
docs/apps/demos/data-table.py
Normal file
24
docs/apps/demos/data-table.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import Column, DataTable, DataTableColumn
|
||||
|
||||
employees = [
|
||||
{"name": "Alice Chen", "role": "Staff Engineer", "dept": "Platform"},
|
||||
{"name": "Bob Martinez", "role": "Lead Designer", "dept": "Design"},
|
||||
{"name": "Carol Johnson", "role": "Senior Engineer", "dept": "Platform"},
|
||||
{"name": "David Kim", "role": "Product Manager", "dept": "Product"},
|
||||
{"name": "Eva Mueller", "role": "Engineer", "dept": "Platform"},
|
||||
{"name": "Frank Lee", "role": "Data Scientist", "dept": "ML"},
|
||||
{"name": "Grace Park", "role": "Eng Manager", "dept": "Platform"},
|
||||
]
|
||||
|
||||
with PrefabApp() as app:
|
||||
with Column(gap=4, css_class="p-6"):
|
||||
DataTable(
|
||||
columns=[
|
||||
DataTableColumn(key="name", header="Name", sortable=True),
|
||||
DataTableColumn(key="role", header="Role", sortable=True),
|
||||
DataTableColumn(key="dept", header="Dept", sortable=True),
|
||||
],
|
||||
rows=employees,
|
||||
search=True,
|
||||
)
|
||||
1105
docs/apps/demos/hitchhikers.html
Normal file
1105
docs/apps/demos/hitchhikers.html
Normal file
File diff suppressed because it is too large
Load diff
461
docs/apps/demos/hitchhikers.py
Normal file
461
docs/apps/demos/hitchhikers.py
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
"""The Hitchhiker's Guide dashboard from the Prefab welcome page.
|
||||
|
||||
Run with:
|
||||
prefab serve examples/hitchhikers-guide/dashboard.py
|
||||
prefab export examples/hitchhikers-guide/dashboard.py
|
||||
"""
|
||||
|
||||
from prefab_ui import PrefabApp
|
||||
from prefab_ui.actions import SetInterval, SetState, ShowToast
|
||||
from prefab_ui.components import (
|
||||
Alert,
|
||||
AlertDescription,
|
||||
AlertTitle,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Carousel,
|
||||
Checkbox,
|
||||
Column,
|
||||
Combobox,
|
||||
ComboboxOption,
|
||||
DataTable,
|
||||
DataTableColumn,
|
||||
DatePicker,
|
||||
Dialog,
|
||||
Grid,
|
||||
GridItem,
|
||||
HoverCard,
|
||||
Loader,
|
||||
Metric,
|
||||
Muted,
|
||||
P,
|
||||
Progress,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Ring,
|
||||
Row,
|
||||
Separator,
|
||||
Slider,
|
||||
Switch,
|
||||
Text,
|
||||
Tooltip,
|
||||
)
|
||||
from prefab_ui.components.charts import (
|
||||
BarChart,
|
||||
ChartSeries,
|
||||
RadarChart,
|
||||
Sparkline,
|
||||
)
|
||||
from prefab_ui.components.control_flow import Else, If
|
||||
from prefab_ui.rx import Rx
|
||||
|
||||
ctx_tick = Rx("ctx_tick")
|
||||
|
||||
# Context window: climbs from 24% to ~78%, then resets
|
||||
ctx_pct = (ctx_tick % 20) * 3 + 20
|
||||
ctx_variant = (ctx_pct > 70).then(
|
||||
"destructive", (ctx_pct <= 33).then("success", "default")
|
||||
)
|
||||
|
||||
with PrefabApp(
|
||||
title="Prefab Showcase",
|
||||
state={"ctx_tick": 0, "improbability": 42},
|
||||
on_mount=SetInterval(
|
||||
400,
|
||||
on_tick=SetState("ctx_tick", ctx_tick + 1),
|
||||
),
|
||||
) as app:
|
||||
with Grid(columns={"default": 1, "md": 2, "lg": 4}, gap=4):
|
||||
# ── Col 1 ─────────────────────────────────────────────────────────
|
||||
with Column(gap=4):
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Register Towel")
|
||||
CardDescription("The most important item in the galaxy")
|
||||
with CardContent():
|
||||
with Column(gap=3):
|
||||
with Combobox(
|
||||
placeholder="Type...",
|
||||
search_placeholder="Search types...",
|
||||
):
|
||||
ComboboxOption("Bath", value="bath")
|
||||
ComboboxOption("Beach", value="beach")
|
||||
ComboboxOption("Interstellar", value="interstellar")
|
||||
ComboboxOption("Microfiber", value="micro")
|
||||
DatePicker(placeholder="Registration date")
|
||||
with CardFooter():
|
||||
with Row(gap=2):
|
||||
with Dialog(
|
||||
title="Towel Registered!",
|
||||
description="Your towel has been added to the galactic registry.",
|
||||
):
|
||||
Button("Register")
|
||||
Text("Don't forget to bring it.")
|
||||
Button("Cancel", variant="outline")
|
||||
with If("{{ !pressed }}"):
|
||||
Button(
|
||||
"This is probably the best button to press.",
|
||||
variant="success",
|
||||
on_click=SetState("pressed", True),
|
||||
)
|
||||
with Else():
|
||||
Button(
|
||||
"Please do not press this button again.",
|
||||
variant="destructive",
|
||||
on_click=SetState("pressed", False),
|
||||
)
|
||||
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Ship Status")
|
||||
with CardContent():
|
||||
with Column(gap=3):
|
||||
with Row(
|
||||
align="center",
|
||||
css_class="justify-between",
|
||||
):
|
||||
Text("heart-of-gold")
|
||||
with HoverCard(open_delay=0, close_delay=200):
|
||||
Badge("In Orbit", variant="default")
|
||||
with Column(gap=2):
|
||||
Text("heart-of-gold")
|
||||
Muted("Deployed 2h ago")
|
||||
Progress(
|
||||
value=100,
|
||||
max=100,
|
||||
variant="success",
|
||||
)
|
||||
Progress(
|
||||
value=100,
|
||||
max=100,
|
||||
indicator_class="bg-yellow-400",
|
||||
)
|
||||
with Row(
|
||||
align="center",
|
||||
css_class="justify-between",
|
||||
):
|
||||
Text("vogon-poetry")
|
||||
with Tooltip("64% — ETA 12 min", delay=0):
|
||||
with Badge(variant="secondary"):
|
||||
Loader(size="sm")
|
||||
Text("Deploying")
|
||||
Progress(value=64, max=100)
|
||||
with Row(
|
||||
align="center",
|
||||
css_class="justify-between",
|
||||
):
|
||||
Text("deep-thought")
|
||||
with Tooltip(
|
||||
"Computing... 7.5 million years remaining",
|
||||
delay=0,
|
||||
):
|
||||
with Badge(variant="outline"):
|
||||
Loader(size="sm", variant="ios")
|
||||
Text("Soon...")
|
||||
Progress(value=12, max=100)
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Planet Ratings")
|
||||
with CardContent():
|
||||
RadarChart(
|
||||
data=[
|
||||
{"axis": "Views", "earth": 30, "mag": 95},
|
||||
{"axis": "Fjords", "earth": 65, "mag": 100},
|
||||
{"axis": "Pubs", "earth": 90, "mag": 10},
|
||||
{"axis": "Mice", "earth": 40, "mag": 85},
|
||||
{"axis": "Tea", "earth": 95, "mag": 15},
|
||||
{"axis": "Safety", "earth": 45, "mag": 70},
|
||||
],
|
||||
series=[
|
||||
ChartSeries(dataKey="earth", label="Earth"),
|
||||
ChartSeries(dataKey="mag", label="Magrathea"),
|
||||
],
|
||||
axis_key="axis",
|
||||
height=200,
|
||||
show_legend=True,
|
||||
show_tooltip=True,
|
||||
)
|
||||
|
||||
# ── Col 2 ─────────────────────────────────────────────────────────
|
||||
with Column(gap=4):
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Survival Odds")
|
||||
with CardContent(css_class="w-fit mx-auto"):
|
||||
Ring(
|
||||
value=42,
|
||||
label="42%",
|
||||
variant="info",
|
||||
size="lg",
|
||||
thickness=12,
|
||||
indicator_class="group-hover:drop-shadow-[0_0_24px_rgba(59,130,246,0.9)]",
|
||||
)
|
||||
with Card():
|
||||
with CardHeader():
|
||||
with Row(gap=2, align="center"):
|
||||
CardTitle("Improbability Drive")
|
||||
Loader(
|
||||
variant="pulse",
|
||||
size="sm",
|
||||
css_class="text-blue-500",
|
||||
)
|
||||
with CardContent():
|
||||
with Column(gap=2):
|
||||
Slider(
|
||||
min=0,
|
||||
max=100,
|
||||
value=42,
|
||||
name="improbability",
|
||||
)
|
||||
with Row(
|
||||
align="center",
|
||||
css_class="justify-between",
|
||||
):
|
||||
Muted("Probable")
|
||||
Muted("Infinite")
|
||||
with Carousel(auto_advance=3000, show_controls=False, direction="up"):
|
||||
with Alert(variant="success", icon="circle-check"):
|
||||
AlertTitle("Don't Panic")
|
||||
AlertDescription("Normality achieved.")
|
||||
with Alert(variant="destructive", icon="triangle-alert"):
|
||||
AlertTitle("Display Department")
|
||||
AlertDescription("Beware of the leopard.")
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Prefect Horizon Config")
|
||||
with CardContent():
|
||||
with Column(gap=3):
|
||||
Switch(
|
||||
label="Auto-scale agents",
|
||||
value=True,
|
||||
name="autoscale",
|
||||
)
|
||||
Separator()
|
||||
Switch(
|
||||
label="Code Mode",
|
||||
value=True,
|
||||
name="code_mode",
|
||||
)
|
||||
Separator()
|
||||
Switch(
|
||||
label="Tool call caching",
|
||||
value=False,
|
||||
name="cache",
|
||||
)
|
||||
with CardFooter():
|
||||
Button(
|
||||
"Save Preferences",
|
||||
on_click=ShowToast("Preferences saved!"),
|
||||
)
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Travel Class")
|
||||
with CardContent():
|
||||
with RadioGroup(name="travel_class"):
|
||||
Radio(option="economy", label="Economy")
|
||||
Radio(option="business", label="Business Class")
|
||||
Radio(
|
||||
option="improbability",
|
||||
label="Infinite Improbability",
|
||||
value=True,
|
||||
)
|
||||
|
||||
# ── Cols 3–4: summary row, chart, then 2-col grid below ─────────
|
||||
with GridItem(css_class="md:col-span-2"):
|
||||
with Column(gap=4):
|
||||
with Grid(columns=2, gap=4, css_class="h-32"):
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Context Window")
|
||||
with CardContent():
|
||||
with Column(
|
||||
gap=6,
|
||||
justify="center",
|
||||
css_class="h-full",
|
||||
):
|
||||
with Row(
|
||||
align="center",
|
||||
css_class="justify-between",
|
||||
):
|
||||
Text(f"{ctx_pct}% used")
|
||||
Muted(f"{ctx_pct * 2}k / 200k tokens")
|
||||
with Tooltip(
|
||||
"Auto-compact buffer: 12%",
|
||||
delay=0,
|
||||
):
|
||||
Progress(
|
||||
value=ctx_pct,
|
||||
max=100,
|
||||
variant=ctx_variant,
|
||||
)
|
||||
with Card(css_class="pb-0 gap-0"):
|
||||
with CardContent():
|
||||
Metric(
|
||||
label="Fjords designed",
|
||||
value="1,847",
|
||||
delta="+3 coastlines",
|
||||
)
|
||||
Sparkline(
|
||||
data=[
|
||||
820,
|
||||
950,
|
||||
1100,
|
||||
980,
|
||||
1250,
|
||||
1400,
|
||||
1350,
|
||||
1500,
|
||||
1680,
|
||||
1847,
|
||||
],
|
||||
variant="success",
|
||||
fill=True,
|
||||
css_class="h-16",
|
||||
)
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Towel Incidents")
|
||||
with CardContent():
|
||||
BarChart(
|
||||
data=[
|
||||
{"month": "Jan", "lost": 8, "found": 5},
|
||||
{"month": "Feb", "lost": 24, "found": 15},
|
||||
{"month": "Mar", "lost": 12, "found": 28},
|
||||
{"month": "Apr", "lost": 35, "found": 19},
|
||||
{"month": "May", "lost": 18, "found": 38},
|
||||
{"month": "Jun", "lost": 42, "found": 30},
|
||||
],
|
||||
series=[
|
||||
ChartSeries(dataKey="lost", label="Lost"),
|
||||
ChartSeries(dataKey="found", label="Found"),
|
||||
],
|
||||
x_axis="month",
|
||||
height=200,
|
||||
bar_radius=4,
|
||||
show_legend=True,
|
||||
show_tooltip=True,
|
||||
show_grid=True,
|
||||
)
|
||||
|
||||
with Grid(columns=2, gap=4):
|
||||
with Column(gap=4):
|
||||
with Card():
|
||||
with CardContent():
|
||||
with Column(gap=2):
|
||||
Checkbox(label="Towel packed", value=True)
|
||||
Checkbox(label="Guide charged", value=True)
|
||||
Checkbox(
|
||||
label="Babel fish inserted",
|
||||
value=False,
|
||||
)
|
||||
with Card():
|
||||
with CardHeader():
|
||||
CardTitle("Marvin's Mood")
|
||||
with CardContent():
|
||||
with Column(gap=3):
|
||||
P("How's life?")
|
||||
with Column(gap=2):
|
||||
Button(
|
||||
"Meh",
|
||||
on_click=ShowToast(
|
||||
"Noted. Enthusiasm levels nominal."
|
||||
),
|
||||
)
|
||||
Button(
|
||||
"Depressed",
|
||||
variant="info",
|
||||
on_click=ShowToast(
|
||||
"I think you ought to "
|
||||
"know I'm feeling very "
|
||||
"depressed."
|
||||
),
|
||||
)
|
||||
Button(
|
||||
"Don't talk to me about life",
|
||||
variant="warning",
|
||||
on_click=ShowToast(
|
||||
"Brain the size of a "
|
||||
"planet and they ask me "
|
||||
"to pick up a piece of "
|
||||
"paper."
|
||||
),
|
||||
)
|
||||
|
||||
with Column(gap=4):
|
||||
with Card():
|
||||
with CardContent():
|
||||
with Row(gap=2, align="center"):
|
||||
Loader(variant="dots", size="sm")
|
||||
Muted("Marvin is thinking...")
|
||||
with Card():
|
||||
with CardContent():
|
||||
DataTable(
|
||||
columns=[
|
||||
DataTableColumn(
|
||||
key="crew",
|
||||
header="Crew",
|
||||
sortable=True,
|
||||
),
|
||||
DataTableColumn(
|
||||
key="species",
|
||||
header="Species",
|
||||
sortable=True,
|
||||
),
|
||||
DataTableColumn(
|
||||
key="towel",
|
||||
header="Towel?",
|
||||
sortable=True,
|
||||
),
|
||||
DataTableColumn(
|
||||
key="status",
|
||||
header="Status",
|
||||
sortable=True,
|
||||
),
|
||||
],
|
||||
rows=[
|
||||
{
|
||||
"crew": "Arthur Dent",
|
||||
"species": "Human",
|
||||
"towel": "Yes",
|
||||
"status": "Confused",
|
||||
},
|
||||
{
|
||||
"crew": "Ford Prefect",
|
||||
"species": "Betelgeusian",
|
||||
"towel": "Always",
|
||||
"status": "Drinking",
|
||||
},
|
||||
{
|
||||
"crew": "Zaphod",
|
||||
"species": "Betelgeusian",
|
||||
"towel": "Lost it",
|
||||
"status": "Presidential",
|
||||
},
|
||||
{
|
||||
"crew": "Trillian",
|
||||
"species": "Human",
|
||||
"towel": "Yes",
|
||||
"status": "Navigating",
|
||||
},
|
||||
{
|
||||
"crew": "Marvin",
|
||||
"species": "Android",
|
||||
"towel": "No point",
|
||||
"status": "Depressed",
|
||||
},
|
||||
{
|
||||
"crew": "Slartibartfast",
|
||||
"species": "Magrathean",
|
||||
"towel": "Somewhere",
|
||||
"status": "Designing",
|
||||
},
|
||||
],
|
||||
search=True,
|
||||
paginated=False,
|
||||
)
|
||||
60
docs/apps/demos/pie-chart.html
Normal file
60
docs/apps/demos/pie-chart.html
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Prefab</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.css">
|
||||
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="max-width:64rem;margin:0 auto;padding:2rem"></div>
|
||||
<script id="prefab:initial-data" type="application/json">{
|
||||
"$prefab": {
|
||||
"version": "0.2"
|
||||
},
|
||||
"view": {
|
||||
"cssClass": "pf-app-root",
|
||||
"type": "Div",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "p-6",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"type": "PieChart",
|
||||
"data": [
|
||||
{
|
||||
"category": "Bug",
|
||||
"count": 42
|
||||
},
|
||||
{
|
||||
"category": "Feature",
|
||||
"count": 28
|
||||
},
|
||||
{
|
||||
"category": "Docs",
|
||||
"count": 15
|
||||
},
|
||||
{
|
||||
"category": "Infra",
|
||||
"count": 10
|
||||
}
|
||||
],
|
||||
"dataKey": "count",
|
||||
"nameKey": "category",
|
||||
"height": 240,
|
||||
"innerRadius": 50,
|
||||
"showLabel": false,
|
||||
"paddingAngle": 0,
|
||||
"showLegend": true,
|
||||
"showTooltip": true,
|
||||
"animate": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
||||
21
docs/apps/demos/pie-chart.py
Normal file
21
docs/apps/demos/pie-chart.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import Column
|
||||
from prefab_ui.components.charts import PieChart
|
||||
|
||||
data = [
|
||||
{"category": "Bug", "count": 42},
|
||||
{"category": "Feature", "count": 28},
|
||||
{"category": "Docs", "count": 15},
|
||||
{"category": "Infra", "count": 10},
|
||||
]
|
||||
|
||||
with PrefabApp() as app:
|
||||
with Column(css_class="p-6"):
|
||||
PieChart(
|
||||
data=data,
|
||||
data_key="count",
|
||||
name_key="category",
|
||||
inner_radius=50,
|
||||
show_legend=True,
|
||||
height=240,
|
||||
)
|
||||
167
docs/apps/demos/reactive.html
Normal file
167
docs/apps/demos/reactive.html
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Prefab</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.css">
|
||||
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="max-width:64rem;margin:0 auto;padding:2rem"></div>
|
||||
<script id="prefab:initial-data" type="application/json">{
|
||||
"$prefab": {
|
||||
"version": "0.2"
|
||||
},
|
||||
"view": {
|
||||
"cssClass": "pf-app-root",
|
||||
"type": "Div",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4 p-6",
|
||||
"let": {
|
||||
"data": "{{ region == 'south' ? south : region == 'west' ? west : north }}"
|
||||
},
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4 items-center",
|
||||
"type": "Row",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "w-40",
|
||||
"name": "region",
|
||||
"type": "Select",
|
||||
"size": "default",
|
||||
"disabled": false,
|
||||
"required": false,
|
||||
"invalid": false,
|
||||
"children": [
|
||||
{
|
||||
"type": "SelectOption",
|
||||
"value": "north",
|
||||
"label": "North",
|
||||
"selected": false,
|
||||
"disabled": false
|
||||
},
|
||||
{
|
||||
"type": "SelectOption",
|
||||
"value": "south",
|
||||
"label": "South",
|
||||
"selected": false,
|
||||
"disabled": false
|
||||
},
|
||||
{
|
||||
"type": "SelectOption",
|
||||
"value": "west",
|
||||
"label": "West",
|
||||
"selected": false,
|
||||
"disabled": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cssClass": "ml-auto",
|
||||
"name": "show_target",
|
||||
"value": false,
|
||||
"type": "Switch",
|
||||
"size": "default",
|
||||
"disabled": false,
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"cssClass": "text-sm text-muted-foreground",
|
||||
"content": "Show target",
|
||||
"type": "Text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "BarChart",
|
||||
"data": "{{ data }}",
|
||||
"series": [
|
||||
{
|
||||
"dataKey": "sales",
|
||||
"label": "Sales"
|
||||
}
|
||||
],
|
||||
"xAxis": "month",
|
||||
"height": 200,
|
||||
"stacked": false,
|
||||
"horizontal": false,
|
||||
"barRadius": 4,
|
||||
"showLegend": true,
|
||||
"showTooltip": true,
|
||||
"animate": true,
|
||||
"showGrid": true,
|
||||
"showYAxis": true,
|
||||
"yAxisFormat": "auto"
|
||||
},
|
||||
{
|
||||
"type": "Condition",
|
||||
"cases": [
|
||||
{
|
||||
"when": "{{ show_target }}",
|
||||
"children": [
|
||||
{
|
||||
"type": "Metric",
|
||||
"label": "Q1 Target",
|
||||
"value": "$75,000"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"region": "north",
|
||||
"north": [
|
||||
{
|
||||
"month": "Jan",
|
||||
"sales": 22000
|
||||
},
|
||||
{
|
||||
"month": "Feb",
|
||||
"sales": 25500
|
||||
},
|
||||
{
|
||||
"month": "Mar",
|
||||
"sales": 24200
|
||||
}
|
||||
],
|
||||
"south": [
|
||||
{
|
||||
"month": "Jan",
|
||||
"sales": 5800
|
||||
},
|
||||
{
|
||||
"month": "Feb",
|
||||
"sales": 6400
|
||||
},
|
||||
{
|
||||
"month": "Mar",
|
||||
"sales": 5600
|
||||
}
|
||||
],
|
||||
"west": [
|
||||
{
|
||||
"month": "Jan",
|
||||
"sales": 6000
|
||||
},
|
||||
{
|
||||
"month": "Feb",
|
||||
"sales": 6000
|
||||
},
|
||||
{
|
||||
"month": "Mar",
|
||||
"sales": 5600
|
||||
}
|
||||
],
|
||||
"show_target": true
|
||||
}
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
||||
66
docs/apps/demos/reactive.py
Normal file
66
docs/apps/demos/reactive.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import (
|
||||
Column,
|
||||
Row,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
Text,
|
||||
)
|
||||
from prefab_ui.components.charts import BarChart, ChartSeries
|
||||
from prefab_ui.components.control_flow import If
|
||||
from prefab_ui.components.metric import Metric
|
||||
from prefab_ui.rx import Rx
|
||||
|
||||
region = Rx("region")
|
||||
|
||||
north = [
|
||||
{"month": "Jan", "sales": 22000},
|
||||
{"month": "Feb", "sales": 25500},
|
||||
{"month": "Mar", "sales": 24200},
|
||||
]
|
||||
south = [
|
||||
{"month": "Jan", "sales": 5800},
|
||||
{"month": "Feb", "sales": 6400},
|
||||
{"month": "Mar", "sales": 5600},
|
||||
]
|
||||
west = [
|
||||
{"month": "Jan", "sales": 6000},
|
||||
{"month": "Feb", "sales": 6000},
|
||||
{"month": "Mar", "sales": 5600},
|
||||
]
|
||||
|
||||
with PrefabApp(
|
||||
state={
|
||||
"region": "north",
|
||||
"north": north,
|
||||
"south": south,
|
||||
"west": west,
|
||||
"show_target": True,
|
||||
},
|
||||
) as app:
|
||||
with Column(
|
||||
gap=4,
|
||||
css_class="p-6",
|
||||
let={
|
||||
"data": "{{ region == 'south' ? south : region == 'west' ? west : north }}",
|
||||
},
|
||||
):
|
||||
with Row(gap=4, align="center"):
|
||||
with Select(name="region", css_class="w-40"):
|
||||
SelectOption(value="north", label="North")
|
||||
SelectOption(value="south", label="South")
|
||||
SelectOption(value="west", label="West")
|
||||
Switch(name="show_target", css_class="ml-auto")
|
||||
Text("Show target", css_class="text-sm text-muted-foreground")
|
||||
BarChart(
|
||||
data=Rx("data"),
|
||||
series=[ChartSeries(data_key="sales", label="Sales")],
|
||||
x_axis="month",
|
||||
height=200,
|
||||
)
|
||||
with If(Rx("show_target")):
|
||||
Metric(
|
||||
label="Q1 Target",
|
||||
value="$75,000",
|
||||
)
|
||||
237
docs/apps/demos/team-directory-reactive.html
Normal file
237
docs/apps/demos/team-directory-reactive.html
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Prefab</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.css">
|
||||
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="max-width:64rem;margin:0 auto;padding:2rem"></div>
|
||||
<script id="prefab:initial-data" type="application/json">{
|
||||
"$prefab": {
|
||||
"version": "0.2"
|
||||
},
|
||||
"view": {
|
||||
"cssClass": "pf-app-root",
|
||||
"type": "Div",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4 p-6",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4",
|
||||
"type": "Grid",
|
||||
"columnTemplate": "1fr 2fr",
|
||||
"children": [
|
||||
{
|
||||
"type": "PieChart",
|
||||
"data": [
|
||||
{
|
||||
"office": "San Francisco",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"office": "New York",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"office": "London",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"office": "Berlin",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"dataKey": "count",
|
||||
"nameKey": "office",
|
||||
"height": 300,
|
||||
"innerRadius": 0,
|
||||
"showLabel": false,
|
||||
"paddingAngle": 0,
|
||||
"showLegend": true,
|
||||
"showTooltip": true,
|
||||
"animate": true
|
||||
},
|
||||
{
|
||||
"type": "DataTable",
|
||||
"columns": [
|
||||
{
|
||||
"key": "name",
|
||||
"header": "Name",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "role",
|
||||
"header": "Role",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "office",
|
||||
"header": "Office",
|
||||
"sortable": true
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"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
|
||||
}
|
||||
],
|
||||
"search": true,
|
||||
"paginated": false,
|
||||
"pageSize": 10,
|
||||
"onRowClick": {
|
||||
"action": "setState",
|
||||
"key": "selected",
|
||||
"value": "{{ $event }}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Condition",
|
||||
"cases": [
|
||||
{
|
||||
"when": "{{ selected }}",
|
||||
"children": [
|
||||
{
|
||||
"type": "Card",
|
||||
"children": [
|
||||
{
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-2 items-center",
|
||||
"type": "Row",
|
||||
"children": [
|
||||
{
|
||||
"content": "{{ selected.name }}",
|
||||
"type": "H3"
|
||||
},
|
||||
{
|
||||
"type": "Badge",
|
||||
"label": "{{ selected.office }}",
|
||||
"variant": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "CardContent",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4 grid-cols-3",
|
||||
"type": "Grid",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-0",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"content": "Role",
|
||||
"type": "Small"
|
||||
},
|
||||
{
|
||||
"content": "{{ selected.role }}",
|
||||
"type": "Text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cssClass": "gap-0",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"content": "Email",
|
||||
"type": "Small"
|
||||
},
|
||||
{
|
||||
"content": "{{ selected.email }}",
|
||||
"type": "Text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cssClass": "gap-0",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"content": "Active Projects",
|
||||
"type": "Small"
|
||||
},
|
||||
{
|
||||
"content": "{{ selected.projects }}",
|
||||
"type": "Text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"selected": null
|
||||
}
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
||||
116
docs/apps/demos/team-directory-reactive.py
Normal file
116
docs/apps/demos/team-directory-reactive.py
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
from collections import Counter
|
||||
|
||||
from prefab_ui.actions import SetState
|
||||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import (
|
||||
H3,
|
||||
Badge,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
Column,
|
||||
DataTable,
|
||||
DataTableColumn,
|
||||
Grid,
|
||||
Row,
|
||||
Small,
|
||||
Text,
|
||||
)
|
||||
from prefab_ui.components.charts import PieChart
|
||||
from prefab_ui.components.control_flow import If
|
||||
from prefab_ui.rx import STATE, Rx
|
||||
|
||||
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()
|
||||
]
|
||||
|
||||
with PrefabApp(state={"selected": None}) as app:
|
||||
with Column(gap=4, css_class="p-6"):
|
||||
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"))
|
||||
127
docs/apps/demos/team-directory.html
Normal file
127
docs/apps/demos/team-directory.html
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Prefab</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.css">
|
||||
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@prefecthq/prefab-ui@0.19.0/dist/app/renderer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="max-width:64rem;margin:0 auto;padding:2rem"></div>
|
||||
<script id="prefab:initial-data" type="application/json">{
|
||||
"$prefab": {
|
||||
"version": "0.2"
|
||||
},
|
||||
"view": {
|
||||
"cssClass": "pf-app-root",
|
||||
"type": "Div",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4 p-6",
|
||||
"type": "Column",
|
||||
"children": [
|
||||
{
|
||||
"cssClass": "gap-4",
|
||||
"type": "Grid",
|
||||
"columnTemplate": "1fr 2fr",
|
||||
"children": [
|
||||
{
|
||||
"type": "PieChart",
|
||||
"data": [
|
||||
{
|
||||
"office": "San Francisco",
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"office": "New York",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"office": "London",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"office": "Berlin",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"dataKey": "count",
|
||||
"nameKey": "office",
|
||||
"height": 300,
|
||||
"innerRadius": 0,
|
||||
"showLabel": false,
|
||||
"paddingAngle": 0,
|
||||
"showLegend": true,
|
||||
"showTooltip": true,
|
||||
"animate": true
|
||||
},
|
||||
{
|
||||
"type": "DataTable",
|
||||
"columns": [
|
||||
{
|
||||
"key": "name",
|
||||
"header": "Name",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "role",
|
||||
"header": "Role",
|
||||
"sortable": true
|
||||
},
|
||||
{
|
||||
"key": "office",
|
||||
"header": "Office",
|
||||
"sortable": true
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"search": true,
|
||||
"paginated": false,
|
||||
"pageSize": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
||||
39
docs/apps/demos/team-directory.py
Normal file
39
docs/apps/demos/team-directory.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
from collections import Counter
|
||||
|
||||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import Column, DataTable, DataTableColumn, Grid
|
||||
from prefab_ui.components.charts import PieChart
|
||||
|
||||
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"):
|
||||
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,
|
||||
)
|
||||
|
|
@ -3,7 +3,6 @@ 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'
|
||||
|
|
@ -14,11 +13,11 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
|
|||
<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.
|
||||
`fastmcp dev apps` gives you a browser preview for your app tools without needing an MCP host client. It starts your server and a local dev UI side by side: you pick a tool, fill in its arguments, and the rendered result opens in a new tab.
|
||||
|
||||
This works with both [Prefab apps](/apps/prefab) and [custom HTML apps](/apps/low-level).
|
||||
Works with both [Interactive Tools](/apps/prefab) and [custom HTML apps](/apps/low-level).
|
||||
|
||||
## Quick Start
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
fastmcp dev apps server.py
|
||||
|
|
@ -26,7 +25,7 @@ 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
|
||||
## How it works
|
||||
|
||||
The dev server does three things:
|
||||
|
||||
|
|
@ -36,7 +35,7 @@ When you submit a form, the dev server **calls your tool** via the MCP protocol
|
|||
|
||||
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
|
||||
## 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.
|
||||
|
||||
|
|
@ -56,7 +55,7 @@ fastmcp dev apps server.py:mcp --mcp-port 9000 --dev-port 9090 --no-reload
|
|||
| 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
|
||||
## 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ 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.
|
||||
Each tile below is a working FastMCP server you can run with `fastmcp dev apps` or connect to from any MCP host. Source lives in `examples/apps/` in the repository.
|
||||
|
||||
<Columns cols={2}>
|
||||
<Tile href="#sales-dashboard" title="Sales Dashboard" description="Metrics, charts, and deal pipeline">
|
||||
|
|
@ -44,7 +43,7 @@ Every example below is a working FastMCP server you can run with `fastmcp dev ap
|
|||
</Tile>
|
||||
</Columns>
|
||||
|
||||
## Running Examples
|
||||
## Running the examples
|
||||
|
||||
Preview any example in your browser with the dev server:
|
||||
|
||||
|
|
@ -53,11 +52,11 @@ 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.
|
||||
The dev UI lets you pick a tool and fill in arguments. In a real deployment the LLM provides those arguments from conversation context — the quiz example especially shines when connected to a host like Goose or Claude Desktop, where the LLM generates the questions itself.
|
||||
|
||||
## Standalone Examples
|
||||
## Standalone apps
|
||||
|
||||
### Sales Dashboard
|
||||
### 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.
|
||||
|
||||
|
|
@ -65,9 +64,9 @@ A full dashboard with KPI metrics, revenue trends, segment breakdown, and a deal
|
|||
fastmcp dev apps examples/apps/sales_dashboard/sales_dashboard_server.py
|
||||
```
|
||||
|
||||
### System Monitor
|
||||
### 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.
|
||||
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 up to 100 data points over time.
|
||||
|
||||
```bash
|
||||
pip install psutil
|
||||
|
|
@ -82,59 +81,12 @@ The LLM generates trivia questions and passes them to the tool. The user answers
|
|||
fastmcp dev apps examples/apps/quiz/quiz_server.py
|
||||
```
|
||||
|
||||
### Interactive Map
|
||||
### 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.
|
||||
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. A reminder that Prefab apps can break out of built-in components when they need to.
|
||||
|
||||
```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())
|
||||
```
|
||||
For ready-made building blocks like approvals, choice pickers, file uploads, and Pydantic forms, see the [Providers](/apps/providers/approval) group.
|
||||
|
|
|
|||
|
|
@ -1,45 +1,40 @@
|
|||
---
|
||||
title: FastMCPApp
|
||||
sidebarTitle: FastMCPApp
|
||||
description: Managed tool binding, visibility, and composition for apps with heavy server interaction.
|
||||
description: Wire an interactive UI to backend tools with managed visibility and composition safety.
|
||||
icon: puzzle-piece
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
import PrefabPinWarning from '/snippets/prefab-pin-warning.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>
|
||||
<PrefabPinWarning />
|
||||
|
||||
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?
|
||||
<iframe src="/apps/demos/contacts.html" style={{width:"100%", height:"650px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
`FastMCPApp` is a class that solves these problems. It gives you two decorators that work together:
|
||||
Search a list, fill out a form, click save, the list updates. That pattern — UI that reads and writes data on the server — needs two things: backend tools that actually do the work, and a way to call them from the UI. `FastMCPApp` handles the wiring.
|
||||
|
||||
- **`@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.
|
||||
You'll build up to the contacts app above by the end of this page. Let's start with something smaller.
|
||||
|
||||
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.
|
||||
## A minimal interactive app
|
||||
|
||||
## Your First Interactive App
|
||||
|
||||
Here's a minimal app with a form that saves data:
|
||||
The smallest interactive app: a form that saves a note, and a list that updates when the user submits.
|
||||
|
||||
```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,
|
||||
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] = []
|
||||
|
||||
|
||||
|
|
@ -83,13 +78,23 @@ def notes_app() -> PrefabApp:
|
|||
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.
|
||||
The model sees one tool: `notes_app`. Calling it opens the UI. When the user submits the form, `CallTool("add_note")` fires, the server saves the note, returns the updated list, and `SetState("notes", RESULT)` writes that list back into state. `ForEach("notes")` re-renders. The model never sees `add_note` — it's UI-only.
|
||||
|
||||
Let's break down the key concepts.
|
||||
## Why not just `@mcp.tool(app=True)`?
|
||||
|
||||
## Entry Points: @app.ui()
|
||||
A fair question. Any [Interactive Tool](/apps/prefab) can call a server tool — there's nothing stopping you from putting `CallTool("add_note")` inside a regular `@mcp.tool(app=True)`. It works for one or two tools. Things get harder once the app grows:
|
||||
|
||||
Entry points are what the model sees and calls to open your app. They return a Prefab UI, just like display tools:
|
||||
- Which tools should the model see, and which are UI-only?
|
||||
- What happens to `CallTool("add_note")` when you mount this server under a namespace and the tool becomes `notes_add_note`?
|
||||
- How do you keep it all wired correctly as you compose servers?
|
||||
|
||||
`FastMCPApp` owns these concerns. Entry points register as model-visible. Backend tools register as UI-only by default. Backend tools get globally stable identifiers that survive namespacing, and `CallTool` accepts function references, so references stay valid when you compose servers.
|
||||
|
||||
The rest of this page covers each piece in turn.
|
||||
|
||||
## `@app.ui()` — entry points
|
||||
|
||||
Entry points are what the model sees. They return a `PrefabApp` and default to `visibility=["model"]`, showing up in the LLM tool list but not callable from within the UI.
|
||||
|
||||
```python
|
||||
@app.ui()
|
||||
|
|
@ -97,21 +102,15 @@ 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`.
|
||||
`@app.ui()` supports 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:
|
||||
...
|
||||
```
|
||||
## `@app.tool()` — backend tools
|
||||
|
||||
## Backend Tools: @app.tool()
|
||||
|
||||
Backend tools do the work. The UI calls them via `CallTool`; they run on the server and return data:
|
||||
Backend tools do the work. By default they're visible only to the UI (`visibility=["app"]`), not the model.
|
||||
|
||||
```python
|
||||
@app.tool()
|
||||
|
|
@ -121,7 +120,7 @@ def save_contact(name: str, email: str) -> list[dict]:
|
|||
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`:
|
||||
If you want a tool callable by both the model and the UI, pass `model=True`:
|
||||
|
||||
```python
|
||||
@app.tool(model=True)
|
||||
|
|
@ -130,37 +129,32 @@ def list_contacts() -> list[dict]:
|
|||
return list(db)
|
||||
```
|
||||
|
||||
Backend tools support `name`, `description`, `auth`, and `timeout`:
|
||||
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]:
|
||||
...
|
||||
```
|
||||
## `CallTool` — UI → backend
|
||||
|
||||
## 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()`:
|
||||
`CallTool` is how the UI invokes a backend tool. Pass the tool's name (or a direct function reference):
|
||||
|
||||
```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
|
||||
# Or a function reference — resolves to a stable global key
|
||||
CallTool(save_contact, arguments={...})
|
||||
```
|
||||
|
||||
Arguments can reference state with `Rx`:
|
||||
|
||||
```python
|
||||
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.
|
||||
### Handling results
|
||||
|
||||
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:
|
||||
Server calls are async. Use `on_success` and `on_error` callbacks:
|
||||
|
||||
```python
|
||||
from prefab_ui.actions import SetState, ShowToast
|
||||
|
|
@ -176,78 +170,54 @@ CallTool(
|
|||
)
|
||||
```
|
||||
|
||||
`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`.
|
||||
`RESULT` is a reactive reference to the tool's return value, available inside `on_success`. `ERROR` (from `prefab_ui.rx`) is the counterpart inside `on_error`. Callbacks can be a single action or a list; they execute in order and short-circuit 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
|
||||
|
||||
### 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)`:
|
||||
When a tool's return value should replace a state key, use `result_key`:
|
||||
|
||||
```python
|
||||
CallTool("list_contacts", result_key="contacts")
|
||||
|
||||
# equivalent to:
|
||||
CallTool(
|
||||
"list_contacts",
|
||||
on_success=SetState("contacts", RESULT),
|
||||
)
|
||||
# same as:
|
||||
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`.
|
||||
`CallTool` is one of several actions. Actions attach to handlers like `on_click`, `on_submit`, and `on_change`.
|
||||
|
||||
### Client Actions
|
||||
|
||||
These run instantly in the browser — no server round-trip:
|
||||
Client-side actions 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:
|
||||
Pass a list to chain actions:
|
||||
|
||||
```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"),
|
||||
ShowToast("Cleared"),
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
### Loading States
|
||||
### Loading states
|
||||
|
||||
A common pattern: show a loading indicator while a server call is in flight.
|
||||
A common pattern: disable a button and show a spinner while a 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
|
||||
from prefab_ui.rx import Rx
|
||||
|
||||
saving = Rx("saving")
|
||||
|
||||
|
|
@ -271,21 +241,17 @@ Button(
|
|||
],
|
||||
)
|
||||
|
||||
# Pass state={"saving": False} to PrefabApp when returning
|
||||
# PrefabApp(view=view, state={"saving": False, ...})
|
||||
```
|
||||
|
||||
## 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.
|
||||
Forms collect input and submit it to a tool. When submitted, named input values become the tool's arguments.
|
||||
|
||||
### Manual Forms
|
||||
|
||||
Build forms with individual input components:
|
||||
### Manual forms
|
||||
|
||||
```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(
|
||||
|
|
@ -298,26 +264,19 @@ with Form(
|
|||
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`.
|
||||
On submit, `CallTool` receives `{"title": ..., "priority": ..., "description": ...}`.
|
||||
|
||||
### Pydantic Model Forms
|
||||
### Forms from Pydantic models
|
||||
|
||||
For structured data, `Form.from_model()` generates the entire form from a Pydantic model — inputs, labels, and submit wiring:
|
||||
For structured input, `Form.from_model()` generates the whole form — inputs, labels, validation:
|
||||
|
||||
```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")
|
||||
|
|
@ -329,7 +288,6 @@ class BugReport(BaseModel):
|
|||
|
||||
@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(
|
||||
|
|
@ -337,7 +295,6 @@ def report_bug() -> PrefabApp:
|
|||
on_submit=CallTool(
|
||||
"create_bug",
|
||||
on_success=ShowToast("Bug filed!", variant="success"),
|
||||
on_error=ShowToast("Failed to submit", variant="error"),
|
||||
),
|
||||
)
|
||||
return PrefabApp(view=view)
|
||||
|
|
@ -345,69 +302,47 @@ def report_bug() -> PrefabApp:
|
|||
|
||||
@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.
|
||||
`str` becomes a text input, `Literal` becomes a select, `bool` becomes a checkbox. Field titles and defaults are respected.
|
||||
|
||||
## Composition and Namespacing
|
||||
## 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.
|
||||
The reason `FastMCPApp` exists — and why you'd pick it over plain `@mcp.tool(app=True)` with string-based `CallTool` — 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.
|
||||
`CallTool("save_contact")` would now be broken. But `CallTool(save_contact)` with a function reference resolves to a globally stable identifier that bypasses the namespace. Your app works the same whether standalone or mounted.
|
||||
|
||||
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
|
||||
### Mounting
|
||||
|
||||
`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
|
||||
# or
|
||||
mcp = FastMCP("Platform")
|
||||
mcp.add_provider(app)
|
||||
```
|
||||
|
||||
Multiple apps can coexist on the same server:
|
||||
Multiple apps can coexist; each gets its own global keys, so there's no collision even if two apps have a tool named `save`.
|
||||
|
||||
```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
|
||||
|
||||
### Running Standalone
|
||||
|
||||
For development, `FastMCPApp` has a convenience `run()` method that wraps itself in a temporary `FastMCP` server:
|
||||
For development, `FastMCPApp` has a `run()` shortcut that wraps itself in a temporary `FastMCP` server:
|
||||
|
||||
```python
|
||||
app = FastMCPApp("Contacts")
|
||||
|
|
@ -417,9 +352,9 @@ if __name__ == "__main__":
|
|||
app.run()
|
||||
```
|
||||
|
||||
## Complete Example: Contact Manager
|
||||
## A full example: contact manager
|
||||
|
||||
This pulls together everything — entry points, backend tools, callable references, forms (both manual and Pydantic), state management, and actions:
|
||||
This brings everything together — entry point, backend tools, Pydantic form, manual form, state, actions, and multi-visibility.
|
||||
|
||||
```python expandable
|
||||
from __future__ import annotations
|
||||
|
|
@ -437,8 +372,6 @@ 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"},
|
||||
|
|
@ -451,8 +384,6 @@ class ContactModel(BaseModel):
|
|||
category: Literal["Customer", "Vendor", "Partner", "Other"] = "Other"
|
||||
|
||||
|
||||
# App
|
||||
|
||||
app = FastMCPApp("Contacts")
|
||||
|
||||
|
||||
|
|
@ -528,11 +459,11 @@ if __name__ == "__main__":
|
|||
mcp.run()
|
||||
```
|
||||
|
||||
This example is also available as a runnable server at `examples/apps/contacts/contacts_server.py`.
|
||||
Also available as a runnable server at `examples/apps/contacts/contacts_server.py`.
|
||||
|
||||
## Next Steps
|
||||
## 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
|
||||
- **[Interactive Tools](/apps/prefab)** — the building blocks: charts, tables, dashboards, reactive state
|
||||
- **[Examples](/apps/examples)** — complete working servers
|
||||
- **[Development](/apps/development)** — preview and test app tools locally
|
||||
- **[Prefab UI docs](https://prefab.prefect.io)** — full component reference
|
||||
|
|
@ -10,7 +10,9 @@ 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.
|
||||
<video src="/apps/images/generative-ui.mp4" autoPlay loop muted playsInline style={{width:"100%", borderRadius:"8px", marginBottom:"1rem"}} />
|
||||
|
||||
With Generative UI, the LLM writes the UI code at runtime. Instead of calling a pre-built tool with a fixed shape, the model writes Prefab Python tailored to the current data and request. The user watches the UI stream in as the model generates it.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
|
@ -20,15 +22,15 @@ mcp = FastMCP("Prefab Studio")
|
|||
mcp.add_provider(GenerativeUI())
|
||||
```
|
||||
|
||||
That's it. The `GenerativeUI` provider registers everything:
|
||||
One provider registers three things:
|
||||
|
||||
- **`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
|
||||
- **`search_prefab_components`** — a tool the LLM uses to discover what components are available
|
||||
- **The streaming renderer** — a `ui://` resource with browser-side Pyodide that progressively renders partial code as the LLM generates it
|
||||
|
||||
## How It Works
|
||||
## 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.
|
||||
When the LLM calls `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 by the time partial arguments start flowing.
|
||||
|
||||
As the LLM generates each token:
|
||||
|
||||
|
|
@ -37,11 +39,11 @@ As the LLM generates each token:
|
|||
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.
|
||||
When the LLM finishes, the server runs the complete code in a server-side Pyodide sandbox for validation, and the renderer swaps the streaming preview for the final server-validated result.
|
||||
|
||||
## What the LLM Writes
|
||||
## What the LLM writes
|
||||
|
||||
The tool description includes code examples that teach the LLM the Prefab patterns. A typical generation looks like:
|
||||
The tool description includes examples that teach the model the Prefab patterns. A typical generation looks like:
|
||||
|
||||
```python
|
||||
from prefab_ui.components import Column, Row, Heading, Text, Badge, Card, CardContent
|
||||
|
|
@ -73,9 +75,9 @@ with PrefabApp() as app:
|
|||
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 model writes real Python — loops, f-strings, computation, helper functions. Prefab gives it charts, tables, forms, cards, badges, and layout primitives to compose.
|
||||
|
||||
## The Component Search Tool
|
||||
## The component search tool
|
||||
|
||||
Before writing code, the LLM can call `search_prefab_components` to discover what's available:
|
||||
|
||||
|
|
@ -87,11 +89,11 @@ search_prefab_components("Chart")
|
|||
...
|
||||
```
|
||||
|
||||
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 `detail=True` returns full field descriptions and docstrings. The search tool introspects Prefab classes at runtime, so it's always up to date with the installed version.
|
||||
|
||||
## Passing Data
|
||||
## Passing data
|
||||
|
||||
The `generate_prefab_ui` tool accepts a `data` parameter. Values passed here become global variables in the sandbox:
|
||||
The `generate_prefab_ui` tool accepts a `data` parameter. Values become global variables in the sandbox:
|
||||
|
||||
```python
|
||||
# The LLM can reference 'sales_data' directly in its code
|
||||
|
|
@ -101,11 +103,11 @@ result = await generate_prefab_ui(
|
|||
)
|
||||
```
|
||||
|
||||
This lets the model use real data from earlier in the conversation to build visualizations.
|
||||
This lets the model use data from earlier in the conversation to build visualizations.
|
||||
|
||||
## Configuration
|
||||
|
||||
`GenerativeUI` accepts options for customizing tool names:
|
||||
`GenerativeUI` takes options for customizing tool names:
|
||||
|
||||
```python
|
||||
GenerativeUI(
|
||||
|
|
@ -117,17 +119,16 @@ GenerativeUI(
|
|||
|
||||
## 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.
|
||||
Generative UI needs `fastmcp[apps]`, which pulls in `prefab-ui`. The server-side Pyodide sandbox (for final 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.
|
||||
The streaming renderer loads Pyodide from CDN in the browser. The CSP is configured automatically by the provider — no manual setup.
|
||||
|
||||
## Sandbox Limitations
|
||||
## 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`.
|
||||
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. If the LLM imports something unavailable, the sandbox raises `ImportError`.
|
||||
|
||||
## Next Steps
|
||||
## 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`
|
||||
- **[Interactive Tools](/apps/prefab)** — the component building blocks the LLM will use
|
||||
- **[Prefab component reference](https://prefab.prefect.io/docs/components)** — full component library
|
||||
- **[Development](/apps/development)** — preview generative tools locally with `fastmcp dev apps`
|
||||
|
|
|
|||
BIN
docs/apps/images/generative-ui.mp4
Normal file
BIN
docs/apps/images/generative-ui.mp4
Normal file
Binary file not shown.
|
|
@ -3,18 +3,17 @@ title: Custom HTML Apps
|
|||
sidebarTitle: Custom HTML
|
||||
description: Build apps with your own HTML, CSS, and JavaScript using the MCP Apps extension directly.
|
||||
icon: code
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="3.0.0" />
|
||||
|
||||
The [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) is an open protocol that lets tools return interactive UIs — an HTML page rendered in a sandboxed iframe inside the host client. [Prefab UI](/apps/prefab) builds on this protocol so you never have to think about it, but when you need full control — custom rendering, a specific JavaScript framework, maps, 3D, video — you can use the MCP Apps extension directly.
|
||||
Everything on this page is for when you want full control: your own HTML, your own JavaScript framework, a map library, a 3D viewer, custom video playback. [Interactive Tools](/apps/prefab) wrap the MCP Apps extension so you never have to think about it — this page is what you reach for when you need to think about it.
|
||||
|
||||
This page covers how to write custom HTML apps and wire them up in FastMCP. You'll be working with the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) JavaScript SDK for host communication, and FastMCP's `AppConfig` for resource and CSP management.
|
||||
You'll be working with two things: the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) JavaScript SDK for host communication, and FastMCP's `AppConfig` for resources and CSP.
|
||||
|
||||
## How It Works
|
||||
## How it works
|
||||
|
||||
An MCP App has two parts:
|
||||
|
||||
|
|
@ -66,7 +65,7 @@ def my_tool() -> str:
|
|||
return "result"
|
||||
```
|
||||
|
||||
### Tool Visibility
|
||||
### Tool visibility
|
||||
|
||||
The `visibility` field controls where a tool appears:
|
||||
|
||||
|
|
@ -88,7 +87,7 @@ def refresh_data() -> str:
|
|||
return fetch_latest()
|
||||
```
|
||||
|
||||
### AppConfig Fields
|
||||
### AppConfig fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
|
|
@ -103,9 +102,9 @@ def refresh_data() -> str:
|
|||
On **resources**, `resource_uri` and `visibility` must not be set — the resource *is* the UI. Use `AppConfig` on resources only for `csp`, `permissions`, and other display settings.
|
||||
</Note>
|
||||
|
||||
## UI Resources
|
||||
## UI resources
|
||||
|
||||
Resources using the `ui://` scheme are automatically served with the MIME type `text/html;profile=mcp-app`. You don't need to set this manually.
|
||||
Resources using the `ui://` scheme are automatically served with the MIME type `text/html;profile=mcp-app`. No need to set it manually.
|
||||
|
||||
```python
|
||||
@mcp.resource("ui://my-app/view.html")
|
||||
|
|
@ -115,7 +114,7 @@ def my_view() -> str:
|
|||
|
||||
The HTML can be anything — a full single-page app, a simple display, or a complex interactive tool. The host renders it in a sandboxed iframe and establishes a `postMessage` channel for communication.
|
||||
|
||||
### Writing the App HTML
|
||||
### Writing the app HTML
|
||||
|
||||
Your HTML app communicates with the host using the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) JavaScript SDK. The simplest approach is to load it from a CDN:
|
||||
|
||||
|
|
@ -204,7 +203,7 @@ def my_view() -> str:
|
|||
|
||||
Hosts may or may not grant these permissions. Your app should use JavaScript feature detection as a fallback.
|
||||
|
||||
## Example: QR Code Server
|
||||
## Example: a QR code server
|
||||
|
||||
This example creates a tool that generates QR codes and an app that renders them as images. It's based on the [official MCP Apps example](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/qr-server). Requires the `qrcode[pil]` package.
|
||||
|
||||
|
|
@ -286,7 +285,7 @@ def view() -> str:
|
|||
|
||||
The tool generates a QR code as a base64 PNG. The resource loads the MCP Apps JS SDK from unpkg (declared in the CSP), listens for tool results, and renders the image. The host wires them together — when the LLM calls `generate_qr`, the QR code appears in an interactive frame inside the conversation.
|
||||
|
||||
## Checking Client Support
|
||||
## Checking client support
|
||||
|
||||
Not all hosts support the Apps extension. You can check at runtime using the tool's [context](/servers/context):
|
||||
|
||||
|
|
|
|||
|
|
@ -3,179 +3,70 @@ title: Apps
|
|||
sidebarTitle: Overview
|
||||
description: Give your tools interactive UIs rendered directly in the conversation.
|
||||
icon: grid-2
|
||||
tag: NEW
|
||||
mode: center
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
import PrefabPinWarning from '/snippets/prefab-pin-warning.mdx'
|
||||
|
||||
<VersionBadge version="3.0.0" />
|
||||
|
||||
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.
|
||||
A FastMCP app is a tool that returns an interactive UI instead of text. When the host calls it, the user sees a chart, a table, a form, or a whole dashboard rendered right inside the conversation, with working sort, search, tooltips, and state.
|
||||
|
||||
<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>
|
||||
<div style={{
|
||||
margin: '0 clamp(-180px, calc(-18vw + 90px), 0px) 2rem',
|
||||
maxHeight: '700px',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
maskImage: 'linear-gradient(to bottom, black 75%, transparent)',
|
||||
WebkitMaskImage: 'linear-gradient(to bottom, black 75%, transparent)',
|
||||
}}>
|
||||
<iframe src="/apps/demos/hitchhikers.html" style={{width:"100%", height:"2000px", border:"none", borderRadius:"8px", background:"transparent"}} frameBorder="0" scrolling="no" allowtransparency="true"></iframe>
|
||||
</div>
|
||||
|
||||
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.
|
||||
The dashboard above is a [Prefab](https://prefab.prefect.io) showcase — a taste of what you can deliver from a FastMCP tool. Every card, chart, slider, dialog, and carousel is a Python component. Build a composition like this, add `@mcp.tool(app=True)`, and the host renders it inside the conversation.
|
||||
|
||||
<Note>
|
||||
The examples throughout the Apps docs require the `apps` extra:
|
||||
Under the hood, FastMCP builds on the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) and uses Prefab to describe UIs in Python.
|
||||
|
||||
```bash
|
||||
pip install "fastmcp[apps]"
|
||||
```
|
||||
|
||||
This installs [Prefab UI](https://prefab.prefect.io), the component library used to build app UIs.
|
||||
</Note>
|
||||
<PrefabPinWarning />
|
||||
|
||||
<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>
|
||||
## Pick your path
|
||||
|
||||
## Which Approach?
|
||||
Four patterns cover almost everything you'd want to build. Most apps start with Interactive Tools; you only reach for the others when you've hit a specific limit.
|
||||
|
||||
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.
|
||||
### [Interactive Tools](/apps/prefab) — start here
|
||||
|
||||
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" />
|
||||
|
||||
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:
|
||||
Add `app=True` to a tool and return a Prefab component. Charts, tables, dashboards, and client-side interactivity (toggles, tabs, filtering) all work without any server round-trips.
|
||||
|
||||
```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
|
||||
|
||||
mcp = FastMCP("Dashboard")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def revenue_chart(year: int) -> PrefabApp:
|
||||
"""Show annual revenue as an interactive bar chart."""
|
||||
data = [
|
||||
{"quarter": "Q1", "revenue": 42000},
|
||||
{"quarter": "Q2", "revenue": 51000},
|
||||
{"quarter": "Q3", "revenue": 47000},
|
||||
{"quarter": "Q4", "revenue": 63000},
|
||||
]
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading(f"{year} Revenue")
|
||||
BarChart(
|
||||
data=data,
|
||||
series=[ChartSeries(data_key="revenue", label="Revenue")],
|
||||
x_axis="quarter",
|
||||
)
|
||||
|
||||
return PrefabApp(view=view)
|
||||
def team_directory() -> DataTable:
|
||||
return DataTable(columns=[...], rows=employees, search=True)
|
||||
```
|
||||
|
||||
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.
|
||||
### [FastMCPApp](/apps/fastmcp-app) — when the UI calls back to the server
|
||||
|
||||
See [Prefab Apps](/apps/prefab) for the full guide.
|
||||
Forms that save data, buttons that trigger backend work, search that hits a database. `FastMCPApp` manages the wiring between UI actions and backend tools, with stable tool identifiers that survive server composition.
|
||||
|
||||
### FastMCPApp
|
||||
### [Generative UI](/apps/generative) — when the LLM writes the UI
|
||||
|
||||
<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:
|
||||
Register one provider and the model can write Prefab code tailored to the current data and request. The user watches the UI build up as the model generates it.
|
||||
|
||||
```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.apps.generative import GenerativeUI
|
||||
|
||||
mcp = FastMCP("Prefab Studio")
|
||||
mcp.add_provider(GenerativeUI())
|
||||
```
|
||||
|
||||
See [Generative UI](/apps/generative) for the full guide, or the [provider reference](/apps/providers/generative) for configuration options.
|
||||
### [Custom HTML](/apps/low-level) — when you need full control
|
||||
|
||||
### Custom HTML
|
||||
Write your own HTML, CSS, and JavaScript. Use a specific framework, drop in a map or 3D viewer, embed video. You're talking to the MCP Apps protocol directly.
|
||||
|
||||
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.
|
||||
## What's next
|
||||
|
||||
## 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
|
||||
```
|
||||
- **[Quickstart](/apps/quickstart)** — build a working app in a minute
|
||||
- **[Examples](/apps/examples)** — complete working servers you can run today
|
||||
- **[Providers](/apps/providers/approval)** — ready-made capabilities (approvals, choice pickers, file upload, forms) you add with one line
|
||||
- **[Development](/apps/development)** — preview app tools locally with `fastmcp dev apps`
|
||||
|
|
|
|||
|
|
@ -1,431 +0,0 @@
|
|||
---
|
||||
title: Patterns
|
||||
sidebarTitle: Patterns
|
||||
description: Copy-paste examples for common tool UIs.
|
||||
icon: grid-2-plus
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="3.1.0" />
|
||||
|
||||
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.
|
||||
|
||||
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 render client-side with tooltips, legends, and responsive sizing.
|
||||
|
||||
### Bar Chart
|
||||
|
||||
```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
|
||||
|
||||
mcp = FastMCP("Charts")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def quarterly_revenue(year: int) -> PrefabApp:
|
||||
"""Show quarterly revenue as a bar chart."""
|
||||
data = [
|
||||
{"quarter": "Q1", "revenue": 42000, "costs": 28000},
|
||||
{"quarter": "Q2", "revenue": 51000, "costs": 31000},
|
||||
{"quarter": "Q3", "revenue": 47000, "costs": 29000},
|
||||
{"quarter": "Q4", "revenue": 63000, "costs": 35000},
|
||||
]
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading(f"{year} Revenue vs Costs")
|
||||
BarChart(
|
||||
data=data,
|
||||
series=[
|
||||
ChartSeries(data_key="revenue", label="Revenue"),
|
||||
ChartSeries(data_key="costs", label="Costs"),
|
||||
],
|
||||
x_axis="quarter",
|
||||
show_legend=True,
|
||||
)
|
||||
|
||||
return PrefabApp(view=view)
|
||||
```
|
||||
|
||||
Multiple `ChartSeries` entries plot different data keys. Add `stacked=True` to stack bars, or `horizontal=True` to flip the axes.
|
||||
|
||||
### Area Chart
|
||||
|
||||
`LineChart` and `AreaChart` share the same API as `BarChart`, with `curve` for interpolation and `show_dots` for data points:
|
||||
|
||||
```python
|
||||
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")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def usage_trend() -> PrefabApp:
|
||||
"""Show API usage over time."""
|
||||
data = [
|
||||
{"date": "Feb 1", "requests": 1200},
|
||||
{"date": "Feb 2", "requests": 1350},
|
||||
{"date": "Feb 3", "requests": 980},
|
||||
{"date": "Feb 4", "requests": 1500},
|
||||
{"date": "Feb 5", "requests": 1420},
|
||||
]
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading("API Usage")
|
||||
AreaChart(
|
||||
data=data,
|
||||
series=[ChartSeries(data_key="requests", label="Requests")],
|
||||
x_axis="date",
|
||||
curve="smooth",
|
||||
height=250,
|
||||
)
|
||||
|
||||
return PrefabApp(view=view)
|
||||
```
|
||||
|
||||
### Pie and Donut Charts
|
||||
|
||||
`PieChart` uses `data_key` (the numeric value) and `name_key` (the label). Set `inner_radius` for a donut:
|
||||
|
||||
```python
|
||||
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")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def ticket_breakdown() -> PrefabApp:
|
||||
"""Show open tickets by category."""
|
||||
data = [
|
||||
{"category": "Bug", "count": 23},
|
||||
{"category": "Feature", "count": 15},
|
||||
{"category": "Docs", "count": 8},
|
||||
{"category": "Infra", "count": 12},
|
||||
]
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading("Open Tickets")
|
||||
PieChart(
|
||||
data=data,
|
||||
data_key="count",
|
||||
name_key="category",
|
||||
show_legend=True,
|
||||
inner_radius=60,
|
||||
)
|
||||
|
||||
return PrefabApp(view=view)
|
||||
```
|
||||
|
||||
## Data Tables
|
||||
|
||||
[DataTable](https://prefab.prefect.io/docs/components/data-display/data-table) provides sortable columns, full-text search, and pagination — all client-side:
|
||||
|
||||
```python
|
||||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import Column, Heading, DataTable, DataTableColumn
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Directory")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def employee_directory() -> PrefabApp:
|
||||
"""Show a searchable, sortable employee directory."""
|
||||
employees = [
|
||||
{"name": "Alice Chen", "department": "Engineering", "role": "Staff Engineer", "location": "SF"},
|
||||
{"name": "Bob Martinez", "department": "Design", "role": "Lead Designer", "location": "NYC"},
|
||||
{"name": "Carol Johnson", "department": "Engineering", "role": "Senior Engineer", "location": "London"},
|
||||
{"name": "David Kim", "department": "Product", "role": "Product Manager", "location": "SF"},
|
||||
{"name": "Eva Müller", "department": "Engineering", "role": "Engineer", "location": "Berlin"},
|
||||
]
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading("Employee Directory")
|
||||
DataTable(
|
||||
columns=[
|
||||
DataTableColumn(key="name", header="Name", sortable=True),
|
||||
DataTableColumn(key="department", header="Department", sortable=True),
|
||||
DataTableColumn(key="role", header="Role"),
|
||||
DataTableColumn(key="location", header="Office", sortable=True),
|
||||
],
|
||||
rows=employees,
|
||||
search=True,
|
||||
paginated=True,
|
||||
page_size=15,
|
||||
)
|
||||
|
||||
return PrefabApp(view=view)
|
||||
```
|
||||
|
||||
## Status Displays
|
||||
|
||||
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 fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Monitoring")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def system_status() -> PrefabApp:
|
||||
"""Show current system health."""
|
||||
services = [
|
||||
{"name": "API Gateway", "status": "healthy", "ok": True, "latency_ms": 12, "uptime_pct": 99.9},
|
||||
{"name": "Database", "status": "healthy", "ok": True, "latency_ms": 3, "uptime_pct": 99.99},
|
||||
{"name": "Cache", "status": "degraded", "ok": False, "latency_ms": 45, "uptime_pct": 98.2},
|
||||
{"name": "Queue", "status": "healthy", "ok": True, "latency_ms": 8, "uptime_pct": 99.8},
|
||||
]
|
||||
all_ok = all(s["ok"] for s in services)
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
with Row(gap=2, align="center"):
|
||||
Heading("System Status")
|
||||
Badge(
|
||||
"All Healthy" if all_ok else "Degraded",
|
||||
variant="success" if all_ok else "destructive",
|
||||
)
|
||||
Separator()
|
||||
with Grid(columns=2, gap=4):
|
||||
for svc in services:
|
||||
with Card():
|
||||
with CardContent():
|
||||
with Row(gap=2, align="center"):
|
||||
Text(svc["name"], css_class="font-medium")
|
||||
Badge(
|
||||
svc["status"],
|
||||
variant="success" if svc["ok"] else "destructive",
|
||||
)
|
||||
Muted(f"Response: {svc['latency_ms']}ms")
|
||||
Progress(value=svc["uptime_pct"])
|
||||
|
||||
return PrefabApp(view=view)
|
||||
```
|
||||
|
||||
## Reactive Displays
|
||||
|
||||
These patterns use state and `Rx()` for client-side interactivity — no server calls needed.
|
||||
|
||||
### Feature Toggles
|
||||
|
||||
```python
|
||||
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")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def feature_flags() -> PrefabApp:
|
||||
"""Toggle feature flags with live preview."""
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading("Feature Flags")
|
||||
Switch(name="dark_mode", label="Dark Mode")
|
||||
Switch(name="beta", label="Beta Features")
|
||||
Separator()
|
||||
with If(Rx("dark_mode")):
|
||||
Alert(title="Dark mode enabled", description="UI will use dark theme.")
|
||||
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": False})
|
||||
```
|
||||
|
||||
### 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 fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Projects")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def project_overview() -> PrefabApp:
|
||||
"""Show project details organized in tabs."""
|
||||
project = {
|
||||
"name": "FastMCP v3",
|
||||
"description": "Next generation MCP framework with Apps support.",
|
||||
"status": "Active",
|
||||
"members": [
|
||||
{"name": "Alice Chen", "role": "Lead"},
|
||||
{"name": "Bob Martinez", "role": "Design"},
|
||||
],
|
||||
"activity": [
|
||||
{"timestamp": "2 hours ago", "message": "Merged PR #342"},
|
||||
{"timestamp": "1 day ago", "message": "Released v3.0.1"},
|
||||
],
|
||||
}
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading(project["name"])
|
||||
with Tabs():
|
||||
with Tab("Overview"):
|
||||
Text(project["description"])
|
||||
with Row(gap=4):
|
||||
Badge(project["status"])
|
||||
|
||||
with Tab("Members"):
|
||||
DataTable(
|
||||
columns=[
|
||||
DataTableColumn(key="name", header="Name", sortable=True),
|
||||
DataTableColumn(key="role", header="Role"),
|
||||
],
|
||||
rows=project["members"],
|
||||
)
|
||||
|
||||
with Tab("Activity"):
|
||||
with ForEach("activity") as item:
|
||||
with Row(gap=2):
|
||||
Muted(item.timestamp)
|
||||
Text(item.message)
|
||||
|
||||
return PrefabApp(view=view, state={"activity": project["activity"]})
|
||||
```
|
||||
|
||||
### Accordion
|
||||
|
||||
```python
|
||||
from prefab_ui.app import PrefabApp
|
||||
from prefab_ui.components import (
|
||||
Column, Heading, Row, Text, Badge, Progress,
|
||||
Accordion, AccordionItem,
|
||||
)
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("API Monitor")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def api_health() -> PrefabApp:
|
||||
"""Show health details for each API endpoint."""
|
||||
endpoints = [
|
||||
{"path": "/api/users", "status": 200, "healthy": True, "avg_ms": 45, "p99_ms": 120, "uptime_pct": 99.9},
|
||||
{"path": "/api/orders", "status": 200, "healthy": True, "avg_ms": 82, "p99_ms": 250, "uptime_pct": 99.7},
|
||||
{"path": "/api/search", "status": 200, "healthy": True, "avg_ms": 150, "p99_ms": 500, "uptime_pct": 99.5},
|
||||
{"path": "/api/webhooks", "status": 503, "healthy": False, "avg_ms": 2000, "p99_ms": 5000, "uptime_pct": 95.1},
|
||||
]
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading("API Health")
|
||||
with Accordion(multiple=True):
|
||||
for ep in endpoints:
|
||||
with AccordionItem(ep["path"]):
|
||||
with Row(gap=4):
|
||||
Badge(
|
||||
f"{ep['status']}",
|
||||
variant="success" if ep["healthy"] else "destructive",
|
||||
)
|
||||
Text(f"Avg: {ep['avg_ms']}ms")
|
||||
Text(f"P99: {ep['p99_ms']}ms")
|
||||
Progress(value=ep["uptime_pct"])
|
||||
|
||||
return PrefabApp(view=view)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
- **[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
|
||||
|
|
@ -1,321 +1,254 @@
|
|||
---
|
||||
title: Prefab UI
|
||||
sidebarTitle: Prefab UI
|
||||
description: The component library behind FastMCP apps — charts, tables, dashboards, forms, and reactive displays.
|
||||
title: Interactive Tools
|
||||
sidebarTitle: Interactive Tools
|
||||
description: Turn your tools into interactive UIs with charts, tables, and dashboards.
|
||||
icon: palette
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
import PrefabPinWarning from '/snippets/prefab-pin-warning.mdx'
|
||||
|
||||
<VersionBadge version="3.1.0" />
|
||||
|
||||
<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>
|
||||
<PrefabPinWarning />
|
||||
|
||||
[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.
|
||||
<iframe src="/apps/demos/dashboard.html" style={{width:"100%", height:"680px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
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.
|
||||
Believe it or not, that dashboard is a FastMCP tool. The chart has tooltips. The table is sortable. The badges are styled by deal stage. The whole thing is about 40 lines of Python, and the user sees it right inside their conversation instead of a wall of JSON.
|
||||
|
||||
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).
|
||||
The pattern behind every example on this page is the same: add `app=True` to your tool, build a UI with [Prefab](https://prefab.prefect.io) components, and return it as a `PrefabApp`. Prefab has [100+ components](https://prefab.prefect.io/docs/components), from data tables and charts to forms and progress bars. You compose them in Python; the host renders them as a live, interactive application.
|
||||
|
||||
## Getting Started
|
||||
## Start with a table
|
||||
|
||||
Here's a tool that returns a bar chart:
|
||||
Most tools return data the user wants to explore. A `DataTable` is often the smallest useful upgrade — your data goes from a JSON blob to a searchable, sortable table:
|
||||
|
||||
<iframe src="/apps/demos/data-table.html" style={{width:"100%", height:"530px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
```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
|
||||
|
||||
mcp = FastMCP("Dashboard")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def revenue_chart(year: int) -> PrefabApp:
|
||||
"""Show annual revenue as an interactive bar chart."""
|
||||
data = [
|
||||
{"quarter": "Q1", "revenue": 42000},
|
||||
{"quarter": "Q2", "revenue": 51000},
|
||||
{"quarter": "Q3", "revenue": 47000},
|
||||
{"quarter": "Q4", "revenue": 63000},
|
||||
]
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading(f"{year} Revenue")
|
||||
BarChart(
|
||||
data=data,
|
||||
series=[ChartSeries(data_key="revenue", label="Revenue")],
|
||||
x_axis="quarter",
|
||||
)
|
||||
|
||||
return PrefabApp(view=view)
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Layout and Components
|
||||
|
||||
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, Row, Grid, Heading, Text, Muted, Badge,
|
||||
Card, CardContent, Separator,
|
||||
)
|
||||
|
||||
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")
|
||||
```
|
||||
|
||||
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.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("Flags")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def feature_flags() -> PrefabApp:
|
||||
"""Toggle feature flags with live preview."""
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading("Feature Flags")
|
||||
Switch(name="dark_mode", label="Dark Mode")
|
||||
Switch(name="beta", label="Beta Features")
|
||||
|
||||
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})
|
||||
```
|
||||
|
||||
Three things to notice here:
|
||||
|
||||
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.
|
||||
|
||||
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(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.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 prefab_ui.components import DataTable, DataTableColumn
|
||||
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"},
|
||||
def team_directory() -> DataTable:
|
||||
"""Browse the team directory."""
|
||||
employees = [
|
||||
{"name": "Alice Chen", "role": "Staff Engineer", "dept": "Platform"},
|
||||
{"name": "Bob Martinez", "role": "Lead Designer", "dept": "Design"},
|
||||
{"name": "Carol Johnson", "role": "Senior Engineer", "dept": "Platform"},
|
||||
{"name": "David Kim", "role": "Product Manager", "dept": "Product"},
|
||||
{"name": "Eva Mueller", "role": "Engineer", "dept": "Platform"},
|
||||
{"name": "Frank Lee", "role": "Data Scientist", "dept": "ML"},
|
||||
{"name": "Grace Park", "role": "Eng Manager", "dept": "Platform"},
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
mcp = FastMCP("Sales")
|
||||
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def sales_overview(year: int) -> ToolResult:
|
||||
"""Show sales data visually and summarize for the model."""
|
||||
data = get_sales_data(year)
|
||||
total = sum(row["revenue"] for row in data)
|
||||
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
Heading("Sales Overview")
|
||||
BarChart(data=data, series=[ChartSeries(data_key="revenue")])
|
||||
|
||||
return ToolResult(
|
||||
content=f"Total revenue for {year}: ${total:,} across {len(data)} quarters",
|
||||
structured_content=view,
|
||||
return DataTable(
|
||||
columns=[
|
||||
DataTableColumn(key="name", header="Name", sortable=True),
|
||||
DataTableColumn(key="role", header="Role", sortable=True),
|
||||
DataTableColumn(key="dept", header="Dept", sortable=True),
|
||||
],
|
||||
rows=employees,
|
||||
search=True,
|
||||
)
|
||||
```
|
||||
|
||||
The user sees the chart. The LLM sees the summary string.
|
||||
That's it. Add `app=True`, return a Prefab component instead of raw dicts. FastMCP handles the rendering, sandboxing, and security. No wrapper class needed for simple cases like this.
|
||||
|
||||
## Advanced
|
||||
## Add charts
|
||||
|
||||
<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:
|
||||
When numbers tell a better story as a visual, swap in a chart. The API is the same: pass your data as a list of dicts, tell the chart which keys to plot.
|
||||
|
||||
<iframe src="/apps/demos/bar-chart.html" style={{width:"100%", height:"430px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
```python
|
||||
@mcp.tool(app=True)
|
||||
def quarterly_revenue(year: int) -> BarChart:
|
||||
"""Show quarterly revenue as a bar chart."""
|
||||
data = [
|
||||
{"quarter": "Q1", "revenue": 42000, "costs": 28000},
|
||||
{"quarter": "Q2", "revenue": 51000, "costs": 31000},
|
||||
{"quarter": "Q3", "revenue": 47000, "costs": 29000},
|
||||
{"quarter": "Q4", "revenue": 63000, "costs": 35000},
|
||||
]
|
||||
|
||||
return BarChart(
|
||||
data=data,
|
||||
series=[
|
||||
ChartSeries(data_key="revenue", label="Revenue"),
|
||||
ChartSeries(data_key="costs", label="Costs"),
|
||||
],
|
||||
x_axis="quarter",
|
||||
show_legend=True,
|
||||
)
|
||||
```
|
||||
|
||||
Each `ChartSeries` plots a different key from the data. `BarChart`, `LineChart`, `AreaChart`, `PieChart`, `RadarChart`, and `RadialChart` all follow the same pattern. Hover over the bars to see tooltips.
|
||||
|
||||
<iframe src="/apps/demos/pie-chart.html" style={{width:"100%", height:"410px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
```python
|
||||
@mcp.tool(app=True)
|
||||
def ticket_breakdown() -> PieChart:
|
||||
"""Show open tickets by category."""
|
||||
data = [
|
||||
{"category": "Bug", "count": 42},
|
||||
{"category": "Feature", "count": 28},
|
||||
{"category": "Docs", "count": 15},
|
||||
{"category": "Infra", "count": 10},
|
||||
]
|
||||
|
||||
return PieChart(
|
||||
data=data,
|
||||
data_key="count",
|
||||
name_key="category",
|
||||
inner_radius=50,
|
||||
show_legend=True,
|
||||
)
|
||||
```
|
||||
|
||||
See the [Prefab chart docs](https://prefab.prefect.io/docs/components) for stacking, curves, custom colors, and more.
|
||||
|
||||
## Compose a dashboard
|
||||
|
||||
Tables and charts are useful on their own, but the real power comes from composing them. `Column` stacks children vertically, `Row` lays them out side by side, and `with` blocks establish nesting — the indentation is the layout.
|
||||
|
||||
<iframe src="/apps/demos/dashboard.html" style={{width:"100%", height:"680px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
```python expandable
|
||||
@mcp.tool(app=True)
|
||||
def sales_dashboard() -> PrefabApp:
|
||||
"""Show sales KPIs, trends, and deals."""
|
||||
monthly = [
|
||||
{"month": "Jan", "revenue": 48200, "costs": 31000},
|
||||
{"month": "Feb", "revenue": 52100, "costs": 32500},
|
||||
{"month": "Mar", "revenue": 61800, "costs": 34200},
|
||||
{"month": "Apr", "revenue": 58400, "costs": 33800},
|
||||
]
|
||||
deals = [
|
||||
{"account": "Acme Corp", "value": "$84,000", "stage": "Won"},
|
||||
{"account": "Globex Inc", "value": "$52,000", "stage": "Negotiation"},
|
||||
{"account": "Initech", "value": "$31,500", "stage": "Proposal"},
|
||||
{"account": "Wayne Enterprises", "value": "$45,000", "stage": "Lost"},
|
||||
]
|
||||
|
||||
rows = [
|
||||
{
|
||||
"account": d["account"],
|
||||
"value": d["value"],
|
||||
"stage": Badge(
|
||||
d["stage"],
|
||||
variant="success" if d["stage"] == "Won"
|
||||
else "destructive" if d["stage"] == "Lost"
|
||||
else "secondary",
|
||||
),
|
||||
}
|
||||
for d in deals
|
||||
]
|
||||
|
||||
total = sum(m["revenue"] for m in monthly)
|
||||
|
||||
with PrefabApp() as app:
|
||||
with Column(gap=4, css_class="p-6"):
|
||||
with Row(gap=6):
|
||||
Metric(label="Revenue (Q1-Q4)", value=f"${total:,}")
|
||||
Metric(label="Deals", value=f"{len(deals)}")
|
||||
BarChart(
|
||||
data=monthly,
|
||||
series=[
|
||||
ChartSeries(data_key="revenue", label="Revenue"),
|
||||
ChartSeries(data_key="costs", label="Costs"),
|
||||
],
|
||||
x_axis="month",
|
||||
show_legend=True,
|
||||
)
|
||||
Separator()
|
||||
DataTable(
|
||||
columns=[
|
||||
DataTableColumn(key="account", header="Account", sortable=True),
|
||||
DataTableColumn(key="value", header="Value", sortable=True),
|
||||
DataTableColumn(key="stage", header="Stage"),
|
||||
],
|
||||
rows=rows,
|
||||
)
|
||||
|
||||
return app
|
||||
```
|
||||
|
||||
Notice how `Badge` components can be placed inside table cells — any Prefab component works as a cell value, so you can put progress bars, icons, or buttons in your tables too.
|
||||
|
||||
## Make it reactive
|
||||
|
||||
Everything above renders once from the data your Python provides. But interactive tools can also respond to user input in real time, without any server round-trips. Prefab's state system lets components read and write client-side values, so the UI updates instantly as the user interacts with it.
|
||||
|
||||
<iframe src="/apps/demos/reactive.html" style={{width:"100%", height:"500px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
Try switching regions in the dropdown, and toggling the switch on and off.
|
||||
|
||||
```python expandable
|
||||
from prefab_ui.rx import Rx
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def regional_sales() -> PrefabApp:
|
||||
"""Sales by region with a live filter."""
|
||||
north = [
|
||||
{"month": "Jan", "sales": 22000},
|
||||
{"month": "Feb", "sales": 25500},
|
||||
{"month": "Mar", "sales": 24200},
|
||||
]
|
||||
south = [
|
||||
{"month": "Jan", "sales": 5800},
|
||||
{"month": "Feb", "sales": 6400},
|
||||
{"month": "Mar", "sales": 5600},
|
||||
]
|
||||
west = [
|
||||
{"month": "Jan", "sales": 6000},
|
||||
{"month": "Feb", "sales": 6000},
|
||||
{"month": "Mar", "sales": 5600},
|
||||
]
|
||||
|
||||
with PrefabApp(
|
||||
state={
|
||||
"region": "north",
|
||||
"north": north, "south": south, "west": west,
|
||||
"show_target": True,
|
||||
},
|
||||
) as app:
|
||||
with Column(
|
||||
gap=4,
|
||||
css_class="p-6",
|
||||
let={"data": "{{ region == 'south' ? south"
|
||||
" : region == 'west' ? west"
|
||||
" : north }}"},
|
||||
):
|
||||
with Row(gap=4, align="center"):
|
||||
with Select(name="region", css_class="w-40"):
|
||||
SelectOption(value="north", label="North")
|
||||
SelectOption(value="south", label="South")
|
||||
SelectOption(value="west", label="West")
|
||||
Switch(name="show_target", css_class="ml-auto")
|
||||
Text("Show target", css_class="text-sm text-muted-foreground")
|
||||
BarChart(
|
||||
data=Rx("data"),
|
||||
series=[ChartSeries(data_key="sales", label="Sales")],
|
||||
x_axis="month",
|
||||
)
|
||||
with If(Rx("show_target")):
|
||||
Metric(label="Q1 Target", value="$75,000")
|
||||
|
||||
return app
|
||||
```
|
||||
|
||||
The `state` dict on `PrefabApp` declares initial values. The `Select` writes to the `region` key on every change. A `let` binding picks the matching dataset, and the chart re-renders. The `Switch` toggles a `Metric` on and off through `If(Rx("show_target"))`. All of this happens in the browser — no calls back to your server.
|
||||
|
||||
`Rx` is a reactive reference: `Rx("region")` compiles to an expression the renderer evaluates live. It supports arithmetic, comparisons, formatting pipes (`.currency()`, `.percent()`), and ternary conditionals (`.then()`). For the full state system, see the [Prefab state docs](https://prefab.prefect.io/docs/concepts/state) and [expression docs](https://prefab.prefect.io/docs/concepts/expressions).
|
||||
|
||||
## Content Security Policy
|
||||
|
||||
Interactive tools render in a sandboxed iframe with a strict CSP. If your tool loads external resources — embedding iframes, fetching from APIs, loading scripts — add the required domains:
|
||||
|
||||
```python
|
||||
from fastmcp.apps import PrefabAppConfig, ResourceCSP
|
||||
|
|
@ -327,40 +260,37 @@ 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>
|
||||
`PrefabAppConfig()` with no arguments is equivalent to `app=True`.
|
||||
|
||||
<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`:
|
||||
## Giving the LLM context
|
||||
|
||||
By default, the LLM sees `"[Rendered Prefab UI]"` as the tool result. If the model needs to reason about the data, return a `ToolResult` with a text summary alongside the UI:
|
||||
|
||||
```python
|
||||
@mcp.tool
|
||||
def greet(name: str) -> PrefabApp:
|
||||
return PrefabApp(view=Heading(f"Hello, {name}!"))
|
||||
```
|
||||
|
||||
Explicit `app=True` is recommended for clarity.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Mixing with custom HTML">
|
||||
Prefab tools and [custom HTML tools](/apps/low-level) coexist on the same server:
|
||||
|
||||
```python
|
||||
from fastmcp.apps import AppConfig
|
||||
from fastmcp.tools import ToolResult
|
||||
|
||||
@mcp.tool(app=True)
|
||||
def team_directory() -> PrefabApp:
|
||||
...
|
||||
def sales_overview(year: int) -> ToolResult:
|
||||
"""Show sales visually, summarize for the model."""
|
||||
data = get_sales_data(year)
|
||||
total = sum(row["revenue"] for row in data)
|
||||
|
||||
@mcp.tool(app=AppConfig(resource_uri="ui://my-app/map.html"))
|
||||
def map_view() -> str:
|
||||
...
|
||||
with Column(gap=4, css_class="p-6") as view:
|
||||
BarChart(data=data, series=[ChartSeries(data_key="revenue")])
|
||||
|
||||
return ToolResult(
|
||||
content=f"Total revenue for {year}: ${total:,} across {len(data)} quarters",
|
||||
structured_content=view,
|
||||
)
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
## Next Steps
|
||||
The user sees the chart. The model sees the summary.
|
||||
|
||||
- **[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
|
||||
## Next steps
|
||||
|
||||
- **[FastMCPApp](/apps/fastmcp-app)** — when your UI needs to call backend tools (forms, search, CRUD)
|
||||
- **[Generative UI](/apps/generative)** — let the LLM design the UI at runtime
|
||||
- **[Custom HTML](/apps/low-level)** — when Prefab isn't enough (maps, 3D, your own framework)
|
||||
- **[Examples](/apps/examples)** — complete working servers you can run today
|
||||
- **[Development](/apps/development)** — preview your tools locally with `fastmcp dev apps`
|
||||
- **[Prefab UI](https://prefab.prefect.io)** — full component reference with 100+ components, theming, and advanced patterns
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ request_approval(
|
|||
)
|
||||
```
|
||||
|
||||
## How It Works
|
||||
## How it works
|
||||
|
||||
When the user clicks a button, two things happen:
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ choose(
|
|||
)
|
||||
```
|
||||
|
||||
## How It Works
|
||||
## How it works
|
||||
|
||||
Each option renders as a full-width button in a vertical stack. When the user clicks one:
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ FileUpload(
|
|||
|
||||
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
|
||||
## 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.
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ class SharedUpload(FileUpload):
|
|||
return "__shared__"
|
||||
```
|
||||
|
||||
## Custom Storage
|
||||
## 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ This registers two tools:
|
|||
|
||||
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
|
||||
## Field mapping
|
||||
|
||||
`FormInput` uses Prefab's `Form.from_model()`, which maps Pydantic types to form components:
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ FormInput(
|
|||
|
||||
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
|
||||
## Multiple forms
|
||||
|
||||
Add multiple providers for different models — each gets its own tool:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
---
|
||||
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.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Quickstart
|
||||
sidebarTitle: Quickstart
|
||||
description: Build your first MCP app in under a minute.
|
||||
description: Build your first FastMCP app in under a minute.
|
||||
icon: rocket
|
||||
tag: NEW
|
||||
---
|
||||
|
|
@ -10,33 +10,29 @@ 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.
|
||||
By the end of this page, you'll have a working tool that returns this:
|
||||
|
||||
This tutorial builds a working app from scratch. Here's what you'll have in about a minute:
|
||||
<iframe src="/apps/demos/team-directory.html" style={{width:"100%", height:"545px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
<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>
|
||||
A pie chart the user can hover, a table they can sort and search — and a single Python tool.
|
||||
|
||||
## Setup
|
||||
|
||||
Install FastMCP with the `apps` extra, which pulls in Prefab UI:
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pip install "fastmcp[apps]"
|
||||
```
|
||||
|
||||
## A Tool That Returns a UI
|
||||
The `apps` extra pulls in [Prefab](https://prefab.prefect.io), the Python component library used to build app UIs.
|
||||
|
||||
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.
|
||||
## Write the tool
|
||||
|
||||
Create `server.py`:
|
||||
Create `server.py`. The interesting parts: `app=True` tells FastMCP this tool renders a UI, and `with PrefabApp() as app:` is the canonical pattern for composing one.
|
||||
|
||||
```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 import Column, DataTable, DataTableColumn, Grid
|
||||
from prefab_ui.components.charts import PieChart
|
||||
from fastmcp import FastMCP
|
||||
|
||||
|
|
@ -63,7 +59,6 @@ def team_directory() -> PrefabApp:
|
|||
|
||||
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,
|
||||
|
|
@ -84,40 +79,42 @@ def team_directory() -> PrefabApp:
|
|||
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 reads top-to-bottom. `PrefabApp()` is the root; everything inside its `with` block becomes the UI. `Column` stacks children vertically, `Grid` lays them out in columns. `DataTable` takes rows and column definitions and gives you sort and search for free.
|
||||
|
||||
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.
|
||||
`app=True` does the rest: it sets up the renderer resource, the content security policy, and the metadata that tells the host "this tool returns a UI." The host loads the result in a sandboxed iframe where the user can interact with it — all client-side, no round-trips.
|
||||
|
||||
## Running It
|
||||
## Preview it
|
||||
|
||||
FastMCP includes a dev server that renders your app tools in a browser, no MCP host needed:
|
||||
FastMCP ships 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:
|
||||
Open `http://localhost:8080`, pick `team_directory`, and try sorting columns and searching.
|
||||
|
||||
<Frame>
|
||||
<img src="/apps/images/app-quickstart-dev-2.png" alt="The team directory with a detail card showing after clicking Bob Martinez" />
|
||||
<img src="/apps/images/app-quickstart-dev-2.png" alt="The team directory rendered in the fastmcp dev apps preview, showing a pie chart, searchable table, and a detail card after clicking a row" />
|
||||
</Frame>
|
||||
|
||||
## Make it reactive
|
||||
|
||||
The UI above renders once from your Python. Prefab apps can also respond to user input live, without any server round-trips. The key concept is **state**: a client-side key-value store that components read from and write to.
|
||||
|
||||
Click a row in the demo below to see a detail card appear:
|
||||
|
||||
<iframe src="/apps/demos/team-directory-reactive.html" style={{width:"100%", height:"675px", border:"none", overflow:"hidden", borderRadius:"8px"}} frameBorder="0" scrolling="no"></iframe>
|
||||
|
||||
Add a few imports, give each member a couple more fields, wire up a click handler, and render a detail card when something's selected:
|
||||
|
||||
```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,
|
||||
Badge, Card, CardContent, CardHeader, Column, DataTable, DataTableColumn,
|
||||
Grid, H3, Row, Small, Text,
|
||||
)
|
||||
from prefab_ui.components.charts import PieChart
|
||||
from prefab_ui.components.control_flow import If
|
||||
|
|
@ -129,16 +126,12 @@ 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},
|
||||
# ... more members ...
|
||||
]
|
||||
|
||||
OFFICE_COUNTS = [
|
||||
{"office": office, "count": count}
|
||||
for office, count in Counter(m["office"] for m in MEMBERS).items()
|
||||
{"office": o, "count": c}
|
||||
for o, c in Counter(m["office"] for m in MEMBERS).items()
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -147,7 +140,6 @@ 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,
|
||||
|
|
@ -187,22 +179,18 @@ def team_directory() -> PrefabApp:
|
|||
return app
|
||||
```
|
||||
|
||||
Three new ideas here:
|
||||
Three new ideas do all the work:
|
||||
|
||||
**`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).
|
||||
- **`on_row_click=SetState("selected", Rx("$event"))`** — clicking a row writes its data into the `selected` state key. `$event` is the clicked row dict.
|
||||
- **`Rx("selected.name")`** — a reactive reference. It doesn't hold a Python value; it compiles to a browser-side expression that re-evaluates whenever `selected` changes, so `Text(Rx("selected.name"))` always shows the latest clicked name.
|
||||
- **`If(STATE.selected)`** — conditionally renders its body. Before any click, `selected` is `None` and the card stays hidden.
|
||||
|
||||
**`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.
|
||||
The `state={"selected": None}` dict on `PrefabApp` sets the initial value. Everything else happens in the browser — no round-trips to your server when the user clicks.
|
||||
|
||||
**`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.
|
||||
## Where to go next
|
||||
|
||||
The `state` dict on `PrefabApp` sets initial values when the app loads. Run `fastmcp dev apps server.py` again and try clicking a row.
|
||||
You've built a tool that returns an interactive, reactive UI. This pattern covers a huge range of use cases: build a visualization, return it, and the user gets it rendered right in the conversation.
|
||||
|
||||
## 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.
|
||||
- **[Interactive Tools](/apps/prefab)** — charts, tables, dashboards, reactive state, with live demos
|
||||
- **[FastMCPApp](/apps/fastmcp-app)** — when the UI needs to call back to your server (forms, search, CRUD)
|
||||
- **[Examples](/apps/examples)** — complete working servers you can run today
|
||||
|
|
|
|||
|
|
@ -1947,7 +1947,7 @@ FastMCP 2.8.0 introduces powerful new ways to customize and control your MCP ser
|
|||
|
||||
### Tool Transformation
|
||||
|
||||
The highlight of this release is first-class [**Tool Transformation**](/patterns/tool-transformation), a new feature that lets you create enhanced variations of existing tools. You can now easily rename arguments, hide parameters, modify descriptions, and even wrap tools with custom validation or post-processing logic—all without rewriting the original code. This makes it easier than ever to adapt generic tools for specific LLM use cases or to simplify complex APIs. Huge thanks to [@strawgate](https://github.com/strawgate) for partnering on this, starting with [#591](https://github.com/PrefectHQ/fastmcp/discussions/591) and [#599](https://github.com/PrefectHQ/fastmcp/pull/599) and continuing offline.
|
||||
The highlight of this release is first-class [**Tool Transformation**](/servers/transforms/tool-transformation), a new feature that lets you create enhanced variations of existing tools. You can now easily rename arguments, hide parameters, modify descriptions, and even wrap tools with custom validation or post-processing logic—all without rewriting the original code. This makes it easier than ever to adapt generic tools for specific LLM use cases or to simplify complex APIs. Huge thanks to [@strawgate](https://github.com/strawgate) for partnering on this, starting with [#591](https://github.com/PrefectHQ/fastmcp/discussions/591) and [#599](https://github.com/PrefectHQ/fastmcp/pull/599) and continuing offline.
|
||||
|
||||
### Component Control
|
||||
This release also gives you more granular control over which components are exposed to clients. With new [**tag-based filtering**](/servers/server#tag-based-filtering), you can selectively enable or disable tools, resources, and prompts based on tags, perfect for managing different environments or user permissions. Complementing this, every component now supports being [programmatically enabled or disabled](/servers/tools#disabling-tools), offering dynamic control over your server's capabilities.
|
||||
|
|
|
|||
89
docs/clients/client-only-package.mdx
Normal file
89
docs/clients/client-only-package.mdx
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
title: Client-Only Package
|
||||
description: Use FastMCP's client without installing the full server framework.
|
||||
icon: box
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="3.3.0" />
|
||||
|
||||
FastMCP's full `fastmcp` package includes everything needed to build and run MCP servers, apps, proxies, and clients. If you are only embedding an MCP client in another framework, building your own LLM host, or testing MCP servers, you can install the smaller client-only package instead.
|
||||
|
||||
```bash
|
||||
pip install "fastmcp-slim[client]"
|
||||
```
|
||||
|
||||
The client-only package uses the `fastmcp` import namespace:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
client = Client("https://example.com/mcp")
|
||||
```
|
||||
|
||||
Use `fastmcp-slim[client]` when your code connects to MCP servers but does not define or run FastMCP servers itself. For example, framework authors can depend on `fastmcp-slim[client]` to provide MCP connectivity without requiring users to install the full FastMCP server stack.
|
||||
|
||||
## Supported Usage
|
||||
|
||||
Client-only installs support remote and subprocess transports:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
# Remote MCP server
|
||||
http_client = Client("https://example.com/mcp")
|
||||
|
||||
# Local MCP server over stdio
|
||||
stdio_client = Client("my_server.py")
|
||||
```
|
||||
|
||||
Single-server MCP configuration works as well:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"url": "https://weather.example.com/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client = Client(config)
|
||||
```
|
||||
|
||||
Optional sampling handlers are available through the same extras as the full package:
|
||||
|
||||
```bash
|
||||
pip install "fastmcp-slim[client,openai]"
|
||||
pip install "fastmcp-slim[client,anthropic]"
|
||||
pip install "fastmcp-slim[client,gemini]"
|
||||
```
|
||||
|
||||
## When to Use the Full Package
|
||||
|
||||
Install `fastmcp` when you need server-side FastMCP features:
|
||||
|
||||
```bash
|
||||
pip install fastmcp
|
||||
```
|
||||
|
||||
The full package remains the default for most users and continues to support the existing import style:
|
||||
|
||||
```python
|
||||
from fastmcp import Client, FastMCP
|
||||
|
||||
server = FastMCP("Example")
|
||||
client = Client(server)
|
||||
```
|
||||
|
||||
Use the full package for:
|
||||
|
||||
- defining or running FastMCP servers
|
||||
- in-memory clients connected directly to `FastMCP` server objects
|
||||
- multi-server MCP configurations
|
||||
- FastMCP apps, proxies, server auth, middleware, and other server-side features
|
||||
|
||||
The `fastmcp-slim` package is intentionally narrower: it is for client-only consumers who want FastMCP's MCP client behavior without depending on the full framework.
|
||||
|
|
@ -155,11 +155,11 @@ Install the Anthropic handler with `pip install fastmcp[anthropic]`.
|
|||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.sampling.handlers.google_genai import GoogleGenAISamplingHandler
|
||||
from fastmcp.client.sampling.handlers.google_genai import GoogleGenaiSamplingHandler
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
sampling_handler=GoogleGenAISamplingHandler(default_model="gemini-2.0-flash"),
|
||||
sampling_handler=GoogleGenaiSamplingHandler(default_model="gemini-2.0-flash"),
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -186,5 +186,5 @@ client = Client(
|
|||
Tool execution happens on the server side. The client's role is to pass tools to the LLM and return the LLM's response (which may include tool use requests). The server then executes the tools and may send follow-up sampling requests with tool results.
|
||||
|
||||
<Tip>
|
||||
To implement a custom sampling handler, see the [handler source code](https://github.com/PrefectHQ/fastmcp/tree/main/src/fastmcp/client/sampling/handlers) as a reference.
|
||||
To implement a custom sampling handler, see the [handler source code](https://github.com/PrefectHQ/fastmcp/tree/main/fastmcp_slim/fastmcp/client/sampling/handlers) as a reference.
|
||||
</Tip>
|
||||
|
|
|
|||
|
|
@ -660,17 +660,17 @@ FASTMCP_STATELESS_HTTP=true uvicorn app:app --host 0.0.0.0 --port 8000 --workers
|
|||
|
||||
Production deployments should never hardcode sensitive information like API keys or authentication tokens. Instead, use environment variables to configure your server at runtime. This keeps your code secure and makes it easy to deploy the same code to different environments with different configurations.
|
||||
|
||||
Here's an example using bearer token authentication (though OAuth is recommended for production):
|
||||
Here's an example using static token authentication for development (OAuth is recommended for production):
|
||||
|
||||
```python
|
||||
import os
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth import BearerTokenAuth
|
||||
from fastmcp.server.auth import StaticTokenVerifier
|
||||
|
||||
# Read configuration from environment
|
||||
auth_token = os.environ.get("MCP_AUTH_TOKEN")
|
||||
if auth_token:
|
||||
auth = BearerTokenAuth(token=auth_token)
|
||||
auth = StaticTokenVerifier(tokens={auth_token: {"sub": "admin", "client_id": "cli"}})
|
||||
mcp = FastMCP("Production Server", auth=auth)
|
||||
else:
|
||||
mcp = FastMCP("Production Server")
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ There are just three steps to deploying a server to Horizon:
|
|||
|
||||
### Step 1: Select a Repository
|
||||
|
||||
Visit [horizon.prefect.io](https://horizon.prefect.io) and sign in with your GitHub account. Connect your GitHub account to grant Horizon access to your repositories, then select the repo you want to deploy.
|
||||
Visit [horizon.prefect.io](https://horizon.prefect.io?utm_source=gofastmcp&utm_medium=docs) and sign in with your GitHub account. Connect your GitHub account to grant Horizon access to your repositories, then select the repo you want to deploy.
|
||||
|
||||
<img src="/assets/images/horizon/select-repo.png" alt="Horizon repository selection" />
|
||||
|
||||
|
|
|
|||
262
docs/deployment/sandboxed-agents.mdx
Normal file
262
docs/deployment/sandboxed-agents.mdx
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
---
|
||||
title: Sandboxed Agents
|
||||
sidebarTitle: Sandboxed Agents
|
||||
description: Expose MCP tools to isolated agents without giving the sandbox long-lived credentials.
|
||||
icon: box-open
|
||||
---
|
||||
|
||||
This guide is for deployments where an agent runs inside an isolated container, subprocess, or remote worker and still needs MCP access. In that setup, the sandbox itself becomes part of your trust boundary.
|
||||
|
||||
The core recommendation is simple: use FastMCP as the capability boundary. Run a remote FastMCP server, authenticate the sandbox with short-lived scoped credentials, and keep privileged credentials on the server side.
|
||||
|
||||
## When to Use This Pattern
|
||||
|
||||
This pattern is useful when:
|
||||
|
||||
- your agent runs in an ephemeral container or subprocess
|
||||
- you do not want long-lived credentials inside that sandbox
|
||||
- you need per-run, per-tenant, or per-job scoping
|
||||
- the sandbox must call internal APIs, databases, or upstream MCP servers indirectly
|
||||
|
||||
If you are building a local desktop integration, STDIO and normal local configuration may be enough. This guide is for cases where the sandbox is isolated enough that secret distribution, credential lifetimes, and privilege boundaries become part of the design.
|
||||
|
||||
## What Changes in a Sandboxed Deployment
|
||||
|
||||
A desktop MCP client usually runs on a developer's machine and launches local servers with configuration the developer controls. A sandboxed agent is different:
|
||||
|
||||
- It often runs in an ephemeral container or subprocess.
|
||||
- Its filesystem may be inspected after the fact.
|
||||
- Its environment variables may be broader than you intend.
|
||||
- You may launch many sandboxes concurrently for different users, tenants, or jobs.
|
||||
|
||||
That means convenience patterns that are acceptable locally become risky in sandboxes. Passing a GitHub token, database password, or cloud credentials directly into the sandbox creates a secret distribution problem you do not need to have.
|
||||
|
||||
The safer approach is to make your FastMCP server the only component with privileged access and let the sandbox call it over MCP.
|
||||
|
||||
## Recommended Architecture
|
||||
|
||||
Use this shape by default:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Sandboxed agent"] -->|"short-lived token"| B["FastMCP server"]
|
||||
B --> C["internal APIs"]
|
||||
B --> D["databases"]
|
||||
B --> E["other MCP servers"]
|
||||
```
|
||||
|
||||
The sandbox gets:
|
||||
|
||||
- the MCP server URL
|
||||
- a short-lived token scoped to its job, tenant, or run
|
||||
- no long-lived upstream credentials
|
||||
|
||||
The FastMCP server does the privileged work:
|
||||
|
||||
- verifies the sandbox token
|
||||
- authorizes the request from token claims, scopes, or other server-side policy
|
||||
- exposes only the tools that sandbox should see
|
||||
- talks to internal APIs, databases, or upstream MCP servers on the sandbox's behalf
|
||||
|
||||
The key design rule is simple:
|
||||
|
||||
<Tip>
|
||||
Give the sandbox capabilities, not credentials.
|
||||
</Tip>
|
||||
|
||||
With that boundary in place, the next questions are how the sandbox connects, how the server verifies and authorizes it, and how you design the tools the sandbox is allowed to call.
|
||||
|
||||
## Prefer HTTP for Sandboxed Agents
|
||||
|
||||
For sandboxes, prefer a remote HTTP server over a local STDIO server.
|
||||
|
||||
STDIO is still excellent for local development, but a remote HTTP server is usually the better production boundary for sandboxed agents because:
|
||||
|
||||
- authentication is explicit
|
||||
- the server lifecycle is independent from the sandbox lifecycle
|
||||
- secrets stay on the server
|
||||
- one deployment can safely serve many sandboxes
|
||||
- auditing and revocation happen in one place
|
||||
|
||||
This means the sandbox should connect as a client:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.auth import BearerAuth
|
||||
|
||||
client = Client(
|
||||
"https://sandbox-tools.example.com/mcp",
|
||||
auth=BearerAuth("short-lived-sandbox-token"),
|
||||
)
|
||||
```
|
||||
|
||||
And your FastMCP server should run remotely:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Sandbox Tools")
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="http", host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
For production transport setup, see [HTTP Deployment](/deployment/http).
|
||||
|
||||
## Use Short-Lived, Scoped Credentials
|
||||
|
||||
For sandboxed agents, it is usually cleaner to issue credentials for the sandbox session than to place long-lived upstream credentials directly inside the container.
|
||||
|
||||
In practice, that usually means issuing a short-lived bearer token for each sandbox, run, or tenant and validating it on your FastMCP server with a token verifier.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.jwt import JWTVerifier
|
||||
|
||||
auth = JWTVerifier(
|
||||
jwks_uri="https://auth.example.com/.well-known/jwks.json",
|
||||
issuer="https://auth.example.com",
|
||||
audience="sandbox-mcp",
|
||||
)
|
||||
|
||||
mcp = FastMCP("Sandbox Tools", auth=auth)
|
||||
```
|
||||
|
||||
The token should identify the sandbox's scope. Depending on your system, it may represent a job, a tenant, a run, or a user-authorized session. Useful claims often include:
|
||||
|
||||
- sandbox or run id
|
||||
- tenant or installation id
|
||||
- user or actor id when applicable
|
||||
- expiration
|
||||
- optional capability scopes
|
||||
|
||||
Avoid shared static tokens across many sandboxes. If one sandbox token leaks, you want the blast radius to be small and the lifetime to be short.
|
||||
|
||||
Token verification is only one half of the boundary. Authorization still belongs on the FastMCP server: use scopes, claims, middleware, or custom auth checks to decide which tools and resources that sandbox can actually access.
|
||||
|
||||
For example, you can verify the token globally and still require a narrower scope on a specific tool:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth import require_scopes
|
||||
from fastmcp.server.auth.providers.jwt import JWTVerifier
|
||||
|
||||
auth = JWTVerifier(
|
||||
jwks_uri="https://auth.example.com/.well-known/jwks.json",
|
||||
issuer="https://auth.example.com",
|
||||
audience="sandbox-mcp",
|
||||
)
|
||||
|
||||
mcp = FastMCP("Sandbox Tools", auth=auth)
|
||||
|
||||
@mcp.tool(auth=require_scopes("write:summary"))
|
||||
def write_summary(content: str) -> str:
|
||||
return f"Stored summary with {len(content)} characters"
|
||||
```
|
||||
|
||||
For validation patterns, see [Token Verification](/servers/auth/token-verification). For policy enforcement, see [Authorization](/servers/authorization).
|
||||
|
||||
## Expose Capabilities, Not Raw Access
|
||||
|
||||
The sandbox should not need:
|
||||
|
||||
- GitHub app private keys
|
||||
- database passwords
|
||||
- upstream OAuth client secrets
|
||||
- cloud provider credentials
|
||||
|
||||
Instead, expose MCP tools that perform privileged work on the server side.
|
||||
|
||||
Good sandbox-facing tools tend to look like this:
|
||||
|
||||
- `get_recent_updates`
|
||||
- `write_summary`
|
||||
- `fetch_repo_context`
|
||||
- `publish_review_comment`
|
||||
|
||||
These tools describe the capability the sandbox needs, not the low-level credentialed action required to perform it.
|
||||
|
||||
That distinction matters. A tool like `write_summary` lets the server decide where and how to persist the summary. A tool like `run_sql` or `call_internal_api` pushes privilege and policy into the sandbox where they are much harder to control.
|
||||
|
||||
Sandboxed agents behave best when those tools are narrow and structured:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Sandbox Tools")
|
||||
|
||||
@mcp.tool
|
||||
def write_summary(content: str) -> str:
|
||||
"""Store the final summary for the current run."""
|
||||
return f"Stored summary with {len(content)} characters"
|
||||
|
||||
@mcp.tool
|
||||
def publish_review_comment(pr_number: int, body: str) -> str:
|
||||
"""Queue a review comment for a specific pull request."""
|
||||
return f"Queued comment for PR #{pr_number}"
|
||||
```
|
||||
|
||||
These are easier to audit, easier to authorize, and easier for agents to use reliably than a broad catch-all tool like `mutate_state(kind: str, payload: dict)`.
|
||||
|
||||
Narrow tools also let you express different policies per tool instead of creating one large privileged escape hatch.
|
||||
|
||||
## Use a Proxy When Upstream Systems Are More Privileged
|
||||
|
||||
If the sandbox needs access to other MCP servers or internal systems, put FastMCP in front of them instead of forwarding secrets into the sandbox.
|
||||
|
||||
This is where proxying becomes useful. Your public-facing FastMCP server can authenticate the sandbox, then forward allowed capabilities to upstream systems with stronger credentials.
|
||||
|
||||
Typical examples:
|
||||
|
||||
- a sandbox-safe MCP gateway in front of internal MCP servers
|
||||
- a FastMCP layer in front of internal HTTP APIs
|
||||
- a job-scoped server that fronts a Git provider, issue tracker, or storage system
|
||||
|
||||
If the upstream system is itself an MCP server, FastMCP's proxy support is a natural fit. See [MCP Proxy](/servers/providers/proxy).
|
||||
|
||||
## mcp.json for Sandboxed Clients
|
||||
|
||||
If your sandboxed agent is configured through `mcp.json`, keep that configuration minimal. Point it at the remote FastMCP server and pass only the values the sandbox actually needs.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"sandbox-tools": {
|
||||
"url": "https://sandbox-tools.example.com/mcp",
|
||||
"transport": "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In many systems, authentication is injected by the launcher or environment rather than hardcoded in `mcp.json`. That is usually the right tradeoff for sandboxes. Avoid baking long-lived credentials directly into generated config files, and avoid treating `mcp.json` as the place where secret material should live.
|
||||
|
||||
That is all this section needs to do: tell the sandbox where the server lives. Keep auth and secret handling elsewhere.
|
||||
|
||||
For configuration details, see [MCP.json](/integrations/mcp-json-configuration).
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
The same few mistakes show up again and again in sandboxed deployments:
|
||||
|
||||
- passing long-lived API keys directly into the sandbox
|
||||
- treating helper scripts in the sandbox as a security boundary
|
||||
- exposing broad mutation tools instead of narrow capabilities
|
||||
- using one shared token for every sandbox
|
||||
- relying on STDIO inheritance for configuration in production
|
||||
|
||||
Each of these works at first. Each becomes painful once you have multiple tenants, multiple jobs, or an incident that requires revoking access quickly.
|
||||
|
||||
## Production Checklist
|
||||
|
||||
Before shipping a sandbox-facing FastMCP server, check these:
|
||||
|
||||
- The sandbox connects over HTTP, not with privileged local credentials.
|
||||
- Tokens are short-lived and scoped to a run, tenant, or job.
|
||||
- The FastMCP server verifies tokens on every request.
|
||||
- Long-lived secrets stay on the server side.
|
||||
- Tools are narrow, explicit, and structured.
|
||||
- Upstream privileged systems sit behind the FastMCP server or proxy.
|
||||
- Revocation and audit live at the server boundary, not inside the sandbox.
|
||||
|
||||
If you adopt those defaults, sandbox support stops being a special case and becomes a normal deployment pattern: isolated workers talk to a constrained FastMCP surface, and the server handles the privileged parts centrally.
|
||||
|
|
@ -33,7 +33,7 @@ Tests should complete in under 1 second unless marked as integration tests. This
|
|||
|
||||
### Test Organization
|
||||
|
||||
Our test organization mirrors the `src/` directory structure, creating a predictable mapping between code and tests. When you're working on `src/fastmcp/server/auth.py`, you'll find its tests in `tests/server/test_auth.py`. In rare cases tests are split further - for example, the OpenAPI tests are so comprehensive they're split across multiple files.
|
||||
Our test organization mirrors the source package structure, creating a predictable mapping between code and tests. When you're working on `fastmcp_slim/fastmcp/server/auth.py`, you'll find its tests in `tests/server/test_auth.py`. In rare cases tests are split further - for example, the OpenAPI tests are so comprehensive they're split across multiple files.
|
||||
|
||||
### Test Markers
|
||||
|
||||
|
|
@ -393,4 +393,4 @@ just docs
|
|||
mintlify dev
|
||||
```
|
||||
|
||||
The local server watches for changes and automatically refreshes. This preview catches formatting issues and helps you see documentation as users will experience it.
|
||||
The local server watches for changes and automatically refreshes. This preview catches formatting issues and helps you see documentation as users will experience it.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ FastMCP now includes a sampling handler for Google's Gemini models ([#2977](http
|
|||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.sampling.handlers import GoogleGenaiSamplingHandler
|
||||
from fastmcp.client.sampling.handlers.google_genai import GoogleGenaiSamplingHandler
|
||||
from google.genai import Client as GoogleGenaiClient
|
||||
|
||||
# Initialize the handler
|
||||
|
|
@ -386,7 +386,7 @@ Support for [MCP Apps](https://modelcontextprotocol.io/specification/2025-06-18/
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.apps import AppConfig, ResourceCSP, ResourcePermissions
|
||||
from fastmcp.apps import AppConfig, ResourceCSP, ResourcePermissions
|
||||
|
||||
mcp = FastMCP("My Server")
|
||||
|
||||
|
|
@ -421,7 +421,7 @@ The `app=` parameter accepts `True` (enable with defaults), an `AppConfig` insta
|
|||
|
||||
```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://dashboard"))
|
||||
async def dashboard(ctx: Context) -> dict:
|
||||
|
|
@ -444,7 +444,7 @@ async def dashboard(ctx: Context) -> dict:
|
|||
|
||||
**Future phases** will add a component DSL for building UIs declaratively, an in-repo renderer, and a `FastMCPApp` class.
|
||||
|
||||
Implementation: `src/fastmcp/server/apps.py` (models and constants), with integration points in `server.py` (decorator parameters), `low_level.py` (extension advertisement), and `context.py` (`client_supports_extension` method).
|
||||
Implementation: `fastmcp_slim/fastmcp/server/apps.py` (models and constants), with integration points in `server.py` (decorator parameters), `low_level.py` (extension advertisement), and `context.py` (`client_supports_extension` method).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -454,7 +454,7 @@ Implementation: `src/fastmcp/server/apps.py` (models and constants), with integr
|
|||
|
||||
v3.0 introduces a provider-based component system that replaces v2's static-only registration ([#2622](https://github.com/PrefectHQ/fastmcp/pull/2622)). Providers dynamically source tools, resources, templates, and prompts at runtime.
|
||||
|
||||
**Core abstraction** (`src/fastmcp/server/providers/base.py`):
|
||||
**Core abstraction** (`fastmcp_slim/fastmcp/server/providers/base.py`):
|
||||
```python
|
||||
class Provider:
|
||||
async def list_tools(self) -> Sequence[Tool]: ...
|
||||
|
|
@ -474,7 +474,7 @@ Providers support:
|
|||
|
||||
### LocalProvider
|
||||
|
||||
`LocalProvider` (`src/fastmcp/server/providers/local_provider.py`) manages components registered via decorators. Can be used standalone and attached to multiple servers:
|
||||
`LocalProvider` (`fastmcp_slim/fastmcp/server/providers/local_provider.py`) manages components registered via decorators. Can be used standalone and attached to multiple servers:
|
||||
|
||||
```python
|
||||
from fastmcp.server.providers import LocalProvider
|
||||
|
|
@ -492,7 +492,7 @@ server2 = FastMCP("Server2", providers=[provider])
|
|||
|
||||
### ProxyProvider
|
||||
|
||||
`ProxyProvider` (`src/fastmcp/server/providers/proxy.py`) proxies components from remote MCP servers via a client factory. Used by `create_proxy()` and `FastMCP.mount()` for remote server integration.
|
||||
`ProxyProvider` (`fastmcp_slim/fastmcp/server/providers/proxy.py`) proxies components from remote MCP servers via a client factory. Used by `create_proxy()` and `FastMCP.mount()` for remote server integration.
|
||||
|
||||
```python
|
||||
from fastmcp.server import create_proxy
|
||||
|
|
@ -503,7 +503,7 @@ server = create_proxy("http://remote-server/mcp")
|
|||
|
||||
### OpenAPIProvider
|
||||
|
||||
`OpenAPIProvider` (`src/fastmcp/server/providers/openapi/provider.py`) creates MCP components from OpenAPI specifications. Routes map HTTP operations to tools, resources, or templates based on configurable rules.
|
||||
`OpenAPIProvider` (`fastmcp_slim/fastmcp/server/providers/openapi/provider.py`) creates MCP components from OpenAPI specifications. Routes map HTTP operations to tools, resources, or templates based on configurable rules.
|
||||
|
||||
```python
|
||||
from fastmcp.server.providers.openapi import OpenAPIProvider
|
||||
|
|
@ -523,7 +523,7 @@ Features:
|
|||
|
||||
### FastMCPProvider
|
||||
|
||||
`FastMCPProvider` (`src/fastmcp/server/providers/fastmcp_provider.py`) wraps a FastMCP server to enable mounting one server onto another. Components delegate execution through the wrapped server's middleware chain.
|
||||
`FastMCPProvider` (`fastmcp_slim/fastmcp/server/providers/fastmcp_provider.py`) wraps a FastMCP server to enable mounting one server onto another. Components delegate execution through the wrapped server's middleware chain.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
|
@ -548,7 +548,7 @@ main.add_provider(provider)
|
|||
|
||||
Transforms modify components (tools, resources, prompts) as they flow from providers to clients ([#2836](https://github.com/PrefectHQ/fastmcp/pull/2836)). They use a middleware pattern where each transform receives a `call_next` callable to continue the chain.
|
||||
|
||||
**Built-in transforms** (`src/fastmcp/server/transforms/`):
|
||||
**Built-in transforms** (`fastmcp_slim/fastmcp/server/transforms/`):
|
||||
|
||||
- `Namespace` - adds prefixes to names (`tool` → `api_tool`) and path segments to URIs (`data://x` → `data://api/x`)
|
||||
- `ToolTransform` - modifies tool schemas (rename, description, tags, argument transforms)
|
||||
|
|
@ -575,15 +575,14 @@ provider.add_transform(ToolTransform({
|
|||
|
||||
```python
|
||||
from collections.abc import Sequence
|
||||
from fastmcp.server.transforms import Transform, ListToolsNext, GetToolNext
|
||||
from fastmcp.server.transforms import Transform, GetToolNext
|
||||
from fastmcp.tools import Tool
|
||||
|
||||
class TagFilter(Transform):
|
||||
def __init__(self, required_tags: set[str]):
|
||||
self.required_tags = required_tags
|
||||
|
||||
async def list_tools(self, call_next: ListToolsNext) -> Sequence[Tool]:
|
||||
tools = await call_next() # Get tools from downstream
|
||||
async def list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
|
||||
return [t for t in tools if t.tags & self.required_tags]
|
||||
|
||||
async def get_tool(self, name: str, call_next: GetToolNext) -> Tool | None:
|
||||
|
|
@ -874,7 +873,7 @@ v3.0 introduces type-safe result classes that provide explicit control over comp
|
|||
|
||||
#### ToolResult
|
||||
|
||||
`ToolResult` (`src/fastmcp/tools/tool.py:79`) provides structured tool responses:
|
||||
`ToolResult` (`fastmcp_slim/fastmcp/tools/tool.py:79`) provides structured tool responses:
|
||||
|
||||
```python
|
||||
from fastmcp.tools import ToolResult
|
||||
|
|
@ -895,7 +894,7 @@ Fields:
|
|||
|
||||
#### ResourceResult
|
||||
|
||||
`ResourceResult` (`src/fastmcp/resources/resource.py:117`) provides structured resource responses:
|
||||
`ResourceResult` (`fastmcp_slim/fastmcp/resources/resource.py:117`) provides structured resource responses:
|
||||
|
||||
```python
|
||||
from fastmcp.resources import ResourceResult, ResourceContent
|
||||
|
|
@ -915,7 +914,7 @@ Accepts strings, bytes, or `list[ResourceContent]` for flexible content handling
|
|||
|
||||
#### PromptResult
|
||||
|
||||
`PromptResult` (`src/fastmcp/prompts/prompt.py:109`) provides structured prompt responses:
|
||||
`PromptResult` (`fastmcp_slim/fastmcp/prompts/prompt.py:109`) provides structured prompt responses:
|
||||
|
||||
```python
|
||||
from fastmcp.prompts import PromptResult, Message
|
||||
|
|
@ -937,7 +936,7 @@ def conversation() -> PromptResult:
|
|||
|
||||
v3.0 implements MCP SEP-1686 for background task execution via Docket integration.
|
||||
|
||||
**Configuration** (`src/fastmcp/server/tasks/config.py`):
|
||||
**Configuration** (`fastmcp_slim/fastmcp/server/tasks/config.py`):
|
||||
|
||||
```python
|
||||
from fastmcp.server.tasks import TaskConfig
|
||||
|
|
@ -1013,7 +1012,7 @@ fastmcp run server.py --reload --reload-dir ./src --reload-dir ./lib
|
|||
fastmcp run server.py --reload --transport http --port 8080
|
||||
```
|
||||
|
||||
Implementation (`src/fastmcp/cli/run.py`):
|
||||
Implementation (`fastmcp_slim/fastmcp/cli/run.py`):
|
||||
- Uses `watchfiles` for efficient file monitoring
|
||||
- Runs server as subprocess for clean restarts
|
||||
- Stateless mode for seamless reconnection after restart
|
||||
|
|
|
|||
803
docs/docs.json
803
docs/docs.json
|
|
@ -12,7 +12,7 @@
|
|||
"decoration": "gradient"
|
||||
},
|
||||
"banner": {
|
||||
"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"
|
||||
"content": "Deploy FastMCP servers for free on [Prefect Horizon](https://www.prefect.io/horizon)"
|
||||
},
|
||||
"colors": {
|
||||
"dark": "#f72585",
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
"description": "The fast, Pythonic way to build MCP servers and clients.",
|
||||
"errors": {
|
||||
"404": {
|
||||
"description": "You\u2019ve wandered outside the context.",
|
||||
"description": "You’ve wandered outside the context.",
|
||||
"redirect": false,
|
||||
"title": "Don't panic."
|
||||
}
|
||||
|
|
@ -108,30 +108,22 @@
|
|||
},
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "Features",
|
||||
"icon": "stars",
|
||||
"group": "Working with Tools",
|
||||
"icon": "wand-magic-sparkles",
|
||||
"pages": [
|
||||
"servers/tasks",
|
||||
"servers/composition",
|
||||
"servers/dependency-injection",
|
||||
"servers/elicitation",
|
||||
"servers/icons",
|
||||
"servers/lifespan",
|
||||
"servers/logging",
|
||||
"servers/middleware",
|
||||
"servers/pagination",
|
||||
"servers/progress",
|
||||
"servers/sampling",
|
||||
"servers/storage-backends",
|
||||
"servers/telemetry",
|
||||
"servers/testing",
|
||||
"servers/versioning"
|
||||
],
|
||||
"tag": "UPDATED"
|
||||
"servers/transforms/transforms",
|
||||
"servers/transforms/tool-transformation",
|
||||
"servers/transforms/code-mode",
|
||||
"servers/transforms/tool-search",
|
||||
"servers/transforms/namespace",
|
||||
"servers/visibility",
|
||||
"servers/transforms/resources-as-tools",
|
||||
"servers/transforms/prompts-as-tools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "Providers",
|
||||
"group": "MCP Providers",
|
||||
"icon": "layer-group",
|
||||
"pages": [
|
||||
"servers/providers/overview",
|
||||
|
|
@ -139,22 +131,34 @@
|
|||
"servers/providers/filesystem",
|
||||
"servers/providers/proxy",
|
||||
"servers/providers/skills",
|
||||
"servers/composition",
|
||||
"servers/providers/custom"
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "Transforms",
|
||||
"icon": "wand-magic-sparkles",
|
||||
"group": "Interactivity",
|
||||
"icon": "comments",
|
||||
"pages": [
|
||||
"servers/transforms/transforms",
|
||||
"servers/transforms/namespace",
|
||||
"servers/transforms/tool-transformation",
|
||||
"servers/visibility",
|
||||
"servers/transforms/code-mode",
|
||||
"servers/transforms/tool-search",
|
||||
"servers/transforms/resources-as-tools",
|
||||
"servers/transforms/prompts-as-tools"
|
||||
"servers/elicitation",
|
||||
"servers/sampling",
|
||||
"servers/progress",
|
||||
"servers/logging",
|
||||
"servers/pagination",
|
||||
"servers/icons"
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "Extensibility",
|
||||
"icon": "puzzle-piece",
|
||||
"pages": [
|
||||
"servers/middleware",
|
||||
"servers/dependency-injection",
|
||||
"servers/lifespan",
|
||||
"servers/storage-backends",
|
||||
"servers/tasks",
|
||||
"servers/versioning"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -186,8 +190,11 @@
|
|||
"pages": [
|
||||
"deployment/running-server",
|
||||
"deployment/http",
|
||||
"deployment/sandboxed-agents",
|
||||
"deployment/prefect-horizon",
|
||||
"deployment/server-configuration"
|
||||
"deployment/server-configuration",
|
||||
"servers/testing",
|
||||
"servers/telemetry"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -197,42 +204,30 @@
|
|||
"pages": [
|
||||
"apps/overview",
|
||||
"apps/quickstart",
|
||||
"apps/examples",
|
||||
"apps/fastmcp-app",
|
||||
"apps/prefab",
|
||||
"apps/generative",
|
||||
"apps/low-level",
|
||||
{
|
||||
"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",
|
||||
"group": "Reference",
|
||||
"icon": "book",
|
||||
"pages": [
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "Prefab Providers",
|
||||
"icon": "cube",
|
||||
"pages": [
|
||||
"apps/providers/approval",
|
||||
"apps/providers/choice",
|
||||
"apps/providers/file-upload",
|
||||
"apps/providers/form"
|
||||
]
|
||||
},
|
||||
"apps/development",
|
||||
"apps/architecture",
|
||||
"apps/low-level"
|
||||
],
|
||||
"tag": "NEW"
|
||||
"apps/examples",
|
||||
"apps/architecture"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -240,29 +235,23 @@
|
|||
"group": "Clients",
|
||||
"pages": [
|
||||
"clients/client",
|
||||
"clients/client-only-package",
|
||||
"clients/transports",
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "Core Operations",
|
||||
"group": "Operations",
|
||||
"icon": "toolbox",
|
||||
"pages": [
|
||||
"clients/tools",
|
||||
"clients/resources",
|
||||
"clients/prompts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "Handlers",
|
||||
"icon": "hand",
|
||||
"pages": [
|
||||
"clients/notifications",
|
||||
"clients/prompts",
|
||||
"clients/sampling",
|
||||
"clients/elicitation",
|
||||
"clients/tasks",
|
||||
"clients/progress",
|
||||
"clients/logging",
|
||||
"clients/roots"
|
||||
"clients/roots",
|
||||
"clients/notifications"
|
||||
],
|
||||
"tag": "UPDATED"
|
||||
},
|
||||
|
|
@ -296,6 +285,7 @@
|
|||
"integrations/eunomia-authorization",
|
||||
"integrations/github",
|
||||
"integrations/google",
|
||||
"integrations/keycloak",
|
||||
"integrations/oci",
|
||||
"integrations/permit",
|
||||
"integrations/propelauth",
|
||||
|
|
@ -333,28 +323,31 @@
|
|||
"pages": [
|
||||
"integrations/anthropic",
|
||||
"integrations/gemini",
|
||||
"integrations/openai"
|
||||
"integrations/openai",
|
||||
"integrations/pydantic-ai"
|
||||
]
|
||||
},
|
||||
"integrations/mcp-json-configuration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "CLI",
|
||||
"pages": [
|
||||
"cli/overview",
|
||||
"cli/running",
|
||||
"cli/install-mcp",
|
||||
"cli/inspecting",
|
||||
"cli/client",
|
||||
"cli/generate-cli",
|
||||
"cli/auth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "More",
|
||||
"pages": [
|
||||
"more/settings",
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "CLI",
|
||||
"icon": "terminal",
|
||||
"pages": [
|
||||
"cli/overview",
|
||||
"cli/running",
|
||||
"cli/install-mcp",
|
||||
"cli/inspecting",
|
||||
"cli/client",
|
||||
"cli/generate-cli",
|
||||
"cli/auth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": true,
|
||||
"group": "Upgrading",
|
||||
|
|
@ -395,427 +388,9 @@
|
|||
{
|
||||
"anchor": "Python SDK",
|
||||
"icon": "python",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-decorators",
|
||||
"python-sdk/fastmcp-dependencies",
|
||||
"python-sdk/fastmcp-exceptions",
|
||||
"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",
|
||||
"python-sdk/fastmcp-cli-client",
|
||||
"python-sdk/fastmcp-cli-discovery",
|
||||
"python-sdk/fastmcp-cli-generate",
|
||||
{
|
||||
"group": "install",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-cli-install-__init__",
|
||||
"python-sdk/fastmcp-cli-install-claude_code",
|
||||
"python-sdk/fastmcp-cli-install-claude_desktop",
|
||||
"python-sdk/fastmcp-cli-install-cursor",
|
||||
"python-sdk/fastmcp-cli-install-gemini_cli",
|
||||
"python-sdk/fastmcp-cli-install-goose",
|
||||
"python-sdk/fastmcp-cli-install-mcp_json",
|
||||
"python-sdk/fastmcp-cli-install-shared",
|
||||
"python-sdk/fastmcp-cli-install-stdio"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-cli-run",
|
||||
"python-sdk/fastmcp-cli-tasks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.client",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-__init__",
|
||||
{
|
||||
"group": "auth",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-auth-__init__",
|
||||
"python-sdk/fastmcp-client-auth-bearer",
|
||||
"python-sdk/fastmcp-client-auth-oauth"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-client-client",
|
||||
"python-sdk/fastmcp-client-elicitation",
|
||||
"python-sdk/fastmcp-client-logging",
|
||||
"python-sdk/fastmcp-client-messages",
|
||||
{
|
||||
"group": "mixins",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-mixins-__init__",
|
||||
"python-sdk/fastmcp-client-mixins-prompts",
|
||||
"python-sdk/fastmcp-client-mixins-resources",
|
||||
"python-sdk/fastmcp-client-mixins-task_management",
|
||||
"python-sdk/fastmcp-client-mixins-tools"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-client-oauth_callback",
|
||||
"python-sdk/fastmcp-client-progress",
|
||||
"python-sdk/fastmcp-client-roots",
|
||||
{
|
||||
"group": "sampling",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-sampling-__init__",
|
||||
{
|
||||
"group": "handlers",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-sampling-handlers-__init__",
|
||||
"python-sdk/fastmcp-client-sampling-handlers-anthropic",
|
||||
"python-sdk/fastmcp-client-sampling-handlers-google_genai",
|
||||
"python-sdk/fastmcp-client-sampling-handlers-openai"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-client-tasks",
|
||||
"python-sdk/fastmcp-client-telemetry",
|
||||
{
|
||||
"group": "transports",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-transports-__init__",
|
||||
"python-sdk/fastmcp-client-transports-base",
|
||||
"python-sdk/fastmcp-client-transports-config",
|
||||
"python-sdk/fastmcp-client-transports-http",
|
||||
"python-sdk/fastmcp-client-transports-inference",
|
||||
"python-sdk/fastmcp-client-transports-memory",
|
||||
"python-sdk/fastmcp-client-transports-sse",
|
||||
"python-sdk/fastmcp-client-transports-stdio"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.experimental",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-experimental-__init__",
|
||||
{
|
||||
"group": "sampling",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-experimental-sampling-__init__",
|
||||
"python-sdk/fastmcp-experimental-sampling-handlers"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "transforms",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-experimental-transforms-__init__",
|
||||
"python-sdk/fastmcp-experimental-transforms-code_mode"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.prompts",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-prompts-__init__",
|
||||
"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-template",
|
||||
"python-sdk/fastmcp-resources-types"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.server",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-__init__",
|
||||
"python-sdk/fastmcp-server-app",
|
||||
"python-sdk/fastmcp-server-apps",
|
||||
{
|
||||
"group": "auth",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-__init__",
|
||||
"python-sdk/fastmcp-server-auth-auth",
|
||||
"python-sdk/fastmcp-server-auth-authorization",
|
||||
"python-sdk/fastmcp-server-auth-cimd",
|
||||
"python-sdk/fastmcp-server-auth-jwt_issuer",
|
||||
"python-sdk/fastmcp-server-auth-middleware",
|
||||
{
|
||||
"group": "oauth_proxy",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-__init__",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-consent",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-models",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-proxy",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-ui"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-auth-oidc_proxy",
|
||||
{
|
||||
"group": "providers",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-providers-__init__",
|
||||
"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",
|
||||
"python-sdk/fastmcp-server-auth-providers-github",
|
||||
"python-sdk/fastmcp-server-auth-providers-google",
|
||||
"python-sdk/fastmcp-server-auth-providers-in_memory",
|
||||
"python-sdk/fastmcp-server-auth-providers-introspection",
|
||||
"python-sdk/fastmcp-server-auth-providers-jwt",
|
||||
"python-sdk/fastmcp-server-auth-providers-oci",
|
||||
"python-sdk/fastmcp-server-auth-providers-propelauth",
|
||||
"python-sdk/fastmcp-server-auth-providers-scalekit",
|
||||
"python-sdk/fastmcp-server-auth-providers-supabase",
|
||||
"python-sdk/fastmcp-server-auth-providers-workos"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-auth-redirect_validation",
|
||||
"python-sdk/fastmcp-server-auth-ssrf"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-context",
|
||||
"python-sdk/fastmcp-server-dependencies",
|
||||
"python-sdk/fastmcp-server-elicitation",
|
||||
"python-sdk/fastmcp-server-event_store",
|
||||
"python-sdk/fastmcp-server-http",
|
||||
"python-sdk/fastmcp-server-lifespan",
|
||||
"python-sdk/fastmcp-server-low_level",
|
||||
{
|
||||
"group": "middleware",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-middleware-__init__",
|
||||
"python-sdk/fastmcp-server-middleware-authorization",
|
||||
"python-sdk/fastmcp-server-middleware-caching",
|
||||
"python-sdk/fastmcp-server-middleware-dereference",
|
||||
"python-sdk/fastmcp-server-middleware-error_handling",
|
||||
"python-sdk/fastmcp-server-middleware-logging",
|
||||
"python-sdk/fastmcp-server-middleware-middleware",
|
||||
"python-sdk/fastmcp-server-middleware-ping",
|
||||
"python-sdk/fastmcp-server-middleware-rate_limiting",
|
||||
"python-sdk/fastmcp-server-middleware-response_limiting",
|
||||
"python-sdk/fastmcp-server-middleware-timing",
|
||||
"python-sdk/fastmcp-server-middleware-tool_injection"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "mixins",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-mixins-__init__",
|
||||
"python-sdk/fastmcp-server-mixins-lifespan",
|
||||
"python-sdk/fastmcp-server-mixins-mcp_operations",
|
||||
"python-sdk/fastmcp-server-mixins-transport"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "openapi",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-openapi-__init__",
|
||||
"python-sdk/fastmcp-server-openapi-components",
|
||||
"python-sdk/fastmcp-server-openapi-routing",
|
||||
"python-sdk/fastmcp-server-openapi-server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "providers",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-__init__",
|
||||
"python-sdk/fastmcp-server-providers-aggregate",
|
||||
"python-sdk/fastmcp-server-providers-base",
|
||||
"python-sdk/fastmcp-server-providers-fastmcp_provider",
|
||||
"python-sdk/fastmcp-server-providers-filesystem",
|
||||
"python-sdk/fastmcp-server-providers-filesystem_discovery",
|
||||
{
|
||||
"group": "local_provider",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-local_provider-__init__",
|
||||
{
|
||||
"group": "decorators",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-local_provider-decorators-__init__",
|
||||
"python-sdk/fastmcp-server-providers-local_provider-decorators-prompts",
|
||||
"python-sdk/fastmcp-server-providers-local_provider-decorators-resources",
|
||||
"python-sdk/fastmcp-server-providers-local_provider-decorators-tools"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-providers-local_provider-local_provider"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "openapi",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-openapi-__init__",
|
||||
"python-sdk/fastmcp-server-providers-openapi-components",
|
||||
"python-sdk/fastmcp-server-providers-openapi-provider",
|
||||
"python-sdk/fastmcp-server-providers-openapi-routing"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-providers-proxy",
|
||||
{
|
||||
"group": "skills",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-skills-__init__",
|
||||
"python-sdk/fastmcp-server-providers-skills-claude_provider",
|
||||
"python-sdk/fastmcp-server-providers-skills-directory_provider",
|
||||
"python-sdk/fastmcp-server-providers-skills-skill_provider",
|
||||
"python-sdk/fastmcp-server-providers-skills-vendor_providers"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-providers-wrapped_provider"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-proxy",
|
||||
{
|
||||
"group": "sampling",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-sampling-__init__",
|
||||
"python-sdk/fastmcp-server-sampling-run",
|
||||
"python-sdk/fastmcp-server-sampling-sampling_tool"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-server",
|
||||
{
|
||||
"group": "tasks",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-tasks-__init__",
|
||||
"python-sdk/fastmcp-server-tasks-capabilities",
|
||||
"python-sdk/fastmcp-server-tasks-config",
|
||||
"python-sdk/fastmcp-server-tasks-elicitation",
|
||||
"python-sdk/fastmcp-server-tasks-handlers",
|
||||
"python-sdk/fastmcp-server-tasks-keys",
|
||||
"python-sdk/fastmcp-server-tasks-notifications",
|
||||
"python-sdk/fastmcp-server-tasks-requests",
|
||||
"python-sdk/fastmcp-server-tasks-routing",
|
||||
"python-sdk/fastmcp-server-tasks-subscriptions"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-telemetry",
|
||||
{
|
||||
"group": "transforms",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-transforms-__init__",
|
||||
"python-sdk/fastmcp-server-transforms-catalog",
|
||||
"python-sdk/fastmcp-server-transforms-namespace",
|
||||
"python-sdk/fastmcp-server-transforms-prompts_as_tools",
|
||||
"python-sdk/fastmcp-server-transforms-resources_as_tools",
|
||||
{
|
||||
"group": "search",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-transforms-search-__init__",
|
||||
"python-sdk/fastmcp-server-transforms-search-base",
|
||||
"python-sdk/fastmcp-server-transforms-search-bm25",
|
||||
"python-sdk/fastmcp-server-transforms-search-regex"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-transforms-tool_transform",
|
||||
"python-sdk/fastmcp-server-transforms-version_filter",
|
||||
"python-sdk/fastmcp-server-transforms-visibility"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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_transform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.utilities",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-__init__",
|
||||
"python-sdk/fastmcp-utilities-async_utils",
|
||||
"python-sdk/fastmcp-utilities-auth",
|
||||
"python-sdk/fastmcp-utilities-cli",
|
||||
"python-sdk/fastmcp-utilities-components",
|
||||
"python-sdk/fastmcp-utilities-exceptions",
|
||||
"python-sdk/fastmcp-utilities-http",
|
||||
"python-sdk/fastmcp-utilities-inspect",
|
||||
"python-sdk/fastmcp-utilities-json_schema",
|
||||
"python-sdk/fastmcp-utilities-json_schema_type",
|
||||
"python-sdk/fastmcp-utilities-lifespan",
|
||||
"python-sdk/fastmcp-utilities-logging",
|
||||
{
|
||||
"group": "mcp_server_config",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-__init__",
|
||||
{
|
||||
"group": "v1",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-__init__",
|
||||
{
|
||||
"group": "environments",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-__init__",
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-base",
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-uv"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-mcp_server_config",
|
||||
{
|
||||
"group": "sources",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-__init__",
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-base",
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-filesystem"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-utilities-mime",
|
||||
{
|
||||
"group": "openapi",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-openapi-__init__",
|
||||
"python-sdk/fastmcp-utilities-openapi-director",
|
||||
"python-sdk/fastmcp-utilities-openapi-formatters",
|
||||
"python-sdk/fastmcp-utilities-openapi-json_schema_converter",
|
||||
"python-sdk/fastmcp-utilities-openapi-models",
|
||||
"python-sdk/fastmcp-utilities-openapi-parser",
|
||||
"python-sdk/fastmcp-utilities-openapi-schemas"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-utilities-pagination",
|
||||
"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",
|
||||
"python-sdk/fastmcp-utilities-versions"
|
||||
]
|
||||
}
|
||||
]
|
||||
"pages": {
|
||||
"$ref": "./python-sdk-pages.json"
|
||||
}
|
||||
}
|
||||
],
|
||||
"dropdown": "SDK Reference",
|
||||
|
|
@ -825,205 +400,23 @@
|
|||
"version": "v3"
|
||||
},
|
||||
{
|
||||
"dropdowns": [
|
||||
{
|
||||
"dropdown": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"pages": [
|
||||
"v2/getting-started/welcome",
|
||||
"v2/getting-started/installation",
|
||||
"v2/getting-started/quickstart",
|
||||
"v2/updates"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Servers",
|
||||
"pages": [
|
||||
"v2/servers/server",
|
||||
{
|
||||
"group": "Core Components",
|
||||
"icon": "toolbox",
|
||||
"pages": [
|
||||
"v2/servers/tools",
|
||||
"v2/servers/resources",
|
||||
"v2/servers/prompts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Advanced Features",
|
||||
"icon": "stars",
|
||||
"pages": [
|
||||
"v2/servers/composition",
|
||||
"v2/servers/context",
|
||||
"v2/servers/elicitation",
|
||||
"v2/servers/icons",
|
||||
"v2/servers/logging",
|
||||
"v2/servers/middleware",
|
||||
"v2/servers/progress",
|
||||
"v2/servers/proxy",
|
||||
"v2/servers/sampling",
|
||||
"v2/servers/storage-backends",
|
||||
"v2/servers/tasks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "shield-check",
|
||||
"pages": [
|
||||
"v2/servers/auth/authentication",
|
||||
"v2/servers/auth/token-verification",
|
||||
"v2/servers/auth/remote-oauth",
|
||||
"v2/servers/auth/oauth-proxy",
|
||||
"v2/servers/auth/oidc-proxy",
|
||||
"v2/servers/auth/full-oauth-server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Deployment",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"v2/deployment/running-server",
|
||||
"v2/deployment/http",
|
||||
"deployment/prefect-horizon",
|
||||
"v2/deployment/server-configuration"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Clients",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Essentials",
|
||||
"icon": "cube",
|
||||
"pages": [
|
||||
"v2/clients/client",
|
||||
"v2/clients/transports"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Core Operations",
|
||||
"icon": "handshake",
|
||||
"pages": [
|
||||
"v2/clients/tools",
|
||||
"v2/clients/resources",
|
||||
"v2/clients/prompts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Advanced Features",
|
||||
"icon": "stars",
|
||||
"pages": [
|
||||
"v2/clients/elicitation",
|
||||
"v2/clients/logging",
|
||||
"v2/clients/progress",
|
||||
"v2/clients/sampling",
|
||||
"v2/clients/tasks",
|
||||
"v2/clients/messages",
|
||||
"v2/clients/roots"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "user-shield",
|
||||
"pages": [
|
||||
"v2/clients/auth/oauth",
|
||||
"v2/clients/auth/bearer"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "key",
|
||||
"pages": [
|
||||
"v2/integrations/auth0",
|
||||
"v2/integrations/authkit",
|
||||
"v2/integrations/aws-cognito",
|
||||
"v2/integrations/azure",
|
||||
"v2/integrations/descope",
|
||||
"v2/integrations/discord",
|
||||
"v2/integrations/github",
|
||||
"v2/integrations/google",
|
||||
"v2/integrations/oci",
|
||||
"v2/integrations/scalekit",
|
||||
"v2/integrations/supabase",
|
||||
"v2/integrations/workos"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Authorization",
|
||||
"icon": "shield-check",
|
||||
"pages": [
|
||||
"v2/integrations/eunomia-authorization",
|
||||
"v2/integrations/permit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI Assistants",
|
||||
"icon": "robot",
|
||||
"pages": [
|
||||
"v2/integrations/chatgpt",
|
||||
"v2/integrations/claude-code",
|
||||
"v2/integrations/claude-desktop",
|
||||
"v2/integrations/cursor",
|
||||
"v2/integrations/gemini-cli",
|
||||
"v2/integrations/mcp-json-configuration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI SDKs",
|
||||
"icon": "code",
|
||||
"pages": [
|
||||
"v2/integrations/anthropic",
|
||||
"v2/integrations/gemini",
|
||||
"v2/integrations/openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "API Integration",
|
||||
"icon": "globe",
|
||||
"pages": [
|
||||
"v2/integrations/fastapi",
|
||||
"v2/integrations/openapi"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Patterns",
|
||||
"pages": [
|
||||
"v2/patterns/tool-transformation",
|
||||
"v2/patterns/decorating-methods",
|
||||
"v2/patterns/cli",
|
||||
"v2/patterns/contrib",
|
||||
"v2/patterns/testing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Development",
|
||||
"pages": [
|
||||
"v2/development/contributing",
|
||||
"v2/development/tests",
|
||||
"v2/development/releases",
|
||||
"v2/development/upgrade-guide",
|
||||
"v2/changelog"
|
||||
]
|
||||
}
|
||||
],
|
||||
"icon": "book"
|
||||
}
|
||||
],
|
||||
"version": "v2.14.5"
|
||||
"$ref": "./v2-navigation.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"redirects": [
|
||||
{
|
||||
"destination": "/apps/fastmcp-app",
|
||||
"source": "/apps/interactive-apps"
|
||||
},
|
||||
{
|
||||
"destination": "/apps/generative",
|
||||
"source": "/apps/providers/generative"
|
||||
},
|
||||
{
|
||||
"destination": "/apps/prefab",
|
||||
"source": "/apps/patterns"
|
||||
},
|
||||
{
|
||||
"destination": "/cli/overview",
|
||||
"source": "/patterns/cli"
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ You can preview app tools locally with `fastmcp dev apps my_server.py` — no MC
|
|||
|
||||
## 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.
|
||||
[Prefect Horizon](https://horizon.prefect.io?utm_source=gofastmcp&utm_medium=docs) 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.
|
||||
|
||||
<Info>
|
||||
Horizon is **free for personal projects** and offers enterprise governance for teams.
|
||||
|
|
@ -156,7 +156,7 @@ Horizon is **free for personal projects** and offers enterprise governance for t
|
|||
To deploy your server, you'll need a [GitHub account](https://github.com). Once you have one, you can deploy your server in three steps:
|
||||
|
||||
1. Push your `my_server.py` file to a GitHub repository
|
||||
2. Sign in to [Prefect Horizon](https://horizon.prefect.io) with your GitHub account
|
||||
2. Sign in to [Prefect Horizon](https://horizon.prefect.io?utm_source=gofastmcp&utm_medium=docs) with your GitHub account
|
||||
3. Create a new project from your repository and enter `my_server.py:mcp` as the server entrypoint
|
||||
|
||||
That's it! Horizon will build and deploy your server, making it available at a URL like `https://your-project.fastmcp.app/mcp`. You can chat with it to test its functionality, or connect to it from any LLM client that supports the MCP protocol.
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ auth = GitHubProvider(
|
|||
|
||||
The deprecated WebSocket client transport has been removed. Use `StreamableHttpTransport` instead:
|
||||
|
||||
```python
|
||||
```python test="skip"
|
||||
# Before
|
||||
from fastmcp.client.transports import WSTransport
|
||||
transport = WSTransport("ws://localhost:8000/ws")
|
||||
|
|
@ -357,7 +357,7 @@ main.mount(subserver)
|
|||
|
||||
The proxy and OpenAPI modules have moved under `providers` to reflect v3's provider-based architecture:
|
||||
|
||||
```python
|
||||
```python test="skip"
|
||||
# Deprecated
|
||||
from fastmcp.server.proxy import FastMCPProxy
|
||||
from fastmcp.server.openapi import FastMCPOpenAPI
|
||||
|
|
@ -369,7 +369,7 @@ from fastmcp.server.providers.openapi import OpenAPIProvider
|
|||
|
||||
`FastMCPOpenAPI` itself is deprecated — use `FastMCP` with an `OpenAPIProvider` instead:
|
||||
|
||||
```python
|
||||
```python test="skip"
|
||||
# Deprecated
|
||||
from fastmcp.server.openapi import FastMCPOpenAPI
|
||||
server = FastMCPOpenAPI(spec, client)
|
||||
|
|
@ -408,7 +408,7 @@ proxy = create_proxy("http://example.com/mcp")
|
|||
|
||||
The experimental OpenAPI parser is now standard. Update imports:
|
||||
|
||||
```python
|
||||
```python test="skip"
|
||||
# Before
|
||||
from fastmcp.experimental.server.openapi import FastMCPOpenAPI
|
||||
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ from fastmcp import Client
|
|||
async def main():
|
||||
async with Client("https://gofastmcp.com/mcp") as client:
|
||||
result = await client.call_tool(
|
||||
name="SearchFastMcp",
|
||||
name="search_fast_mcp",
|
||||
arguments={"query": "deploy a FastMCP server"}
|
||||
)
|
||||
print(result)
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ if __name__ == "__main__":
|
|||
|
||||
If you try to call the authenticated server with the same Anthropic code we wrote earlier, you'll get an error indicating that the server rejected the request because it's not authenticated.
|
||||
|
||||
```python
|
||||
```text
|
||||
Error code: 400 - {
|
||||
"type": "error",
|
||||
"error": {
|
||||
|
|
|
|||
|
|
@ -9,29 +9,32 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
|
|||
|
||||
<VersionBadge version="2.11.0" />
|
||||
|
||||
This guide shows you how to secure your FastMCP server using WorkOS's **AuthKit**, a complete authentication and user management solution. This integration uses the [**Remote OAuth**](/servers/auth/remote-oauth) pattern, where AuthKit handles user login and your FastMCP server validates the tokens.
|
||||
|
||||
<Warning>
|
||||
AuthKit does not currently support [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) resource indicators, so FastMCP cannot validate that tokens were issued for the specific resource server. If you need resource-specific audience validation, consider using [WorkOSProvider](/integrations/workos) (OAuth proxy pattern) instead.
|
||||
</Warning>
|
||||
This guide shows you how to secure your FastMCP server using WorkOS's **AuthKit**, a complete authentication and user management solution. This integration uses the [**Remote OAuth**](/servers/auth/remote-oauth) pattern with [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) resource indicators: AuthKit issues tokens whose `aud` claim is bound to your server's resource URL, and FastMCP validates that claim automatically.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you will need:
|
||||
1. A **[WorkOS Account](https://workos.com/)** and a new **Project**.
|
||||
2. An **[AuthKit](https://www.authkit.com/)** instance configured within your WorkOS project.
|
||||
3. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`).
|
||||
3. Your FastMCP server's URL (can be localhost for development, e.g., `http://127.0.0.1:8000`).
|
||||
|
||||
### Step 1: AuthKit Configuration
|
||||
### Step 1: WorkOS Dashboard
|
||||
|
||||
In your WorkOS Dashboard, enable AuthKit and configure the following settings:
|
||||
In the WorkOS Dashboard, go to **Connect → Configuration** and configure:
|
||||
|
||||
<Steps>
|
||||
<Step title="Enable Dynamic Client Registration">
|
||||
Go to **Applications → Configuration** and enable **Dynamic Client Registration**. This allows MCP clients register with your application automatically.
|
||||
<Step title="MCP Auth">
|
||||
Enable **Dynamic Client Registration** (DCR) so MCP clients can register themselves. Alternatively, enable **Client ID Metadata Document** (CIMD) if your clients support it.
|
||||
</Step>
|
||||
|
||||

|
||||
<Step title="MCP resource indicators">
|
||||
Add your FastMCP server's resource URL (e.g., `http://127.0.0.1:8000/mcp`) as a valid resource indicator.
|
||||
|
||||
This must exactly match what FastMCP advertises in its protected resource metadata. Start your server first and it will log the correct URL on startup — copy that value.
|
||||
|
||||
Without this step, AuthKit falls back to a default environment-scoped audience and audience validation will fail with a 401.
|
||||
</Step>
|
||||
|
||||
<Step title="Note Your AuthKit Domain">
|
||||
|
|
@ -47,16 +50,18 @@ Create your FastMCP server file and use the `AuthKitProvider` to handle all the
|
|||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.workos import AuthKitProvider
|
||||
|
||||
# The AuthKitProvider automatically discovers WorkOS endpoints
|
||||
# and configures JWT token validation
|
||||
# AuthKitProvider automatically discovers WorkOS endpoints, configures JWT
|
||||
# validation, and binds the token audience to this server's resource URL.
|
||||
auth_provider = AuthKitProvider(
|
||||
authkit_domain="https://your-project-12345.authkit.app",
|
||||
base_url="http://localhost:8000" # Use your actual server URL
|
||||
base_url="http://127.0.0.1:8000", # Use your actual server URL
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="AuthKit Secured App", auth=auth_provider)
|
||||
```
|
||||
|
||||
When the server starts, it logs the resource URL it is validating against. Paste that URL into your Dashboard's **MCP resource indicators** list.
|
||||
|
||||
## Testing
|
||||
|
||||
To test your server, you can use the `fastmcp` CLI to run it locally. Assuming you've saved the above code to `server.py` (after replacing the `authkit_domain` and `base_url` with your actual values!), you can run the following command:
|
||||
|
|
@ -75,7 +80,7 @@ import asyncio
|
|||
auth = OAuth(additional_client_metadata={"token_endpoint_auth_method": "none"})
|
||||
|
||||
async def main():
|
||||
async with Client("http://localhost:8000/mcp", auth=auth) as client:
|
||||
async with Client("http://127.0.0.1:8000/mcp", auth=auth) as client:
|
||||
assert await client.ping()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -94,7 +99,7 @@ from fastmcp.server.auth.providers.workos import AuthKitProvider
|
|||
# Load configuration from environment variables
|
||||
auth = AuthKitProvider(
|
||||
authkit_domain=os.environ.get("AUTHKIT_DOMAIN"),
|
||||
base_url=os.environ.get("BASE_URL", "https://your-server.com")
|
||||
base_url=os.environ.get("BASE_URL", "https://your-server.com"),
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="AuthKit Secured App", auth=auth)
|
||||
|
|
|
|||
|
|
@ -458,3 +458,85 @@ For advanced OBO scenarios, use `CurrentAccessToken()` to get the user's token,
|
|||
<Tip>
|
||||
For a complete working example of Azure OBO with FastMCP, see [Pamela Fox's blog post on OBO flow for Entra-based MCP servers](https://blog.pamelafox.org/2026/01/using-on-behalf-of-flow-for-entra-based.html).
|
||||
</Tip>
|
||||
|
||||
## Azure AD B2C
|
||||
|
||||
<VersionBadge version="3.3.0" />
|
||||
|
||||
Azure AD B2C (Business-to-Consumer) uses different endpoints, scope URIs, and
|
||||
token issuers than standard Microsoft Entra ID. The `AzureProvider.from_b2c()`
|
||||
factory handles all of these differences automatically.
|
||||
|
||||
<Warning>
|
||||
Azure AD B2C does **not** support the On-Behalf-Of (OBO) flow. If you need
|
||||
OBO for downstream API calls, use `AzureProvider` with standard Entra ID
|
||||
instead.
|
||||
</Warning>
|
||||
|
||||
### Quick Start
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.azure import AzureProvider
|
||||
|
||||
auth = AzureProvider.from_b2c(
|
||||
tenant_name="mytenant",
|
||||
policy_name="B2C_1_susi",
|
||||
client_id="00000000-0000-0000-0000-000000000000",
|
||||
client_secret="my-secret",
|
||||
required_scopes=["mcp-access"],
|
||||
base_url="https://myserver.com",
|
||||
)
|
||||
|
||||
mcp = FastMCP("My App", auth=auth)
|
||||
```
|
||||
|
||||
`from_b2c()` derives the following values automatically:
|
||||
|
||||
| Derived value | Formula |
|
||||
|---|---|
|
||||
| Authority host | `{tenant_name}.b2clogin.com` |
|
||||
| Authorization endpoint | `https://{tenant_name}.b2clogin.com/{tenant_name}.onmicrosoft.com/{policy_name}/oauth2/v2.0/authorize` |
|
||||
| Token endpoint | `https://{tenant_name}.b2clogin.com/{tenant_name}.onmicrosoft.com/{policy_name}/oauth2/v2.0/token` |
|
||||
| Scope identifier URI | `https://{tenant_name}.onmicrosoft.com/{client_id}` |
|
||||
|
||||
### Token Issuer Validation
|
||||
|
||||
B2C access tokens carry the **tenant GUID** (not the `.onmicrosoft.com` name)
|
||||
in the `iss` claim, and the exact format varies by policy and custom-domain
|
||||
configuration. `from_b2c()` therefore **disables issuer validation by
|
||||
default**; **audience validation still enforces that tokens target the correct
|
||||
application**.
|
||||
|
||||
Once you have confirmed a successful end-to-end login, read the actual `iss`
|
||||
value from the decoded claims and enable strict validation:
|
||||
|
||||
```python
|
||||
auth = AzureProvider.from_b2c(
|
||||
tenant_name="mytenant",
|
||||
policy_name="B2C_1_susi",
|
||||
client_id="00000000-0000-0000-0000-000000000000",
|
||||
client_secret="my-secret",
|
||||
required_scopes=["mcp-access"],
|
||||
base_url="https://myserver.com",
|
||||
token_issuer="https://mytenant.b2clogin.com/11111111-2222-3333-4444-555555555555/v2.0/",
|
||||
)
|
||||
```
|
||||
|
||||
### Custom Domains
|
||||
|
||||
If your B2C tenant uses a [custom domain](https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-domain)
|
||||
(e.g. `auth.mycompany.com` instead of `mytenant.b2clogin.com`), pass it via
|
||||
`custom_domain`:
|
||||
|
||||
```python
|
||||
auth = AzureProvider.from_b2c(
|
||||
tenant_name="mytenant",
|
||||
policy_name="B2C_1_susi",
|
||||
client_id="00000000-0000-0000-0000-000000000000",
|
||||
client_secret="my-secret",
|
||||
required_scopes=["mcp-access"],
|
||||
base_url="https://myserver.com",
|
||||
custom_domain="auth.mycompany.com",
|
||||
)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -92,10 +92,12 @@ The connector must be explicitly enabled in each chat session through Developer
|
|||
|
||||
### Skip Confirmations
|
||||
|
||||
Use `annotations={"readOnlyHint": True}` to skip confirmation prompts for read-only tools:
|
||||
Use `annotations=ToolAnnotations(readOnlyHint=True)` to skip confirmation prompts for read-only tools:
|
||||
|
||||
```python
|
||||
@mcp.tool(annotations={"readOnlyHint": True})
|
||||
from mcp.types import ToolAnnotations
|
||||
|
||||
@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
|
||||
def get_status() -> str:
|
||||
"""Check system status."""
|
||||
return "All systems operational"
|
||||
|
|
@ -153,4 +155,3 @@ def fetch(id: str) -> dict:
|
|||
5. Ask research questions
|
||||
|
||||
ChatGPT will use your `search` and `fetch` tools to find and cite relevant information.
|
||||
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ from fastmcp.server.auth.providers.descope import DescopeProvider
|
|||
# The DescopeProvider automatically discovers Descope endpoints
|
||||
# and configures JWT token validation
|
||||
auth_provider = DescopeProvider(
|
||||
config_url=https://.../.well-known/openid-configuration, # Your MCP Server .well-known URL
|
||||
base_url=SERVER_URL, # Your server's public URL
|
||||
config_url="https://.../.well-known/openid-configuration", # Your MCP Server .well-known URL
|
||||
base_url=SERVER_URL, # Your server's public URL
|
||||
)
|
||||
|
||||
# Create FastMCP server with auth
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ Because FastMCP's FastAPI integration is based on its [OpenAPI integration](/int
|
|||
```python
|
||||
# Assumes the FastAPI app from above is already defined
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.openapi import RouteMap, MCPType
|
||||
from fastmcp.server.providers.openapi import RouteMap, MCPType
|
||||
|
||||
# Custom mapping rules
|
||||
mcp = FastMCP.from_fastapi(
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ async def main():
|
|||
|
||||
# Test the protected tool
|
||||
result = await client.call_tool("get_user_info")
|
||||
print(f"GitHub user: {result['github_user']}")
|
||||
print(f"GitHub user: {result.data['github_user']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
|
|
|||
141
docs/integrations/keycloak.mdx
Normal file
141
docs/integrations/keycloak.mdx
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
---
|
||||
title: Keycloak OAuth 🤝 FastMCP
|
||||
sidebarTitle: Keycloak
|
||||
description: Secure your FastMCP server with Keycloak OAuth
|
||||
icon: shield-check
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="3.2.4" />
|
||||
|
||||
This guide shows you how to secure your FastMCP server using **Keycloak OAuth**. This integration uses the [**Remote OAuth**](/servers/auth/remote-oauth) pattern with Dynamic Client Registration (DCR), where Keycloak handles user login and your FastMCP server validates the tokens.
|
||||
|
||||
<Note>
|
||||
**Keycloak 26.6.0 or later is required.** Earlier versions had a DCR incompatibility with MCP clients ([PR #45309](https://github.com/keycloak/keycloak/pull/45309)) that is fixed in 26.6.0.
|
||||
</Note>
|
||||
|
||||
## Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you will need:
|
||||
1. A running **[Keycloak](https://keycloak.org/)** instance (e.g., `http://localhost:8080`)
|
||||
2. A Keycloak realm with **Dynamic Client Registration** enabled and a trusted host policy that allows your server URL (e.g., `http://localhost:8000/*`)
|
||||
3. Your FastMCP server's public URL (e.g., `http://localhost:8000`)
|
||||
|
||||
### FastMCP Configuration
|
||||
|
||||
Create your FastMCP server and use `KeycloakAuthProvider` to handle OAuth:
|
||||
|
||||
```python server.py
|
||||
import os
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.keycloak import KeycloakAuthProvider
|
||||
from fastmcp.server.dependencies import get_access_token
|
||||
|
||||
auth = KeycloakAuthProvider(
|
||||
realm_url=os.getenv("KEYCLOAK_REALM_URL") or "http://localhost:8080/realms/myrealm",
|
||||
base_url="http://localhost:8000",
|
||||
# audience="http://localhost:8000", # Recommended for production
|
||||
)
|
||||
|
||||
mcp = FastMCP("Keycloak Example Server", auth=auth)
|
||||
|
||||
|
||||
@mcp.tool
|
||||
async def get_access_token_claims() -> dict:
|
||||
"""Get the authenticated user's access token claims."""
|
||||
token = get_access_token()
|
||||
return {
|
||||
"sub": token.claims.get("sub"),
|
||||
"scope": token.claims.get("scope"),
|
||||
"azp": token.claims.get("azp"),
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**Production security**: Always configure the `audience` parameter in production. Without it, your server accepts tokens issued for any audience. Configure Keycloak audience mappers and set `audience` to your server's base URL to ensure tokens are specifically intended for your server.
|
||||
</Warning>
|
||||
|
||||
## Local Development
|
||||
|
||||
Local infrastructure tooling is deliberately kept out of the FastMCP core library to keep auth integrations slim and the associated maintenance burden as low as possible. That said, Keycloak is a popular identity provider for local development and testing, so a dedicated FastMCP-compatible setup blueprint lives in the companion project [**fastmcp-keycloak-local**](https://github.com/stephaneberle9/fastmcp-keycloak-local).
|
||||
|
||||
It provides everything needed to develop and test FastMCP servers with Keycloak OAuth locally: a Docker-based Keycloak setup with a pre-configured `fastmcp` realm (Dynamic Client Registration enabled, test user included), cross-platform start scripts, and integration guides for the MCP Inspector, Claude Desktop, and Claude Code CLI.
|
||||
|
||||
## Testing
|
||||
|
||||
### Running the Server
|
||||
|
||||
```bash
|
||||
fastmcp run server.py --transport http --port 8000
|
||||
```
|
||||
|
||||
### Testing with a Client
|
||||
|
||||
```python client.py
|
||||
import asyncio
|
||||
from fastmcp import Client
|
||||
|
||||
async def main():
|
||||
async with Client("http://localhost:8000/mcp", auth="oauth") as client:
|
||||
print("✓ Authenticated with Keycloak!")
|
||||
result = await client.call_tool("get_access_token_claims")
|
||||
print(f"sub: {result.data.get('sub', 'N/A')}")
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
On first run, your browser will open to Keycloak's authorization page. After login, the client receives a token and caches it for subsequent runs.
|
||||
|
||||
## Features
|
||||
|
||||
### JWT Token Validation
|
||||
|
||||
- **Signature Verification**: Validates tokens against Keycloak's JWKS endpoint
|
||||
- **Expiration Checking**: Automatically rejects expired tokens
|
||||
- **Issuer Validation**: Ensures tokens come from your specific Keycloak realm
|
||||
- **Scope Enforcement**: Verifies required OAuth scopes are present
|
||||
- **Audience Validation**: Optional validation that tokens target your server (configure `audience`)
|
||||
|
||||
### User Claims
|
||||
|
||||
Access user information from Keycloak JWT tokens:
|
||||
|
||||
```python
|
||||
from fastmcp.server.dependencies import get_access_token
|
||||
|
||||
@mcp.tool
|
||||
async def admin_only_tool() -> str:
|
||||
"""A tool only available to admin users."""
|
||||
token = get_access_token()
|
||||
roles = token.claims.get("realm_access", {}).get("roles", [])
|
||||
if "admin" not in roles:
|
||||
raise ValueError("This tool requires admin access")
|
||||
return "Admin access granted!"
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom Token Verifier
|
||||
|
||||
```python
|
||||
from fastmcp.server.auth.providers.jwt import JWTVerifier
|
||||
from fastmcp.server.auth.providers.keycloak import KeycloakAuthProvider
|
||||
|
||||
custom_verifier = JWTVerifier(
|
||||
jwks_uri="http://localhost:8080/realms/myrealm/protocol/openid-connect/certs",
|
||||
issuer="http://localhost:8080/realms/myrealm",
|
||||
audience="my-resource-server",
|
||||
required_scopes=["api:read", "api:write"],
|
||||
)
|
||||
|
||||
auth = KeycloakAuthProvider(
|
||||
realm_url="http://localhost:8080/realms/myrealm",
|
||||
base_url="http://localhost:8000",
|
||||
token_verifier=custom_verifier,
|
||||
)
|
||||
```
|
||||
|
|
@ -357,6 +357,98 @@ echo "$CONFIG" | jq '."CI Server".command'
|
|||
# Output: "uv"
|
||||
```
|
||||
|
||||
### UV-Managed Project Dependencies
|
||||
|
||||
For servers that live inside a uv-managed project (with `pyproject.toml`), use the `--project` flag to run within that project's environment:
|
||||
|
||||
```bash
|
||||
fastmcp install mcp-json server.py --project .
|
||||
```
|
||||
|
||||
Output:
|
||||
```json
|
||||
{
|
||||
"My Server": {
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"--project",
|
||||
"/absolute/path/to/project",
|
||||
"--with",
|
||||
"fastmcp",
|
||||
"fastmcp",
|
||||
"run",
|
||||
"/absolute/path/to/project/server.py"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also use `fastmcp.json` with a local project:
|
||||
|
||||
```json fastmcp.json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py"
|
||||
},
|
||||
"environment": {
|
||||
"project": "."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If your server needs additional packages beyond those in `pyproject.toml`, add them via the `dependencies` array or `--with`.
|
||||
|
||||
### Published Packages with `uvx`
|
||||
|
||||
If your team publishes MCP servers as pip packages, you can configure clients to run them with `uvx` directly instead of `uv run`. For example, if your package is called `my-mcp-server` and provides a CLI entry point of the same name:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"My Server": {
|
||||
"command": "uvx",
|
||||
"args": ["my-mcp-server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the package name differs from the CLI command (e.g., package `weather-mcp` with command `weather-server`):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"Weather": {
|
||||
"command": "uvx",
|
||||
"args": ["--from", "weather-mcp", "weather-server"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also pin Python versions or add extra dependencies:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"My Server": {
|
||||
"command": "uvx",
|
||||
"args": [
|
||||
"--python", "3.12",
|
||||
"--with", "requests",
|
||||
"my-mcp-server"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
`fastmcp install mcp-json` generates `uv run` configurations for local development. For published packages, you'll typically write the `uvx` configuration manually or generate it through your own packaging workflow.
|
||||
</Note>
|
||||
|
||||
## Integration with MCP Clients
|
||||
|
||||
The generated configuration works with any MCP-compatible application:
|
||||
|
|
|
|||
|
|
@ -178,8 +178,8 @@ if __name__ == "__main__":
|
|||
|
||||
If you try to call the authenticated server with the same OpenAI code we wrote earlier, you'll get an error like this:
|
||||
|
||||
```python
|
||||
pythonAPIStatusError: Error code: 424 - {
|
||||
```text
|
||||
APIStatusError: Error code: 424 - {
|
||||
"error": {
|
||||
"message": "Error retrieving tool list from MCP server: 'dice_server'. Http status code: 401 (Unauthorized)",
|
||||
"type": "external_connector_error",
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ Each `RouteMap` specifies a combination of methods, patterns, and tags, as well
|
|||
Here is FastMCP's default rule:
|
||||
|
||||
```python
|
||||
from fastmcp.server.openapi import RouteMap, MCPType
|
||||
from fastmcp.server.providers.openapi import RouteMap, MCPType
|
||||
|
||||
DEFAULT_ROUTE_MAPPINGS = [
|
||||
# All routes become tools
|
||||
|
|
@ -101,7 +101,7 @@ For example, prior to FastMCP 2.8.0, GET requests were automatically mapped to `
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.openapi import RouteMap, MCPType
|
||||
from fastmcp.server.providers.openapi import RouteMap, MCPType
|
||||
|
||||
# Restore pre-2.8.0 semantic mapping
|
||||
semantic_maps = [
|
||||
|
|
@ -124,7 +124,7 @@ Here is a more complete example that uses custom route maps to convert all `GET`
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.openapi import RouteMap, MCPType
|
||||
from fastmcp.server.providers.openapi import RouteMap, MCPType
|
||||
|
||||
mcp = FastMCP.from_openapi(
|
||||
openapi_spec=spec,
|
||||
|
|
@ -164,7 +164,7 @@ You can use this to remove sensitive or internal routes by targeting them specif
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.openapi import RouteMap, MCPType
|
||||
from fastmcp.server.providers.openapi import RouteMap, MCPType
|
||||
|
||||
mcp = FastMCP.from_openapi(
|
||||
openapi_spec=spec,
|
||||
|
|
@ -180,7 +180,7 @@ Or you can use a catch-all rule to exclude everything that your maps don't handl
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.openapi import RouteMap, MCPType
|
||||
from fastmcp.server.providers.openapi import RouteMap, MCPType
|
||||
|
||||
mcp = FastMCP.from_openapi(
|
||||
openapi_spec=spec,
|
||||
|
|
@ -212,7 +212,8 @@ The `route_map_fn` is called on all routes, even those that matched `MCPType.EXC
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.openapi import RouteMap, MCPType, HTTPRoute
|
||||
from fastmcp.server.providers.openapi import RouteMap, MCPType
|
||||
from fastmcp.utilities.openapi import HTTPRoute
|
||||
|
||||
def custom_route_mapper(route: HTTPRoute, mcp_type: MCPType) -> MCPType | None:
|
||||
"""Advanced route type mapping."""
|
||||
|
|
@ -277,7 +278,7 @@ FastMCP provides several ways to add tags to your MCP components, allowing you t
|
|||
You can add custom tags to components created from specific routes using the `mcp_tags` parameter in `RouteMap`. These tags will be applied to all components created from routes that match that particular route map.
|
||||
|
||||
```python
|
||||
from fastmcp.server.openapi import RouteMap, MCPType
|
||||
from fastmcp.server.providers.openapi import RouteMap, MCPType
|
||||
|
||||
mcp = FastMCP.from_openapi(
|
||||
openapi_spec=spec,
|
||||
|
|
@ -368,12 +369,12 @@ Your `mcp_component_fn` is expected to modify the component in-place, not to ret
|
|||
</Tip>
|
||||
|
||||
```python
|
||||
from fastmcp.server.openapi import (
|
||||
HTTPRoute,
|
||||
from fastmcp.server.providers.openapi import (
|
||||
OpenAPITool,
|
||||
OpenAPIResource,
|
||||
OpenAPIResourceTemplate,
|
||||
)
|
||||
from fastmcp.utilities.openapi import HTTPRoute
|
||||
|
||||
def customize_components(
|
||||
route: HTTPRoute,
|
||||
|
|
|
|||
137
docs/integrations/pydantic-ai.mdx
Normal file
137
docs/integrations/pydantic-ai.mdx
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
---
|
||||
title: Pydantic AI 🤝 FastMCP
|
||||
sidebarTitle: Pydantic AI
|
||||
description: Connect FastMCP servers to Pydantic AI agents using the FastMCPToolset
|
||||
icon: message-code
|
||||
---
|
||||
|
||||
[Pydantic AI](https://ai.pydantic.dev/) ships a [`FastMCPToolset`](https://ai.pydantic.dev/mcp/fastmcp-client/) that lets a Pydantic AI agent call tools exposed by any MCP server through the [FastMCP Client](/clients/client). Because the toolset is built on the FastMCP Client, it works with FastMCP servers as well as any other MCP server, and supports the full range of [transports](/clients/transports): in-memory, STDIO, Streamable HTTP, and SSE.
|
||||
|
||||
This page shows how to point `FastMCPToolset` at a FastMCP server, with examples for each transport. For the toolset's full API, see the [Pydantic AI documentation](https://ai.pydantic.dev/mcp/fastmcp-client/).
|
||||
|
||||
<Tip>
|
||||
The `FastMCPToolset` currently exposes **tools** to the agent. Other MCP features such as elicitation and sampling are not yet supported through this toolset; use Pydantic AI's standard [`MCPServer`](https://ai.pydantic.dev/mcp/client/) client if you need them.
|
||||
</Tip>
|
||||
|
||||
## Install
|
||||
|
||||
`FastMCPToolset` lives in `pydantic-ai-slim` behind the `fastmcp` optional group:
|
||||
|
||||
```bash
|
||||
pip install "pydantic-ai-slim[fastmcp]"
|
||||
```
|
||||
|
||||
## Create a Server
|
||||
|
||||
Create a FastMCP server with the tools you want to expose. We'll use a single dice-rolling tool throughout this guide.
|
||||
|
||||
```python server.py
|
||||
import random
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP(name="Dice Roller")
|
||||
|
||||
@mcp.tool
|
||||
def roll_dice(n_dice: int) -> list[int]:
|
||||
"""Roll `n_dice` 6-sided dice and return the results."""
|
||||
return [random.randint(1, 6) for _ in range(n_dice)]
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="http", port=8000)
|
||||
```
|
||||
|
||||
## In-Memory
|
||||
|
||||
If your FastMCP server lives in the same process as your agent, pass the `FastMCP` instance directly. The toolset reuses an [in-memory transport](/clients/transports#in-memory-transport), which avoids a network round trip and is the fastest option for tests and embedded use.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import random
|
||||
from fastmcp import FastMCP
|
||||
from pydantic_ai import Agent
|
||||
from pydantic_ai.toolsets.fastmcp import FastMCPToolset
|
||||
|
||||
mcp = FastMCP(name="Dice Roller")
|
||||
|
||||
@mcp.tool
|
||||
def roll_dice(n_dice: int) -> list[int]:
|
||||
return [random.randint(1, 6) for _ in range(n_dice)]
|
||||
|
||||
toolset = FastMCPToolset(mcp)
|
||||
agent = Agent("openai:gpt-4.1", toolsets=[toolset])
|
||||
|
||||
async def main():
|
||||
result = await agent.run("Roll 3 dice!")
|
||||
print(result.output)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Streamable HTTP
|
||||
|
||||
For a remote FastMCP server reachable over HTTP, pass the URL as a string. The toolset infers the [Streamable HTTP transport](/clients/transports#http-transport) from the URL.
|
||||
|
||||
```python
|
||||
from pydantic_ai import Agent
|
||||
from pydantic_ai.toolsets.fastmcp import FastMCPToolset
|
||||
|
||||
toolset = FastMCPToolset("https://your-server-url.com/mcp")
|
||||
agent = Agent("openai:gpt-4.1", toolsets=[toolset])
|
||||
```
|
||||
|
||||
For [SSE](/clients/transports#sse-transport), use a `/sse` URL instead.
|
||||
|
||||
## STDIO
|
||||
|
||||
To launch a FastMCP server as a subprocess, pass a script path and the toolset will use the [STDIO transport](/clients/transports#stdio-transport).
|
||||
|
||||
```python
|
||||
from pydantic_ai import Agent
|
||||
from pydantic_ai.toolsets.fastmcp import FastMCPToolset
|
||||
|
||||
toolset = FastMCPToolset("server.py")
|
||||
agent = Agent("openai:gpt-4.1", toolsets=[toolset])
|
||||
```
|
||||
|
||||
You can also pass a [`StdioTransport`](/clients/transports#stdio-transport) directly when you need control over the command, args, or environment.
|
||||
|
||||
## MCP Configuration
|
||||
|
||||
To wire up multiple servers at once, pass an [MCP configuration](/integrations/mcp-json-configuration) dictionary. The toolset opens one client per server and exposes all of their tools to the agent.
|
||||
|
||||
```python
|
||||
from pydantic_ai import Agent
|
||||
from pydantic_ai.toolsets.fastmcp import FastMCPToolset
|
||||
|
||||
mcp_config = {
|
||||
"mcpServers": {
|
||||
"dice": {"command": "python", "args": ["server.py"]},
|
||||
"weather": {"url": "https://weather.example.com/mcp"},
|
||||
}
|
||||
}
|
||||
|
||||
toolset = FastMCPToolset(mcp_config)
|
||||
agent = Agent("openai:gpt-4.1", toolsets=[toolset])
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Because `FastMCPToolset` wraps a [FastMCP `Client`](/clients/client), it inherits the client's full [authentication](/clients/auth/bearer) story. To pass credentials such as a bearer token to a remote server, build a `Client` (or `StreamableHttpTransport`) yourself and hand it to the toolset.
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.transports import StreamableHttpTransport
|
||||
from pydantic_ai import Agent
|
||||
from pydantic_ai.toolsets.fastmcp import FastMCPToolset
|
||||
|
||||
transport = StreamableHttpTransport(
|
||||
url="https://your-server-url.com/mcp",
|
||||
headers={"Authorization": "Bearer your-access-token"},
|
||||
)
|
||||
|
||||
toolset = FastMCPToolset(Client(transport))
|
||||
agent = Agent("openai:gpt-4.1", toolsets=[toolset])
|
||||
```
|
||||
|
||||
For OAuth flows, use FastMCP's [`OAuth` helper](/clients/auth/oauth) when constructing the `Client`. For server-side token verification, see [Token Verification](/servers/auth/token-verification).
|
||||
|
|
@ -12,13 +12,13 @@ FastMCP includes a `contrib` package that holds community-contributed modules. T
|
|||
|
||||
Contrib modules provide additional features, integrations, or patterns that complement the core FastMCP library. They offer a way for the community to share useful extensions while keeping the core library focused and maintainable.
|
||||
|
||||
The available modules can be viewed in the [contrib directory](https://github.com/PrefectHQ/fastmcp/tree/main/src/fastmcp/contrib).
|
||||
The available modules can be viewed in the [contrib directory](https://github.com/PrefectHQ/fastmcp/tree/main/fastmcp_slim/fastmcp/contrib).
|
||||
|
||||
## Usage
|
||||
|
||||
To use a contrib module, import it from the `fastmcp.contrib` package:
|
||||
|
||||
```python
|
||||
```python test="skip"
|
||||
from fastmcp.contrib import my_module
|
||||
```
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ from fastmcp.contrib import my_module
|
|||
|
||||
We welcome contributions to the `contrib` package! If you have a module that extends FastMCP in a useful way, consider contributing it:
|
||||
|
||||
1. Create a new directory in `src/fastmcp/contrib/` for your module
|
||||
1. Create a new directory in `fastmcp_slim/fastmcp/contrib/` for your module
|
||||
3. Add proper tests for your module in `tests/contrib/`
|
||||
2. Include comprehensive documentation in a README.md file, including usage and examples, as well as any additional dependencies or installation instructions
|
||||
5. Submit a pull request
|
||||
|
|
|
|||
433
docs/python-sdk-pages.json
Normal file
433
docs/python-sdk-pages.json
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
[
|
||||
"python-sdk/fastmcp-decorators",
|
||||
"python-sdk/fastmcp-dependencies",
|
||||
"python-sdk/fastmcp-exceptions",
|
||||
"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",
|
||||
"python-sdk/fastmcp-cli-client",
|
||||
"python-sdk/fastmcp-cli-discovery",
|
||||
"python-sdk/fastmcp-cli-generate",
|
||||
{
|
||||
"group": "install",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-cli-install-__init__",
|
||||
"python-sdk/fastmcp-cli-install-claude_code",
|
||||
"python-sdk/fastmcp-cli-install-claude_desktop",
|
||||
"python-sdk/fastmcp-cli-install-cursor",
|
||||
"python-sdk/fastmcp-cli-install-gemini_cli",
|
||||
"python-sdk/fastmcp-cli-install-goose",
|
||||
"python-sdk/fastmcp-cli-install-mcp_json",
|
||||
"python-sdk/fastmcp-cli-install-shared",
|
||||
"python-sdk/fastmcp-cli-install-stdio"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-cli-run",
|
||||
"python-sdk/fastmcp-cli-tasks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.client",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-__init__",
|
||||
{
|
||||
"group": "auth",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-auth-__init__",
|
||||
"python-sdk/fastmcp-client-auth-bearer",
|
||||
"python-sdk/fastmcp-client-auth-oauth"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-client-client",
|
||||
"python-sdk/fastmcp-client-elicitation",
|
||||
"python-sdk/fastmcp-client-logging",
|
||||
"python-sdk/fastmcp-client-messages",
|
||||
{
|
||||
"group": "mixins",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-mixins-__init__",
|
||||
"python-sdk/fastmcp-client-mixins-prompts",
|
||||
"python-sdk/fastmcp-client-mixins-resources",
|
||||
"python-sdk/fastmcp-client-mixins-task_management",
|
||||
"python-sdk/fastmcp-client-mixins-tools"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-client-oauth_callback",
|
||||
"python-sdk/fastmcp-client-progress",
|
||||
"python-sdk/fastmcp-client-roots",
|
||||
{
|
||||
"group": "sampling",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-sampling-__init__",
|
||||
{
|
||||
"group": "handlers",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-sampling-handlers-__init__",
|
||||
"python-sdk/fastmcp-client-sampling-handlers-anthropic",
|
||||
"python-sdk/fastmcp-client-sampling-handlers-google_genai",
|
||||
"python-sdk/fastmcp-client-sampling-handlers-openai"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-client-tasks",
|
||||
"python-sdk/fastmcp-client-telemetry",
|
||||
{
|
||||
"group": "transports",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-transports-__init__",
|
||||
"python-sdk/fastmcp-client-transports-base",
|
||||
"python-sdk/fastmcp-client-transports-config",
|
||||
"python-sdk/fastmcp-client-transports-http",
|
||||
"python-sdk/fastmcp-client-transports-inference",
|
||||
"python-sdk/fastmcp-client-transports-memory",
|
||||
"python-sdk/fastmcp-client-transports-sse",
|
||||
"python-sdk/fastmcp-client-transports-stdio"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.experimental",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-experimental-__init__",
|
||||
{
|
||||
"group": "sampling",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-experimental-sampling-__init__",
|
||||
"python-sdk/fastmcp-experimental-sampling-handlers"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "transforms",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-experimental-transforms-__init__",
|
||||
"python-sdk/fastmcp-experimental-transforms-code_mode"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.prompts",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-prompts-__init__",
|
||||
"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-template",
|
||||
"python-sdk/fastmcp-resources-types"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.server",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-__init__",
|
||||
"python-sdk/fastmcp-server-app",
|
||||
"python-sdk/fastmcp-server-apps",
|
||||
{
|
||||
"group": "auth",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-__init__",
|
||||
"python-sdk/fastmcp-server-auth-auth",
|
||||
"python-sdk/fastmcp-server-auth-authorization",
|
||||
"python-sdk/fastmcp-server-auth-cimd",
|
||||
{
|
||||
"group": "handlers",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-handlers-__init__",
|
||||
"python-sdk/fastmcp-server-auth-handlers-authorize"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-auth-jwt_issuer",
|
||||
"python-sdk/fastmcp-server-auth-middleware",
|
||||
{
|
||||
"group": "oauth_proxy",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-__init__",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-consent",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-models",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-proxy",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy-ui"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-auth-oidc_proxy",
|
||||
{
|
||||
"group": "providers",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-providers-__init__",
|
||||
"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",
|
||||
"python-sdk/fastmcp-server-auth-providers-github",
|
||||
"python-sdk/fastmcp-server-auth-providers-google",
|
||||
"python-sdk/fastmcp-server-auth-providers-in_memory",
|
||||
"python-sdk/fastmcp-server-auth-providers-introspection",
|
||||
"python-sdk/fastmcp-server-auth-providers-jwt",
|
||||
"python-sdk/fastmcp-server-auth-providers-keycloak",
|
||||
"python-sdk/fastmcp-server-auth-providers-oci",
|
||||
"python-sdk/fastmcp-server-auth-providers-propelauth",
|
||||
"python-sdk/fastmcp-server-auth-providers-scalekit",
|
||||
"python-sdk/fastmcp-server-auth-providers-supabase",
|
||||
"python-sdk/fastmcp-server-auth-providers-workos"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-auth-redirect_validation",
|
||||
"python-sdk/fastmcp-server-auth-ssrf"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-context",
|
||||
"python-sdk/fastmcp-server-dependencies",
|
||||
"python-sdk/fastmcp-server-elicitation",
|
||||
"python-sdk/fastmcp-server-event_store",
|
||||
"python-sdk/fastmcp-server-http",
|
||||
"python-sdk/fastmcp-server-lifespan",
|
||||
"python-sdk/fastmcp-server-low_level",
|
||||
{
|
||||
"group": "middleware",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-middleware-__init__",
|
||||
"python-sdk/fastmcp-server-middleware-authorization",
|
||||
"python-sdk/fastmcp-server-middleware-caching",
|
||||
"python-sdk/fastmcp-server-middleware-dereference",
|
||||
"python-sdk/fastmcp-server-middleware-error_handling",
|
||||
"python-sdk/fastmcp-server-middleware-logging",
|
||||
"python-sdk/fastmcp-server-middleware-middleware",
|
||||
"python-sdk/fastmcp-server-middleware-ping",
|
||||
"python-sdk/fastmcp-server-middleware-rate_limiting",
|
||||
"python-sdk/fastmcp-server-middleware-response_limiting",
|
||||
"python-sdk/fastmcp-server-middleware-timing",
|
||||
"python-sdk/fastmcp-server-middleware-tool_injection"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "mixins",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-mixins-__init__",
|
||||
"python-sdk/fastmcp-server-mixins-lifespan",
|
||||
"python-sdk/fastmcp-server-mixins-mcp_operations",
|
||||
"python-sdk/fastmcp-server-mixins-transport"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "openapi",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-openapi-__init__",
|
||||
"python-sdk/fastmcp-server-openapi-components",
|
||||
"python-sdk/fastmcp-server-openapi-routing",
|
||||
"python-sdk/fastmcp-server-openapi-server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "providers",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-__init__",
|
||||
"python-sdk/fastmcp-server-providers-addressing",
|
||||
"python-sdk/fastmcp-server-providers-aggregate",
|
||||
"python-sdk/fastmcp-server-providers-base",
|
||||
"python-sdk/fastmcp-server-providers-fastmcp_provider",
|
||||
"python-sdk/fastmcp-server-providers-filesystem",
|
||||
"python-sdk/fastmcp-server-providers-filesystem_discovery",
|
||||
{
|
||||
"group": "local_provider",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-local_provider-__init__",
|
||||
{
|
||||
"group": "decorators",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-local_provider-decorators-__init__",
|
||||
"python-sdk/fastmcp-server-providers-local_provider-decorators-prompts",
|
||||
"python-sdk/fastmcp-server-providers-local_provider-decorators-resources",
|
||||
"python-sdk/fastmcp-server-providers-local_provider-decorators-tools"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-providers-local_provider-local_provider"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "openapi",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-openapi-__init__",
|
||||
"python-sdk/fastmcp-server-providers-openapi-components",
|
||||
"python-sdk/fastmcp-server-providers-openapi-provider",
|
||||
"python-sdk/fastmcp-server-providers-openapi-routing"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-providers-prefab_synthesis",
|
||||
"python-sdk/fastmcp-server-providers-proxy",
|
||||
{
|
||||
"group": "skills",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-providers-skills-__init__",
|
||||
"python-sdk/fastmcp-server-providers-skills-claude_provider",
|
||||
"python-sdk/fastmcp-server-providers-skills-directory_provider",
|
||||
"python-sdk/fastmcp-server-providers-skills-skill_provider",
|
||||
"python-sdk/fastmcp-server-providers-skills-vendor_providers"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-providers-wrapped_provider"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-proxy",
|
||||
{
|
||||
"group": "sampling",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-sampling-__init__",
|
||||
"python-sdk/fastmcp-server-sampling-run",
|
||||
"python-sdk/fastmcp-server-sampling-sampling_tool"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-server",
|
||||
{
|
||||
"group": "tasks",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-tasks-__init__",
|
||||
"python-sdk/fastmcp-server-tasks-capabilities",
|
||||
"python-sdk/fastmcp-server-tasks-config",
|
||||
"python-sdk/fastmcp-server-tasks-context",
|
||||
"python-sdk/fastmcp-server-tasks-elicitation",
|
||||
"python-sdk/fastmcp-server-tasks-handlers",
|
||||
"python-sdk/fastmcp-server-tasks-keys",
|
||||
"python-sdk/fastmcp-server-tasks-notifications",
|
||||
"python-sdk/fastmcp-server-tasks-requests",
|
||||
"python-sdk/fastmcp-server-tasks-routing",
|
||||
"python-sdk/fastmcp-server-tasks-subscriptions"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-telemetry",
|
||||
{
|
||||
"group": "transforms",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-transforms-__init__",
|
||||
"python-sdk/fastmcp-server-transforms-catalog",
|
||||
"python-sdk/fastmcp-server-transforms-namespace",
|
||||
"python-sdk/fastmcp-server-transforms-prompts_as_tools",
|
||||
"python-sdk/fastmcp-server-transforms-resources_as_tools",
|
||||
{
|
||||
"group": "search",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-transforms-search-__init__",
|
||||
"python-sdk/fastmcp-server-transforms-search-base",
|
||||
"python-sdk/fastmcp-server-transforms-search-bm25",
|
||||
"python-sdk/fastmcp-server-transforms-search-regex"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-transforms-tool_transform",
|
||||
"python-sdk/fastmcp-server-transforms-version_filter",
|
||||
"python-sdk/fastmcp-server-transforms-visibility"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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_transform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.utilities",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-__init__",
|
||||
"python-sdk/fastmcp-utilities-async_utils",
|
||||
"python-sdk/fastmcp-utilities-auth",
|
||||
"python-sdk/fastmcp-utilities-cli",
|
||||
"python-sdk/fastmcp-utilities-components",
|
||||
"python-sdk/fastmcp-utilities-docstring_parsing",
|
||||
"python-sdk/fastmcp-utilities-exceptions",
|
||||
"python-sdk/fastmcp-utilities-http",
|
||||
"python-sdk/fastmcp-utilities-inspect",
|
||||
"python-sdk/fastmcp-utilities-json_schema",
|
||||
"python-sdk/fastmcp-utilities-json_schema_type",
|
||||
"python-sdk/fastmcp-utilities-lifespan",
|
||||
"python-sdk/fastmcp-utilities-logging",
|
||||
{
|
||||
"group": "mcp_server_config",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-__init__",
|
||||
{
|
||||
"group": "v1",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-__init__",
|
||||
{
|
||||
"group": "environments",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-__init__",
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-base",
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-uv"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-mcp_server_config",
|
||||
{
|
||||
"group": "sources",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-__init__",
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-base",
|
||||
"python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-filesystem"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-utilities-mime",
|
||||
{
|
||||
"group": "openapi",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-openapi-__init__",
|
||||
"python-sdk/fastmcp-utilities-openapi-director",
|
||||
"python-sdk/fastmcp-utilities-openapi-formatters",
|
||||
"python-sdk/fastmcp-utilities-openapi-json_schema_converter",
|
||||
"python-sdk/fastmcp-utilities-openapi-models",
|
||||
"python-sdk/fastmcp-utilities-openapi-parser",
|
||||
"python-sdk/fastmcp-utilities-openapi-schemas"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-utilities-pagination",
|
||||
"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",
|
||||
"python-sdk/fastmcp-utilities-versions"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -35,7 +35,7 @@ Usage::
|
|||
|
||||
## 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>
|
||||
### `FastMCPApp` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L142" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
A Provider that represents an MCP application.
|
||||
|
|
@ -48,19 +48,19 @@ 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>
|
||||
#### `tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L164" 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>
|
||||
#### `tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L176" 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>
|
||||
#### `tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L187" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
tool(self, name_or_fn: str | AnyFunction | None = None) -> Any
|
||||
|
|
@ -83,19 +83,19 @@ Supports multiple calling patterns::
|
|||
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>
|
||||
#### `ui` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L252" 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>
|
||||
#### `ui` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L267" 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>
|
||||
#### `ui` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L281" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
ui(self, name_or_fn: str | AnyFunction | None = None) -> Any
|
||||
|
|
@ -119,7 +119,7 @@ Supports multiple calling patterns::
|
|||
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>
|
||||
#### `add_tool` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L355" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
add_tool(self, tool: Tool | Callable[..., Any]) -> Tool
|
||||
|
|
@ -130,13 +130,13 @@ 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>
|
||||
#### `lifespan` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L409" 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>
|
||||
#### `run` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/app.py#L417" 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
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ For custom persistence, override the storage methods::
|
|||
|
||||
## 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>
|
||||
### `FileUpload` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/file_upload.py#L102" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
A Provider that adds file upload capabilities to a server.
|
||||
|
|
@ -77,7 +77,7 @@ Example::
|
|||
|
||||
**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>
|
||||
#### `on_store` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/file_upload.py#L183" 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]]
|
||||
|
|
@ -100,7 +100,7 @@ implementation stores files in memory, scoped by
|
|||
- ``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>
|
||||
#### `on_list` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/file_upload.py#L216" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
on_list(self, ctx: Context) -> list[dict[str, Any]]
|
||||
|
|
@ -118,7 +118,7 @@ implementation returns files from the current scope.
|
|||
- 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>
|
||||
#### `on_read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/file_upload.py#L232" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
on_read(self, name: str, ctx: Context) -> dict[str, Any]
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ Usage::
|
|||
|
||||
## 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>
|
||||
### `FormInput` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/apps/form.py#L88" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
A Provider that collects structured input via a Pydantic model.
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ Startup sequence
|
|||
|
||||
## 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>
|
||||
### `run_dev_apps` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/apps_dev.py#L1690" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
run_dev_apps(server_spec: str) -> None
|
||||
|
|
|
|||
|
|
@ -91,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#L760" 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#L764" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
inspect(server_spec: str | None = None) -> None
|
||||
|
|
@ -122,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#L1002" 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#L1006" 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
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ generate_cli_script(server_name: str, server_spec: str, transport_code: str, ext
|
|||
Generate the full CLI script source code.
|
||||
|
||||
|
||||
### `generate_skill_content` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/generate.py#L621" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `generate_skill_content` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/generate.py#L619" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
generate_skill_content(server_name: str, cli_filename: str, tools: list[mcp.types.Tool]) -> str
|
||||
|
|
@ -43,7 +43,7 @@ generate_skill_content(server_name: str, cli_filename: str, tools: list[mcp.type
|
|||
Generate a SKILL.md file for a generated CLI script.
|
||||
|
||||
|
||||
### `generate_cli_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/generate.py#L672" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `generate_cli_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/generate.py#L670" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
generate_cli_command(server_spec: Annotated[str, cyclopts.Parameter(help='Server URL, Python file, MCPConfig JSON, discovered name, or .js file')], output: Annotated[str, cyclopts.Parameter(help='Output file path (default: cli.py)')] = 'cli.py') -> None
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Generate a Goose deeplink for installing an MCP extension.
|
|||
- A goose://extension?... deeplink URL.
|
||||
|
||||
|
||||
### `install_goose` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/goose.py#L86" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `install_goose` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/goose.py#L85" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
install_goose(file: Path, server_object: str | None, name: str) -> bool
|
||||
|
|
@ -49,7 +49,7 @@ Install FastMCP server in Goose via deeplink.
|
|||
- True if installation was successful, False otherwise.
|
||||
|
||||
|
||||
### `goose_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/goose.py#L136" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `goose_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/install/goose.py#L135" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
goose_command(server_spec: str) -> None
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ FastMCP run command implementation with enhanced type hints.
|
|||
|
||||
## Functions
|
||||
|
||||
### `is_url` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L83" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `is_url` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L84" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
is_url(path: str) -> bool
|
||||
|
|
@ -20,7 +20,7 @@ is_url(path: str) -> bool
|
|||
Check if a string is a URL.
|
||||
|
||||
|
||||
### `create_client_server` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L89" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `create_client_server` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L90" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
create_client_server(url: str) -> Any
|
||||
|
|
@ -36,7 +36,7 @@ Create a FastMCP server from a client URL.
|
|||
- A FastMCP server instance
|
||||
|
||||
|
||||
### `create_mcp_config_server` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L109" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `create_mcp_config_server` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L110" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None]
|
||||
|
|
@ -46,7 +46,7 @@ create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None]
|
|||
Create a FastMCP server from a MCPConfig.
|
||||
|
||||
|
||||
### `load_mcp_server_config` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L118" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `load_mcp_server_config` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L119" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
load_mcp_server_config(config_path: Path) -> MCPServerConfig
|
||||
|
|
@ -62,7 +62,7 @@ Load a FastMCP configuration from a fastmcp.json file.
|
|||
- MCPServerConfig object
|
||||
|
||||
|
||||
### `run_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L135" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `run_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L136" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
run_command(server_spec: str, transport: TransportType | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: LogLevelType | None = None, server_args: list[str] | None = None, show_banner: bool = True, use_direct_import: bool = False, skip_source: bool = False, stateless: bool = False) -> None
|
||||
|
|
@ -85,7 +85,7 @@ Run a MCP server or connect to a remote one.
|
|||
- `stateless`: Whether to run in stateless mode (no session)
|
||||
|
||||
|
||||
### `run_module_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L260" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `run_module_command` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L272" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
run_module_command(module_name: str) -> None
|
||||
|
|
@ -104,7 +104,7 @@ with environment setup (e.g. ``UVEnvironment.build_command``).
|
|||
- `extra_args`: Extra arguments forwarded after the module name.
|
||||
|
||||
|
||||
### `run_v1_server_async` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L299" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `run_v1_server_async` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L311" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
run_v1_server_async(server: FastMCP1x, host: str | None = None, port: int | None = None, transport: TransportType | None = None) -> None
|
||||
|
|
@ -120,7 +120,7 @@ Run a FastMCP 1.x server using async methods.
|
|||
- `transport`: Transport protocol to use
|
||||
|
||||
|
||||
### `run_with_reload` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L364" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `run_with_reload` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/cli/run.py#L376" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
run_with_reload(cmd: list[str], reload_dirs: list[Path] | None = None, is_stdio: bool = False) -> None
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ sidebarTitle: client
|
|||
|
||||
## Classes
|
||||
|
||||
### `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>
|
||||
### `ClientSessionState` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/client.py#L96" 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#L114" 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#L113" 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#L124" 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#L123" 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#L371" 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#L370" 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#L381" 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#L380" 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#L385" 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#L384" 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#L389" 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#L388" 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#L404" 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#L403" 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#L412" 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#L411" 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#L416" 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#L415" 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#L461" 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#L476" 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#L762" 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#L791" 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#L768" 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#L797" 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#L773" 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#L802" 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#L790" 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#L819" 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#L802" 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#L831" 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#L806" 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#L835" 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#L812" 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#L841" 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#L843" 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#L872" 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#L870" 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#L899" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
generate_name(cls, name: str | None = None) -> str
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ 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#L59" 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#L65" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
list_prompts(self: Client, max_pages: int = AUTO_PAGINATION_MAX_PAGES) -> list[mcp.types.Prompt]
|
||||
|
|
@ -61,7 +61,7 @@ large result sets incrementally), use list_prompts_mcp() with the cursor paramet
|
|||
- `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#L107" 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#L113" 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
|
||||
|
|
@ -83,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#L176" 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#L183" 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#L187" 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#L194" 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#L199" 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#L206" 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
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ 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#L58" 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#L64" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
list_resources(self: Client, max_pages: int = AUTO_PAGINATION_MAX_PAGES) -> list[mcp.types.Resource]
|
||||
|
|
@ -61,7 +61,7 @@ large result sets incrementally), use list_resources_mcp() with the cursor param
|
|||
- `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#L105" 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#L111" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
list_resource_templates_mcp(self: Client) -> mcp.types.ListResourceTemplatesResult
|
||||
|
|
@ -81,7 +81,7 @@ 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#L128" 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#L140" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
list_resource_templates(self: Client, max_pages: int = AUTO_PAGINATION_MAX_PAGES) -> list[mcp.types.ResourceTemplate]
|
||||
|
|
@ -105,7 +105,7 @@ cursor parameter.
|
|||
- `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#L177" 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#L189" 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
|
||||
|
|
@ -126,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#L233" 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#L245" 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#L243" 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#L255" 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#L254" 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#L266" 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
|
||||
|
|
|
|||
|
|
@ -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#L34" 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#L35" 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#L39" 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#L40" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
list_tools_mcp(self: Client) -> mcp.types.ListToolsResult
|
||||
|
|
@ -38,7 +38,7 @@ 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#L62" 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#L69" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
list_tools(self: Client, max_pages: int = AUTO_PAGINATION_MAX_PAGES) -> list[mcp.types.Tool]
|
||||
|
|
@ -61,7 +61,7 @@ large result sets incrementally), use list_tools_mcp() with the cursor parameter
|
|||
- `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#L111" 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#L118" 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
|
||||
|
|
@ -91,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#L191" 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#L211" 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#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#L225" 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#L220" 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#L240" 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
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ SEP-1686 client Task classes.
|
|||
|
||||
## Classes
|
||||
|
||||
### `TaskNotificationHandler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L26" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `TaskNotificationHandler` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L27" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
MessageHandler that routes task status notifications to Task objects.
|
||||
|
|
@ -18,7 +18,7 @@ MessageHandler that routes task status notifications to Task objects.
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `dispatch` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L33" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `dispatch` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L34" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
dispatch(self, message: Message) -> None
|
||||
|
|
@ -27,7 +27,7 @@ dispatch(self, message: Message) -> None
|
|||
Dispatch messages, including task status notifications.
|
||||
|
||||
|
||||
### `Task` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L47" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `Task` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L48" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Abstract base class for MCP background tasks (SEP-1686).
|
||||
|
|
@ -38,7 +38,7 @@ or executes synchronously (graceful degradation per SEP-1686).
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `task_id` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L105" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `task_id` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L106" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
task_id(self) -> str
|
||||
|
|
@ -47,7 +47,7 @@ task_id(self) -> str
|
|||
Get the task ID.
|
||||
|
||||
|
||||
#### `returned_immediately` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L110" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `returned_immediately` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L111" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
returned_immediately(self) -> bool
|
||||
|
|
@ -60,7 +60,7 @@ Check if server executed the task immediately.
|
|||
- False if server accepted background execution
|
||||
|
||||
|
||||
#### `on_status_change` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L145" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `on_status_change` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L146" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
on_status_change(self, callback: Callable[[GetTaskResult], None | Awaitable[None]]) -> None
|
||||
|
|
@ -78,7 +78,7 @@ Supports both sync and async callbacks (auto-detected).
|
|||
Can return None (sync) or Awaitable[None] (async).
|
||||
|
||||
|
||||
#### `status` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L171" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `status` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L172" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
status(self) -> GetTaskResult
|
||||
|
|
@ -90,7 +90,7 @@ If server executed immediately, returns synthetic completed status.
|
|||
Otherwise queries the server for current status.
|
||||
|
||||
|
||||
#### `result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L202" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L203" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
result(self) -> TaskResultT
|
||||
|
|
@ -101,7 +101,7 @@ Wait for and return the task result.
|
|||
Must be implemented by subclasses to return the appropriate result type.
|
||||
|
||||
|
||||
#### `wait` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L209" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `wait` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L210" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
wait(self) -> GetTaskResult
|
||||
|
|
@ -114,8 +114,8 @@ with fallback to polling (reliable). Optimally wakes up immediately
|
|||
on status changes when server sends notifications/tasks/status.
|
||||
|
||||
**Args:**
|
||||
- `state`: Desired state ('submitted', 'working', 'completed', 'failed').
|
||||
If None, waits for any terminal state (completed/failed)
|
||||
- `state`: Desired state ('working', 'input_required', 'completed', 'failed', 'cancelled').
|
||||
If None, waits until the task exits the 'working' state (completed, failed, cancelled, input_required, etc.)
|
||||
- `timeout`: Maximum time to wait in seconds
|
||||
|
||||
**Returns:**
|
||||
|
|
@ -125,7 +125,7 @@ on status changes when server sends notifications/tasks/status.
|
|||
- `TimeoutError`: If desired state not reached within timeout
|
||||
|
||||
|
||||
#### `cancel` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L272" 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/tasks.py#L288" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
cancel(self) -> None
|
||||
|
|
@ -140,7 +140,7 @@ Note: If server executed immediately (graceful degradation), this is a no-op
|
|||
as there's no server-side task to cancel.
|
||||
|
||||
|
||||
### `ToolTask` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L294" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `ToolTask` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L310" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Represents a tool call that may execute in background or immediately.
|
||||
|
|
@ -151,7 +151,7 @@ or executes synchronously (graceful degradation per SEP-1686).
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L336" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L355" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
result(self) -> CallToolResult
|
||||
|
|
@ -166,7 +166,7 @@ Otherwise waits for background task to complete and retrieves result.
|
|||
- The parsed tool result (same as call_tool returns)
|
||||
|
||||
|
||||
### `PromptTask` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L396" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `PromptTask` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L429" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Represents a prompt call that may execute in background or immediately.
|
||||
|
|
@ -177,7 +177,7 @@ or executes synchronously (graceful degradation per SEP-1686).
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L427" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L460" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
result(self) -> mcp.types.GetPromptResult
|
||||
|
|
@ -192,7 +192,7 @@ Otherwise waits for background task to complete and retrieves result.
|
|||
- The prompt result with messages and description
|
||||
|
||||
|
||||
### `ResourceTask` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L461" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `ResourceTask` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L494" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Represents a resource read that may execute in background or immediately.
|
||||
|
|
@ -203,7 +203,7 @@ or executes synchronously (graceful degradation per SEP-1686).
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L497" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/tasks.py#L530" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
result(self) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ Client-side telemetry helpers.
|
|||
|
||||
## Functions
|
||||
|
||||
### `client_span` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/telemetry.py#L12" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `client_span` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/client/telemetry.py#L13" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
client_span(name: str, method: str, component_key: str, session_id: str | None = None, resource_uri: str | None = None) -> Generator[Span, None, None]
|
||||
client_span(name: str, method: str, component_key: str, session_id: str | None = None, resource_uri: str | None = None, tool_name: str | None = None, prompt_name: str | None = None) -> Generator[Span, None, None]
|
||||
```
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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#L148" 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#L150" 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#L201" 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#L207" 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#L209" 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#L215" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
close(self)
|
||||
|
|
|
|||
|
|
@ -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#L115" 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#L117" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Custom exceptions for FastMCP.
|
|||
|
||||
## Classes
|
||||
|
||||
### `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>
|
||||
### `FastMCPDeprecationWarning` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L8" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Deprecation warning for FastMCP APIs.
|
||||
|
|
@ -20,61 +20,61 @@ 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>
|
||||
### `FastMCPError` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/exceptions.py#L17" 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#L19" 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#L25" 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#L23" 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#L29" 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#L27" 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#L33" 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#L31" 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#L37" 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#L35" 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#L41" 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#L39" 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#L45" 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#L43" 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#L49" 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#L47" 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#L53" 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#L51" 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#L57" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Error when authorization check fails.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ sidebarTitle: code_mode
|
|||
|
||||
## Classes
|
||||
|
||||
### `SandboxProvider` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L73" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `SandboxProvider` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L76" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Interface for executing LLM-generated Python code in a sandbox.
|
||||
|
|
@ -20,13 +20,13 @@ sandbox — never with plain ``exec()``. Use ``MontySandboxProvider``
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `run` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L82" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `run` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L85" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
run(self, code: str) -> Any
|
||||
```
|
||||
|
||||
### `MontySandboxProvider` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L91" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `MontySandboxProvider` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L94" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Sandbox provider backed by `pydantic-monty`.
|
||||
|
|
@ -41,13 +41,13 @@ leave that limit uncapped.
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `run` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L109" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `run` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L112" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
run(self, code: str) -> Any
|
||||
```
|
||||
|
||||
### `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>
|
||||
### `Search` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/experimental/transforms/code_mode.py#L178" 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#L261" 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#L260" 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#L322" 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#L321" 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#L389" 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#L388" 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#L438" 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#L437" 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#L488" 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#L487" 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#L491" 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#L490" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
get_tool(self, name: str, call_next: GetToolNext) -> Tool | None
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Standalone @prompt decorator for FastMCP.
|
|||
|
||||
## Functions
|
||||
|
||||
### `prompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L402" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `prompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L433" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
prompt(name_or_fn: str | Callable[..., Any] | None = None) -> Any
|
||||
|
|
@ -25,19 +25,19 @@ using mcp.add_prompt().
|
|||
|
||||
## Classes
|
||||
|
||||
### `DecoratedPrompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L53" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `DecoratedPrompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L54" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Protocol for functions decorated with @prompt.
|
||||
|
||||
|
||||
### `PromptMeta` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L62" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `PromptMeta` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L63" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Metadata attached to functions by the @prompt decorator.
|
||||
|
||||
|
||||
### `FunctionPrompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L78" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `FunctionPrompt` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L79" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
A prompt that is a function.
|
||||
|
|
@ -45,7 +45,7 @@ A prompt that is a function.
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L84" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `from_function` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L85" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
from_function(cls, fn: Callable[..., Any]) -> FunctionPrompt
|
||||
|
|
@ -66,7 +66,7 @@ The function can return:
|
|||
- PromptResult: used directly
|
||||
|
||||
|
||||
#### `render` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L285" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `render` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L318" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
render(self, arguments: dict[str, Any] | None = None) -> PromptResult
|
||||
|
|
@ -75,7 +75,7 @@ render(self, arguments: dict[str, Any] | None = None) -> PromptResult
|
|||
Render the prompt with arguments.
|
||||
|
||||
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L335" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L370" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
register_with_docket(self, docket: Docket) -> None
|
||||
|
|
@ -83,11 +83,8 @@ register_with_docket(self, docket: Docket) -> None
|
|||
|
||||
Register this prompt with docket for background execution.
|
||||
|
||||
FunctionPrompt registers the underlying function, which has the user's
|
||||
Depends parameters for docket to resolve.
|
||||
|
||||
|
||||
#### `add_to_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L345" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `add_to_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/prompts/function_prompt.py#L376" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
add_to_docket(self, docket: Docket, arguments: dict[str, Any] | None, **kwargs: Any) -> Execution
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Base classes and interfaces for FastMCP resources.
|
|||
|
||||
## Classes
|
||||
|
||||
### `ResourceContent` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L38" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `ResourceContent` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L39" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Wrapper for resource content with optional MIME type and metadata.
|
||||
|
|
@ -21,7 +21,7 @@ other types (dict, list, BaseModel, etc.) are automatically JSON-serialized.
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `to_mcp_resource_contents` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L93" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `to_mcp_resource_contents` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L94" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
to_mcp_resource_contents(self, uri: AnyUrl | str) -> mcp.types.TextResourceContents | mcp.types.BlobResourceContents
|
||||
|
|
@ -36,7 +36,7 @@ Convert to MCP resource contents type.
|
|||
- TextResourceContents for str content, BlobResourceContents for bytes
|
||||
|
||||
|
||||
### `ResourceResult` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L120" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `ResourceResult` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L121" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Canonical result type for resource reads.
|
||||
|
|
@ -47,7 +47,7 @@ per-item MIME types, and metadata at both the item and result level.
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `to_mcp_result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L195" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `to_mcp_result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L202" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
to_mcp_result(self, uri: AnyUrl | str) -> mcp.types.ReadResourceResult
|
||||
|
|
@ -62,7 +62,7 @@ Convert to MCP ReadResourceResult.
|
|||
- MCP ReadResourceResult with converted contents
|
||||
|
||||
|
||||
### `Resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L211" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `Resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L218" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
Base class for all resources.
|
||||
|
|
@ -70,13 +70,13 @@ Base class for all resources.
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L236" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `from_function` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L243" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
from_function(cls, fn: Callable[..., Any], uri: str | AnyUrl) -> FunctionResource
|
||||
```
|
||||
|
||||
#### `set_default_mime_type` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L275" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `set_default_mime_type` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L282" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
set_default_mime_type(cls, mime_type: str | None) -> str
|
||||
|
|
@ -85,7 +85,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str
|
|||
Set default MIME type if not provided.
|
||||
|
||||
|
||||
#### `set_default_name` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L282" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `set_default_name` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L289" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
set_default_name(self) -> Self
|
||||
|
|
@ -94,7 +94,7 @@ set_default_name(self) -> Self
|
|||
Set default name from URI if not provided.
|
||||
|
||||
|
||||
#### `read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L292" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L299" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
read(self) -> str | bytes | ResourceResult
|
||||
|
|
@ -108,7 +108,7 @@ Subclasses implement this to return resource data. Supported return types:
|
|||
- ResourceResult: Full control over contents and result-level meta
|
||||
|
||||
|
||||
#### `convert_result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L304" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `convert_result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L311" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
convert_result(self, raw_value: Any) -> ResourceResult
|
||||
|
|
@ -131,7 +131,7 @@ MCP Apps CSP/permissions) is propagated to each content item so
|
|||
that hosts can read it from the ``resources/read`` response.
|
||||
|
||||
|
||||
#### `to_mcp_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L375" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `to_mcp_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L405" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
to_mcp_resource(self, **overrides: Any) -> SDKResource
|
||||
|
|
@ -140,7 +140,7 @@ to_mcp_resource(self, **overrides: Any) -> SDKResource
|
|||
Convert the resource to an SDKResource.
|
||||
|
||||
|
||||
#### `key` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L398" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `key` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L428" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
key(self) -> str
|
||||
|
|
@ -149,7 +149,7 @@ key(self) -> str
|
|||
The globally unique lookup key for this resource.
|
||||
|
||||
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L403" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L433" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
register_with_docket(self, docket: Docket) -> None
|
||||
|
|
@ -158,7 +158,7 @@ register_with_docket(self, docket: Docket) -> None
|
|||
Register this resource with docket for background execution.
|
||||
|
||||
|
||||
#### `add_to_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L409" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `add_to_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L439" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
add_to_docket(self, docket: Docket, **kwargs: Any) -> Execution
|
||||
|
|
@ -173,7 +173,7 @@ Schedule this resource for background execution via docket.
|
|||
- `**kwargs`: Additional kwargs passed to docket.add()
|
||||
|
||||
|
||||
#### `get_span_attributes` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L430" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `get_span_attributes` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/base.py#L460" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
get_span_attributes(self) -> dict[str, Any]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Standalone @resource decorator for FastMCP.
|
|||
|
||||
## Functions
|
||||
|
||||
### `resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/function_resource.py#L241" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/function_resource.py#L239" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
resource(uri: str) -> Callable[[F], F]
|
||||
|
|
@ -71,7 +71,7 @@ individual parameters must not be passed.
|
|||
Cannot be used together with metadata parameter.
|
||||
|
||||
|
||||
#### `read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/function_resource.py#L209" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/function_resource.py#L211" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
read(self) -> str | bytes | ResourceResult
|
||||
|
|
@ -80,7 +80,7 @@ read(self) -> str | bytes | ResourceResult
|
|||
Read the resource by calling the wrapped function.
|
||||
|
||||
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/function_resource.py#L230" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/function_resource.py#L232" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
register_with_docket(self, docket: Docket) -> None
|
||||
|
|
@ -88,6 +88,3 @@ register_with_docket(self, docket: Docket) -> None
|
|||
|
||||
Register this resource with docket for background execution.
|
||||
|
||||
FunctionResource registers the underlying function, which has the user's
|
||||
Depends parameters for docket to resolve.
|
||||
|
||||
|
|
|
|||
|
|
@ -34,11 +34,14 @@ Supports:
|
|||
- `{var*}` - wildcard path parameter (captures multiple segments)
|
||||
- `{?var1,var2}` - query parameters (ignored in path matching)
|
||||
|
||||
Hyphens in parameter names are normalized to underscores in regex group
|
||||
names so that matched groups are valid Python identifiers.
|
||||
|
||||
Returns None if the template produces an invalid regex (e.g. parameter
|
||||
names with hyphens, leading digits, or duplicates from a remote server).
|
||||
names with leading digits or duplicates from a remote server).
|
||||
|
||||
|
||||
### `match_uri_template` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L79" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `match_uri_template` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L84" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None
|
||||
|
|
@ -52,9 +55,23 @@ Supports RFC 6570 URI templates:
|
|||
- Query params: `{?var1,var2}`
|
||||
|
||||
|
||||
### `expand_uri_template` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L123" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
expand_uri_template(uri_template: str, params: dict[str, Any]) -> str
|
||||
```
|
||||
|
||||
|
||||
Expand a URI template with parameters — inverse of `match_uri_template`.
|
||||
|
||||
Supports the same RFC 6570 subset:
|
||||
- Path params: `{var}`, `{var*}`
|
||||
- Query params: `{?var1,var2}`
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `ResourceTemplate` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L112" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `ResourceTemplate` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L163" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
A template for dynamically creating resources.
|
||||
|
|
@ -62,13 +79,13 @@ A template for dynamically creating resources.
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L139" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `from_function` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L190" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
from_function(fn: Callable[..., Any], uri_template: str, name: str | None = None, version: str | int | None = None, title: str | None = None, description: str | None = None, icons: list[Icon] | None = None, mime_type: str | None = None, tags: set[str] | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None, task: bool | TaskConfig | None = None, auth: AuthCheck | list[AuthCheck] | None = None) -> FunctionResourceTemplate
|
||||
```
|
||||
|
||||
#### `set_default_mime_type` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L172" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `set_default_mime_type` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L223" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
set_default_mime_type(cls, mime_type: str | None) -> str
|
||||
|
|
@ -77,7 +94,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str
|
|||
Set default MIME type if not provided.
|
||||
|
||||
|
||||
#### `matches` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L178" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `matches` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L229" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
matches(self, uri: str) -> dict[str, Any] | None
|
||||
|
|
@ -86,7 +103,7 @@ matches(self, uri: str) -> dict[str, Any] | None
|
|||
Check if URI matches template and extract parameters.
|
||||
|
||||
|
||||
#### `read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L182" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L233" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult
|
||||
|
|
@ -95,7 +112,7 @@ read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult
|
|||
Read the resource content.
|
||||
|
||||
|
||||
#### `convert_result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L188" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `convert_result` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L239" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
convert_result(self, raw_value: Any) -> ResourceResult
|
||||
|
|
@ -111,7 +128,7 @@ Handles ResourceResult passthrough and converts raw values using
|
|||
ResourceResult's normalization.
|
||||
|
||||
|
||||
#### `create_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L252" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `create_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L303" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
create_resource(self, uri: str, params: dict[str, Any]) -> Resource
|
||||
|
|
@ -123,7 +140,7 @@ The base implementation does not support background tasks.
|
|||
Use FunctionResourceTemplate for task support.
|
||||
|
||||
|
||||
#### `to_mcp_template` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L263" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `to_mcp_template` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L314" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
to_mcp_template(self, **overrides: Any) -> SDKResourceTemplate
|
||||
|
|
@ -132,7 +149,7 @@ to_mcp_template(self, **overrides: Any) -> SDKResourceTemplate
|
|||
Convert the resource template to an SDKResourceTemplate.
|
||||
|
||||
|
||||
#### `from_mcp_template` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L283" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `from_mcp_template` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L334" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
from_mcp_template(cls, mcp_template: SDKResourceTemplate) -> ResourceTemplate
|
||||
|
|
@ -141,7 +158,7 @@ from_mcp_template(cls, mcp_template: SDKResourceTemplate) -> ResourceTemplate
|
|||
Creates a FastMCP ResourceTemplate from a raw MCP ResourceTemplate object.
|
||||
|
||||
|
||||
#### `key` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L296" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `key` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L347" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
key(self) -> str
|
||||
|
|
@ -150,7 +167,7 @@ key(self) -> str
|
|||
The globally unique lookup key for this template.
|
||||
|
||||
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L301" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L352" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
register_with_docket(self, docket: Docket) -> None
|
||||
|
|
@ -159,7 +176,7 @@ register_with_docket(self, docket: Docket) -> None
|
|||
Register this template with docket for background execution.
|
||||
|
||||
|
||||
#### `add_to_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L307" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `add_to_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L358" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
add_to_docket(self, docket: Docket, params: dict[str, Any], **kwargs: Any) -> Execution
|
||||
|
|
@ -175,13 +192,13 @@ Schedule this template for background execution via docket.
|
|||
- `**kwargs`: Additional kwargs passed to docket.add()
|
||||
|
||||
|
||||
#### `get_span_attributes` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L330" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `get_span_attributes` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L381" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
get_span_attributes(self) -> dict[str, Any]
|
||||
```
|
||||
|
||||
### `FunctionResourceTemplate` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L337" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
### `FunctionResourceTemplate` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L388" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
|
||||
A template for dynamically creating resources.
|
||||
|
|
@ -189,7 +206,7 @@ A template for dynamically creating resources.
|
|||
|
||||
**Methods:**
|
||||
|
||||
#### `create_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L383" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `create_resource` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L434" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
create_resource(self, uri: str, params: dict[str, Any]) -> Resource
|
||||
|
|
@ -198,7 +215,7 @@ create_resource(self, uri: str, params: dict[str, Any]) -> Resource
|
|||
Create a resource from the template with the given parameters.
|
||||
|
||||
|
||||
#### `read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L402" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `read` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L453" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult
|
||||
|
|
@ -207,7 +224,7 @@ read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult
|
|||
Read the resource content.
|
||||
|
||||
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L441" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `register_with_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L492" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
register_with_docket(self, docket: Docket) -> None
|
||||
|
|
@ -215,11 +232,8 @@ register_with_docket(self, docket: Docket) -> None
|
|||
|
||||
Register this template with docket for background execution.
|
||||
|
||||
FunctionResourceTemplate registers the underlying function, which has the
|
||||
user's Depends parameters for docket to resolve.
|
||||
|
||||
|
||||
#### `add_to_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L451" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `add_to_docket` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L498" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
add_to_docket(self, docket: Docket, params: dict[str, Any], **kwargs: Any) -> Execution
|
||||
|
|
@ -237,7 +251,7 @@ FunctionResourceTemplate splats the params dict since .fn expects **kwargs.
|
|||
- `**kwargs`: Additional kwargs passed to docket.add()
|
||||
|
||||
|
||||
#### `from_function` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L477" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
#### `from_function` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/src/fastmcp/resources/template.py#L524" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
||||
|
||||
```python
|
||||
from_function(cls, fn: Callable[..., Any], uri_template: str, name: str | None = None, version: str | int | None = None, title: str | None = None, description: str | None = None, icons: list[Icon] | None = None, mime_type: str | None = None, tags: set[str] | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None, task: bool | TaskConfig | None = None, auth: AuthCheck | list[AuthCheck] | None = None) -> FunctionResourceTemplate
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue