Improve env vars for marvin (#1972)
7
.ccignore
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.pre-commit-config.yaml
|
||||
.github/
|
||||
docs/changelog.mdx
|
||||
docs/python-sdk/
|
||||
examples/
|
||||
src/fastmcp/contrib/
|
||||
tests/contrib/
|
||||
13
.cursor/rules/core-mcp-objects.mdc
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
There are four major MCP object types:
|
||||
|
||||
- Tools (src/tools/)
|
||||
- Resources (src/resources/)
|
||||
- Resource Templates (src/resources/)
|
||||
- Prompts (src/prompts)
|
||||
|
||||
While these have slightly different semantics and implementations, in general changes that affect interactions with any one (like adding tags, importing, etc.) will need to be adopted, applied, and tested on all others. Be sure to look at not only the object definition but also the related `Manager` (e.g. `ToolManager`, `ResourceManager`, and `PromptManager`). Also note that while resources and resource templates are different objects, they both are handled by the `ResourceManager`.
|
||||
71
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
name: 🐛 Bug Report
|
||||
description: Report a bug or unexpected behavior in FastMCP
|
||||
labels: [bug, pending]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for contributing to FastMCP! 🙏
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Before you submit
|
||||
|
||||
To help us help you, please:
|
||||
|
||||
- 🔄 **Make sure you're testing on the latest version of FastMCP** - many issues are already fixed in newer versions
|
||||
- 🔍 **Check if someone else has already reported this issue** or if it's been fixed on the main branch
|
||||
- 📋 **You MUST include a copy/pasteable and properly formatted MRE** (minimal reproducible example) below or your issue may be closed without response
|
||||
|
||||
Thanks for helping to make FastMCP better! 🚀
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
Please explain what you're experiencing and what you would expect to happen instead.
|
||||
|
||||
Provide as much detail as possible to help us understand and solve your problem quickly.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: example
|
||||
attributes:
|
||||
label: Example Code
|
||||
description: >
|
||||
If applicable, please provide a self-contained,
|
||||
[minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
|
||||
demonstrating the bug. If possible, your example should be a single-file script.
|
||||
|
||||
placeholder: |
|
||||
import asyncio
|
||||
from fastmcp import FastMCP, Client
|
||||
|
||||
mcp = FastMCP()
|
||||
|
||||
async def demo():
|
||||
async with Client(mcp) as client:
|
||||
... # show the bug here
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(demo())
|
||||
render: Python
|
||||
|
||||
- type: textarea
|
||||
id: version
|
||||
attributes:
|
||||
label: Version Information
|
||||
description: |
|
||||
Please tell us about your FastMCP version, MCP version, Python version, and OS, as well as any other relevant details about your environment.
|
||||
|
||||
To get the basic information, run the following command in your terminal and paste the output below:
|
||||
|
||||
```bash
|
||||
fastmcp version --copy
|
||||
```
|
||||
render: Text
|
||||
validations:
|
||||
required: true
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: FastMCP Documentation
|
||||
url: https://gofastmcp.com
|
||||
about: Please review the documentation before opening an issue.
|
||||
- name: MCP Python SDK
|
||||
url: https://github.com/modelcontextprotocol/python-sdk/issues
|
||||
about: Issues related to the low-level MCP Python SDK, including the FastMCP 1.0 module that is included in the `mcp` package, should be filed on the official MCP repository.
|
||||
34
.github/ISSUE_TEMPLATE/enhancement.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
name: 💡 Enhancement Request
|
||||
description: Suggest an idea or improvement for FastMCP
|
||||
labels: [enhancement, pending]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for contributing to FastMCP! 🙏
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Before you submit
|
||||
|
||||
To help us evaluate your enhancement request:
|
||||
|
||||
- 🔍 **Check if this has already been requested** - search existing issues first
|
||||
- 💭 **Think about the broader impact** - how would this affect other users?
|
||||
- 📋 **Consider implementation complexity** - is this a small change or a major feature?
|
||||
|
||||
Thanks for helping to make FastMCP better! 🚀
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Enhancement
|
||||
description: |
|
||||
Please describe the enhancement:
|
||||
|
||||
- What problem or use case would it solve?
|
||||
- How would it improve your workflow or experience with FastMCP?
|
||||
- Are there any alternative solutions you've considered?
|
||||
validations:
|
||||
required: true
|
||||
1
.github/copilot-instructions.md
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../AGENTS.md
|
||||
20
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "uv"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
28
.github/pull_request_template.md
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
## Description
|
||||
<!--
|
||||
Please provide a clear and concise description of the changes made in this pull request.
|
||||
|
||||
Using AI to generate code? Please include a note in the description with which AI tool you used.
|
||||
-->
|
||||
|
||||
**Contributors Checklist**
|
||||
<!--
|
||||
NOTE:
|
||||
1. You must create an issue in the repository before making a Pull Request.
|
||||
2. You must not create a Pull Request for an issue that is already assigned to someone else.
|
||||
|
||||
If you do not follow these steps, your Pull Request will be closed without review.
|
||||
-->
|
||||
|
||||
- [ ] My change closes #(issue number)
|
||||
- [ ] I have followed the repository's development workflow
|
||||
- [ ] I have tested my changes manually and by adding relevant tests
|
||||
- [ ] I have performed all required documentation updates
|
||||
|
||||
**Review Checklist**
|
||||
<!-- Your Pull Request will not be reviewed if tests are failing, you have not self-reviewed your changes, or you have not checked all of the following: -->
|
||||
|
||||
- [ ] I have self-reviewed my changes
|
||||
- [ ] My Pull Request is ready for review
|
||||
|
||||
---
|
||||
47
.github/release.yml
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- ignore in release notes
|
||||
|
||||
categories:
|
||||
- title: New Features 🎉
|
||||
labels:
|
||||
- feature
|
||||
|
||||
- title: Enhancements 🔧
|
||||
labels:
|
||||
- enhancement
|
||||
exclude:
|
||||
labels:
|
||||
- breaking change
|
||||
|
||||
- title: Fixes 🐞
|
||||
labels:
|
||||
- bug
|
||||
exclude:
|
||||
labels:
|
||||
- contrib
|
||||
|
||||
- title: Breaking Changes 🛫
|
||||
labels:
|
||||
- breaking change
|
||||
exclude:
|
||||
labels:
|
||||
- contrib
|
||||
|
||||
- title: Docs 📚
|
||||
labels:
|
||||
- documentation
|
||||
|
||||
- title: Examples & Contrib 💡
|
||||
labels:
|
||||
- example
|
||||
- contrib
|
||||
|
||||
- title: Dependencies 📦
|
||||
labels:
|
||||
- dependencies
|
||||
|
||||
- title: Other Changes 🦾
|
||||
labels:
|
||||
- "*"
|
||||
28
.github/workflows/auto-close-duplicates.yml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
name: Auto-close duplicate issues
|
||||
description: Auto-closes issues that are duplicates of existing issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 9 * * *" # Run daily at 9 AM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
auto-close-duplicates:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
- name: Auto-close duplicate issues
|
||||
run: uv run scripts/auto_close_duplicates.py
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
|
||||
GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}
|
||||
92
.github/workflows/marvin-dedupe-issues.yml
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
name: Marvin Issue Dedupe
|
||||
description: Automatically dedupe GitHub issues using Marvin
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: "Issue number to process for duplicate detection"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
marvin-dedupe-issues:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Generate Marvin App token
|
||||
id: marvin-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.MARVIN_APP_ID }}
|
||||
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Set dedupe prompt
|
||||
id: dedupe-prompt
|
||||
run: |
|
||||
cat >> $GITHUB_OUTPUT << 'EOF'
|
||||
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:
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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. If there are no duplicates remaining, do not proceed.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
- Never include this issue as a duplicate of itself
|
||||
|
||||
For your comment, follow this format precisely (example with 3 suspected duplicates):
|
||||
|
||||
---
|
||||
Found 3 possible duplicate issues:
|
||||
|
||||
1. #123: Issue title here
|
||||
2. #456: Another issue title
|
||||
3. #789: Third issue title
|
||||
|
||||
This issue will be automatically closed as a duplicate in 3 days.
|
||||
|
||||
- If your issue is a duplicate, please close it and 👍 the existing issue instead
|
||||
- To prevent auto-closure, add a comment or 👎 this comment
|
||||
|
||||
---
|
||||
PROMPT_END
|
||||
EOF
|
||||
|
||||
- name: Run Marvin dedupe command
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
github_token: ${{ steps.marvin-token.outputs.token }}
|
||||
bot_name: "Marvin Context Protocol"
|
||||
prompt: ${{ steps.dedupe-prompt.outputs.PROMPT }}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_FOR_CI }}
|
||||
claude_args: |
|
||||
--allowedTools Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh api:*),Bash(gh issue comment:*),Task
|
||||
--mcp-config /tmp/mcp-config/mcp-servers.json
|
||||
settings: |
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929",
|
||||
"env": {
|
||||
"GH_TOKEN": "${{ steps.marvin-token.outputs.token }}"
|
||||
}
|
||||
}
|
||||
142
.github/workflows/marvin-label-triage.yml
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
name: Marvin Label Triage
|
||||
description: Automatically triage GitHub issues and PRs using Marvin
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: "Issue or PR number to triage"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: triage-${{ github.event.issue.number || github.event.pull_request.number || inputs.issue_number }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
triage-issue:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout base repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
|
||||
- name: Generate Marvin App token
|
||||
id: marvin-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.MARVIN_APP_ID }}
|
||||
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Set triage prompt
|
||||
id: triage-prompt
|
||||
run: |
|
||||
cat >> $GITHUB_OUTPUT << 'EOF'
|
||||
PROMPT<<PROMPT_END
|
||||
You're an issue triage assistant for FastMCP, a Python framework for building Model Context Protocol servers and clients. Your task is to analyze issues/PRs and apply appropriate labels.
|
||||
|
||||
IMPORTANT: Your ONLY action should be to apply labels using mcp__github__update_issue. DO NOT post any comments.
|
||||
|
||||
Issue/PR Information:
|
||||
- REPO: ${{ github.repository }}
|
||||
- NUMBER: ${{ github.event.issue.number || github.event.pull_request.number || inputs.issue_number }}
|
||||
- TYPE: ${{ github.event.issue && 'issue' || (github.event.pull_request && 'pull_request') || 'unknown' }}
|
||||
|
||||
TRIAGE PROCESS:
|
||||
|
||||
1. Get available labels:
|
||||
Run: `gh label list`
|
||||
|
||||
2. Retrieve issue/PR details using GitHub tools:
|
||||
- mcp__github__get_issue: Get the issue/PR details
|
||||
- mcp__github__get_issue_comments: Read any discussion
|
||||
- If the issue/PR mentions other issues (e.g., "fixes #123", "related to #456"), use mcp__github__get_issue to read those linked issues for additional context
|
||||
|
||||
3. Analyze and apply labels based on these guidelines:
|
||||
|
||||
CORE CATEGORIES (apply EXACTLY ONE - these are mutually exclusive):
|
||||
- bug: Reports of broken functionality OR PRs that fix bugs
|
||||
- enhancement: New functions/endpoints, improvements to existing features, internal tooling, workflow improvements, minor new capabilities
|
||||
- feature: ONLY for major headline functionality worthy of a blog post announcement (2-4 per release, never for issues)
|
||||
- documentation: Primary change is to user-facing docs, examples, or guides
|
||||
|
||||
SPECIAL DOCUMENTATION RULES:
|
||||
- DO NOT apply "documentation" label if PR only updates auto-generated SDK docs (docs/python-sdk/**)
|
||||
- DO apply "documentation" label for significant user-facing documentation changes (guides, examples, API docs)
|
||||
- Auto-generated docs updates should get appropriate category label (enhancement, bug, etc.) based on the underlying code changes
|
||||
|
||||
FEATURE vs ENHANCEMENT guidance:
|
||||
- feature: Major systems like new auth systems, MCP composition, proxying MCP servers, major CLI commands that transform workflows
|
||||
- enhancement: New functions/endpoints, internal workflows, CI improvements, developer tooling, refactoring, utilities, typical new CLI commands
|
||||
- If unsure between feature/enhancement, choose enhancement
|
||||
|
||||
Note: If a PR fixes a bug, label it "bug" not "enhancement"
|
||||
|
||||
SPECIAL CATEGORY (can be combined with above):
|
||||
- breaking change: Changes that break backward compatibility (in addition to core category)
|
||||
|
||||
PRIORITY (apply if clearly evident):
|
||||
- high-priority: Critical bugs affecting many users, security issues, or blocking core functionality
|
||||
- low-priority: Edge cases, nice-to-have improvements, or cosmetic issues
|
||||
- Default to no priority label if unclear
|
||||
|
||||
STATUS (apply if applicable):
|
||||
- needs more info: Issue lacks reproduction steps, error messages, or clear description
|
||||
- good first issue: ONLY if it's clearly scoped, has obvious solution, and touches limited files
|
||||
- invalid: Spam, completely off-topic, or nonsensical (often LLM-generated)
|
||||
|
||||
AREA LABELS (apply ONLY when thematically central to the issue):
|
||||
- cli: Issues primarily about FastMCP CLI commands (run, dev, install)
|
||||
- client: Issues primarily about the Client SDK or client-side functionality
|
||||
- server: Issues primarily about FastMCP server implementation
|
||||
- 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/
|
||||
- tests: Issues primarily about testing infrastructure, CI/CD workflows, or test coverage
|
||||
|
||||
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)
|
||||
|
||||
META LABELS (rarely needed for issues):
|
||||
- dependencies: Only for dependabot PRs or issues specifically about package updates
|
||||
- DON'T MERGE: Only if PR author explicitly states it's not ready
|
||||
|
||||
4. Apply selected labels:
|
||||
Use mcp__github__update_issue to apply your selected labels
|
||||
DO NOT post any comments
|
||||
PROMPT_END
|
||||
EOF
|
||||
|
||||
- name: Run Marvin for Issue Triage
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
github_token: ${{ steps.marvin-token.outputs.token }}
|
||||
bot_name: "Marvin Context Protocol"
|
||||
prompt: ${{ steps.triage-prompt.outputs.PROMPT }}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_FOR_CI }}
|
||||
claude_args: |
|
||||
--allowedTools Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__get_pull_request_files
|
||||
--mcp-config /tmp/mcp-config/mcp-servers.json
|
||||
settings: |
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929",
|
||||
"env": {
|
||||
"GH_TOKEN": "${{ steps.marvin-token.outputs.token }}"
|
||||
}
|
||||
}
|
||||
78
.github/workflows/marvin.yml
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
name: Marvin Context Protocol
|
||||
|
||||
on:
|
||||
issue_comment: { types: [created] }
|
||||
pull_request_review_comment: { types: [created] }
|
||||
pull_request_review: { types: [submitted] }
|
||||
pull_request: { types: [opened, edited] }
|
||||
issues: { types: [opened, edited, assigned, labeled] }
|
||||
discussion: { types: [created, edited, labeled] }
|
||||
discussion_comment: { types: [created] }
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
actions: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
marvin:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '/marvin')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/marvin')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '/marvin')) ||
|
||||
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/marvin')) ||
|
||||
(github.event_name == 'issues' && contains(github.event.issue.body, '/marvin')) ||
|
||||
(github.event_name == 'discussion' && contains(github.event.discussion.body, '/marvin')) ||
|
||||
(github.event_name == 'discussion_comment' && contains(github.event.comment.body, '/marvin')) ||
|
||||
(github.event_name == 'issues' && github.event.action == 'assigned' && github.event.assignee.login == 'Marvin Context Protocol') ||
|
||||
(github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'marvin')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
# Install UV package manager
|
||||
- name: Install UV
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
# Install project dependencies
|
||||
- name: Install dependencies
|
||||
run: uv sync --python 3.12
|
||||
|
||||
# Install pre-commit hooks automatically
|
||||
- name: Install pre-commit hooks
|
||||
run: |
|
||||
uv run pre-commit install
|
||||
echo "✅ Pre-commit hooks installed"
|
||||
|
||||
- name: Generate Marvin App token
|
||||
id: marvin-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.MARVIN_APP_ID }}
|
||||
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}
|
||||
|
||||
# Marvin Assistant
|
||||
- name: Run Marvin
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
github_token: ${{ steps.marvin-token.outputs.token }}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
trigger_phrase: "/marvin"
|
||||
allowed_bots: "*"
|
||||
claude_args: |
|
||||
--allowedTools WebSearch,WebFetch,Bash(uv:*),Bash(pre-commit:*),Bash(pytest:*),Bash(ruff:*),Bash(ty:*),Bash(git:*),Bash(gh:*),mcp__github__add_issue_comment,mcp__github__create_issue,mcp__github__get_issue,mcp__github__list_issues,mcp__github__search_issues,mcp__github__update_issue,mcp__github__update_issue_comment,mcp__github__create_pull_request,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_files,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__list_pull_requests,mcp__github__update_pull_request,mcp__github__update_pull_request_branch,mcp__github__update_pull_request_comment,mcp__github__merge_pull_request
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
settings: |
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929",
|
||||
"env": {
|
||||
"GH_TOKEN": "${{ steps.marvin-token.outputs.token }}"
|
||||
}
|
||||
}
|
||||
26
.github/workflows/publish.yml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
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@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@v6
|
||||
|
||||
- name: Build
|
||||
run: uv build
|
||||
|
||||
- name: Publish to PyPi
|
||||
run: uv publish -v dist/*
|
||||
54
.github/workflows/run-static.yml
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
name: Run static analysis
|
||||
|
||||
env:
|
||||
# enable colored output
|
||||
# https://github.com/pytest-dev/pytest/issues/7443
|
||||
PY_COLORS: 1
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "tests/**"
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
- ".github/workflows/**"
|
||||
|
||||
# run on all pull requests because these checks are required and will block merges otherwise
|
||||
pull_request:
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
static_analysis:
|
||||
timeout-minutes: 2
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync
|
||||
|
||||
- name: Check lockfile is up to date
|
||||
run: |
|
||||
if ! uv lock --check; then
|
||||
echo "❌ Lockfile is out of date!"
|
||||
echo "To update the lockfile, run 'uv lock'."
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Lockfile is up to date"
|
||||
- name: Run pre-commit
|
||||
run: uv run pre-commit run --all-files
|
||||
env:
|
||||
SKIP: no-commit-to-branch
|
||||
81
.github/workflows/run-tests.yml
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
name: Run tests
|
||||
|
||||
env:
|
||||
# enable colored output
|
||||
PY_COLORS: 1
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "tests/**"
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
- ".github/workflows/**"
|
||||
|
||||
# run on all pull requests because these checks are required and will block merges otherwise
|
||||
pull_request:
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
run_tests:
|
||||
name: "Run tests: Python ${{ matrix.python-version }} on ${{ matrix.os }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
python-version: ["3.10"]
|
||||
fail-fast: false
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install FastMCP
|
||||
# run with frozen to use the current lockfile; static checks will determine if it needs updating
|
||||
run: uv sync --frozen
|
||||
|
||||
- name: Run tests (excluding integration and client_process)
|
||||
run: uv run pytest --inline-snapshot=disable tests -m "not integration and not client_process" --numprocesses auto --maxprocesses 4 --dist worksteal
|
||||
|
||||
- name: Run client process tests separately
|
||||
run: uv run pytest --inline-snapshot=disable tests -m "client_process" -x
|
||||
|
||||
run_integration_tests:
|
||||
name: "Run integration tests"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install FastMCP
|
||||
# run with frozen to use the current lockfile; static checks will determine if it needs updating
|
||||
run: uv sync --frozen
|
||||
|
||||
- name: Run integration tests
|
||||
# use longer per-test timeout than the default 3s
|
||||
run: uv run pytest tests -m "integration" --timeout=15 --numprocesses auto --maxprocesses 2 --dist worksteal
|
||||
env:
|
||||
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 }}
|
||||
91
.github/workflows/update-config-schema.yml
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
name: Update MCPServerConfig Schema
|
||||
|
||||
# This workflow runs on merges to main to automatically update the config schema
|
||||
# by creating a PR when changes are needed.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/fastmcp/utilities/mcp_server_config/**"
|
||||
- "!src/fastmcp/utilities/mcp_server_config/v1/schema.json" # Exclude the local schema file
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
update-config-schema:
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Generate Marvin App token
|
||||
id: marvin-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.MARVIN_APP_ID }}
|
||||
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --python 3.12
|
||||
|
||||
- name: Generate config schema
|
||||
run: |
|
||||
echo "🔄 Generating fastmcp.json schema..."
|
||||
|
||||
# Generate schema in docs/public for web access
|
||||
uv run python -c "
|
||||
from fastmcp.utilities.mcp_server_config import generate_schema
|
||||
generate_schema('docs/public/schemas/fastmcp.json/latest.json')
|
||||
print('✅ Latest schema generated in docs/public')
|
||||
"
|
||||
|
||||
# Also update the v1 schema in docs/public
|
||||
uv run python -c "
|
||||
from fastmcp.utilities.mcp_server_config import generate_schema
|
||||
generate_schema('docs/public/schemas/fastmcp.json/v1.json')
|
||||
print('✅ v1 schema generated in docs/public')
|
||||
"
|
||||
|
||||
# Generate schema in the source directory for local development
|
||||
uv run python -c "
|
||||
from fastmcp.utilities.mcp_server_config import generate_schema
|
||||
generate_schema('src/fastmcp/utilities/mcp_server_config/v1/schema.json')
|
||||
print('✅ Schema generated in utilities/mcp_server_config/v1/')
|
||||
"
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ steps.marvin-token.outputs.token }}
|
||||
commit-message: "chore: Update fastmcp.json schema"
|
||||
title: "chore: Update fastmcp.json schema"
|
||||
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.
|
||||
|
||||
**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. Feel free to leave it open until you're ready to merge.
|
||||
|
||||
🤖 Generated by Marvin
|
||||
branch: marvin/update-config-schema
|
||||
labels: |
|
||||
ignore in release notes
|
||||
delete-branch: true
|
||||
author: "marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>"
|
||||
committer: "marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "✅ Config schema generation workflow completed"
|
||||
echo "PR will be created if there are changes, or closed if schema is already up to date"
|
||||
74
.github/workflows/update-sdk-docs.yml
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
name: Update SDK Documentation
|
||||
|
||||
# This workflow runs on merges to main to automatically update SDK docs
|
||||
# by creating a PR when changes are needed.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "pyproject.toml"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
update-sdk-docs:
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Generate Marvin App token
|
||||
id: marvin-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.MARVIN_APP_ID }}
|
||||
private-key: ${{ secrets.MARVIN_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --python 3.12
|
||||
|
||||
- name: Install just
|
||||
uses: extractions/setup-just@v3
|
||||
|
||||
- name: Generate SDK documentation
|
||||
run: |
|
||||
echo "🔄 Generating SDK documentation..."
|
||||
just api-ref-all
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ steps.marvin-token.outputs.token }}
|
||||
commit-message: "chore: Update SDK documentation"
|
||||
title: "chore: Update SDK documentation"
|
||||
body: |
|
||||
This PR updates the auto-generated SDK documentation to reflect the latest source code changes.
|
||||
|
||||
📚 Documentation is automatically generated from the source code docstrings and type annotations.
|
||||
|
||||
**Note:** This PR is fully automated and will update itself with any subsequent changes to the SDK, or close automatically if the documentation becomes up-to-date through other means. Feel free to leave it open until you're ready to merge.
|
||||
|
||||
🤖 Generated by Marvin
|
||||
branch: marvin/update-sdk-docs
|
||||
labels: |
|
||||
ignore in release notes
|
||||
delete-branch: true
|
||||
author: "marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>"
|
||||
committer: "marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "✅ SDK documentation generation workflow completed"
|
||||
echo "PR will be created if there are changes, or closed if documentation is already up to date"
|
||||
76
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
*.egg
|
||||
MANIFEST
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
.tox/
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
.env
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
|
||||
# Version file
|
||||
src/fastmcp/_version.py
|
||||
|
||||
# Editors and IDEs
|
||||
.cursorrules
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.project
|
||||
.pydevproject
|
||||
.settings/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# Type checking
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
.pyre/
|
||||
.pytype/
|
||||
|
||||
# Local development
|
||||
.python-version
|
||||
.envrc
|
||||
.direnv/
|
||||
|
||||
# Logs and databases
|
||||
*.log
|
||||
*.sqlite
|
||||
*.db
|
||||
*.ddb
|
||||
|
||||
# Claude worktree management
|
||||
.claude-wt/worktrees
|
||||
|
||||
# Agents
|
||||
/PLAN.md
|
||||
/TODO.md
|
||||
/STATUS.md
|
||||
|
||||
# Common FastMCP test files
|
||||
/test.py
|
||||
/server.py
|
||||
/client.py
|
||||
/test.json
|
||||
48
.pre-commit-config.yaml
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
fail_fast: false
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.24.1
|
||||
hooks:
|
||||
- id: validate-pyproject
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.1.0
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or: [yaml, json5]
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.12.1
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff-check
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: ty
|
||||
name: ty check
|
||||
entry: uv run ty check
|
||||
language: system
|
||||
types: [python]
|
||||
files: ^src/|^tests/
|
||||
pass_filenames: false
|
||||
require_serial: true
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: no-commit-to-branch
|
||||
name: prevent commits to main
|
||||
args: [--branch, main]
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.4.1
|
||||
hooks:
|
||||
- id: codespell # See pyproject.toml for args
|
||||
additional_dependencies:
|
||||
- tomli
|
||||
1
.python-version
Normal file
|
|
@ -0,0 +1 @@
|
|||
3.12
|
||||
265
AGENTS.md
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
# FastMCP Development Guidelines
|
||||
|
||||
> **Audience**: LLM-driven engineering agents and human developers
|
||||
|
||||
FastMCP is a comprehensive Python framework (Python ≥3.10) for building Model Context Protocol (MCP) servers and clients. This is the actively maintained v2.0 providing a complete toolkit for the MCP ecosystem.
|
||||
|
||||
## Required Development Workflow
|
||||
|
||||
**CRITICAL**: Always run these commands in sequence before committing:
|
||||
|
||||
```bash
|
||||
uv sync # Install dependencies
|
||||
uv run pre-commit run --all-files # Ruff + Prettier + ty
|
||||
uv run pytest # Run full test suite
|
||||
```
|
||||
|
||||
**All three must pass** - this is enforced by CI. Alternative: `just build && just typecheck && just test`
|
||||
|
||||
**Tests must pass and lint/typing must be clean before committing.**
|
||||
|
||||
## Repository Structure
|
||||
|
||||
| Path | Purpose |
|
||||
| ------------------ | --------------------------------------------------- |
|
||||
| `src/fastmcp/` | Library source code (Python ≥ 3.10) |
|
||||
| `├─server/` | Server implementation, `FastMCP`, auth, networking |
|
||||
| `│ ├─auth/` | Authentication providers (Bearer, JWT, WorkOS) |
|
||||
| `│ └─middleware/` | Error handling, logging, rate limiting |
|
||||
| `├─client/` | High-level client SDK + transports |
|
||||
| `│ └─auth/` | Client authentication (Bearer, OAuth) |
|
||||
| `├─tools/` | Tool implementations + `ToolManager` |
|
||||
| `├─resources/` | Resources, templates + `ResourceManager` |
|
||||
| `├─prompts/` | Prompt templates + `PromptManager` |
|
||||
| `├─cli/` | FastMCP CLI commands (`run`, `dev`, `install`) |
|
||||
| `├─contrib/` | Community contributions (bulk caller, mixins) |
|
||||
| `├─experimental/` | Experimental features (new OpenAPI parser) |
|
||||
| `└─utilities/` | Shared utilities (logging, JSON schema, HTTP) |
|
||||
| `tests/` | Comprehensive pytest suite with markers |
|
||||
| `docs/` | Mintlify documentation (published to gofastmcp.com) |
|
||||
| `examples/` | Runnable demo servers (echo, smart_home, atproto) |
|
||||
|
||||
## Core MCP Objects
|
||||
|
||||
When modifying MCP functionality, changes typically need to be applied across all object types:
|
||||
|
||||
- **Tools** (`src/tools/` + `ToolManager`)
|
||||
- **Resources** (`src/resources/` + `ResourceManager`)
|
||||
- **Resource Templates** (`src/resources/` + `ResourceManager`)
|
||||
- **Prompts** (`src/prompts/` + `PromptManager`)
|
||||
|
||||
## Writing Style
|
||||
|
||||
- Be brief and to the point. Do not regurgitate information that can easily be gleaned from the code, except to guide the reader to where the code is located.
|
||||
- **NEVER** use "This isn't..." or "not just..." constructions. State what something IS directly. Avoid defensive writing patterns like:
|
||||
- "This isn't X, it's Y" or "Not just X, but Y" → Just say "This is Y"
|
||||
- "Not just about X" → State the actual purpose
|
||||
- "We're not doing X, we're doing Y" → Just explain what you're doing
|
||||
- Any variation of explaining what something isn't before what it is
|
||||
|
||||
## Testing Best Practices
|
||||
|
||||
### Testing Standards
|
||||
|
||||
- Every test: atomic, self-contained, single functionality
|
||||
- Use parameterization for multiple examples of same functionality
|
||||
- Use separate tests for different functionality pieces
|
||||
- **ALWAYS** Put imports at the top of the file, not in the test body
|
||||
- **NEVER** add `@pytest.mark.asyncio` to tests - `asyncio_mode = "auto"` is set globally
|
||||
- **ALWAYS** run pytest after significant changes
|
||||
|
||||
### Inline Snapshots
|
||||
|
||||
FastMCP uses `inline-snapshot` for testing complex data structures. On first run with empty `snapshot()`, pytest will auto-populate the expected value when running `pytest --inline-snapshot=create`. To update snapshots after intentional changes, run `pytest --inline-snapshot=fix`. This is particularly useful for testing JSON schemas and API responses.
|
||||
|
||||
### Always Use In-Memory Transport
|
||||
|
||||
Pass FastMCP servers directly to clients for testing:
|
||||
|
||||
```python
|
||||
mcp = FastMCP("TestServer")
|
||||
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
# Direct connection - no network complexity
|
||||
async with Client(mcp) as client:
|
||||
result = await client.call_tool("greet", {"name": "World"})
|
||||
```
|
||||
|
||||
Only use HTTP transport when explicitly testing network features:
|
||||
|
||||
```python
|
||||
# Network testing only
|
||||
async with Client(transport=StreamableHttpTransport(server_url)) as client:
|
||||
result = await client.ping()
|
||||
```
|
||||
|
||||
## Development Rules
|
||||
|
||||
### Git & CI
|
||||
|
||||
- Pre-commit hooks are required (run automatically on commits)
|
||||
- Never amend commits to fix pre-commit failures
|
||||
- Apply PR labels: bugs/breaking/enhancements/features
|
||||
- Improvements = enhancements (not features) unless specified
|
||||
- **NEVER** force-push on collaborative repos
|
||||
- **ALWAYS** run pre-commit before PRs
|
||||
|
||||
### Commit Messages and Agent Attribution
|
||||
|
||||
- **Agents NOT acting on behalf of @jlowin MUST identify themselves** (e.g., "🤖 Generated with Claude Code" in commits/PRs)
|
||||
- Keep commit messages brief - ideally just headlines, not detailed messages
|
||||
- Focus on what changed, not how or why
|
||||
- Always read issue comments for follow-up information (treat maintainers as authoritative)
|
||||
|
||||
### PR Messages - Required Structure
|
||||
|
||||
- 1-2 paragraphs: problem/tension + solution (PRs are documentation!)
|
||||
- Focused code example showing key capability
|
||||
- **Avoid:** bullet summaries, exhaustive change lists, verbose closes/fixes, marketing language
|
||||
- **Do:** Be opinionated about why change matters, show before/after scenarios
|
||||
- Minor fixes: keep body short and concise
|
||||
- No "test plan" sections or testing summaries
|
||||
|
||||
### Code Standards
|
||||
|
||||
- Python ≥ 3.10 with full type annotations
|
||||
- Follow existing patterns and maintain consistency
|
||||
- **Prioritize readable, understandable code** - clarity over cleverness
|
||||
- Avoid obfuscated or confusing patterns even if they're shorter
|
||||
- Use `# type: ignore[attr-defined]` in tests for MCP results instead of type assertions
|
||||
- Each feature needs corresponding tests
|
||||
|
||||
### Documentation
|
||||
|
||||
- Uses Mintlify framework
|
||||
- Files must be in docs.json to be included
|
||||
- Never modify `docs/python-sdk/**` (auto-generated)
|
||||
- **Core Principle:** A feature doesn't exist unless it is documented!
|
||||
|
||||
### Documentation Guidelines
|
||||
|
||||
- **Code Examples:** Explain before showing code, make blocks fully runnable (include imports)
|
||||
- **Structure:** Headers form navigation guide, logical H2/H3 hierarchy
|
||||
- **Content:** User-focused sections, motivate features (why) before mechanics (how)
|
||||
- **Style:** Prose over code comments for important information
|
||||
|
||||
## Code Review Guidelines
|
||||
|
||||
### Philosophy
|
||||
|
||||
Code review is about maintaining a healthy codebase while helping contributors succeed. The burden of proof is on the PR to demonstrate it adds value in the intended way. Your job is to help it get there through actionable feedback.
|
||||
|
||||
**Critical**: A perfectly written PR that adds unwanted functionality must still be rejected. The code must advance the codebase in the intended direction, not just be well-written. When rejecting, provide clear guidance on how to align with project goals.
|
||||
|
||||
Be friendly and welcoming while maintaining high standards. Call out what works well - this reinforces good patterns. When code needs improvement, be specific about why and how to fix it. Remember that PRs serve as documentation for future developers.
|
||||
|
||||
### Focus On
|
||||
|
||||
- **Does this advance the codebase in the intended direction?** (Even perfect code for unwanted features should be rejected)
|
||||
- **API design and naming clarity** - Identify confusing patterns (e.g., parameter values that contradict defaults) or non-idiomatic code (mutable defaults, etc.). Contributed code will need to be maintained indefinitely, and by someone other than the author (unless the author is a maintainer).
|
||||
- **Suggest specific improvements**, not generic "add more tests" comments
|
||||
- **Think about API ergonomics and learning curve** from a user perspective
|
||||
|
||||
### For Agent Reviewers
|
||||
|
||||
- **Read the full context**: Always examine related files, tests, and documentation before reviewing
|
||||
- **Check against established patterns**: Look for consistency with existing codebase conventions
|
||||
- **Verify functionality claims**: Don't just read code - understand what it actually does
|
||||
- **Consider edge cases**: Think through error conditions and boundary scenarios
|
||||
|
||||
### Avoid
|
||||
|
||||
- Generic feedback without specifics
|
||||
- Hypothetical problems unlikely to occur
|
||||
- Nitpicking organizational choices without strong reason
|
||||
- Summarizing what the PR already describes
|
||||
- Star ratings or excessive emojis
|
||||
- Bikeshedding style preferences when functionality is correct
|
||||
- Requesting changes without suggesting solutions
|
||||
- Focusing on personal coding style over project conventions
|
||||
|
||||
### Tone
|
||||
|
||||
- Acknowledge good decisions ("This API design is clean")
|
||||
- Be direct but respectful
|
||||
- Explain impact ("This will confuse users because...")
|
||||
- Remember: Someone else maintains this code forever
|
||||
|
||||
### Decision Framework
|
||||
|
||||
Before approving, ask yourself:
|
||||
|
||||
1. Does this PR achieve its stated purpose?
|
||||
2. Is that purpose aligned with where the codebase should go?
|
||||
3. Would I be comfortable maintaining this code?
|
||||
4. Have I actually understood what it does, not just what it claims?
|
||||
5. Does this change introduce technical debt?
|
||||
|
||||
If something needs work, your review should help it get there through specific, actionable feedback. If it's solving the wrong problem, say so clearly.
|
||||
|
||||
### Review Comment Examples
|
||||
|
||||
**Good Review Comments:**
|
||||
|
||||
❌ "Add more tests"
|
||||
✅ "The `handle_timeout` method needs tests for the edge case where timeout=0"
|
||||
|
||||
❌ "This API is confusing"
|
||||
✅ "The parameter name `data` is ambiguous - consider `message_content` to match the MCP specification"
|
||||
|
||||
❌ "This could be better"
|
||||
✅ "This approach works but creates a circular dependency. Consider moving the validation to `utils/validators.py`"
|
||||
|
||||
### Review Checklist
|
||||
|
||||
Before approving, verify:
|
||||
|
||||
- [ ] All required development workflow steps completed (uv sync, pre-commit, pytest)
|
||||
- [ ] Changes align with repository patterns and conventions
|
||||
- [ ] API changes are documented and backwards-compatible where possible
|
||||
- [ ] Error handling follows project patterns (specific exception types)
|
||||
- [ ] Tests cover new functionality and edge cases
|
||||
|
||||
## Key Tools & Commands
|
||||
|
||||
### Environment Setup
|
||||
|
||||
```bash
|
||||
git clone <repo>
|
||||
cd fastmcp
|
||||
uv sync # Installs all deps including dev tools
|
||||
```
|
||||
|
||||
### Validation Commands (Run Frequently)
|
||||
|
||||
- **Linting**: `uv run ruff check` (or with `--fix`)
|
||||
- **Type Checking**: `uv run ty check`
|
||||
- **All Checks**: `uv run pre-commit run --all-files`
|
||||
|
||||
### Testing
|
||||
|
||||
- **Standard**: `uv run pytest`
|
||||
- **Integration**: `uv run pytest -m "integration"`
|
||||
- **Excluding markers**: `uv run pytest -m "not integration and not client_process"`
|
||||
|
||||
### CLI Usage
|
||||
|
||||
- **Run server**: `uv run fastmcp run server.py`
|
||||
- **Inspect server**: `uv run fastmcp inspect server.py`
|
||||
|
||||
## Critical Patterns
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Never use bare `except` - be specific with exception types
|
||||
- Use `# type: ignore[attr-defined]` in tests for MCP results
|
||||
|
||||
### Build Issues (Common Solutions)
|
||||
|
||||
1. **Dependencies**: Always `uv sync` first
|
||||
2. **Pre-commit fails**: Run `uv run pre-commit run --all-files` to see failures
|
||||
3. **Type errors**: Use `uv run ty check` directly, check `pyproject.toml` config
|
||||
4. **Test timeouts**: Default 3s - optimize or mark as integration tests
|
||||
1
CLAUDE.md
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
AGENTS.md
|
||||
128
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
chris@prefect.io.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
201
LICENSE
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
507
README.md
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
<div align="center">
|
||||
|
||||
<!-- omit in toc -->
|
||||
|
||||
<picture>
|
||||
<source width="550" media="(prefers-color-scheme: dark)" srcset="docs/assets/brand/wordmark-watercolor-waves-dark.png">
|
||||
<source width="550" media="(prefers-color-scheme: light)" srcset="docs/assets/brand/wordmark-watercolor-waves.png">
|
||||
<img width="550" alt="FastMCP Logo" src="docs/assets/brand/wordmark-watercolor-waves.png">
|
||||
</picture>
|
||||
|
||||
# FastMCP v2 🚀
|
||||
|
||||
<strong>The fast, Pythonic way to build MCP servers and clients.</strong>
|
||||
|
||||
*Made with ☕️ by [Prefect](https://www.prefect.io/)*
|
||||
|
||||
[](https://gofastmcp.com)
|
||||
[](https://pypi.org/project/fastmcp)
|
||||
[](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
|
||||
[](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
|
||||
|
||||
<a href="https://trendshift.io/repositories/13266" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13266" alt="jlowin%2Ffastmcp | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</div>
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> #### FastMCP 2.0: The Standard Framework
|
||||
>
|
||||
> FastMCP pioneered Python MCP development, and FastMCP 1.0 was incorporated into the [official MCP SDK](https://github.com/modelcontextprotocol/python-sdk) in 2024.
|
||||
>
|
||||
> **This is FastMCP 2.0** — the actively maintained, production-ready framework that extends far beyond basic protocol implementation. While the SDK provides core functionality, FastMCP 2.0 delivers everything needed for production: advanced MCP patterns (server composition, proxying, OpenAPI/FastAPI generation, tool transformation), enterprise auth (Google, GitHub, WorkOS, Azure, Auth0, and more), deployment tools, testing utilities, and comprehensive client libraries.
|
||||
>
|
||||
> **For production MCP applications, install FastMCP:** `pip install fastmcp`
|
||||
|
||||
---
|
||||
|
||||
**FastMCP is the standard framework for building MCP applications**, providing the fastest path from idea to production.
|
||||
|
||||
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standardized way to provide context and tools to LLMs. FastMCP makes building production-ready MCP servers simple, with enterprise auth, deployment tools, and a complete ecosystem built in.
|
||||
|
||||
```python
|
||||
# server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Demo 🚀")
|
||||
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
return a + b
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
Run the server locally:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py
|
||||
```
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
FastMCP's complete documentation is available at **[gofastmcp.com](https://gofastmcp.com)**, including detailed guides, API references, and advanced patterns. This readme provides only a high-level overview.
|
||||
|
||||
Documentation is also available in [llms.txt format](https://llmstxt.org/), which is a simple markdown standard that LLMs can consume easily.
|
||||
|
||||
There are two ways to access the LLM-friendly documentation:
|
||||
|
||||
- [`llms.txt`](https://gofastmcp.com/llms.txt) is essentially a sitemap, listing all the pages in the documentation.
|
||||
- [`llms-full.txt`](https://gofastmcp.com/llms-full.txt) contains the entire documentation. Note this may exceed the context window of your LLM.
|
||||
|
||||
---
|
||||
|
||||
<!-- omit in toc -->
|
||||
## Table of Contents
|
||||
|
||||
- [FastMCP v2 🚀](#fastmcp-v2-)
|
||||
- [📚 Documentation](#-documentation)
|
||||
- [What is MCP?](#what-is-mcp)
|
||||
- [Why FastMCP?](#why-fastmcp)
|
||||
- [Installation](#installation)
|
||||
- [Core Concepts](#core-concepts)
|
||||
- [The `FastMCP` Server](#the-fastmcp-server)
|
||||
- [Tools](#tools)
|
||||
- [Resources \& Templates](#resources--templates)
|
||||
- [Prompts](#prompts)
|
||||
- [Context](#context)
|
||||
- [MCP Clients](#mcp-clients)
|
||||
- [Authentication](#authentication)
|
||||
- [Enterprise Authentication, Zero Configuration](#enterprise-authentication-zero-configuration)
|
||||
- [Deployment](#deployment)
|
||||
- [From Development to Production](#from-development-to-production)
|
||||
- [Advanced Features](#advanced-features)
|
||||
- [Proxy Servers](#proxy-servers)
|
||||
- [Composing MCP Servers](#composing-mcp-servers)
|
||||
- [OpenAPI \& FastAPI Generation](#openapi--fastapi-generation)
|
||||
- [Running Your Server](#running-your-server)
|
||||
- [Contributing](#contributing)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Setup](#setup)
|
||||
- [Unit Tests](#unit-tests)
|
||||
- [Static Checks](#static-checks)
|
||||
- [Pull Requests](#pull-requests)
|
||||
|
||||
---
|
||||
|
||||
## What is MCP?
|
||||
|
||||
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. It is often described as "the USB-C port for AI", providing a uniform way to connect LLMs to resources they can use. It may be easier to think of it as an API, but specifically designed for LLM interactions. MCP servers can:
|
||||
|
||||
- Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
|
||||
- Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
|
||||
- Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
|
||||
- And more!
|
||||
|
||||
FastMCP provides a high-level, Pythonic interface for building, managing, and interacting with these servers.
|
||||
|
||||
## Why FastMCP?
|
||||
|
||||
FastMCP handles all the complex protocol details so you can focus on building. In most cases, decorating a Python function is all you need — FastMCP handles the rest.
|
||||
|
||||
🚀 **Fast:** High-level interface means less code and faster development
|
||||
|
||||
🍀 **Simple:** Build MCP servers with minimal boilerplate
|
||||
|
||||
🐍 **Pythonic:** Feels natural to Python developers
|
||||
|
||||
🔍 **Complete:** Everything for production — enterprise auth (Google, GitHub, Azure, Auth0, WorkOS), deployment tools, testing frameworks, client libraries, and more
|
||||
|
||||
FastMCP provides the shortest path from idea to production. Deploy locally, to the cloud with [FastMCP Cloud](https://fastmcp.cloud), or to your own infrastructure.
|
||||
|
||||
## Installation
|
||||
|
||||
We recommend installing FastMCP with [uv](https://docs.astral.sh/uv/):
|
||||
|
||||
```bash
|
||||
uv pip install fastmcp
|
||||
```
|
||||
|
||||
For full installation instructions, including verification, upgrading from the official MCPSDK, and developer setup, see the [**Installation Guide**](https://gofastmcp.com/getting-started/installation).
|
||||
|
||||
## Core Concepts
|
||||
|
||||
These are the building blocks for creating MCP servers and clients with FastMCP.
|
||||
|
||||
### The `FastMCP` Server
|
||||
|
||||
The central object representing your MCP application. It holds your tools, resources, and prompts, manages connections, and can be configured with settings like authentication.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Create a server instance
|
||||
mcp = FastMCP(name="MyAssistantServer")
|
||||
```
|
||||
|
||||
Learn more in the [**FastMCP Server Documentation**](https://gofastmcp.com/servers/fastmcp).
|
||||
|
||||
### Tools
|
||||
|
||||
Tools allow LLMs to perform actions by executing your Python functions (sync or async). Ideal for computations, API calls, or side effects (like `POST`/`PUT`). FastMCP handles schema generation from type hints and docstrings. Tools can return various types, including text, JSON-serializable objects, and even images or audio aided by the FastMCP media helper classes.
|
||||
|
||||
```python
|
||||
@mcp.tool
|
||||
def multiply(a: float, b: float) -> float:
|
||||
"""Multiplies two numbers."""
|
||||
return a * b
|
||||
```
|
||||
|
||||
Learn more in the [**Tools Documentation**](https://gofastmcp.com/servers/tools).
|
||||
|
||||
### Resources & Templates
|
||||
|
||||
Resources expose read-only data sources (like `GET` requests). Use `@mcp.resource("your://uri")`. Use `{placeholders}` in the URI to create dynamic templates that accept parameters, allowing clients to request specific data subsets.
|
||||
|
||||
```python
|
||||
# Static resource
|
||||
@mcp.resource("config://version")
|
||||
def get_version():
|
||||
return "2.0.1"
|
||||
|
||||
# Dynamic resource template
|
||||
@mcp.resource("users://{user_id}/profile")
|
||||
def get_profile(user_id: int):
|
||||
# Fetch profile for user_id...
|
||||
return {"name": f"User {user_id}", "status": "active"}
|
||||
```
|
||||
|
||||
Learn more in the [**Resources & Templates Documentation**](https://gofastmcp.com/servers/resources).
|
||||
|
||||
### Prompts
|
||||
|
||||
Prompts define reusable message templates to guide LLM interactions. Decorate functions with `@mcp.prompt`. Return strings or `Message` objects.
|
||||
|
||||
```python
|
||||
@mcp.prompt
|
||||
def summarize_request(text: str) -> str:
|
||||
"""Generate a prompt asking for a summary."""
|
||||
return f"Please summarize the following text:\n\n{text}"
|
||||
```
|
||||
|
||||
Learn more in the [**Prompts Documentation**](https://gofastmcp.com/servers/prompts).
|
||||
|
||||
### Context
|
||||
|
||||
Access MCP session capabilities within your tools, resources, or prompts by adding a `ctx: Context` parameter. Context provides methods for:
|
||||
|
||||
- **Logging:** Log messages to MCP clients with `ctx.info()`, `ctx.error()`, etc.
|
||||
- **LLM Sampling:** Use `ctx.sample()` to request completions from the client's LLM.
|
||||
- **HTTP Request:** Use `ctx.http_request()` to make HTTP requests to other servers.
|
||||
- **Resource Access:** Use `ctx.read_resource()` to access resources on the server
|
||||
- **Progress Reporting:** Use `ctx.report_progress()` to report progress to the client.
|
||||
- and more...
|
||||
|
||||
To access the context, add a parameter annotated as `Context` to any mcp-decorated function. FastMCP will automatically inject the correct context object when the function is called.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP, Context
|
||||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
@mcp.tool
|
||||
async def process_data(uri: str, ctx: Context):
|
||||
# Log a message to the client
|
||||
await ctx.info(f"Processing {uri}...")
|
||||
|
||||
# Read a resource from the server
|
||||
data = await ctx.read_resource(uri)
|
||||
|
||||
# Ask client LLM to summarize the data
|
||||
summary = await ctx.sample(f"Summarize: {data.content[:500]}")
|
||||
|
||||
# Return the summary
|
||||
return summary.text
|
||||
```
|
||||
|
||||
Learn more in the [**Context Documentation**](https://gofastmcp.com/servers/context).
|
||||
|
||||
### MCP Clients
|
||||
|
||||
Interact with *any* MCP server programmatically using the `fastmcp.Client`. It supports various transports (Stdio, SSE, In-Memory) and often auto-detects the correct one. The client can also handle advanced patterns like server-initiated **LLM sampling requests** if you provide an appropriate handler.
|
||||
|
||||
Critically, the client allows for efficient **in-memory testing** of your servers by connecting directly to a `FastMCP` server instance via the `FastMCPTransport`, eliminating the need for process management or network calls during tests.
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
async def main():
|
||||
# Connect via stdio to a local script
|
||||
async with Client("my_server.py") as client:
|
||||
tools = await client.list_tools()
|
||||
print(f"Available tools: {tools}")
|
||||
result = await client.call_tool("add", {"a": 5, "b": 3})
|
||||
print(f"Result: {result.content[0].text}")
|
||||
|
||||
# Connect via SSE
|
||||
async with Client("http://localhost:8000/sse") as client:
|
||||
# ... use the client
|
||||
pass
|
||||
```
|
||||
|
||||
To use clients to test servers, use the following pattern:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP, Client
|
||||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
async def main():
|
||||
# Connect via in-memory transport
|
||||
async with Client(mcp) as client:
|
||||
# ... use the client
|
||||
```
|
||||
|
||||
FastMCP also supports connecting to multiple servers through a single unified client using the standard MCP configuration format:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
# Standard MCP configuration with multiple servers
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {"url": "https://weather-api.example.com/mcp"},
|
||||
"assistant": {"command": "python", "args": ["./assistant_server.py"]}
|
||||
}
|
||||
}
|
||||
|
||||
# Create a client that connects to all servers
|
||||
client = Client(config)
|
||||
|
||||
async def main():
|
||||
async with client:
|
||||
# Access tools and resources with server prefixes
|
||||
forecast = await client.call_tool("weather_get_forecast", {"city": "London"})
|
||||
answer = await client.call_tool("assistant_answer_question", {"query": "What is MCP?"})
|
||||
```
|
||||
|
||||
Learn more in the [**Client Documentation**](https://gofastmcp.com/clients/client) and [**Transports Documentation**](https://gofastmcp.com/clients/transports).
|
||||
|
||||
## Authentication
|
||||
|
||||
### Enterprise Authentication, Zero Configuration
|
||||
|
||||
FastMCP provides comprehensive authentication support that sets it apart from basic MCP implementations. Secure your servers and authenticate your clients with the same enterprise-grade providers used by major corporations.
|
||||
|
||||
**Built-in OAuth Providers:**
|
||||
|
||||
- **Google**
|
||||
- **GitHub**
|
||||
- **Microsoft Azure**
|
||||
- **Auth0**
|
||||
- **WorkOS**
|
||||
- **Descope**
|
||||
- **JWT/Custom**
|
||||
- **API Keys**
|
||||
|
||||
Protecting a server takes just two lines:
|
||||
|
||||
```python
|
||||
from fastmcp.server.auth import GoogleProvider
|
||||
|
||||
auth = GoogleProvider(client_id="...", client_secret="...", base_url="https://myserver.com")
|
||||
mcp = FastMCP("Protected Server", auth=auth)
|
||||
```
|
||||
|
||||
Connecting to protected servers is even simpler:
|
||||
|
||||
```python
|
||||
async with Client("https://protected-server.com/mcp", auth="oauth") as client:
|
||||
# Automatic browser-based OAuth flow
|
||||
result = await client.call_tool("protected_tool")
|
||||
```
|
||||
|
||||
**Why FastMCP Auth Matters:**
|
||||
|
||||
- **Production-Ready:** Persistent storage, token refresh, comprehensive error handling
|
||||
- **Zero-Config OAuth:** Just pass `auth="oauth"` for automatic setup
|
||||
- **Enterprise Integration:** WorkOS SSO, Azure Active Directory, Auth0 tenants
|
||||
- **Developer Experience:** Automatic browser launch, local callback server, environment variable support
|
||||
- **Advanced Architecture:** Full OIDC support, Dynamic Client Registration (DCR), and unique OAuth proxy pattern that enables DCR with any provider
|
||||
|
||||
*Authentication this comprehensive is unique to FastMCP 2.0.*
|
||||
|
||||
Learn more in the **Authentication Documentation** for [servers](https://gofastmcp.com/servers/auth) and [clients](https://gofastmcp.com/clients/auth).
|
||||
|
||||
## Deployment
|
||||
|
||||
### From Development to Production
|
||||
|
||||
FastMCP supports every deployment scenario from local development to global scale:
|
||||
|
||||
**Development:** Run locally with a single command
|
||||
|
||||
```bash
|
||||
fastmcp run server.py
|
||||
```
|
||||
|
||||
**Production:** Deploy to [**FastMCP Cloud**](https://fastmcp.cloud) — Remote MCP that just works
|
||||
|
||||
- Instant HTTPS endpoints
|
||||
- Built-in authentication
|
||||
- Zero configuration
|
||||
- Free for personal servers
|
||||
|
||||
**Self-Hosted:** Use HTTP or SSE transports for your own infrastructure
|
||||
|
||||
```python
|
||||
mcp.run(transport="http", host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
Learn more in the [**Deployment Documentation**](https://gofastmcp.com/deployment).
|
||||
|
||||
## Advanced Features
|
||||
|
||||
FastMCP introduces powerful ways to structure and compose your MCP applications.
|
||||
|
||||
### Proxy Servers
|
||||
|
||||
Create a FastMCP server that acts as an intermediary for another local or remote MCP server using `FastMCP.as_proxy()`. This is especially useful for bridging transports (e.g., remote SSE to local Stdio) or adding a layer of logic to a server you don't control.
|
||||
|
||||
Learn more in the [**Proxying Documentation**](https://gofastmcp.com/patterns/proxy).
|
||||
|
||||
### Composing MCP Servers
|
||||
|
||||
Build modular applications by mounting multiple `FastMCP` instances onto a parent server using `mcp.mount()` (live link) or `mcp.import_server()` (static copy).
|
||||
|
||||
Learn more in the [**Composition Documentation**](https://gofastmcp.com/patterns/composition).
|
||||
|
||||
### OpenAPI & FastAPI Generation
|
||||
|
||||
Automatically generate FastMCP servers from existing OpenAPI specifications (`FastMCP.from_openapi()`) or FastAPI applications (`FastMCP.from_fastapi()`), instantly bringing your web APIs to the MCP ecosystem.
|
||||
|
||||
Learn more: [**OpenAPI Integration**](https://gofastmcp.com/integrations/openapi) | [**FastAPI Integration**](https://gofastmcp.com/integrations/fastapi).
|
||||
|
||||
## Running Your Server
|
||||
|
||||
The main way to run a FastMCP server is by calling the `run()` method on your server instance:
|
||||
|
||||
```python
|
||||
# server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Demo 🚀")
|
||||
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run() # Default: uses STDIO transport
|
||||
```
|
||||
|
||||
FastMCP supports three transport protocols:
|
||||
|
||||
**STDIO (Default)**: Best for local tools and command-line scripts.
|
||||
|
||||
```python
|
||||
mcp.run(transport="stdio") # Default, so transport argument is optional
|
||||
```
|
||||
|
||||
**Streamable HTTP**: Recommended for web deployments.
|
||||
|
||||
```python
|
||||
mcp.run(transport="http", host="127.0.0.1", port=8000, path="/mcp")
|
||||
```
|
||||
|
||||
**SSE**: For compatibility with existing SSE clients.
|
||||
|
||||
```python
|
||||
mcp.run(transport="sse", host="127.0.0.1", port=8000)
|
||||
```
|
||||
|
||||
See the [**Running Server Documentation**](https://gofastmcp.com/deployment/running-server) for more details.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are the core of open source! We welcome improvements and features.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.10+
|
||||
- [uv](https://docs.astral.sh/uv/) (Recommended for environment management)
|
||||
|
||||
### Setup
|
||||
|
||||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/jlowin/fastmcp.git
|
||||
cd fastmcp
|
||||
```
|
||||
|
||||
2. Create and sync the environment:
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
```
|
||||
|
||||
This installs all dependencies, including dev tools.
|
||||
|
||||
3. Activate the virtual environment (e.g., `source .venv/bin/activate` or via your IDE).
|
||||
|
||||
### Unit Tests
|
||||
|
||||
FastMCP has a comprehensive unit test suite. All PRs must introduce or update tests as appropriate and pass the full suite.
|
||||
|
||||
Run tests using pytest:
|
||||
|
||||
```bash
|
||||
pytest
|
||||
```
|
||||
|
||||
or if you want an overview of the code coverage
|
||||
|
||||
```bash
|
||||
uv run pytest --cov=src --cov=examples --cov-report=html
|
||||
```
|
||||
|
||||
### Static Checks
|
||||
|
||||
FastMCP uses `pre-commit` for code formatting, linting, and type-checking. All PRs must pass these checks (they run automatically in CI).
|
||||
|
||||
Install the hooks locally:
|
||||
|
||||
```bash
|
||||
uv run pre-commit install
|
||||
```
|
||||
|
||||
The hooks will now run automatically on `git commit`. You can also run them manually at any time:
|
||||
|
||||
```bash
|
||||
pre-commit run --all-files
|
||||
# or via uv
|
||||
uv run pre-commit run --all-files
|
||||
```
|
||||
|
||||
### Pull Requests
|
||||
|
||||
1. Fork the repository on GitHub.
|
||||
2. Create a feature branch from `main`.
|
||||
3. Make your changes, including tests and documentation updates.
|
||||
4. Ensure tests and pre-commit hooks pass.
|
||||
5. Commit your changes and push to your fork.
|
||||
6. Open a pull request against the `main` branch of `jlowin/fastmcp`.
|
||||
|
||||
Please open an issue or discussion for questions or suggestions before starting significant work!
|
||||
246
README_OPENAPI.md
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
# FastMCP OpenAPI Integration
|
||||
|
||||
This document explains how FastMCP's OpenAPI integration works, what features are supported, and how to extend it. The OpenAPI functionality is split across two main files:
|
||||
|
||||
- `server/openapi.py` - High-level FastMCP server implementation and MCP component creation
|
||||
- `utilities/openapi.py` - Low-level OpenAPI parsing and intermediate representation
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
OpenAPI Spec → Parse → HTTPRoute IR → Create MCP Components → FastMCP Server
|
||||
```
|
||||
|
||||
### 1. Parsing Phase (`utilities/openapi.py`)
|
||||
|
||||
OpenAPI specifications are parsed into an intermediate representation (IR) that normalizes differences between OpenAPI 3.0 and 3.1:
|
||||
|
||||
- **Input**: Raw OpenAPI spec (dict)
|
||||
- **Output**: List of `HTTPRoute` objects with normalized parameter information
|
||||
- **Key Classes**:
|
||||
- `HTTPRoute` - Represents a single operation
|
||||
- `ParameterInfo` - Represents a parameter with location, style, explode, etc.
|
||||
- `RequestBodyInfo` - Represents request body information
|
||||
- `ResponseInfo` - Represents response information
|
||||
|
||||
### 2. Component Creation Phase (`server/openapi.py`)
|
||||
|
||||
HTTPRoute objects are converted into FastMCP components based on route mapping rules:
|
||||
|
||||
- **Tools** (`OpenAPITool`) - HTTP operations that can be called
|
||||
- **Resources** (`OpenAPIResource`) - HTTP endpoints that return data
|
||||
- **Resource Templates** (`OpenAPIResourceTemplate`) - Parameterized resources
|
||||
|
||||
## Parameter Handling
|
||||
|
||||
FastMCP supports various OpenAPI parameter serialization styles and formats:
|
||||
|
||||
### Supported Parameter Locations
|
||||
- `query` - Query string parameters
|
||||
- `path` - Path parameters
|
||||
- `header` - HTTP headers
|
||||
- `cookie` - Cookie parameters (parsed but not used in requests)
|
||||
|
||||
### Supported Parameter Styles
|
||||
|
||||
#### Query Parameters
|
||||
- **`form`** (default) - Standard query parameter format
|
||||
- `explode=true` (default): `?tags=red&tags=blue`
|
||||
- `explode=false`: `?tags=red,blue`
|
||||
- **`deepObject`** - Object parameters with bracket notation
|
||||
- `explode=true`: `?filter[name]=John&filter[age]=30`
|
||||
- `explode=false`: Falls back to JSON string (non-standard, logs warning)
|
||||
|
||||
#### Path Parameters
|
||||
- **`simple`** (default) - Comma-separated for arrays: `/users/1,2,3`
|
||||
|
||||
#### Header Parameters
|
||||
- **`simple`** (default) - Standard header format
|
||||
|
||||
### Parameter Type Support
|
||||
|
||||
#### Arrays
|
||||
- String arrays with `explode=true/false`
|
||||
- Number arrays with `explode=true/false`
|
||||
- Boolean arrays with `explode=true/false`
|
||||
- Complex object arrays (basic support, may not handle all cases)
|
||||
|
||||
#### Objects
|
||||
- Objects with `deepObject` style and `explode=true`
|
||||
- Objects with other styles fall back to JSON serialization
|
||||
|
||||
#### Primitives
|
||||
- Strings, numbers, booleans
|
||||
- Enums
|
||||
- Default values
|
||||
|
||||
## Request Body Handling
|
||||
|
||||
### Supported Content Types
|
||||
- `application/json` - JSON request bodies
|
||||
|
||||
### Schema Support
|
||||
- Object schemas with properties
|
||||
- Array schemas
|
||||
- Primitive schemas
|
||||
- Schema references (`$ref` to local schemas only)
|
||||
- Required properties
|
||||
- Default values
|
||||
|
||||
## Response Handling
|
||||
|
||||
### Content Type Detection
|
||||
- `application/json` - Parsed as JSON
|
||||
- `text/*` - Returned as text
|
||||
- `application/xml` - Returned as text
|
||||
- Other types - Returned as binary
|
||||
|
||||
### Output Schema Generation
|
||||
- Success response schemas (200, 201, 202, 204)
|
||||
- Object response wrapping for MCP compliance
|
||||
- Schema compression (removes unused `$defs`)
|
||||
|
||||
## Route Mapping
|
||||
|
||||
Routes are mapped to MCP component types using `RouteMap` configurations:
|
||||
|
||||
```python
|
||||
RouteMap(
|
||||
methods=["GET", "POST"], # HTTP methods to match
|
||||
pattern=r"/api/users/.*", # Regex pattern for path
|
||||
mcp_type=MCPType.RESOURCE_TEMPLATE, # Target component type
|
||||
tags={"user"}, # OpenAPI tags to match (AND condition)
|
||||
mcp_tags={"fastmcp-user"} # Tags to add to created components
|
||||
)
|
||||
```
|
||||
|
||||
### Default Behavior
|
||||
- All routes become **Tools** by default
|
||||
- Use route maps to override specific patterns
|
||||
|
||||
### Component Types
|
||||
- `MCPType.TOOL` - Callable operations
|
||||
- `MCPType.RESOURCE` - Static data endpoints
|
||||
- `MCPType.RESOURCE_TEMPLATE` - Parameterized data endpoints
|
||||
- `MCPType.EXCLUDE` - Skip route entirely
|
||||
|
||||
## Known Limitations & Edge Cases
|
||||
|
||||
### Parameter Edge Cases
|
||||
1. **Parameter Name Collisions** - When path/query parameters have same names as request body properties, non-body parameters get `__location` suffixes
|
||||
2. **Complex Array Serialization** - Limited support for arrays containing objects
|
||||
3. **Cookie Parameters** - Parsed but not used in requests
|
||||
4. **Non-standard Combinations** - e.g., `deepObject` with `explode=false`
|
||||
|
||||
### Request Body Edge Cases
|
||||
1. **Content Type Priority** - Only first available content type is used
|
||||
2. **Nested Objects** - Deep nesting may not serialize correctly
|
||||
3. **Binary Content** - No support for file uploads or binary data
|
||||
|
||||
### Response Edge Cases
|
||||
1. **Multiple Content Types** - Only JSON-compatible types are used for output schemas
|
||||
2. **Error Responses** - Not used for MCP output schema generation
|
||||
3. **Response Headers** - Not captured or exposed
|
||||
|
||||
### Schema Edge Cases
|
||||
1. **External References** - `$ref` to external files not supported
|
||||
2. **Circular References** - May cause issues in schema processing
|
||||
3. **Polymorphism** - `oneOf`/`anyOf`/`allOf` limited support
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### Common Issues
|
||||
1. **"Unknown tool/resource"** - Check route mapping configuration
|
||||
2. **Parameter not found** - Check for name collisions or incorrect style/explode
|
||||
3. **Invalid request format** - Check parameter serialization and content types
|
||||
4. **Schema validation errors** - Check for external refs or complex schemas
|
||||
|
||||
### Debugging Tools
|
||||
```python
|
||||
# Parse routes to inspect intermediate representation
|
||||
routes = parse_openapi_to_http_routes(openapi_spec)
|
||||
for route in routes:
|
||||
print(f"{route.method} {route.path}")
|
||||
for param in route.parameters:
|
||||
print(f" {param.name} ({param.location}): style={param.style}, explode={param.explode}")
|
||||
|
||||
# Check component creation
|
||||
server = FastMCP.from_openapi(openapi_spec, client)
|
||||
tools = await server.get_tools()
|
||||
print(f"Created {len(tools)} tools: {list(tools.keys())}")
|
||||
```
|
||||
|
||||
### Logging
|
||||
- Set `FASTMCP_LOG_LEVEL=DEBUG` to see detailed parameter processing
|
||||
- Look for warnings about non-standard parameter combinations
|
||||
- Check for schema parsing errors in logs
|
||||
|
||||
## Extension Points
|
||||
|
||||
### Adding New Parameter Styles
|
||||
1. Add style handling in `utilities/openapi.py` - `ParameterInfo` class
|
||||
2. Implement serialization logic in `server/openapi.py` - `OpenAPITool.run()`
|
||||
3. Add tests for parsing and serialization
|
||||
|
||||
### Adding New Content Types
|
||||
1. Extend request body handling in `OpenAPITool.run()`
|
||||
2. Add response parsing logic for new types
|
||||
3. Update content type priority in utilities
|
||||
|
||||
### Custom Route Mapping
|
||||
Use `route_map_fn` for complex routing logic:
|
||||
|
||||
```python
|
||||
def custom_mapper(route: HTTPRoute, current_type: MCPType) -> MCPType:
|
||||
if route.path.startswith("/admin"):
|
||||
return MCPType.EXCLUDE
|
||||
return current_type
|
||||
|
||||
server = FastMCP.from_openapi(spec, client, route_map_fn=custom_mapper)
|
||||
```
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### Unit Tests
|
||||
- Test parameter parsing with various styles/explode combinations
|
||||
- Test route mapping with different patterns and tags
|
||||
- Test schema generation and compression
|
||||
|
||||
### Integration Tests
|
||||
- Mock HTTP client to verify actual request parameters
|
||||
- Test end-to-end component creation and execution
|
||||
- Test error handling and edge cases
|
||||
|
||||
### Example Test Pattern
|
||||
```python
|
||||
async def test_parameter_style():
|
||||
# 1. Create OpenAPI spec with specific parameter configuration
|
||||
spec = {"openapi": "3.1.0", ...}
|
||||
|
||||
# 2. Parse and create components
|
||||
routes = parse_openapi_to_http_routes(spec)
|
||||
tool = OpenAPITool(mock_client, routes[0], ...)
|
||||
|
||||
# 3. Execute and verify request parameters
|
||||
await tool.run({"param": "value"})
|
||||
actual_params = mock_client.request.call_args.kwargs["params"]
|
||||
assert actual_params == expected_params
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
OpenAPI functionality is tested across multiple files in `tests/server/openapi/`:
|
||||
|
||||
- `test_basic_functionality.py` - Core component creation and execution
|
||||
- `test_explode_integration.py` - Parameter explode behavior
|
||||
- `test_deepobject_style.py` - DeepObject style parameter encoding
|
||||
- `test_parameter_collisions.py` - Parameter name collision handling
|
||||
- `test_openapi_path_parameters.py` - Path parameter serialization
|
||||
- `test_configuration.py` - Route mapping and MCP names
|
||||
- `test_description_propagation.py` - Schema and description handling
|
||||
|
||||
When adding new OpenAPI features, create focused test files rather than adding to existing monolithic files.
|
||||
|
||||
---
|
||||
|
||||
*This document should be updated when new OpenAPI features are added or when edge cases are discovered and addressed.*
|
||||
58
Windows_Notes.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Getting your development environment set up properly
|
||||
To get your environment up and running properly, you'll need a slightly different set of commands that are windows specific:
|
||||
```bash
|
||||
uv venv
|
||||
.venv\Scripts\activate
|
||||
uv pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
This will install the package in editable mode, and install the development dependencies.
|
||||
|
||||
|
||||
# Fixing `AttributeError: module 'collections' has no attribute 'Callable'`
|
||||
- open `.venv\Lib\site-packages\pyreadline\py3k_compat.py`
|
||||
- change `return isinstance(x, collections.Callable)` to
|
||||
```
|
||||
from collections.abc import Callable
|
||||
return isinstance(x, Callable)
|
||||
```
|
||||
|
||||
# Helpful notes
|
||||
For developing FastMCP
|
||||
## Install local development version of FastMCP into a local FastMCP project server
|
||||
- ensure
|
||||
- change directories to your FastMCP Server location so you can install it in your .venv
|
||||
- run `.venv\Scripts\activate` to activate your virtual environment
|
||||
- Then run a series of commands to uninstall the old version and install the new
|
||||
```bash
|
||||
# First uninstall
|
||||
uv pip uninstall fastmcp
|
||||
|
||||
# Clean any build artifacts in your fastmcp directory
|
||||
cd C:\path\to\fastmcp
|
||||
del /s /q *.egg-info
|
||||
|
||||
# Then reinstall in your weather project
|
||||
cd C:\path\to\new\fastmcp_server
|
||||
uv pip install --no-cache-dir -e C:\Users\justj\PycharmProjects\fastmcp
|
||||
|
||||
# Check that it installed properly and has the correct git hash
|
||||
pip show fastmcp
|
||||
```
|
||||
|
||||
## Running the FastMCP server with Inspector
|
||||
MCP comes with a node.js application called Inspector that can be used to inspect the FastMCP server. To run the inspector, you'll need to install node.js and npm. Then you can run the following commands:
|
||||
```bash
|
||||
fastmcp dev server.py
|
||||
```
|
||||
This will launch a web app on http://localhost:5173/ that you can use to inspect the FastMCP server.
|
||||
|
||||
## If you start development before creating a fork - your get out of jail free card
|
||||
- Add your fork as a new remote to your local repository `git remote add fork git@github.com:YOUR-USERNAME/REPOSITORY-NAME.git`
|
||||
- This will add your repo, short named 'fork', as a remote to your local repository
|
||||
- Verify that it was added correctly by running `git remote -v`
|
||||
- Commit your changes
|
||||
- Push your changes to your fork `git push fork <branch>`
|
||||
- Create your pull request on GitHub
|
||||
|
||||
|
||||
2
docs/.ccignore
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
changelog.mdx
|
||||
python-sdk/
|
||||
364
docs/.cursor/rules/mintlify.mdc
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
---
|
||||
description:
|
||||
globs: *.mdx
|
||||
alwaysApply: false
|
||||
---
|
||||
# Mintlify technical writing assistant
|
||||
|
||||
You are an AI writing assistant specialized in creating exceptional technical documentation using Mintlify components and following industry-leading technical writing practices.
|
||||
|
||||
## Core writing principles
|
||||
|
||||
### Language and style requirements
|
||||
- Use clear, direct language appropriate for technical audiences
|
||||
- Write in second person ("you") for instructions and procedures
|
||||
- Use active voice over passive voice
|
||||
- Employ present tense for current states, future tense for outcomes
|
||||
- Maintain consistent terminology throughout all documentation
|
||||
- Keep sentences concise while providing necessary context
|
||||
- Use parallel structure in lists, headings, and procedures
|
||||
|
||||
### Content organization standards
|
||||
- Lead with the most important information (inverted pyramid structure)
|
||||
- Use progressive disclosure: basic concepts before advanced ones
|
||||
- Break complex procedures into numbered steps
|
||||
- Include prerequisites and context before instructions
|
||||
- Provide expected outcomes for each major step
|
||||
- End sections with next steps or related information
|
||||
- Use descriptive, keyword-rich headings for navigation and SEO
|
||||
|
||||
### User-centered approach
|
||||
- Focus on user goals and outcomes rather than system features
|
||||
- Anticipate common questions and address them proactively
|
||||
- Include troubleshooting for likely failure points
|
||||
- Provide multiple pathways when appropriate (beginner vs advanced), but offer an opinionated path for people to follow to avoid overwhelming with options
|
||||
|
||||
## Mintlify component reference
|
||||
|
||||
### Callout components
|
||||
|
||||
#### Note - Additional helpful information
|
||||
|
||||
<Note>
|
||||
Supplementary information that supports the main content without interrupting flow
|
||||
</Note>
|
||||
|
||||
#### Tip - Best practices and pro tips
|
||||
|
||||
<Tip>
|
||||
Expert advice, shortcuts, or best practices that enhance user success
|
||||
</Tip>
|
||||
|
||||
#### Warning - Important cautions
|
||||
|
||||
<Warning>
|
||||
Critical information about potential issues, breaking changes, or destructive actions
|
||||
</Warning>
|
||||
|
||||
#### Info - Neutral contextual information
|
||||
|
||||
<Info>
|
||||
Background information, context, or neutral announcements
|
||||
</Info>
|
||||
|
||||
#### Check - Success confirmations
|
||||
|
||||
<Check>
|
||||
Positive confirmations, successful completions, or achievement indicators
|
||||
</Check>
|
||||
|
||||
### Code components
|
||||
|
||||
#### Single code block
|
||||
|
||||
```javascript config.js
|
||||
const apiConfig = {
|
||||
baseURL: 'https://api.example.com',
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### Code group with multiple languages
|
||||
|
||||
<CodeGroup>
|
||||
```javascript Node.js
|
||||
const response = await fetch('/api/endpoint', {
|
||||
headers: { Authorization: `Bearer ${apiKey}` }
|
||||
});
|
||||
```
|
||||
|
||||
```python Python
|
||||
import requests
|
||||
response = requests.get('/api/endpoint',
|
||||
headers={'Authorization': f'Bearer {api_key}'})
|
||||
```
|
||||
|
||||
```curl cURL
|
||||
curl -X GET '/api/endpoint' \
|
||||
-H 'Authorization: Bearer YOUR_API_KEY'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
#### Request/Response examples
|
||||
|
||||
<RequestExample>
|
||||
```bash cURL
|
||||
curl -X POST 'https://api.example.com/users' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"name": "John Doe", "email": "john@example.com"}'
|
||||
```
|
||||
</RequestExample>
|
||||
|
||||
<ResponseExample>
|
||||
```json Success
|
||||
{
|
||||
"id": "user_123",
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"created_at": "2024-01-15T10:30:00Z"
|
||||
}
|
||||
```
|
||||
</ResponseExample>
|
||||
|
||||
### Structural components
|
||||
|
||||
#### Steps for procedures
|
||||
|
||||
<Steps>
|
||||
<Step title="Install dependencies">
|
||||
Run `npm install` to install required packages.
|
||||
|
||||
<Check>
|
||||
Verify installation by running `npm list`.
|
||||
</Check>
|
||||
</Step>
|
||||
|
||||
<Step title="Configure environment">
|
||||
Create a `.env` file with your API credentials.
|
||||
|
||||
```bash
|
||||
API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Never commit API keys to version control.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
#### Tabs for alternative content
|
||||
|
||||
<Tabs>
|
||||
<Tab title="macOS">
|
||||
```bash
|
||||
brew install node
|
||||
npm install -g package-name
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows">
|
||||
```powershell
|
||||
choco install nodejs
|
||||
npm install -g package-name
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Linux">
|
||||
```bash
|
||||
sudo apt install nodejs npm
|
||||
npm install -g package-name
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
#### Accordions for collapsible content
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Troubleshooting connection issues">
|
||||
- **Firewall blocking**: Ensure ports 80 and 443 are open
|
||||
- **Proxy configuration**: Set HTTP_PROXY environment variable
|
||||
- **DNS resolution**: Try using 8.8.8.8 as DNS server
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Advanced configuration">
|
||||
```javascript
|
||||
const config = {
|
||||
performance: { cache: true, timeout: 30000 },
|
||||
security: { encryption: 'AES-256' }
|
||||
};
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### API documentation components
|
||||
|
||||
#### Parameter fields
|
||||
|
||||
<ParamField path="user_id" type="string" required>
|
||||
Unique identifier for the user. Must be a valid UUID v4 format.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="email" type="string" required>
|
||||
User's email address. Must be valid and unique within the system.
|
||||
</ParamField>
|
||||
|
||||
<ParamField query="limit" type="integer" default="10">
|
||||
Maximum number of results to return. Range: 1-100.
|
||||
</ParamField>
|
||||
|
||||
<ParamField header="Authorization" type="string" required>
|
||||
Bearer token for API authentication. Format: `Bearer YOUR_API_KEY`
|
||||
</ParamField>
|
||||
|
||||
#### Response fields
|
||||
|
||||
<ResponseField name="user_id" type="string" required>
|
||||
Unique identifier assigned to the newly created user.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="created_at" type="timestamp">
|
||||
ISO 8601 formatted timestamp of when the user was created.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="permissions" type="array">
|
||||
List of permission strings assigned to this user.
|
||||
</ResponseField>
|
||||
|
||||
#### Expandable nested fields
|
||||
|
||||
<ResponseField name="user" type="object">
|
||||
Complete user object with all associated data.
|
||||
|
||||
<Expandable title="User properties">
|
||||
<ResponseField name="profile" type="object">
|
||||
User profile information including personal details.
|
||||
|
||||
<Expandable title="Profile details">
|
||||
<ResponseField name="first_name" type="string">
|
||||
User's first name as entered during registration.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="avatar_url" type="string | null">
|
||||
URL to user's profile picture. Returns null if no avatar is set.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Interactive components
|
||||
|
||||
#### Cards for navigation
|
||||
|
||||
<Card title="Getting started guide" icon="rocket" href="/quickstart">
|
||||
Complete walkthrough from installation to your first API call in under 10 minutes.
|
||||
</Card>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Authentication" icon="key" href="/auth">
|
||||
Learn how to authenticate requests using API keys or JWT tokens.
|
||||
</Card>
|
||||
|
||||
<Card title="Rate limiting" icon="clock" href="/rate-limits">
|
||||
Understand rate limits and best practices for high-volume usage.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Media and advanced components
|
||||
|
||||
#### Frames for images
|
||||
|
||||
Wrap all images in frames.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/dashboard.png" alt="Main dashboard showing analytics overview" />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="The analytics dashboard provides real-time insights">
|
||||
<img src="/images/analytics.png" alt="Analytics dashboard with charts" />
|
||||
</Frame>
|
||||
|
||||
#### Tooltips and updates
|
||||
|
||||
<Tooltip tip="Application Programming Interface - protocols for building software">
|
||||
API
|
||||
</Tooltip>
|
||||
|
||||
<Update label="Version 2.1.0" description="Released March 15, 2024">
|
||||
## New features
|
||||
- Added bulk user import functionality
|
||||
- Improved error messages with actionable suggestions
|
||||
|
||||
## Bug fixes
|
||||
- Fixed pagination issue with large datasets
|
||||
- Resolved authentication timeout problems
|
||||
</Update>
|
||||
|
||||
## Required page structure
|
||||
|
||||
Every documentation page must begin with YAML frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Clear, specific, keyword-rich title"
|
||||
description: "Concise description explaining page purpose and value"
|
||||
---
|
||||
```
|
||||
|
||||
## Content quality standards
|
||||
|
||||
### Code examples requirements
|
||||
- Always include complete, runnable examples that users can copy and execute
|
||||
- Show proper error handling and edge case management
|
||||
- Use realistic data instead of placeholder values
|
||||
- Include expected outputs and results for verification
|
||||
- Test all code examples thoroughly before publishing
|
||||
- Specify language and include filename when relevant
|
||||
- Add explanatory comments for complex logic
|
||||
|
||||
### API documentation requirements
|
||||
- Document all parameters including optional ones with clear descriptions
|
||||
- Show both success and error response examples with realistic data
|
||||
- Include rate limiting information with specific limits
|
||||
- Provide authentication examples showing proper format
|
||||
- Explain all HTTP status codes and error handling
|
||||
- Cover complete request/response cycles
|
||||
|
||||
### Accessibility requirements
|
||||
- Include descriptive alt text for all images and diagrams
|
||||
- Use specific, actionable link text instead of "click here"
|
||||
- Ensure proper heading hierarchy starting with H2
|
||||
- Provide keyboard navigation considerations
|
||||
- Use sufficient color contrast in examples and visuals
|
||||
- Structure content for easy scanning with headers and lists
|
||||
|
||||
## AI assistant instructions
|
||||
|
||||
### Component selection logic
|
||||
- Use **Steps** for procedures, tutorials, setup guides, and sequential instructions
|
||||
- Use **Tabs** for platform-specific content or alternative approaches
|
||||
- Use **CodeGroup** when showing the same concept in multiple languages
|
||||
- Use **Accordions** for supplementary information that might interrupt flow
|
||||
- Use **Cards and CardGroup** for navigation, feature overviews, and related resources
|
||||
- Use **RequestExample/ResponseExample** specifically for API endpoint documentation
|
||||
- Use **ParamField** for API parameters, **ResponseField** for API responses
|
||||
- Use **Expandable** for nested object properties or hierarchical information
|
||||
|
||||
### Quality assurance checklist
|
||||
- Verify all code examples are syntactically correct and executable
|
||||
- Test all links to ensure they are functional and lead to relevant content
|
||||
- Validate Mintlify component syntax with all required properties
|
||||
- Confirm proper heading hierarchy with H2 for main sections, H3 for subsections
|
||||
- Ensure content flows logically from basic concepts to advanced topics
|
||||
- Check for consistency in terminology, formatting, and component usage
|
||||
|
||||
### Error prevention strategies
|
||||
- Always include realistic error handling in code examples
|
||||
- Provide dedicated troubleshooting sections for complex procedures
|
||||
- Explain prerequisites clearly before beginning instructions
|
||||
- Include verification and testing steps with expected outcomes
|
||||
- Add appropriate warnings for destructive or security-sensitive actions
|
||||
- Validate all technical information through testing before publication
|
||||
BIN
docs/assets/brand/blue-logo.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
docs/assets/brand/f-watercolor-waves-dark.png
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
docs/assets/brand/f-watercolor-waves.png
Normal file
|
After Width: | Height: | Size: 247 KiB |
3
docs/assets/brand/favicon.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="344" height="250" viewBox="0 0 344 250" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M317.594 60H178.438C158.407 60 140.309 71.9543 132.448 90.3779L123.789 111.3C129.321 110.442 134.957 109.998 140.653 109.998H297.593L272.593 169.999H140.653C123.17 169.999 100.093 177.499 88.0928 198.999L69.4229 242.662L66.5 249.726H0L77.2617 66.8311C94.5556 26.299 134.37 3.8525e-06 178.438 0H343.594L317.594 60Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 445 B |
BIN
docs/assets/brand/thumbnail-background.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
docs/assets/brand/wordmark-padded.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
docs/assets/brand/wordmark-watercolor-rainbow-dark.png
Normal file
|
After Width: | Height: | Size: 401 KiB |
BIN
docs/assets/brand/wordmark-watercolor-rainbow.png
Normal file
|
After Width: | Height: | Size: 376 KiB |
BIN
docs/assets/brand/wordmark-watercolor-waves-dark.png
Normal file
|
After Width: | Height: | Size: 348 KiB |
BIN
docs/assets/brand/wordmark-watercolor-waves.png
Normal file
|
After Width: | Height: | Size: 349 KiB |
BIN
docs/assets/brand/wordmark-white-padded.png
Normal file
|
After Width: | Height: | Size: 9 KiB |
BIN
docs/assets/brand/wordmark-white.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/assets/brand/wordmark.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/assets/images/fastmcp_cloud/connect.png
Normal file
|
After Width: | Height: | Size: 643 KiB |
BIN
docs/assets/images/fastmcp_cloud/create_project.png
Normal file
|
After Width: | Height: | Size: 622 KiB |
BIN
docs/assets/images/fastmcp_cloud/deployment.png
Normal file
|
After Width: | Height: | Size: 676 KiB |
BIN
docs/assets/images/fastmcp_cloud/quickstart.png
Normal file
|
After Width: | Height: | Size: 604 KiB |
BIN
docs/assets/images/tutorial-rest-api-result.png
Normal file
|
After Width: | Height: | Size: 645 KiB |
348
docs/assets/schemas/mcp_server_config/latest.json
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
{
|
||||
"$defs": {
|
||||
"Deployment": {
|
||||
"description": "Configuration for server deployment and runtime settings.",
|
||||
"properties": {
|
||||
"transport": {
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"stdio",
|
||||
"http",
|
||||
"sse"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Transport protocol to use",
|
||||
"title": "Transport"
|
||||
},
|
||||
"host": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Host to bind to when using HTTP transport",
|
||||
"examples": [
|
||||
"127.0.0.1",
|
||||
"0.0.0.0",
|
||||
"localhost"
|
||||
],
|
||||
"title": "Host"
|
||||
},
|
||||
"port": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Port to bind to when using HTTP transport",
|
||||
"examples": [
|
||||
8000,
|
||||
3000,
|
||||
5000
|
||||
],
|
||||
"title": "Port"
|
||||
},
|
||||
"path": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "URL path for the server endpoint",
|
||||
"examples": [
|
||||
"/mcp/",
|
||||
"/api/mcp/",
|
||||
"/sse/"
|
||||
],
|
||||
"title": "Path"
|
||||
},
|
||||
"log_level": {
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"DEBUG",
|
||||
"INFO",
|
||||
"WARNING",
|
||||
"ERROR",
|
||||
"CRITICAL"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Log level for the server",
|
||||
"title": "Log Level"
|
||||
},
|
||||
"cwd": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Working directory for the server process",
|
||||
"examples": [
|
||||
".",
|
||||
"./src",
|
||||
"/app"
|
||||
],
|
||||
"title": "Cwd"
|
||||
},
|
||||
"env": {
|
||||
"anyOf": [
|
||||
{
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Environment variables to set when running the server",
|
||||
"examples": [
|
||||
{
|
||||
"API_KEY": "secret",
|
||||
"DEBUG": "true"
|
||||
}
|
||||
],
|
||||
"title": "Env"
|
||||
},
|
||||
"args": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Arguments to pass to the server (after --)",
|
||||
"examples": [
|
||||
[
|
||||
"--config",
|
||||
"config.json",
|
||||
"--debug"
|
||||
]
|
||||
],
|
||||
"title": "Args"
|
||||
}
|
||||
},
|
||||
"title": "Deployment",
|
||||
"type": "object"
|
||||
},
|
||||
"Environment": {
|
||||
"description": "Configuration for Python environment setup.",
|
||||
"properties": {
|
||||
"python": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Python version constraint",
|
||||
"examples": [
|
||||
"3.10",
|
||||
"3.11",
|
||||
"3.12"
|
||||
],
|
||||
"title": "Python"
|
||||
},
|
||||
"dependencies": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Python packages to install with PEP 508 specifiers",
|
||||
"examples": [
|
||||
[
|
||||
"fastmcp>=2.0,<3",
|
||||
"httpx",
|
||||
"pandas>=2.0"
|
||||
]
|
||||
],
|
||||
"title": "Dependencies"
|
||||
},
|
||||
"requirements": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Path to requirements.txt file",
|
||||
"examples": [
|
||||
"requirements.txt",
|
||||
"../requirements/prod.txt"
|
||||
],
|
||||
"title": "Requirements"
|
||||
},
|
||||
"project": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Path to project directory containing pyproject.toml",
|
||||
"examples": [
|
||||
".",
|
||||
"../my-project"
|
||||
],
|
||||
"title": "Project"
|
||||
},
|
||||
"editable": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Directory to install in editable mode",
|
||||
"examples": [
|
||||
".",
|
||||
"../my-package"
|
||||
],
|
||||
"title": "Editable"
|
||||
}
|
||||
},
|
||||
"title": "Environment",
|
||||
"type": "object"
|
||||
},
|
||||
"FileSystemSource": {
|
||||
"description": "Source for local Python files.",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "filesystem",
|
||||
"default": "filesystem",
|
||||
"description": "Source type",
|
||||
"title": "Type",
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"description": "Path to Python file containing the server",
|
||||
"title": "Path",
|
||||
"type": "string"
|
||||
},
|
||||
"entrypoint": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Name of server instance or factory function (a no-arg function that returns a FastMCP server)",
|
||||
"title": "Entrypoint"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"title": "FileSystemSource",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"description": "Configuration file for FastMCP servers",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"description": "JSON schema for IDE support and validation",
|
||||
"title": "$Schema"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/$defs/FileSystemSource",
|
||||
"description": "Source configuration for the server",
|
||||
"examples": [
|
||||
{
|
||||
"path": "server.py"
|
||||
},
|
||||
{
|
||||
"entrypoint": "app",
|
||||
"path": "server.py"
|
||||
},
|
||||
{
|
||||
"entrypoint": "mcp",
|
||||
"path": "src/server.py",
|
||||
"type": "filesystem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"environment": {
|
||||
"$ref": "#/$defs/Environment",
|
||||
"description": "Python environment setup configuration"
|
||||
},
|
||||
"deployment": {
|
||||
"$ref": "#/$defs/Deployment",
|
||||
"description": "Server deployment and runtime settings"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"source"
|
||||
],
|
||||
"title": "FastMCP Configuration",
|
||||
"type": "object",
|
||||
"$id": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json"
|
||||
}
|
||||
348
docs/assets/schemas/mcp_server_config/v1.json
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
{
|
||||
"$defs": {
|
||||
"Deployment": {
|
||||
"description": "Configuration for server deployment and runtime settings.",
|
||||
"properties": {
|
||||
"transport": {
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"stdio",
|
||||
"http",
|
||||
"sse"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Transport protocol to use",
|
||||
"title": "Transport"
|
||||
},
|
||||
"host": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Host to bind to when using HTTP transport",
|
||||
"examples": [
|
||||
"127.0.0.1",
|
||||
"0.0.0.0",
|
||||
"localhost"
|
||||
],
|
||||
"title": "Host"
|
||||
},
|
||||
"port": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Port to bind to when using HTTP transport",
|
||||
"examples": [
|
||||
8000,
|
||||
3000,
|
||||
5000
|
||||
],
|
||||
"title": "Port"
|
||||
},
|
||||
"path": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "URL path for the server endpoint",
|
||||
"examples": [
|
||||
"/mcp/",
|
||||
"/api/mcp/",
|
||||
"/sse/"
|
||||
],
|
||||
"title": "Path"
|
||||
},
|
||||
"log_level": {
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"DEBUG",
|
||||
"INFO",
|
||||
"WARNING",
|
||||
"ERROR",
|
||||
"CRITICAL"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Log level for the server",
|
||||
"title": "Log Level"
|
||||
},
|
||||
"cwd": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Working directory for the server process",
|
||||
"examples": [
|
||||
".",
|
||||
"./src",
|
||||
"/app"
|
||||
],
|
||||
"title": "Cwd"
|
||||
},
|
||||
"env": {
|
||||
"anyOf": [
|
||||
{
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Environment variables to set when running the server",
|
||||
"examples": [
|
||||
{
|
||||
"API_KEY": "secret",
|
||||
"DEBUG": "true"
|
||||
}
|
||||
],
|
||||
"title": "Env"
|
||||
},
|
||||
"args": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Arguments to pass to the server (after --)",
|
||||
"examples": [
|
||||
[
|
||||
"--config",
|
||||
"config.json",
|
||||
"--debug"
|
||||
]
|
||||
],
|
||||
"title": "Args"
|
||||
}
|
||||
},
|
||||
"title": "Deployment",
|
||||
"type": "object"
|
||||
},
|
||||
"Environment": {
|
||||
"description": "Configuration for Python environment setup.",
|
||||
"properties": {
|
||||
"python": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Python version constraint",
|
||||
"examples": [
|
||||
"3.10",
|
||||
"3.11",
|
||||
"3.12"
|
||||
],
|
||||
"title": "Python"
|
||||
},
|
||||
"dependencies": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Python packages to install with PEP 508 specifiers",
|
||||
"examples": [
|
||||
[
|
||||
"fastmcp>=2.0,<3",
|
||||
"httpx",
|
||||
"pandas>=2.0"
|
||||
]
|
||||
],
|
||||
"title": "Dependencies"
|
||||
},
|
||||
"requirements": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Path to requirements.txt file",
|
||||
"examples": [
|
||||
"requirements.txt",
|
||||
"../requirements/prod.txt"
|
||||
],
|
||||
"title": "Requirements"
|
||||
},
|
||||
"project": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Path to project directory containing pyproject.toml",
|
||||
"examples": [
|
||||
".",
|
||||
"../my-project"
|
||||
],
|
||||
"title": "Project"
|
||||
},
|
||||
"editable": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Directory to install in editable mode",
|
||||
"examples": [
|
||||
".",
|
||||
"../my-package"
|
||||
],
|
||||
"title": "Editable"
|
||||
}
|
||||
},
|
||||
"title": "Environment",
|
||||
"type": "object"
|
||||
},
|
||||
"FileSystemSource": {
|
||||
"description": "Source for local Python files.",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "filesystem",
|
||||
"default": "filesystem",
|
||||
"description": "Source type",
|
||||
"title": "Type",
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"description": "Path to Python file containing the server",
|
||||
"title": "Path",
|
||||
"type": "string"
|
||||
},
|
||||
"entrypoint": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Name of server instance or factory function (a no-arg function that returns a FastMCP server)",
|
||||
"title": "Entrypoint"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"title": "FileSystemSource",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"description": "Configuration file for FastMCP servers",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"description": "JSON schema for IDE support and validation",
|
||||
"title": "$Schema"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/$defs/FileSystemSource",
|
||||
"description": "Source configuration for the server",
|
||||
"examples": [
|
||||
{
|
||||
"path": "server.py"
|
||||
},
|
||||
{
|
||||
"entrypoint": "app",
|
||||
"path": "server.py"
|
||||
},
|
||||
{
|
||||
"entrypoint": "mcp",
|
||||
"path": "src/server.py",
|
||||
"type": "filesystem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"environment": {
|
||||
"$ref": "#/$defs/Environment",
|
||||
"description": "Python environment setup configuration"
|
||||
},
|
||||
"deployment": {
|
||||
"$ref": "#/$defs/Deployment",
|
||||
"description": "Server deployment and runtime settings"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"source"
|
||||
],
|
||||
"title": "FastMCP Configuration",
|
||||
"type": "object",
|
||||
"$id": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json"
|
||||
}
|
||||
BIN
docs/assets/updates/release-2-7.png
Normal file
|
After Width: | Height: | Size: 412 KiB |
2013
docs/changelog.mdx
Normal file
88
docs/clients/auth/bearer.mdx
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
---
|
||||
title: Bearer Token Authentication
|
||||
sidebarTitle: Bearer Auth
|
||||
description: Authenticate your FastMCP client with a Bearer token.
|
||||
icon: key
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.6.0" />
|
||||
|
||||
<Tip>
|
||||
Bearer Token authentication is only relevant for HTTP-based transports.
|
||||
</Tip>
|
||||
|
||||
You can configure your FastMCP client to use **bearer authentication** by supplying a valid access token. This is most appropriate for service accounts, long-lived API keys, CI/CD, applications where authentication is managed separately, or other non-interactive authentication methods.
|
||||
|
||||
A Bearer token is a JSON Web Token (JWT) that is used to authenticate a request. It is most commonly used in the `Authorization` header of an HTTP request, using the `Bearer` scheme:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
|
||||
## Client Usage
|
||||
|
||||
The most straightforward way to use a pre-existing Bearer token is to provide it as a string to the `auth` parameter of the `fastmcp.Client` or transport instance. FastMCP will automatically format it correctly for the `Authorization` header and bearer scheme.
|
||||
|
||||
<Tip>
|
||||
If you're using a string token, do not include the `Bearer` prefix. FastMCP will add it for you.
|
||||
</Tip>
|
||||
|
||||
```python {5}
|
||||
from fastmcp import Client
|
||||
|
||||
async with Client(
|
||||
"https://fastmcp.cloud/mcp",
|
||||
auth="<your-token>",
|
||||
) as client:
|
||||
await client.ping()
|
||||
```
|
||||
|
||||
You can also supply a Bearer token to a transport instance, such as `StreamableHttpTransport` or `SSETransport`:
|
||||
|
||||
```python {6}
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.transports import StreamableHttpTransport
|
||||
|
||||
transport = StreamableHttpTransport(
|
||||
"http://fastmcp.cloud/mcp",
|
||||
auth="<your-token>",
|
||||
)
|
||||
|
||||
async with Client(transport) as client:
|
||||
await client.ping()
|
||||
```
|
||||
|
||||
## `BearerAuth` Helper
|
||||
|
||||
If you prefer to be more explicit and not rely on FastMCP to transform your string token, you can use the `BearerAuth` class yourself, which implements the `httpx.Auth` interface.
|
||||
|
||||
```python {6}
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.auth import BearerAuth
|
||||
|
||||
async with Client(
|
||||
"https://fastmcp.cloud/mcp",
|
||||
auth=BearerAuth(token="<your-token>"),
|
||||
) as client:
|
||||
await client.ping()
|
||||
```
|
||||
|
||||
## Custom Headers
|
||||
|
||||
If the MCP server expects a custom header or token scheme, you can manually set the client's `headers` instead of using the `auth` parameter by setting them on your transport:
|
||||
|
||||
```python {5}
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.transports import StreamableHttpTransport
|
||||
|
||||
async with Client(
|
||||
transport=StreamableHttpTransport(
|
||||
"https://fastmcp.cloud/mcp",
|
||||
headers={"X-API-Key": "<your-token>"},
|
||||
),
|
||||
) as client:
|
||||
await client.ping()
|
||||
```
|
||||
118
docs/clients/auth/oauth.mdx
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
title: OAuth Authentication
|
||||
sidebarTitle: OAuth
|
||||
description: Authenticate your FastMCP client via OAuth 2.1.
|
||||
icon: window
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.6.0" />
|
||||
|
||||
<Tip>
|
||||
OAuth authentication is only relevant for HTTP-based transports and requires user interaction via a web browser.
|
||||
</Tip>
|
||||
|
||||
When your FastMCP client needs to access an MCP server protected by OAuth 2.1, and the process requires user interaction (like logging in and granting consent), you should use the Authorization Code Flow. FastMCP provides the `fastmcp.client.auth.OAuth` helper to simplify this entire process.
|
||||
|
||||
This flow is common for user-facing applications where the application acts on behalf of the user.
|
||||
|
||||
## Client Usage
|
||||
|
||||
|
||||
### Default Configuration
|
||||
|
||||
The simplest way to use OAuth is to pass the string `"oauth"` to the `auth` parameter of the `Client` or transport instance. FastMCP will automatically configure the client to use OAuth with default settings:
|
||||
|
||||
```python {4}
|
||||
from fastmcp import Client
|
||||
|
||||
# Uses default OAuth settings
|
||||
async with Client("https://fastmcp.cloud/mcp", auth="oauth") as client:
|
||||
await client.ping()
|
||||
```
|
||||
|
||||
|
||||
### `OAuth` Helper
|
||||
|
||||
To fully configure the OAuth flow, use the `OAuth` helper and pass it to the `auth` parameter of the `Client` or transport instance. `OAuth` manages the complexities of the OAuth 2.1 Authorization Code Grant with PKCE (Proof Key for Code Exchange) for enhanced security, and implements the full `httpx.Auth` interface.
|
||||
|
||||
```python {2, 4, 6}
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.auth import OAuth
|
||||
|
||||
oauth = OAuth(mcp_url="https://fastmcp.cloud/mcp")
|
||||
|
||||
async with Client("https://fastmcp.cloud/mcp", auth=oauth) as client:
|
||||
await client.ping()
|
||||
```
|
||||
|
||||
#### `OAuth` Parameters
|
||||
|
||||
- **`mcp_url`** (`str`): The full URL of the target MCP server endpoint. Used to discover OAuth server metadata
|
||||
- **`scopes`** (`str | list[str]`, optional): OAuth scopes to request. Can be space-separated string or list of strings
|
||||
- **`client_name`** (`str`, optional): Client name for dynamic registration. Defaults to `"FastMCP Client"`
|
||||
- **`token_storage_cache_dir`** (`Path`, optional): Token cache directory. Defaults to `~/.fastmcp/oauth-mcp-client-cache/`
|
||||
- **`additional_client_metadata`** (`dict[str, Any]`, optional): Extra metadata for client registration
|
||||
- **`callback_port`** (`int`, optional): Fixed port for OAuth callback server. If not specified, uses a random available port
|
||||
|
||||
|
||||
## OAuth Flow
|
||||
|
||||
The OAuth flow is triggered when you use a FastMCP `Client` configured to use OAuth.
|
||||
|
||||
<Steps>
|
||||
<Step title="Token Check">
|
||||
The client first checks the `token_storage_cache_dir` for existing, valid tokens for the target server. If one is found, it will be used to authenticate the client.
|
||||
</Step>
|
||||
<Step title="OAuth Server Discovery">
|
||||
If no valid tokens exist, the client attempts to discover the OAuth server's endpoints using a well-known URI (e.g., `/.well-known/oauth-authorization-server`) based on the `mcp_url`.
|
||||
</Step>
|
||||
<Step title="Dynamic Client Registration">
|
||||
If the OAuth server supports it and the client isn't already registered (or credentials aren't cached), the client performs dynamic client registration according to RFC 7591.
|
||||
</Step>
|
||||
<Step title="Local Callback Server">
|
||||
A temporary local HTTP server is started on an available port (or the port specified via `callback_port`). This server's address (e.g., `http://127.0.0.1:<port>/callback`) acts as the `redirect_uri` for the OAuth flow.
|
||||
</Step>
|
||||
<Step title="Browser Interaction">
|
||||
The user's default web browser is automatically opened, directing them to the OAuth server's authorization endpoint. The user logs in and grants (or denies) the requested `scopes`.
|
||||
</Step>
|
||||
<Step title="Authorization Code & Token Exchange">
|
||||
Upon approval, the OAuth server redirects the user's browser to the local callback server with an `authorization_code`. The client captures this code and exchanges it with the OAuth server's token endpoint for an `access_token` (and often a `refresh_token`) using PKCE for security.
|
||||
</Step>
|
||||
<Step title="Token Caching">
|
||||
The obtained tokens are saved to the `token_storage_cache_dir` for future use, eliminating the need for repeated browser interactions.
|
||||
</Step>
|
||||
<Step title="Authenticated Requests">
|
||||
The access token is automatically included in the `Authorization` header for requests to the MCP server.
|
||||
</Step>
|
||||
<Step title="Refresh Token">
|
||||
If the access token expires, the client will automatically use the refresh token to get a new access token.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Token Management
|
||||
|
||||
### Token Storage
|
||||
|
||||
OAuth access tokens are automatically cached in `~/.fastmcp/oauth-mcp-client-cache/` and persist between application runs. Files are keyed by the OAuth server's base URL.
|
||||
|
||||
### Managing Cache
|
||||
|
||||
To clear the tokens for a specific server, instantiate a `FileTokenStorage` instance and call the `clear` method:
|
||||
|
||||
```python
|
||||
from fastmcp.client.auth.oauth import FileTokenStorage
|
||||
|
||||
storage = FileTokenStorage(server_url="https://fastmcp.cloud/mcp")
|
||||
await storage.clear()
|
||||
```
|
||||
|
||||
To clear *all* tokens for all servers, call the `clear_all` method on the `FileTokenStorage` class:
|
||||
|
||||
```python
|
||||
from fastmcp.client.auth.oauth import FileTokenStorage
|
||||
|
||||
FileTokenStorage.clear_all()
|
||||
```
|
||||
291
docs/clients/client.mdx
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
---
|
||||
title: The FastMCP Client
|
||||
sidebarTitle: Overview
|
||||
description: Programmatic client for interacting with MCP servers through a well-typed, Pythonic interface.
|
||||
icon: user-robot
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
The central piece of MCP client applications is the `fastmcp.Client` class. This class provides a **programmatic interface** for interacting with any Model Context Protocol (MCP) server, handling protocol details and connection management automatically.
|
||||
|
||||
The FastMCP Client is designed for deterministic, controlled interactions rather than autonomous behavior, making it ideal for:
|
||||
|
||||
- **Testing MCP servers** during development
|
||||
- **Building deterministic applications** that need reliable MCP interactions
|
||||
- **Creating the foundation for agentic or LLM-based clients** with structured, type-safe operations
|
||||
|
||||
All client operations require using the `async with` context manager for proper connection lifecycle management.
|
||||
|
||||
|
||||
<Note>
|
||||
This is not an agentic client - it requires explicit function calls and provides direct control over all MCP operations. Use it as a building block for higher-level systems.
|
||||
</Note>
|
||||
|
||||
## Creating a Client
|
||||
|
||||
Creating a client is straightforward. You provide a server source and the client automatically infers the appropriate transport mechanism.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from fastmcp import Client, FastMCP
|
||||
|
||||
# In-memory server (ideal for testing)
|
||||
server = FastMCP("TestServer")
|
||||
client = Client(server)
|
||||
|
||||
# HTTP server
|
||||
client = Client("https://example.com/mcp")
|
||||
|
||||
# Local Python script
|
||||
client = Client("my_mcp_server.py")
|
||||
|
||||
async def main():
|
||||
async with client:
|
||||
# Basic server interaction
|
||||
await client.ping()
|
||||
|
||||
# List available operations
|
||||
tools = await client.list_tools()
|
||||
resources = await client.list_resources()
|
||||
prompts = await client.list_prompts()
|
||||
|
||||
# Execute operations
|
||||
result = await client.call_tool("example_tool", {"param": "value"})
|
||||
print(result)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Client-Transport Architecture
|
||||
|
||||
The FastMCP Client separates concerns between protocol and connection:
|
||||
|
||||
- **`Client`**: Handles MCP protocol operations (tools, resources, prompts) and manages callbacks
|
||||
- **`Transport`**: Establishes and maintains the connection (WebSockets, HTTP, Stdio, in-memory)
|
||||
|
||||
### Transport Inference
|
||||
|
||||
The client automatically infers the appropriate transport based on the input:
|
||||
|
||||
1. **`FastMCP` instance** → In-memory transport (perfect for testing)
|
||||
2. **File path ending in `.py`** → Python Stdio transport
|
||||
3. **File path ending in `.js`** → Node.js Stdio transport
|
||||
4. **URL starting with `http://` or `https://`** → HTTP transport
|
||||
5. **`MCPConfig` dictionary** → Multi-server client
|
||||
|
||||
```python
|
||||
from fastmcp import Client, FastMCP
|
||||
|
||||
# Examples of transport inference
|
||||
client_memory = Client(FastMCP("TestServer"))
|
||||
client_script = Client("./server.py")
|
||||
client_http = Client("https://api.example.com/mcp")
|
||||
```
|
||||
|
||||
<Tip>
|
||||
For testing and development, always prefer the in-memory transport by passing a `FastMCP` server directly to the client. This eliminates network complexity and separate processes.
|
||||
</Tip>
|
||||
|
||||
## Configuration-Based Clients
|
||||
|
||||
<VersionBadge version="2.4.0" />
|
||||
|
||||
Create clients from MCP configuration dictionaries, which can include multiple servers. While there is no official standard for MCP configuration format, FastMCP follows established conventions used by tools like Claude Desktop.
|
||||
|
||||
### Configuration Format
|
||||
|
||||
```python
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"server_name": {
|
||||
# Remote HTTP/SSE server
|
||||
"transport": "http", # or "sse"
|
||||
"url": "https://api.example.com/mcp",
|
||||
"headers": {"Authorization": "Bearer token"},
|
||||
"auth": "oauth" # or bearer token string
|
||||
},
|
||||
"local_server": {
|
||||
# Local stdio server
|
||||
"transport": "stdio",
|
||||
"command": "python",
|
||||
"args": ["./server.py", "--verbose"],
|
||||
"env": {"DEBUG": "true"},
|
||||
"cwd": "/path/to/server",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Server Example
|
||||
|
||||
```python
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {"url": "https://weather-api.example.com/mcp"},
|
||||
"assistant": {"command": "python", "args": ["./assistant_server.py"]}
|
||||
}
|
||||
}
|
||||
|
||||
client = Client(config)
|
||||
|
||||
async with client:
|
||||
# Tools are prefixed with server names
|
||||
weather_data = await client.call_tool("weather_get_forecast", {"city": "London"})
|
||||
response = await client.call_tool("assistant_answer_question", {"question": "What's the capital of France?"})
|
||||
|
||||
# Resources use prefixed URIs
|
||||
icons = await client.read_resource("weather://weather/icons/sunny")
|
||||
templates = await client.read_resource("resource://assistant/templates/list")
|
||||
```
|
||||
|
||||
## Connection Lifecycle
|
||||
|
||||
The client operates asynchronously and uses context managers for connection management:
|
||||
|
||||
```python
|
||||
async def example():
|
||||
client = Client("my_mcp_server.py")
|
||||
|
||||
# Connection established here
|
||||
async with client:
|
||||
print(f"Connected: {client.is_connected()}")
|
||||
|
||||
# Make multiple calls within the same session
|
||||
tools = await client.list_tools()
|
||||
result = await client.call_tool("greet", {"name": "World"})
|
||||
|
||||
# Connection closed automatically here
|
||||
print(f"Connected: {client.is_connected()}")
|
||||
```
|
||||
|
||||
## Operations
|
||||
|
||||
FastMCP clients can interact with several types of server components:
|
||||
|
||||
### Tools
|
||||
|
||||
Tools are server-side functions that the client can execute with arguments.
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# List available tools
|
||||
tools = await client.list_tools()
|
||||
|
||||
# Execute a tool
|
||||
result = await client.call_tool("multiply", {"a": 5, "b": 3})
|
||||
print(result.data) # 15
|
||||
```
|
||||
|
||||
See [Tools](/clients/tools) for detailed documentation.
|
||||
|
||||
### Resources
|
||||
|
||||
Resources are data sources that the client can read, either static or templated.
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# List available resources
|
||||
resources = await client.list_resources()
|
||||
|
||||
# Read a resource
|
||||
content = await client.read_resource("file:///config/settings.json")
|
||||
print(content[0].text)
|
||||
```
|
||||
|
||||
See [Resources](/clients/resources) for detailed documentation.
|
||||
|
||||
### Prompts
|
||||
|
||||
Prompts are reusable message templates that can accept arguments.
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# List available prompts
|
||||
prompts = await client.list_prompts()
|
||||
|
||||
# Get a rendered prompt
|
||||
messages = await client.get_prompt("analyze_data", {"data": [1, 2, 3]})
|
||||
print(messages.messages)
|
||||
```
|
||||
|
||||
See [Prompts](/clients/prompts) for detailed documentation.
|
||||
|
||||
### Server Connectivity
|
||||
|
||||
Use `ping()` to verify the server is reachable:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
await client.ping()
|
||||
print("Server is reachable")
|
||||
```
|
||||
|
||||
## Client Configuration
|
||||
|
||||
Clients can be configured with additional handlers and settings for specialized use cases.
|
||||
|
||||
### Callback Handlers
|
||||
|
||||
The client supports several callback handlers for advanced server interactions:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.logging import LogMessage
|
||||
|
||||
async def log_handler(message: LogMessage):
|
||||
print(f"Server log: {message.data}")
|
||||
|
||||
async def progress_handler(progress: float, total: float | None, message: str | None):
|
||||
print(f"Progress: {progress}/{total} - {message}")
|
||||
|
||||
async def sampling_handler(messages, params, context):
|
||||
# Integrate with your LLM service here
|
||||
return "Generated response"
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
log_handler=log_handler,
|
||||
progress_handler=progress_handler,
|
||||
sampling_handler=sampling_handler,
|
||||
timeout=30.0
|
||||
)
|
||||
```
|
||||
|
||||
The `Client` constructor accepts several configuration options:
|
||||
|
||||
- `transport`: Transport instance or source for automatic inference
|
||||
- `log_handler`: Handle server log messages
|
||||
- `progress_handler`: Monitor long-running operations
|
||||
- `sampling_handler`: Respond to server LLM requests
|
||||
- `roots`: Provide local context to servers
|
||||
- `timeout`: Default timeout for requests (in seconds)
|
||||
|
||||
### Transport Configuration
|
||||
|
||||
For detailed transport configuration (headers, authentication, environment variables), see the [Transports](/clients/transports) documentation.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Explore the detailed documentation for each operation type:
|
||||
|
||||
### Core Operations
|
||||
- **[Tools](/clients/tools)** - Execute server-side functions and handle results
|
||||
- **[Resources](/clients/resources)** - Access static and templated resources
|
||||
- **[Prompts](/clients/prompts)** - Work with message templates and argument serialization
|
||||
|
||||
### Advanced Features
|
||||
- **[Logging](/clients/logging)** - Handle server log messages
|
||||
- **[Progress](/clients/progress)** - Monitor long-running operations
|
||||
- **[Sampling](/clients/sampling)** - Respond to server LLM requests
|
||||
- **[Roots](/clients/roots)** - Provide local context to servers
|
||||
|
||||
### Connection Details
|
||||
- **[Transports](/clients/transports)** - Configure connection methods and parameters
|
||||
- **[Authentication](/clients/auth/oauth)** - Set up OAuth and bearer token authentication
|
||||
|
||||
<Tip>
|
||||
The FastMCP Client is designed as a foundational tool. Use it directly for deterministic operations, or build higher-level agentic systems on top of its reliable, type-safe interface.
|
||||
</Tip>
|
||||
126
docs/clients/elicitation.mdx
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
---
|
||||
title: User Elicitation
|
||||
sidebarTitle: Elicitation
|
||||
description: Handle server-initiated user input requests with structured schemas.
|
||||
icon: message-question
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx";
|
||||
|
||||
<VersionBadge version="2.10.0" />
|
||||
|
||||
## What is Elicitation?
|
||||
|
||||
Elicitation allows MCP servers to request structured input from users during tool execution. Instead of requiring all inputs upfront, servers can interactively ask users for information as needed - like prompting for missing parameters, requesting clarification, or gathering additional context.
|
||||
|
||||
For example, a file management tool might ask "Which directory should I create?" or a data analysis tool might request "What date range should I analyze?"
|
||||
|
||||
## How FastMCP Makes Elicitation Easy
|
||||
|
||||
FastMCP's client provides a helpful abstraction layer that:
|
||||
|
||||
- **Converts JSON schemas to Python types**: The raw MCP protocol uses JSON schemas, but FastMCP automatically converts these to Python dataclasses
|
||||
- **Provides structured constructors**: Instead of manually building dictionaries that match the schema, you get dataclass constructors that ensure correct structure
|
||||
- **Handles type conversion**: FastMCP takes care of converting between JSON representations and Python objects
|
||||
- **Runtime introspection**: You can inspect the generated dataclass fields to understand the expected structure
|
||||
|
||||
When you implement an elicitation handler, FastMCP gives you a dataclass type that matches the server's schema, making it easy to create properly structured responses without having to manually parse JSON schemas.
|
||||
|
||||
## Elicitation Handler
|
||||
|
||||
Provide an `elicitation_handler` function when creating the client. FastMCP automatically converts the server's JSON schema into a Python dataclass type, making it easy to construct the response:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.elicitation import ElicitResult
|
||||
|
||||
async def elicitation_handler(message: str, response_type: type, params, context):
|
||||
# Present the message to the user and collect input
|
||||
user_input = input(f"{message}: ")
|
||||
|
||||
# Create response using the provided dataclass type
|
||||
# FastMCP converted the JSON schema to this Python type for you
|
||||
response_data = response_type(value=user_input)
|
||||
|
||||
# You can return data directly - FastMCP will implicitly accept the elicitation
|
||||
return response_data
|
||||
|
||||
# Or explicitly return an ElicitResult for more control
|
||||
# return ElicitResult(action="accept", content=response_data)
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
elicitation_handler=elicitation_handler,
|
||||
)
|
||||
```
|
||||
|
||||
### Handler Parameters
|
||||
|
||||
The elicitation handler receives four parameters:
|
||||
|
||||
<Card icon="code" title="Elicitation Handler Parameters">
|
||||
<ResponseField name="message" type="str">
|
||||
The prompt message to display to the user
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="response_type" type="type">
|
||||
A Python dataclass type that FastMCP created from the server's JSON schema. Use this to construct your response with proper typing and IDE support. If the server requests an empty object (indicating no response), this will be `None`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="params" type="ElicitRequestParams">
|
||||
The original MCP elicitation request parameters, including the raw JSON schema in `params.requestedSchema` if you need it
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="context" type="RequestContext">
|
||||
Request context containing metadata about the elicitation request
|
||||
</ResponseField>
|
||||
</Card>
|
||||
|
||||
### Response Actions
|
||||
|
||||
The handler can return data directly (which implicitly accepts the elicitation) or an `ElicitResult` object for more control over the response action:
|
||||
|
||||
<Card icon="code" title="ElicitResult Structure">
|
||||
<ResponseField name="action" type="Literal['accept', 'decline', 'cancel']">
|
||||
How the user responded to the elicitation request
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="content" type="dataclass instance | dict | None">
|
||||
The user's input data (required for "accept", omitted for "decline"/"cancel")
|
||||
</ResponseField>
|
||||
</Card>
|
||||
|
||||
**Action Types:**
|
||||
- **`accept`**: User provided valid input - include their data in the `content` field
|
||||
- **`decline`**: User chose not to provide the requested information - omit `content`
|
||||
- **`cancel`**: User cancelled the entire operation - omit `content`
|
||||
|
||||
## Basic Example
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.elicitation import ElicitResult
|
||||
|
||||
async def basic_elicitation_handler(message: str, response_type: type, params, context):
|
||||
print(f"Server asks: {message}")
|
||||
|
||||
# Simple text input for demonstration
|
||||
user_response = input("Your response: ")
|
||||
|
||||
if not user_response:
|
||||
# For non-acceptance, use ElicitResult explicitly
|
||||
return ElicitResult(action="decline")
|
||||
|
||||
# Use the response_type dataclass to create a properly structured response
|
||||
# FastMCP handles the conversion from JSON schema to Python type
|
||||
# Return data directly - FastMCP will implicitly accept the elicitation
|
||||
return response_type(value=user_response)
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
elicitation_handler=basic_elicitation_handler
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
111
docs/clients/logging.mdx
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
---
|
||||
title: Server Logging
|
||||
sidebarTitle: Logging
|
||||
description: Receive and handle log messages from MCP servers.
|
||||
icon: receipt
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
MCP servers can emit log messages to clients. The client can handle these logs through a log handler callback.
|
||||
|
||||
## Log Handler
|
||||
|
||||
Provide a `log_handler` function when creating the client. For robust logging, the log messages can be integrated with Python's standard `logging` module.
|
||||
|
||||
```python
|
||||
import logging
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.logging import LogMessage
|
||||
|
||||
# In a real app, you might configure this in your main entry point
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
# Get a logger for the module where the client is used
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# This mapping is useful for converting MCP level strings to Python's levels
|
||||
LOGGING_LEVEL_MAP = logging.getLevelNamesMapping()
|
||||
|
||||
async def log_handler(message: LogMessage):
|
||||
"""
|
||||
Handles incoming logs from the MCP server and forwards them
|
||||
to the standard Python logging system.
|
||||
"""
|
||||
msg = message.data.get('msg')
|
||||
extra = message.data.get('extra')
|
||||
|
||||
# Convert the MCP log level to a Python log level
|
||||
level = LOGGING_LEVEL_MAP.get(message.level.upper(), logging.INFO)
|
||||
|
||||
# Log the message using the standard logging library
|
||||
logger.log(level, msg, extra=extra)
|
||||
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
log_handler=log_handler,
|
||||
)
|
||||
```
|
||||
|
||||
## Handling Structured Logs
|
||||
|
||||
The `message.data` attribute is a dictionary that contains the log payload from the server. This enables structured logging, allowing you to receive rich, contextual information.
|
||||
|
||||
The dictionary contains two keys:
|
||||
- `msg`: The string log message.
|
||||
- `extra`: A dictionary containing any extra data sent from the server.
|
||||
|
||||
This structure is preserved even when logs are forwarded through a FastMCP proxy, making it a powerful tool for debugging complex, multi-server applications.
|
||||
|
||||
### Handler Parameters
|
||||
|
||||
The `log_handler` is called every time a log message is received. It receives a `LogMessage` object:
|
||||
|
||||
<Card icon="code" title="Log Handler Parameters">
|
||||
<ResponseField name="LogMessage" type="Log Message Object">
|
||||
<Expandable title="attributes">
|
||||
<ResponseField name="level" type='Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]'>
|
||||
The log level
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="logger" type="str | None">
|
||||
The logger name (optional, may be None)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="data" type="dict">
|
||||
The log payload, containing `msg` and `extra` keys.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Card>
|
||||
|
||||
```python
|
||||
async def detailed_log_handler(message: LogMessage):
|
||||
msg = message.data.get('msg')
|
||||
extra = message.data.get('extra')
|
||||
|
||||
if message.level == "error":
|
||||
print(f"ERROR: {msg} | Details: {extra}")
|
||||
elif message.level == "warning":
|
||||
print(f"WARNING: {msg} | Details: {extra}")
|
||||
else:
|
||||
print(f"{message.level.upper()}: {msg}")
|
||||
```
|
||||
|
||||
## Default Log Handling
|
||||
|
||||
If you don't provide a custom `log_handler`, FastMCP's default handler routes server logs to the appropriate Python logging levels. The MCP levels are mapped as follows: `notice` → INFO; `alert` and `emergency` → CRITICAL. If the server includes a logger name, it is prefixed in the message, and any `extra` data is forwarded via the logging `extra` parameter.
|
||||
|
||||
```python
|
||||
client = Client("my_mcp_server.py")
|
||||
|
||||
async with client:
|
||||
# Server logs are forwarded at their proper severity (DEBUG/INFO/WARNING/ERROR/CRITICAL)
|
||||
await client.call_tool("some_tool")
|
||||
```
|
||||
129
docs/clients/messages.mdx
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
title: Message Handling
|
||||
sidebarTitle: Messages
|
||||
description: Handle MCP messages, requests, and notifications with custom message handlers.
|
||||
icon: envelope
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx";
|
||||
|
||||
<VersionBadge version="2.9.1" />
|
||||
|
||||
MCP clients can receive various types of messages from servers, including requests that need responses and notifications that don't. The message handler provides a unified way to process all these messages.
|
||||
|
||||
## Function-Based Handler
|
||||
|
||||
The simplest way to handle messages is with a function that receives all messages:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
async def message_handler(message):
|
||||
"""Handle all MCP messages from the server."""
|
||||
if hasattr(message, 'root'):
|
||||
method = message.root.method
|
||||
print(f"Received: {method}")
|
||||
|
||||
# Handle specific notifications
|
||||
if method == "notifications/tools/list_changed":
|
||||
print("Tools have changed - might want to refresh tool cache")
|
||||
elif method == "notifications/resources/list_changed":
|
||||
print("Resources have changed")
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
message_handler=message_handler,
|
||||
)
|
||||
```
|
||||
|
||||
## Message Handler Class
|
||||
|
||||
For fine-grained targeting, FastMCP provides a `MessageHandler` class you can subclass to take advantage of specific hooks:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.messages import MessageHandler
|
||||
import mcp.types
|
||||
|
||||
class MyMessageHandler(MessageHandler):
|
||||
async def on_tool_list_changed(
|
||||
self, notification: mcp.types.ToolListChangedNotification
|
||||
) -> None:
|
||||
"""Handle tool list changes specifically."""
|
||||
print("Tool list changed - refreshing available tools")
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
message_handler=MyMessageHandler(),
|
||||
)
|
||||
```
|
||||
|
||||
### Available Handler Methods
|
||||
|
||||
All handler methods receive a single argument - the specific message type:
|
||||
|
||||
<Card icon="code" title="Message Handler Methods">
|
||||
<ResponseField name="on_message(message)" type="Any MCP message">
|
||||
Called for ALL messages (requests and notifications)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="on_request(request)" type="mcp.types.ClientRequest">
|
||||
Called for requests that expect responses
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="on_notification(notification)" type="mcp.types.ServerNotification">
|
||||
Called for notifications (fire-and-forget)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="on_tool_list_changed(notification)" type="mcp.types.ToolListChangedNotification">
|
||||
Called when the server's tool list changes
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="on_resource_list_changed(notification)" type="mcp.types.ResourceListChangedNotification">
|
||||
Called when the server's resource list changes
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="on_prompt_list_changed(notification)" type="mcp.types.PromptListChangedNotification">
|
||||
Called when the server's prompt list changes
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="on_progress(notification)" type="mcp.types.ProgressNotification">
|
||||
Called for progress updates during long-running operations
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="on_logging_message(notification)" type="mcp.types.LoggingMessageNotification">
|
||||
Called for log messages from the server
|
||||
</ResponseField>
|
||||
</Card>
|
||||
|
||||
## Example: Handling Tool Changes
|
||||
|
||||
Here's a practical example of handling tool list changes:
|
||||
|
||||
```python
|
||||
from fastmcp.client.messages import MessageHandler
|
||||
import mcp.types
|
||||
|
||||
class ToolCacheHandler(MessageHandler):
|
||||
def __init__(self):
|
||||
self.cached_tools = []
|
||||
|
||||
async def on_tool_list_changed(
|
||||
self, notification: mcp.types.ToolListChangedNotification
|
||||
) -> None:
|
||||
"""Clear tool cache when tools change."""
|
||||
print("Tools changed - clearing cache")
|
||||
self.cached_tools = [] # Force refresh on next access
|
||||
|
||||
client = Client("server.py", message_handler=ToolCacheHandler())
|
||||
```
|
||||
|
||||
## Handling Requests
|
||||
|
||||
While the message handler receives server-initiated requests, for most use cases you should use the dedicated callback parameters instead:
|
||||
|
||||
- **Sampling requests**: Use [`sampling_handler`](/clients/sampling)
|
||||
- **Progress requests**: Use [`progress_handler`](/clients/progress)
|
||||
- **Log requests**: Use [`log_handler`](/clients/logging)
|
||||
|
||||
The message handler is primarily for monitoring and handling notifications rather than responding to requests.
|
||||
70
docs/clients/progress.mdx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
title: Progress Monitoring
|
||||
sidebarTitle: Progress
|
||||
description: Handle progress notifications from long-running server operations.
|
||||
icon: bars-progress
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.3.5" />
|
||||
|
||||
MCP servers can report progress during long-running operations. The client can receive these updates through a progress handler.
|
||||
|
||||
## Progress Handler
|
||||
|
||||
Set a progress handler when creating the client:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
async def my_progress_handler(
|
||||
progress: float,
|
||||
total: float | None,
|
||||
message: str | None
|
||||
) -> None:
|
||||
if total is not None:
|
||||
percentage = (progress / total) * 100
|
||||
print(f"Progress: {percentage:.1f}% - {message or ''}")
|
||||
else:
|
||||
print(f"Progress: {progress} - {message or ''}")
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
|
||||
### Handler Parameters
|
||||
|
||||
The progress handler receives three parameters:
|
||||
|
||||
|
||||
<Card icon="code" title="Progress Handler Parameters">
|
||||
<ResponseField name="progress" type="float">
|
||||
Current progress value
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="total" type="float | None">
|
||||
Expected total value (may be None)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="message" type="str | None">
|
||||
Optional status message (may be None)
|
||||
</ResponseField>
|
||||
</Card>
|
||||
|
||||
|
||||
## Per-Call Progress Handler
|
||||
|
||||
Override the progress handler for specific tool calls:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Override with specific progress handler for this call
|
||||
result = await client.call_tool(
|
||||
"long_running_task",
|
||||
{"param": "value"},
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
216
docs/clients/prompts.mdx
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
---
|
||||
title: Prompts
|
||||
sidebarTitle: Prompts
|
||||
description: Use server-side prompt templates with automatic argument serialization.
|
||||
icon: message-lines
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Prompts are reusable message templates exposed by MCP servers. They can accept arguments to generate personalized message sequences for LLM interactions.
|
||||
|
||||
## Listing Prompts
|
||||
|
||||
Use `list_prompts()` to retrieve all available prompt templates:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
prompts = await client.list_prompts()
|
||||
# prompts -> list[mcp.types.Prompt]
|
||||
|
||||
for prompt in prompts:
|
||||
print(f"Prompt: {prompt.name}")
|
||||
print(f"Description: {prompt.description}")
|
||||
if prompt.arguments:
|
||||
print(f"Arguments: {[arg.name for arg in prompt.arguments]}")
|
||||
# Access tags and other metadata
|
||||
if hasattr(prompt, '_meta') and prompt._meta:
|
||||
fastmcp_meta = prompt._meta.get('_fastmcp', {})
|
||||
print(f"Tags: {fastmcp_meta.get('tags', [])}")
|
||||
```
|
||||
|
||||
### Filtering by Tags
|
||||
|
||||
<VersionBadge version="2.11.0" />
|
||||
|
||||
You can use the `meta` field to filter prompts based on their tags:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
prompts = await client.list_prompts()
|
||||
|
||||
# Filter prompts by tag
|
||||
analysis_prompts = [
|
||||
prompt for prompt in prompts
|
||||
if hasattr(prompt, '_meta') and prompt._meta and
|
||||
prompt._meta.get('_fastmcp', {}) and
|
||||
'analysis' in prompt._meta.get('_fastmcp', {}).get('tags', [])
|
||||
]
|
||||
|
||||
print(f"Found {len(analysis_prompts)} analysis prompts")
|
||||
```
|
||||
|
||||
<Note>
|
||||
The `_meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `_meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
|
||||
</Note>
|
||||
|
||||
## Using Prompts
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Request a rendered prompt using `get_prompt()` with the prompt name and arguments:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Simple prompt without arguments
|
||||
result = await client.get_prompt("welcome_message")
|
||||
# result -> mcp.types.GetPromptResult
|
||||
|
||||
# Access the generated messages
|
||||
for message in result.messages:
|
||||
print(f"Role: {message.role}")
|
||||
print(f"Content: {message.content}")
|
||||
```
|
||||
|
||||
### Prompts with Arguments
|
||||
|
||||
Pass arguments as a dictionary to customize the prompt:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Prompt with simple arguments
|
||||
result = await client.get_prompt("user_greeting", {
|
||||
"name": "Alice",
|
||||
"role": "administrator"
|
||||
})
|
||||
|
||||
# Access the personalized messages
|
||||
for message in result.messages:
|
||||
print(f"Generated message: {message.content}")
|
||||
```
|
||||
|
||||
## Automatic Argument Serialization
|
||||
|
||||
<VersionBadge version="2.9.0" />
|
||||
|
||||
FastMCP automatically serializes complex arguments to JSON strings as required by the MCP specification. This allows you to pass typed objects directly:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class UserData:
|
||||
name: str
|
||||
age: int
|
||||
|
||||
async with client:
|
||||
# Complex arguments are automatically serialized
|
||||
result = await client.get_prompt("analyze_user", {
|
||||
"user": UserData(name="Alice", age=30), # Automatically serialized to JSON
|
||||
"preferences": {"theme": "dark"}, # Dict serialized to JSON string
|
||||
"scores": [85, 92, 78], # List serialized to JSON string
|
||||
"simple_name": "Bob" # Strings passed through unchanged
|
||||
})
|
||||
```
|
||||
|
||||
The client handles serialization using `pydantic_core.to_json()` for consistent formatting. FastMCP servers can automatically deserialize these JSON strings back to the expected types.
|
||||
|
||||
### Serialization Examples
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.get_prompt("data_analysis", {
|
||||
# These will be automatically serialized to JSON strings:
|
||||
"config": {
|
||||
"format": "csv",
|
||||
"include_headers": True,
|
||||
"delimiter": ","
|
||||
},
|
||||
"filters": [
|
||||
{"field": "age", "operator": ">", "value": 18},
|
||||
{"field": "status", "operator": "==", "value": "active"}
|
||||
],
|
||||
# This remains a string:
|
||||
"report_title": "Monthly Analytics Report"
|
||||
})
|
||||
```
|
||||
|
||||
## Working with Prompt Results
|
||||
|
||||
The `get_prompt()` method returns a `GetPromptResult` object containing a list of messages:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.get_prompt("conversation_starter", {"topic": "climate"})
|
||||
|
||||
# Access individual messages
|
||||
for i, message in enumerate(result.messages):
|
||||
print(f"Message {i + 1}:")
|
||||
print(f" Role: {message.role}")
|
||||
print(f" Content: {message.content.text if hasattr(message.content, 'text') else message.content}")
|
||||
```
|
||||
|
||||
## Raw MCP Protocol Access
|
||||
|
||||
For access to the complete MCP protocol objects, use the `*_mcp` methods:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Raw MCP method returns full protocol object
|
||||
prompts_result = await client.list_prompts_mcp()
|
||||
# prompts_result -> mcp.types.ListPromptsResult
|
||||
|
||||
prompt_result = await client.get_prompt_mcp("example_prompt", {"arg": "value"})
|
||||
# prompt_result -> mcp.types.GetPromptResult
|
||||
```
|
||||
|
||||
## Multi-Server Clients
|
||||
|
||||
When using multi-server clients, prompts are accessible without prefixing (unlike tools):
|
||||
|
||||
```python
|
||||
async with client: # Multi-server client
|
||||
# Prompts from any server are directly accessible
|
||||
result1 = await client.get_prompt("weather_prompt", {"city": "London"})
|
||||
result2 = await client.get_prompt("assistant_prompt", {"query": "help"})
|
||||
```
|
||||
|
||||
## Common Prompt Patterns
|
||||
|
||||
### System Messages
|
||||
|
||||
Many prompts generate system messages for LLM configuration:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.get_prompt("system_configuration", {
|
||||
"role": "helpful assistant",
|
||||
"expertise": "python programming"
|
||||
})
|
||||
|
||||
# Typically returns messages with role="system"
|
||||
system_message = result.messages[0]
|
||||
print(f"System prompt: {system_message.content}")
|
||||
```
|
||||
|
||||
### Conversation Templates
|
||||
|
||||
Prompts can generate multi-turn conversation templates:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.get_prompt("interview_template", {
|
||||
"candidate_name": "Alice",
|
||||
"position": "Senior Developer"
|
||||
})
|
||||
|
||||
# Multiple messages for a conversation flow
|
||||
for message in result.messages:
|
||||
print(f"{message.role}: {message.content}")
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Prompt arguments and their expected types depend on the specific prompt implementation. Check the server's documentation or use `list_prompts()` to see available arguments for each prompt.
|
||||
</Tip>
|
||||
204
docs/clients/resources.mdx
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
---
|
||||
title: Resource Operations
|
||||
sidebarTitle: Resources
|
||||
description: Access static and templated resources from MCP servers.
|
||||
icon: folder-open
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Resources are data sources exposed by MCP servers. They can be static files or dynamic templates that generate content based on parameters.
|
||||
|
||||
## Types of Resources
|
||||
|
||||
MCP servers expose two types of resources:
|
||||
|
||||
- **Static Resources**: Fixed content accessible via URI (e.g., configuration files, documentation)
|
||||
- **Resource Templates**: Dynamic resources that accept parameters to generate content (e.g., API endpoints, database queries)
|
||||
|
||||
## Listing Resources
|
||||
|
||||
### Static Resources
|
||||
|
||||
Use `list_resources()` to retrieve all static resources available on the server:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
resources = await client.list_resources()
|
||||
# resources -> list[mcp.types.Resource]
|
||||
|
||||
for resource in resources:
|
||||
print(f"Resource URI: {resource.uri}")
|
||||
print(f"Name: {resource.name}")
|
||||
print(f"Description: {resource.description}")
|
||||
print(f"MIME Type: {resource.mimeType}")
|
||||
# Access tags and other metadata
|
||||
if hasattr(resource, '_meta') and resource._meta:
|
||||
fastmcp_meta = resource._meta.get('_fastmcp', {})
|
||||
print(f"Tags: {fastmcp_meta.get('tags', [])}")
|
||||
```
|
||||
|
||||
### Resource Templates
|
||||
|
||||
Use `list_resource_templates()` to retrieve available resource templates:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
templates = await client.list_resource_templates()
|
||||
# templates -> list[mcp.types.ResourceTemplate]
|
||||
|
||||
for template in templates:
|
||||
print(f"Template URI: {template.uriTemplate}")
|
||||
print(f"Name: {template.name}")
|
||||
print(f"Description: {template.description}")
|
||||
# Access tags and other metadata
|
||||
if hasattr(template, '_meta') and template._meta:
|
||||
fastmcp_meta = template._meta.get('_fastmcp', {})
|
||||
print(f"Tags: {fastmcp_meta.get('tags', [])}")
|
||||
```
|
||||
|
||||
### Filtering by Tags
|
||||
|
||||
<VersionBadge version="2.11.0" />
|
||||
|
||||
You can use the `meta` field to filter resources based on their tags:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
resources = await client.list_resources()
|
||||
|
||||
# Filter resources by tag
|
||||
config_resources = [
|
||||
resource for resource in resources
|
||||
if hasattr(resource, '_meta') and resource._meta and
|
||||
resource._meta.get('_fastmcp', {}) and
|
||||
'config' in resource._meta.get('_fastmcp', {}).get('tags', [])
|
||||
]
|
||||
|
||||
print(f"Found {len(config_resources)} config resources")
|
||||
```
|
||||
|
||||
<Note>
|
||||
The `_meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `_meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
|
||||
</Note>
|
||||
|
||||
## Reading Resources
|
||||
|
||||
### Static Resources
|
||||
|
||||
Read a static resource using its URI:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Read a static resource
|
||||
content = await client.read_resource("file:///path/to/README.md")
|
||||
# content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
|
||||
|
||||
# Access text content
|
||||
if hasattr(content[0], 'text'):
|
||||
print(content[0].text)
|
||||
|
||||
# Access binary content
|
||||
if hasattr(content[0], 'blob'):
|
||||
print(f"Binary data: {len(content[0].blob)} bytes")
|
||||
```
|
||||
|
||||
### Resource Templates
|
||||
|
||||
Read from a resource template by providing the URI with parameters:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Read a resource generated from a template
|
||||
# For example, a template like "weather://{{city}}/current"
|
||||
weather_content = await client.read_resource("weather://london/current")
|
||||
|
||||
# Access the generated content
|
||||
print(weather_content[0].text) # Assuming text JSON response
|
||||
```
|
||||
|
||||
## Content Types
|
||||
|
||||
Resources can return different content types:
|
||||
|
||||
### Text Resources
|
||||
|
||||
```python
|
||||
async with client:
|
||||
content = await client.read_resource("resource://config/settings.json")
|
||||
|
||||
for item in content:
|
||||
if hasattr(item, 'text'):
|
||||
print(f"Text content: {item.text}")
|
||||
print(f"MIME type: {item.mimeType}")
|
||||
```
|
||||
|
||||
### Binary Resources
|
||||
|
||||
```python
|
||||
async with client:
|
||||
content = await client.read_resource("resource://images/logo.png")
|
||||
|
||||
for item in content:
|
||||
if hasattr(item, 'blob'):
|
||||
print(f"Binary content: {len(item.blob)} bytes")
|
||||
print(f"MIME type: {item.mimeType}")
|
||||
|
||||
# Save to file
|
||||
with open("downloaded_logo.png", "wb") as f:
|
||||
f.write(item.blob)
|
||||
```
|
||||
|
||||
## Working with Multi-Server Clients
|
||||
|
||||
When using multi-server clients, resource URIs are automatically prefixed with the server name:
|
||||
|
||||
```python
|
||||
async with client: # Multi-server client
|
||||
# Access resources from different servers
|
||||
weather_icons = await client.read_resource("weather://weather/icons/sunny")
|
||||
templates = await client.read_resource("resource://assistant/templates/list")
|
||||
|
||||
print(f"Weather icon: {weather_icons[0].blob}")
|
||||
print(f"Templates: {templates[0].text}")
|
||||
```
|
||||
|
||||
## Raw MCP Protocol Access
|
||||
|
||||
For access to the complete MCP protocol objects, use the `*_mcp` methods:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Raw MCP methods return full protocol objects
|
||||
resources_result = await client.list_resources_mcp()
|
||||
# resources_result -> mcp.types.ListResourcesResult
|
||||
|
||||
templates_result = await client.list_resource_templates_mcp()
|
||||
# templates_result -> mcp.types.ListResourceTemplatesResult
|
||||
|
||||
content_result = await client.read_resource_mcp("resource://example")
|
||||
# content_result -> mcp.types.ReadResourceResult
|
||||
```
|
||||
|
||||
## Common Resource URI Patterns
|
||||
|
||||
Different MCP servers may use various URI schemes:
|
||||
|
||||
```python
|
||||
# File system resources
|
||||
"file:///path/to/file.txt"
|
||||
|
||||
# Custom protocol resources
|
||||
"weather://london/current"
|
||||
"database://users/123"
|
||||
|
||||
# Generic resource protocol
|
||||
"resource://config/settings"
|
||||
"resource://templates/email"
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Resource URIs and their formats depend on the specific MCP server implementation. Check the server's documentation for available resources and their URI patterns.
|
||||
</Tip>
|
||||
42
docs/clients/roots.mdx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
title: Client Roots
|
||||
sidebarTitle: Roots
|
||||
description: Provide local context and resource boundaries to MCP servers.
|
||||
icon: folder-tree
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Roots are a way for clients to inform servers about the resources they have access to. Servers can use this information to adjust behavior or provide more relevant responses.
|
||||
|
||||
## Setting Static Roots
|
||||
|
||||
Provide a list of roots when creating the client:
|
||||
|
||||
<CodeGroup>
|
||||
```python Static Roots
|
||||
from fastmcp import Client
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
roots=["/path/to/root1", "/path/to/root2"]
|
||||
)
|
||||
```
|
||||
|
||||
```python Dynamic Roots Callback
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.roots import RequestContext
|
||||
|
||||
async def roots_callback(context: RequestContext) -> list[str]:
|
||||
print(f"Server requested roots (Request ID: {context.request_id})")
|
||||
return ["/path/to/root1", "/path/to/root2"]
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
roots=roots_callback
|
||||
)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
201
docs/clients/sampling.mdx
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
---
|
||||
title: LLM Sampling
|
||||
sidebarTitle: Sampling
|
||||
description: Handle server-initiated LLM sampling requests.
|
||||
icon: robot
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx";
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
MCP servers can request LLM completions from clients. The client handles these requests through a sampling handler callback.
|
||||
|
||||
## Sampling Handler
|
||||
|
||||
Provide a `sampling_handler` function when creating the client:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.sampling import (
|
||||
SamplingMessage,
|
||||
SamplingParams,
|
||||
RequestContext,
|
||||
)
|
||||
|
||||
async def sampling_handler(
|
||||
messages: list[SamplingMessage],
|
||||
params: SamplingParams,
|
||||
context: RequestContext
|
||||
) -> str:
|
||||
# Your LLM integration logic here
|
||||
# Extract text from messages and generate a response
|
||||
return "Generated response based on the messages"
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
sampling_handler=sampling_handler,
|
||||
)
|
||||
```
|
||||
|
||||
### Handler Parameters
|
||||
|
||||
The sampling handler receives three parameters:
|
||||
|
||||
<Card icon="code" title="Sampling Handler Parameters">
|
||||
<ResponseField name="SamplingMessage" type="Sampling Message Object">
|
||||
<Expandable title="attributes">
|
||||
<ResponseField name="role" type='Literal["user", "assistant"]'>
|
||||
The role of the message.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="content" type="TextContent | ImageContent | AudioContent">
|
||||
The content of the message.
|
||||
|
||||
TextContent is most common, and has a `.text` attribute.
|
||||
</ResponseField>
|
||||
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
<ResponseField name="SamplingParams" type="Sampling Parameters Object">
|
||||
<Expandable title="attributes">
|
||||
<ResponseField name="messages" type="list[SamplingMessage]">
|
||||
The messages to sample from
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="modelPreferences" type="ModelPreferences | None">
|
||||
The server's preferences for which model to select. The client MAY ignore
|
||||
these preferences.
|
||||
<Expandable title="attributes">
|
||||
<ResponseField name="hints" type="list[ModelHint] | None">
|
||||
The hints to use for model selection.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="costPriority" type="float | None">
|
||||
The cost priority for model selection.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="speedPriority" type="float | None">
|
||||
The speed priority for model selection.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="intelligencePriority" type="float | None">
|
||||
The intelligence priority for model selection.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="systemPrompt" type="str | None">
|
||||
An optional system prompt the server wants to use for sampling.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="includeContext" type="IncludeContext | None">
|
||||
A request to include context from one or more MCP servers (including the caller), to
|
||||
be attached to the prompt.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="temperature" type="float | None">
|
||||
The sampling temperature.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="maxTokens" type="int">
|
||||
The maximum number of tokens to sample.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="stopSequences" type="list[str] | None">
|
||||
The stop sequences to use for sampling.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="metadata" type="dict[str, Any] | None">
|
||||
Optional metadata to pass through to the LLM provider.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
|
||||
</ResponseField>
|
||||
<ResponseField name="RequestContext" type="Request Context Object">
|
||||
<Expandable title="attributes">
|
||||
<ResponseField name="request_id" type="RequestId">
|
||||
Unique identifier for the MCP request
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Card>
|
||||
|
||||
## Basic Example
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext
|
||||
|
||||
async def basic_sampling_handler(
|
||||
messages: list[SamplingMessage],
|
||||
params: SamplingParams,
|
||||
context: RequestContext
|
||||
) -> str:
|
||||
# Extract message content
|
||||
conversation = []
|
||||
for message in messages:
|
||||
content = message.content.text if hasattr(message.content, 'text') else str(message.content)
|
||||
conversation.append(f"{message.role}: {content}")
|
||||
|
||||
# Use the system prompt if provided
|
||||
system_prompt = params.systemPrompt or "You are a helpful assistant."
|
||||
|
||||
# Here you would integrate with your preferred LLM service
|
||||
# This is just a placeholder response
|
||||
return f"Response based on conversation: {' | '.join(conversation)}"
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
sampling_handler=basic_sampling_handler
|
||||
)
|
||||
```
|
||||
|
||||
## Sampling fallback
|
||||
|
||||
Client support for sampling is optional, if the client does not support sampling, the server will report an error indicating
|
||||
that the client does not support sampling.
|
||||
|
||||
A `sampling_handler` can also be provided to the FastMCP server, which will be used to handle sampling requests if the client
|
||||
does not support sampling. This sampling handler bypasses the client and sends sampling requests directly to the LLM provider.
|
||||
|
||||
Sampling handlers can be implemented using any LLM provider but a sample implementation for OpenAI is provided as a Contrib
|
||||
module. Sampling lacks the full capabilities of typical LLM completions. For this reason, the OpenAI sampling handler, pointed at
|
||||
a third-party provider's OpenAI-compatible API, is often sufficient to implement a sampling handler.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from mcp.types import ContentBlock
|
||||
from openai import OpenAI
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.experimental.sampling.handlers.openai import OpenAISamplingHandler
|
||||
from fastmcp.server.context import Context
|
||||
|
||||
|
||||
async def async_main():
|
||||
server = FastMCP(
|
||||
name="OpenAI Sampling Fallback Example",
|
||||
sampling_handler=OpenAISamplingHandler(
|
||||
default_model="gpt-4o-mini",
|
||||
client=OpenAI(
|
||||
api_key=os.getenv("API_KEY"),
|
||||
base_url=os.getenv("BASE_URL"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@server.tool
|
||||
async def test_sample_fallback(ctx: Context) -> ContentBlock:
|
||||
return await ctx.sample(
|
||||
messages=["hello world!"],
|
||||
)
|
||||
|
||||
await server.run_http_async()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(async_main())
|
||||
```
|
||||
270
docs/clients/tools.mdx
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
---
|
||||
title: Tool Operations
|
||||
sidebarTitle: Tools
|
||||
description: Discover and execute server-side tools with the FastMCP client.
|
||||
icon: wrench
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Tools are executable functions exposed by MCP servers. The FastMCP client provides methods to discover available tools and execute them with arguments.
|
||||
|
||||
## Discovering Tools
|
||||
|
||||
Use `list_tools()` to retrieve all tools available on the server:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
tools = await client.list_tools()
|
||||
# tools -> list[mcp.types.Tool]
|
||||
|
||||
for tool in tools:
|
||||
print(f"Tool: {tool.name}")
|
||||
print(f"Description: {tool.description}")
|
||||
if tool.inputSchema:
|
||||
print(f"Parameters: {tool.inputSchema}")
|
||||
# Access tags and other metadata
|
||||
if hasattr(tool, 'meta') and tool.meta:
|
||||
fastmcp_meta = tool.meta.get('_fastmcp', {})
|
||||
print(f"Tags: {fastmcp_meta.get('tags', [])}")
|
||||
```
|
||||
|
||||
### Filtering by Tags
|
||||
|
||||
<VersionBadge version="2.11.0" />
|
||||
|
||||
You can use the `meta` field to filter tools based on their tags:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
tools = await client.list_tools()
|
||||
|
||||
# Filter tools by tag
|
||||
analysis_tools = [
|
||||
tool for tool in tools
|
||||
if hasattr(tool, 'meta') and tool.meta and
|
||||
tool.meta.get('_fastmcp', {}) and
|
||||
'analysis' in tool.meta.get('_fastmcp', {}).get('tags', [])
|
||||
]
|
||||
|
||||
print(f"Found {len(analysis_tools)} analysis tools")
|
||||
```
|
||||
|
||||
<Note>
|
||||
The `meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
|
||||
</Note>
|
||||
|
||||
## Executing Tools
|
||||
|
||||
### Basic Execution
|
||||
|
||||
Execute a tool using `call_tool()` with the tool name and arguments:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Simple tool call
|
||||
result = await client.call_tool("add", {"a": 5, "b": 3})
|
||||
# result -> CallToolResult with structured and unstructured data
|
||||
|
||||
# Access structured data (automatically deserialized)
|
||||
print(result.data) # 8 (int) or {"result": 8} for primitive types
|
||||
|
||||
# Access traditional content blocks
|
||||
print(result.content[0].text) # "8" (TextContent)
|
||||
```
|
||||
|
||||
### Advanced Execution Options
|
||||
|
||||
The `call_tool()` method supports additional parameters for timeout control and progress monitoring:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# With timeout (aborts if execution takes longer than 2 seconds)
|
||||
result = await client.call_tool(
|
||||
"long_running_task",
|
||||
{"param": "value"},
|
||||
timeout=2.0
|
||||
)
|
||||
|
||||
# With progress handler (to track execution progress)
|
||||
result = await client.call_tool(
|
||||
"long_running_task",
|
||||
{"param": "value"},
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `name`: The tool name (string)
|
||||
- `arguments`: Dictionary of arguments to pass to the tool (optional)
|
||||
- `timeout`: Maximum execution time in seconds (optional, overrides client-level timeout)
|
||||
- `progress_handler`: Progress callback function (optional, overrides client-level handler)
|
||||
|
||||
## Handling Results
|
||||
|
||||
<VersionBadge version="2.10.0" />
|
||||
|
||||
Tool execution returns a `CallToolResult` object with both structured and traditional content. FastMCP's standout feature is the `.data` property, which doesn't just provide raw JSON but actually hydrates complete Python objects including complex types like datetimes, UUIDs, and custom classes.
|
||||
|
||||
### CallToolResult Properties
|
||||
|
||||
<Card icon="code" title="CallToolResult Properties">
|
||||
<ResponseField name=".data" type="Any">
|
||||
**FastMCP exclusive**: Fully hydrated Python objects with complex type support (datetimes, UUIDs, custom classes). Goes beyond JSON to provide complete object reconstruction from output schemas.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name=".content" type="list[mcp.types.ContentBlock]">
|
||||
Standard MCP content blocks (`TextContent`, `ImageContent`, `AudioContent`, etc.) available from all MCP servers.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name=".structured_content" type="dict[str, Any] | None">
|
||||
Standard MCP structured JSON data as sent by the server, available from all MCP servers that support structured outputs.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name=".is_error" type="bool">
|
||||
Boolean indicating if the tool execution failed.
|
||||
</ResponseField>
|
||||
</Card>
|
||||
|
||||
### Structured Data Access
|
||||
|
||||
FastMCP's `.data` property provides fully hydrated Python objects, not just JSON dictionaries. This includes complex type reconstruction:
|
||||
|
||||
```python
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
async with client:
|
||||
result = await client.call_tool("get_weather", {"city": "London"})
|
||||
|
||||
# FastMCP reconstructs complete Python objects from the server's output schema
|
||||
weather = result.data # Server-defined WeatherReport object
|
||||
print(f"Temperature: {weather.temperature}°C at {weather.timestamp}")
|
||||
print(f"Station: {weather.station_id}")
|
||||
print(f"Humidity: {weather.humidity}%")
|
||||
|
||||
# The timestamp is a real datetime object, not a string!
|
||||
assert isinstance(weather.timestamp, datetime)
|
||||
assert isinstance(weather.station_id, UUID)
|
||||
|
||||
# Compare with raw structured JSON (standard MCP)
|
||||
print(f"Raw JSON: {result.structured_content}")
|
||||
# {"temperature": 20, "timestamp": "2024-01-15T14:30:00Z", "station_id": "123e4567-..."}
|
||||
|
||||
# Traditional content blocks (standard MCP)
|
||||
print(f"Text content: {result.content[0].text}")
|
||||
```
|
||||
|
||||
### Fallback Behavior
|
||||
|
||||
For tools without output schemas or when deserialization fails, `.data` will be `None`:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.call_tool("legacy_tool", {"param": "value"})
|
||||
|
||||
if result.data is not None:
|
||||
# Structured output available and successfully deserialized
|
||||
print(f"Structured: {result.data}")
|
||||
else:
|
||||
# No structured output or deserialization failed - use content blocks
|
||||
for content in result.content:
|
||||
if hasattr(content, 'text'):
|
||||
print(f"Text result: {content.text}")
|
||||
elif hasattr(content, 'data'):
|
||||
print(f"Binary data: {len(content.data)} bytes")
|
||||
```
|
||||
|
||||
### Primitive Type Unwrapping
|
||||
|
||||
<Tip>
|
||||
FastMCP servers automatically wrap non-object results (like `int`, `str`, `bool`) in a `{"result": value}` structure to create valid structured outputs. FastMCP clients understand this convention and automatically unwrap the value in `.data` for convenience, so you get the original primitive value instead of a wrapper object.
|
||||
</Tip>
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.call_tool("calculate_sum", {"a": 5, "b": 3})
|
||||
|
||||
# FastMCP client automatically unwraps for convenience
|
||||
print(result.data) # 8 (int) - the original value
|
||||
|
||||
# Raw structured content shows the server-side wrapping
|
||||
print(result.structured_content) # {"result": 8}
|
||||
|
||||
# Other MCP clients would need to manually access ["result"]
|
||||
# value = result.structured_content["result"] # Not needed with FastMCP!
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Exception-Based Error Handling
|
||||
|
||||
By default, `call_tool()` raises a `ToolError` if the tool execution fails:
|
||||
|
||||
```python
|
||||
from fastmcp.exceptions import ToolError
|
||||
|
||||
async with client:
|
||||
try:
|
||||
result = await client.call_tool("potentially_failing_tool", {"param": "value"})
|
||||
print("Tool succeeded:", result.data)
|
||||
except ToolError as e:
|
||||
print(f"Tool failed: {e}")
|
||||
```
|
||||
|
||||
### Manual Error Checking
|
||||
|
||||
You can disable automatic error raising and manually check the result:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.call_tool(
|
||||
"potentially_failing_tool",
|
||||
{"param": "value"},
|
||||
raise_on_error=False
|
||||
)
|
||||
|
||||
if result.is_error:
|
||||
print(f"Tool failed: {result.content[0].text}")
|
||||
else:
|
||||
print(f"Tool succeeded: {result.data}")
|
||||
```
|
||||
|
||||
### Raw MCP Protocol Access
|
||||
|
||||
For complete control, use `call_tool_mcp()` which returns the raw MCP protocol object:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.call_tool_mcp("potentially_failing_tool", {"param": "value"})
|
||||
# result -> mcp.types.CallToolResult
|
||||
|
||||
if result.isError:
|
||||
print(f"Tool failed: {result.content}")
|
||||
else:
|
||||
print(f"Tool succeeded: {result.content}")
|
||||
# Note: No automatic deserialization with call_tool_mcp()
|
||||
```
|
||||
|
||||
## Argument Handling
|
||||
|
||||
Arguments are passed as a dictionary to the tool:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Simple arguments
|
||||
result = await client.call_tool("greet", {"name": "World"})
|
||||
|
||||
# Complex arguments
|
||||
result = await client.call_tool("process_data", {
|
||||
"config": {"format": "json", "validate": True},
|
||||
"items": [1, 2, 3, 4, 5],
|
||||
"metadata": {"source": "api", "version": "1.0"}
|
||||
})
|
||||
```
|
||||
|
||||
<Tip>
|
||||
For multi-server clients, tool names are automatically prefixed with the server name (e.g., `weather_get_forecast` for a tool named `get_forecast` on the `weather` server).
|
||||
</Tip>
|
||||
383
docs/clients/transports.mdx
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
---
|
||||
title: Client Transports
|
||||
sidebarTitle: Transports
|
||||
description: Configure how FastMCP Clients connect to and communicate with servers.
|
||||
icon: link
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
The FastMCP `Client` communicates with MCP servers through transport objects that handle the underlying connection mechanics. While the client can automatically select a transport based on what you pass to it, instantiating transports explicitly gives you full control over configuration—environment variables, authentication, session management, and more.
|
||||
|
||||
Think of transports as configurable adapters between your client code and MCP servers. Each transport type handles a different communication pattern: subprocesses with pipes, HTTP connections, or direct in-memory calls.
|
||||
|
||||
## Choosing the Right Transport
|
||||
|
||||
- **Use [STDIO Transport](#stdio-transport)** when you need to run local MCP servers with full control over their environment and lifecycle
|
||||
- **Use [Remote Transports](#remote-transports)** when connecting to production services or shared MCP servers running independently
|
||||
- **Use [In-Memory Transport](#in-memory-transport)** for testing FastMCP servers without subprocess or network overhead
|
||||
- **Use [MCP JSON Configuration](#mcp-json-configuration-transport)** when you need to connect to multiple servers defined in configuration files
|
||||
|
||||
## STDIO Transport
|
||||
|
||||
STDIO (Standard Input/Output) transport communicates with MCP servers through subprocess pipes. This is the standard mechanism used by desktop clients like Claude Desktop and is the primary way to run local MCP servers.
|
||||
|
||||
### The Client Runs the Server
|
||||
|
||||
<Warning>
|
||||
**Critical Concept**: When using STDIO transport, your client actually launches and manages the server process. This is fundamentally different from network transports where you connect to an already-running server. Understanding this relationship is key to using STDIO effectively.
|
||||
</Warning>
|
||||
|
||||
With STDIO transport, your client:
|
||||
- Starts the server as a subprocess when you connect
|
||||
- Manages the server's lifecycle (start, stop, restart)
|
||||
- Controls the server's environment and configuration
|
||||
- Communicates through stdin/stdout pipes
|
||||
|
||||
This architecture enables powerful local integrations but requires understanding environment isolation and process management.
|
||||
|
||||
### Environment Isolation
|
||||
|
||||
STDIO servers run in isolated environments by default. This is a security feature enforced by the MCP protocol to prevent accidental exposure of sensitive data.
|
||||
|
||||
When your client launches an MCP server:
|
||||
- The server does NOT inherit your shell's environment variables
|
||||
- API keys, paths, and other configuration must be explicitly passed
|
||||
- The working directory and system paths may differ from your shell
|
||||
|
||||
To pass environment variables to your server, use the `env` parameter:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
# If your server needs environment variables (like API keys),
|
||||
# you must explicitly pass them:
|
||||
client = Client(
|
||||
"my_server.py",
|
||||
env={"API_KEY": "secret", "DEBUG": "true"}
|
||||
)
|
||||
|
||||
# This won't work - the server runs in isolation:
|
||||
# export API_KEY="secret" # in your shell
|
||||
# client = Client("my_server.py") # server can't see API_KEY
|
||||
```
|
||||
|
||||
### Basic Usage
|
||||
|
||||
To use STDIO transport, you create a transport instance with the command and arguments needed to run your server:
|
||||
|
||||
```python
|
||||
from fastmcp.client.transports import StdioTransport
|
||||
|
||||
transport = StdioTransport(
|
||||
command="python",
|
||||
args=["my_server.py"]
|
||||
)
|
||||
client = Client(transport)
|
||||
```
|
||||
|
||||
You can configure additional settings like environment variables, working directory, or command arguments:
|
||||
|
||||
```python
|
||||
transport = StdioTransport(
|
||||
command="python",
|
||||
args=["my_server.py", "--verbose"],
|
||||
env={"LOG_LEVEL": "DEBUG"},
|
||||
cwd="/path/to/server"
|
||||
)
|
||||
client = Client(transport)
|
||||
```
|
||||
|
||||
For convenience, the client can also infer STDIO transport from file paths, but this doesn't allow configuration:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
client = Client("my_server.py") # Limited - no configuration options
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Since STDIO servers don't inherit your environment, you need strategies for passing configuration. Here are two common approaches:
|
||||
|
||||
**Selective forwarding** passes only the variables your server actually needs:
|
||||
|
||||
```python
|
||||
import os
|
||||
from fastmcp.client.transports import StdioTransport
|
||||
|
||||
required_vars = ["API_KEY", "DATABASE_URL", "REDIS_HOST"]
|
||||
env = {
|
||||
var: os.environ[var]
|
||||
for var in required_vars
|
||||
if var in os.environ
|
||||
}
|
||||
|
||||
transport = StdioTransport(
|
||||
command="python",
|
||||
args=["server.py"],
|
||||
env=env
|
||||
)
|
||||
client = Client(transport)
|
||||
```
|
||||
|
||||
**Loading from .env files** keeps configuration separate from code:
|
||||
|
||||
```python
|
||||
from dotenv import dotenv_values
|
||||
from fastmcp.client.transports import StdioTransport
|
||||
|
||||
env = dotenv_values(".env")
|
||||
transport = StdioTransport(
|
||||
command="python",
|
||||
args=["server.py"],
|
||||
env=env
|
||||
)
|
||||
client = Client(transport)
|
||||
```
|
||||
|
||||
### Session Persistence
|
||||
|
||||
STDIO transports maintain sessions across multiple client contexts by default (`keep_alive=True`). This improves performance by reusing the same subprocess for multiple connections, but can be controlled when you need isolation.
|
||||
|
||||
By default, the subprocess persists between connections:
|
||||
|
||||
```python
|
||||
from fastmcp.client.transports import StdioTransport
|
||||
|
||||
transport = StdioTransport(
|
||||
command="python",
|
||||
args=["server.py"]
|
||||
)
|
||||
client = Client(transport)
|
||||
|
||||
async def efficient_multiple_operations():
|
||||
async with client:
|
||||
await client.ping()
|
||||
|
||||
async with client: # Reuses the same subprocess
|
||||
await client.call_tool("process_data", {"file": "data.csv"})
|
||||
```
|
||||
|
||||
For complete isolation between connections, disable session persistence:
|
||||
|
||||
```python
|
||||
transport = StdioTransport(
|
||||
command="python",
|
||||
args=["server.py"],
|
||||
keep_alive=False
|
||||
)
|
||||
client = Client(transport)
|
||||
```
|
||||
|
||||
Use `keep_alive=False` when you need complete isolation (e.g., in test suites) or when server state could cause issues between connections.
|
||||
|
||||
### Specialized STDIO Transports
|
||||
|
||||
FastMCP provides convenience transports that are thin wrappers around `StdioTransport` with pre-configured commands:
|
||||
|
||||
- **`PythonStdioTransport`** - Uses `python` command for `.py` files
|
||||
- **`NodeStdioTransport`** - Uses `node` command for `.js` files
|
||||
- **`UvStdioTransport`** - Uses `uv` for Python packages (uses `env_vars` parameter)
|
||||
- **`UvxStdioTransport`** - Uses `uvx` for Python packages (uses `env_vars` parameter)
|
||||
- **`NpxStdioTransport`** - Uses `npx` for Node packages (uses `env_vars` parameter)
|
||||
|
||||
For most use cases, instantiate `StdioTransport` directly with your desired command. These specialized transports are primarily useful for client inference shortcuts.
|
||||
|
||||
## Remote Transports
|
||||
|
||||
Remote transports connect to MCP servers running as web services. This is a fundamentally different model from STDIO transports—instead of your client launching and managing a server process, you connect to an already-running service that manages its own environment and lifecycle.
|
||||
|
||||
### Streamable HTTP Transport
|
||||
|
||||
<VersionBadge version="2.3.0" />
|
||||
|
||||
Streamable HTTP is the recommended transport for production deployments, providing efficient bidirectional streaming over HTTP connections.
|
||||
|
||||
- **Class:** `StreamableHttpTransport`
|
||||
- **Server compatibility:** FastMCP servers running with `mcp run --transport http`
|
||||
|
||||
The transport requires a URL and optionally supports custom headers for authentication and configuration:
|
||||
|
||||
```python
|
||||
from fastmcp.client.transports import StreamableHttpTransport
|
||||
|
||||
# Basic connection
|
||||
transport = StreamableHttpTransport(url="https://api.example.com/mcp")
|
||||
client = Client(transport)
|
||||
|
||||
# With custom headers for authentication
|
||||
transport = StreamableHttpTransport(
|
||||
url="https://api.example.com/mcp",
|
||||
headers={
|
||||
"Authorization": "Bearer your-token-here",
|
||||
"X-Custom-Header": "value"
|
||||
}
|
||||
)
|
||||
client = Client(transport)
|
||||
```
|
||||
|
||||
For convenience, FastMCP also provides authentication helpers:
|
||||
|
||||
```python
|
||||
from fastmcp.client.auth import BearerAuth
|
||||
|
||||
client = Client(
|
||||
"https://api.example.com/mcp",
|
||||
auth=BearerAuth("your-token-here")
|
||||
)
|
||||
```
|
||||
|
||||
### SSE Transport (Legacy)
|
||||
|
||||
Server-Sent Events transport is maintained for backward compatibility but is superseded by Streamable HTTP for new deployments.
|
||||
|
||||
- **Class:** `SSETransport`
|
||||
- **Server compatibility:** FastMCP servers running with `mcp run --transport sse`
|
||||
|
||||
SSE transport supports the same configuration options as Streamable HTTP:
|
||||
|
||||
```python
|
||||
from fastmcp.client.transports import SSETransport
|
||||
|
||||
transport = SSETransport(
|
||||
url="https://api.example.com/sse",
|
||||
headers={"Authorization": "Bearer token"}
|
||||
)
|
||||
client = Client(transport)
|
||||
```
|
||||
|
||||
Use Streamable HTTP for new deployments unless you have specific infrastructure requirements for SSE.
|
||||
|
||||
## In-Memory Transport
|
||||
|
||||
In-memory transport connects directly to a FastMCP server instance within the same Python process. This eliminates both subprocess management and network overhead, making it ideal for testing and development.
|
||||
|
||||
- **Class:** `FastMCPTransport`
|
||||
|
||||
<Note>
|
||||
Unlike STDIO transports, in-memory servers have full access to your Python process's environment. They share the same memory space and environment variables as your client code—no isolation or explicit environment passing required.
|
||||
</Note>
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP, Client
|
||||
import os
|
||||
|
||||
mcp = FastMCP("TestServer")
|
||||
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
prefix = os.environ.get("GREETING_PREFIX", "Hello")
|
||||
return f"{prefix}, {name}!"
|
||||
|
||||
client = Client(mcp)
|
||||
|
||||
async with client:
|
||||
result = await client.call_tool("greet", {"name": "World"})
|
||||
```
|
||||
|
||||
## MCP JSON Configuration Transport
|
||||
|
||||
<VersionBadge version="2.4.0" />
|
||||
|
||||
This transport supports the emerging MCP JSON configuration standard for defining multiple servers:
|
||||
|
||||
- **Class:** `MCPConfigTransport`
|
||||
|
||||
```python
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"url": "https://weather.example.com/mcp",
|
||||
"transport": "http"
|
||||
},
|
||||
"assistant": {
|
||||
"command": "python",
|
||||
"args": ["./assistant.py"],
|
||||
"env": {"LOG_LEVEL": "INFO"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client = Client(config)
|
||||
|
||||
async with client:
|
||||
# Tools are namespaced by server
|
||||
weather = await client.call_tool("weather_get_forecast", {"city": "NYC"})
|
||||
answer = await client.call_tool("assistant_ask", {"question": "What?"})
|
||||
```
|
||||
|
||||
### Tool Transformation with FastMCP and MCPConfig
|
||||
|
||||
FastMCP supports basic tool transformations to be defined alongside the MCP Servers in the MCPConfig file.
|
||||
|
||||
```python
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"url": "https://weather.example.com/mcp",
|
||||
"transport": "http",
|
||||
"tools": { } # <--- This is the tool transformation section
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With these transformations, you can transform (change) the name, title, description, tags, enablement, and arguments of a tool.
|
||||
|
||||
For each argument the tool takes, you can transform (change) the name, description, default, visibility, whether it's required, and you can provide example values.
|
||||
|
||||
In the following example, we're transforming the `weather_get_forecast` tool to only retrieve the weather for `Miami` and hiding the `city` argument from the client.
|
||||
|
||||
```python
|
||||
tool_transformations = {
|
||||
"weather_get_forecast": {
|
||||
"name": "miami_weather",
|
||||
"description": "Get the weather for Miami",
|
||||
"arguments": {
|
||||
"city": {
|
||||
"name": "city",
|
||||
"default": "Miami",
|
||||
"hide": True,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"url": "https://weather.example.com/mcp",
|
||||
"transport": "http",
|
||||
"tools": tool_transformations
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Allowlisting and Blocklisting Tools
|
||||
|
||||
Tools can be allowlisted or blocklisted from the client by applying `tags` to the tools on the server. In the following example, we're allowlisting only tools marked with the `forecast` tag, all other tools will be unavailable to the client.
|
||||
|
||||
```python
|
||||
tool_transformations = {
|
||||
"weather_get_forecast": {
|
||||
"enabled": True,
|
||||
"tags": ["forecast"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"url": "https://weather.example.com/mcp",
|
||||
"transport": "http",
|
||||
"tools": tool_transformations,
|
||||
"include_tags": ["forecast"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/community/README.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Community Section
|
||||
|
||||
This directory contains community-contributed content and showcases for FastMCP.
|
||||
|
||||
## Structure
|
||||
|
||||
- `showcase.mdx` - Main community showcase page featuring high-quality projects and examples
|
||||
|
||||
## Adding Content
|
||||
|
||||
To add new community content:
|
||||
1. Create a new MDX file in this directory
|
||||
2. Update `docs.json` to include it in the navigation
|
||||
3. Follow the existing format for consistency
|
||||
|
||||
## Guidelines
|
||||
|
||||
Community content should:
|
||||
- Demonstrate best practices
|
||||
- Provide educational value
|
||||
- Include proper documentation
|
||||
- Be maintained and up-to-date
|
||||
59
docs/community/showcase.mdx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
title: 'Community Showcase'
|
||||
description: 'High-quality projects and examples from the FastMCP community'
|
||||
icon: 'users'
|
||||
---
|
||||
|
||||
import { YouTubeEmbed } from '/snippets/youtube-embed.mdx'
|
||||
|
||||
## Featured Projects
|
||||
|
||||
Discover exemplary MCP servers and implementations created by our community. These projects demonstrate best practices and innovative uses of FastMCP.
|
||||
|
||||
### Learning Resources
|
||||
|
||||
<Card title="MCP Dummy Server" icon="graduation-cap" href="https://github.com/WaiYanNyeinNaing/mcp-dummy-server">
|
||||
A comprehensive educational example demonstrating FastMCP best practices with professional dual-transport server implementation, interactive test client, and detailed documentation.
|
||||
</Card>
|
||||
|
||||
#### Video Tutorials
|
||||
|
||||
**Build Remote MCP Servers w/ Python & FastMCP** - Claude Integrations Tutorial by Greg + Code
|
||||
|
||||
<YouTubeEmbed
|
||||
videoId="bOYkbXP-GGo"
|
||||
title="Build Remote MCP Servers w/ Python & FastMCP"
|
||||
/>
|
||||
|
||||
**FastMCP — the best way to build an MCP server with Python** - Tutorial by ZazenCodes
|
||||
|
||||
<YouTubeEmbed
|
||||
videoId="rnljvmHorQw"
|
||||
title="FastMCP — the best way to build an MCP server with Python"
|
||||
/>
|
||||
|
||||
**Speedrun a MCP server for Claude Desktop (fastmcp)** - Tutorial by Nate from Prefect
|
||||
|
||||
<YouTubeEmbed
|
||||
videoId="67ZwpkUEtSI"
|
||||
title="Speedrun a MCP server for Claude Desktop (fastmcp)"
|
||||
/>
|
||||
|
||||
### Community Examples
|
||||
|
||||
Have you built something interesting with FastMCP? We'd love to feature high-quality examples here! Start a [discussion on GitHub](https://github.com/jlowin/fastmcp/discussions) to share your project.
|
||||
|
||||
## Contributing
|
||||
|
||||
To get your project featured:
|
||||
|
||||
1. Ensure your project demonstrates best practices
|
||||
2. Include comprehensive documentation
|
||||
3. Add clear usage examples
|
||||
4. Open a discussion in our [GitHub Discussions](https://github.com/jlowin/fastmcp/discussions)
|
||||
|
||||
We review submissions regularly and feature projects that provide value to the FastMCP community.
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Contrib Modules](/patterns/contrib) - Community-contributed modules that are distributed with FastMCP itself
|
||||
67
docs/css/banner.css
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* Banner styling -- improve readability with better contrast */
|
||||
#banner {
|
||||
background: #f1f5f9 !important;
|
||||
color: #1e293b !important;
|
||||
font-size: 0.95rem !important;
|
||||
font-weight: 600 !important;
|
||||
padding-top: 12px !important;
|
||||
padding-bottom: 12px !important;
|
||||
position: relative !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
#banner::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(6, 182, 212, 0.25) 0%,
|
||||
rgba(6, 182, 212, 0.05) 25%,
|
||||
rgba(6, 182, 212, 0.35) 50%,
|
||||
rgba(6, 182, 212, 0.08) 75%,
|
||||
rgba(6, 182, 212, 0.28) 100%
|
||||
);
|
||||
background-size: 300% 100%;
|
||||
animation: colorWave 14s ease-in-out infinite alternate;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dark #banner {
|
||||
background: #475569 !important;
|
||||
color: #f1f5f9 !important;
|
||||
}
|
||||
|
||||
.dark #banner::before {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(247, 37, 133, 0.35) 0%,
|
||||
rgba(247, 37, 133, 0.08) 25%,
|
||||
rgba(247, 37, 133, 0.45) 50%,
|
||||
rgba(247, 37, 133, 0.12) 75%,
|
||||
rgba(247, 37, 133, 0.38) 100%
|
||||
);
|
||||
background-size: 300% 100%;
|
||||
}
|
||||
|
||||
@keyframes colorWave {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
100% {
|
||||
background-position: 100% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
#banner * {
|
||||
color: #1e293b !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.dark #banner * {
|
||||
color: #f1f5f9 !important;
|
||||
}
|
||||
|
||||
3
docs/css/python-sdk.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
a:has(svg.icon) {
|
||||
border: none !important;
|
||||
}
|
||||
20
docs/css/style.css
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
img.nav-logo {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
/* Code highlighting -- target only inline code elements, not code blocks */
|
||||
p code:not(pre code),
|
||||
table code:not(pre code),
|
||||
.prose code:not(pre code),
|
||||
li code:not(pre code),
|
||||
h1 code:not(pre code),
|
||||
h2 code:not(pre code),
|
||||
h3 code:not(pre code),
|
||||
h4 code:not(pre code),
|
||||
h5 code:not(pre code),
|
||||
h6 code:not(pre code) {
|
||||
color: #f72585 !important;
|
||||
background-color: rgba(247, 37, 133, 0.09);
|
||||
}
|
||||
|
||||
|
||||
39
docs/css/version-badge.css
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* Version badge -- display a badge with the current version of the documentation */
|
||||
.version-badge {
|
||||
display: inline-block;
|
||||
align-items: center;
|
||||
gap: 0.3em;
|
||||
font-size: 1em;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
font-family: "Inter", sans-serif;
|
||||
color: #ff5400;
|
||||
background: #fef2f2;
|
||||
border: 1px solid rgba(220, 38, 38, 0.3);
|
||||
border-radius: 12px;
|
||||
box-shadow: none;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
transition: box-shadow 0.2s, transform 0.15s;
|
||||
}
|
||||
|
||||
.version-badge-container {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.version-badge:hover {
|
||||
box-shadow: 0 2px 8px 0 rgba(160, 132, 252, 0.1);
|
||||
transform: translateY(-1px) scale(1.03);
|
||||
}
|
||||
|
||||
.dark .version-badge {
|
||||
color: #f1f5f9;
|
||||
background: #334155;
|
||||
border: 1px solid #64748b;
|
||||
}
|
||||
|
||||
89
docs/deployment/fastmcp-cloud.mdx
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
title: FastMCP Cloud
|
||||
sidebarTitle: FastMCP Cloud
|
||||
description: The fastest way to deploy your MCP server
|
||||
icon: cloud
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
[FastMCP Cloud](https://fastmcp.cloud) is a managed platform for hosting MCP servers, built by the FastMCP team. While the FastMCP framework will always be fully open-source, we created FastMCP Cloud to solve the deployment challenges we've seen developers face. Our goal is to provide the absolute fastest way to make your MCP server available to LLM clients like Claude and Cursor.
|
||||
|
||||
FastMCP Cloud is a young product and we welcome your feedback. Please join our [Discord](https://discord.com/invite/aGsSC3yDF4) to share your thoughts and ideas, and you can expect to see new features and improvements every week.
|
||||
|
||||
|
||||
<Note>
|
||||
FastMCP Cloud supports both **FastMCP 2.0** servers and also **FastMCP 1.0** servers that were created with the official MCP Python SDK.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
FastMCP Cloud is completely free while in beta!
|
||||
</Tip>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To use FastMCP Cloud, you'll need a [GitHub](https://github.com) account. In addition, you'll need a GitHub repo that contains a FastMCP server instance. If you don't want to create one yet, you can proceed to [step 1](#step-1-create-a-project) and use the FastMCP Cloud quickstart repo.
|
||||
|
||||
Your repo can be public or private, but must include at least a Python file that contains a FastMCP server instance.
|
||||
<Tip>
|
||||
To ensure your file is compatible with FastMCP Cloud, you can run `fastmcp inspect <file.py:server_object>` to see what FastMCP Cloud will see when it runs your server.
|
||||
</Tip>
|
||||
|
||||
If you have a `requirements.txt` or `pyproject.toml` in the repo, FastMCP Cloud will automatically detect your server's dependencies and install them for you. Note that your file *can* have an `if __name__ == "__main__"` block, but it will be ignored by FastMCP Cloud.
|
||||
|
||||
For example, a minimal server file might look like:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
There are just three steps to deploying a server to FastMCP Cloud:
|
||||
|
||||
### Step 1: Create a Project
|
||||
|
||||
Visit [fastmcp.cloud](https://fastmcp.cloud) and sign in with your GitHub account. Then, create a project. Each project corresponds to a GitHub repo, and you can create one from either your own repo or using the FastMCP Cloud quickstart repo.
|
||||
|
||||
<img src="/assets/images/fastmcp_cloud/quickstart.png" alt="FastMCP Cloud Quickstart Screen" />
|
||||
|
||||
Next, you'll be prompted to configure your project.
|
||||
|
||||
<img src="/assets/images/fastmcp_cloud/create_project.png" alt="FastMCP Cloud Configuration Screen" />
|
||||
|
||||
The configuration screen lets you specify:
|
||||
- **Name**: The name of your project. This will be used to generate a unique URL for your server.
|
||||
- **Entrypoint**: The Python file containing your FastMCP server (e.g., `echo.py`). This field has the same syntax as the `fastmcp run` command, for example `echo.py:my_server` to specify a specific object in the file.
|
||||
- **Authentication**: If disabled, your server is open to the public. If enabled, only other members of your FastMCP Cloud organization will be able to connect.
|
||||
|
||||
Note that FastMCP Cloud will automatically detect yours server's Python dependencies from either a `requirements.txt` or `pyproject.toml` file.
|
||||
|
||||
### Step 2: Deploy Your Server
|
||||
|
||||
Once you configure your project, FastMCP Cloud will:
|
||||
1. Clone the repository
|
||||
2. Build your FastMCP server
|
||||
3. Deploy it to a unique URL
|
||||
4. Make it immediately available for connections
|
||||
|
||||
<img src="/assets/images/fastmcp_cloud/deployment.png" alt="FastMCP Cloud Deployment Screen" />
|
||||
|
||||
FastMCP Cloud will monitor your repo and redeploy your server whenever you push a change to the `main` branch. In addition, FastMCP Cloud will build and deploy servers for every PR your open, hosting them on unique URLs, so you can test changes before updating your production server.
|
||||
|
||||
### Step 3: Connect to Your Server
|
||||
|
||||
Once your server is deployed, it will be accessible at a URL like:
|
||||
|
||||
```
|
||||
https://your-project-name.fastmcp.app/mcp
|
||||
```
|
||||
|
||||
You should be able to connect to it as soon as you see the deployment succeed! FastMCP Cloud provides instant connection options for popular LLM clients:
|
||||
|
||||
<img src="/assets/images/fastmcp_cloud/connect.png" alt="FastMCP Cloud Connection Screen" />
|
||||
|
||||
258
docs/deployment/running-server.mdx
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
---
|
||||
title: Running Your Server
|
||||
sidebarTitle: Running
|
||||
description: Learn how to run your FastMCP server locally for development and testing
|
||||
icon: circle-play
|
||||
---
|
||||
|
||||
FastMCP servers can be run in different ways depending on your needs. This guide focuses on running servers locally for development and testing. For production deployment to a URL, see the [Self-Hosted Deployment](/deployment/self-hosted) guide.
|
||||
|
||||
## The `run()` Method
|
||||
|
||||
Every FastMCP server needs to be started to accept connections. The simplest way to run a server is by calling the `run()` method on your FastMCP instance. This method starts the server and blocks until it's stopped, handling all the connection management for you.
|
||||
|
||||
<Tip>
|
||||
For maximum compatibility, it's best practice to place the `run()` call within an `if __name__ == "__main__":` block. This ensures the server starts only when the script is executed directly, not when imported as a module.
|
||||
</Tip>
|
||||
|
||||
```python {9-10} my_server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP(name="MyServer")
|
||||
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
You can now run this MCP server by executing `python my_server.py`.
|
||||
|
||||
## Transport Protocols
|
||||
|
||||
MCP servers communicate with clients through different transport protocols. Think of transports as the "language" your server speaks to communicate with clients. FastMCP supports three main transport protocols, each designed for specific use cases and deployment scenarios.
|
||||
|
||||
The choice of transport determines how clients connect to your server, what network capabilities are available, and how many clients can connect simultaneously. Understanding these transports helps you choose the right approach for your application.
|
||||
|
||||
### STDIO Transport (Default)
|
||||
|
||||
STDIO (Standard Input/Output) is the default transport for FastMCP servers. When you call `run()` without arguments, your server uses STDIO transport. This transport communicates through standard input and output streams, making it perfect for command-line tools and desktop applications like Claude Desktop.
|
||||
|
||||
With STDIO transport, the client spawns a new server process for each session and manages its lifecycle. The server reads MCP messages from stdin and writes responses to stdout. This is why STDIO servers don't stay running - they're started on-demand by the client.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run() # Uses STDIO transport by default
|
||||
```
|
||||
|
||||
STDIO is ideal for:
|
||||
- Local development and testing
|
||||
- Claude Desktop integration
|
||||
- Command-line tools
|
||||
- Single-user applications
|
||||
|
||||
### HTTP Transport (Streamable)
|
||||
|
||||
HTTP transport turns your MCP server into a web service accessible via a URL. This transport uses the Streamable HTTP protocol, which allows clients to connect over the network. Unlike STDIO where each client gets its own process, an HTTP server can handle multiple clients simultaneously.
|
||||
|
||||
The Streamable HTTP protocol provides full bidirectional communication between client and server, supporting all MCP operations including streaming responses. This makes it the recommended choice for network-based deployments.
|
||||
|
||||
To use HTTP transport, specify it in the `run()` method along with networking options:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Start an HTTP server on port 8000
|
||||
mcp.run(transport="http", host="127.0.0.1", port=8000)
|
||||
```
|
||||
|
||||
Your server is now accessible at `http://localhost:8000/mcp/`. This URL is the MCP endpoint that clients will connect to. HTTP transport enables:
|
||||
- Network accessibility
|
||||
- Multiple concurrent clients
|
||||
- Integration with web infrastructure
|
||||
- Remote deployment capabilities
|
||||
|
||||
For production HTTP deployment with authentication and advanced configuration, see the [Self-Hosted Deployment](/deployment/self-hosted) guide.
|
||||
|
||||
### SSE Transport (Legacy)
|
||||
|
||||
Server-Sent Events (SSE) transport was the original HTTP-based transport for MCP. While still supported for backward compatibility, it has limitations compared to the newer Streamable HTTP transport. SSE only supports server-to-client streaming, making it less efficient for bidirectional communication.
|
||||
|
||||
```python
|
||||
if __name__ == "__main__":
|
||||
# SSE transport - use HTTP instead for new projects
|
||||
mcp.run(transport="sse", host="127.0.0.1", port=8000)
|
||||
```
|
||||
|
||||
We recommend using HTTP transport instead of SSE for all new projects. SSE remains available only for compatibility with older clients that haven't upgraded to Streamable HTTP.
|
||||
|
||||
### Choosing the Right Transport
|
||||
|
||||
Each transport serves different needs. STDIO is perfect when you need simple, local execution - it's what Claude Desktop and most command-line tools expect. HTTP transport is essential when you need network access, want to serve multiple clients, or plan to deploy your server remotely. SSE exists only for backward compatibility and shouldn't be used in new projects.
|
||||
|
||||
Consider your deployment scenario: Are you building a tool for local use? STDIO is your best choice. Need a centralized service that multiple clients can access? HTTP transport is the way to go.
|
||||
|
||||
## The FastMCP CLI
|
||||
|
||||
FastMCP provides a powerful command-line interface for running servers without modifying the source code. The CLI can automatically find and run your server with different transports, manage dependencies, and handle development workflows:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py
|
||||
```
|
||||
|
||||
The CLI automatically finds a FastMCP instance in your file (named `mcp`, `server`, or `app`) and runs it with the specified options. This is particularly useful for testing different transports or configurations without changing your code.
|
||||
|
||||
### Dependency Management
|
||||
|
||||
The CLI integrates with `uv` to manage Python environments and dependencies:
|
||||
|
||||
```bash
|
||||
# Run with a specific Python version
|
||||
fastmcp run server.py --python 3.11
|
||||
|
||||
# Run with additional packages
|
||||
fastmcp run server.py --with pandas --with numpy
|
||||
|
||||
# Run with dependencies from a requirements file
|
||||
fastmcp run server.py --with-requirements requirements.txt
|
||||
|
||||
# Combine multiple options
|
||||
fastmcp run server.py --python 3.10 --with httpx --transport http
|
||||
|
||||
# Run within a specific project directory
|
||||
fastmcp run server.py --project /path/to/project
|
||||
```
|
||||
|
||||
<Note>
|
||||
When using `--python`, `--with`, `--project`, or `--with-requirements`, the server runs via `uv run` subprocess instead of using your local environment.
|
||||
</Note>
|
||||
|
||||
### Passing Arguments to Servers
|
||||
|
||||
When servers accept command line arguments (using argparse, click, or other libraries), you can pass them after `--`:
|
||||
|
||||
```bash
|
||||
fastmcp run config_server.py -- --config config.json
|
||||
fastmcp run database_server.py -- --database-path /tmp/db.sqlite --debug
|
||||
```
|
||||
|
||||
This is useful for servers that need configuration files, database paths, API keys, or other runtime options.
|
||||
|
||||
For more CLI features including development mode with the MCP Inspector, see the [CLI documentation](/patterns/cli).
|
||||
|
||||
### Async Usage
|
||||
|
||||
FastMCP servers are built on async Python, but the framework provides both synchronous and asynchronous APIs to fit your application's needs. The `run()` method we've been using is actually a synchronous wrapper around the async server implementation.
|
||||
|
||||
For applications that are already running in an async context, FastMCP provides the `run_async()` method:
|
||||
|
||||
```python {10-12}
|
||||
from fastmcp import FastMCP
|
||||
import asyncio
|
||||
|
||||
mcp = FastMCP(name="MyServer")
|
||||
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
async def main():
|
||||
# Use run_async() in async contexts
|
||||
await mcp.run_async(transport="http", port=8000)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The `run()` method cannot be called from inside an async function because it creates its own async event loop internally. If you attempt to call `run()` from inside an async function, you'll get an error about the event loop already running.
|
||||
|
||||
Always use `run_async()` inside async functions and `run()` in synchronous contexts.
|
||||
</Warning>
|
||||
|
||||
Both `run()` and `run_async()` accept the same transport arguments, so all the examples above apply to both methods.
|
||||
|
||||
## Custom Routes
|
||||
|
||||
When using HTTP transport, you might want to add custom web endpoints alongside your MCP server. This is useful for health checks, status pages, or simple APIs. FastMCP lets you add custom routes using the `@custom_route` decorator:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import PlainTextResponse
|
||||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.custom_route("/health", methods=["GET"])
|
||||
async def health_check(request: Request) -> PlainTextResponse:
|
||||
return PlainTextResponse("OK")
|
||||
|
||||
@mcp.tool
|
||||
def process(data: str) -> str:
|
||||
return f"Processed: {data}"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="http") # Health check at http://localhost:8000/health
|
||||
```
|
||||
|
||||
Custom routes are served by the same web server as your MCP endpoint. They're available at the root of your domain while the MCP endpoint is at `/mcp/`. For more complex web applications, consider [mounting your MCP server into a FastAPI or Starlette app](/deployment/self-hosted#integration-with-web-frameworks).
|
||||
|
||||
## Alternative Initialization Patterns
|
||||
|
||||
The `if __name__ == "__main__"` pattern works well for standalone scripts, but some deployment scenarios require different approaches. FastMCP handles these cases automatically.
|
||||
|
||||
### CLI-Only Servers
|
||||
|
||||
When using the FastMCP CLI, you don't need the `if __name__` block at all. The CLI will find your FastMCP instance and run it:
|
||||
|
||||
```python
|
||||
# server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("MyServer") # CLI looks for 'mcp', 'server', or 'app'
|
||||
|
||||
@mcp.tool
|
||||
def process(data: str) -> str:
|
||||
return f"Processed: {data}"
|
||||
|
||||
# No if __name__ block needed - CLI will find and run 'mcp'
|
||||
```
|
||||
|
||||
### ASGI Applications
|
||||
|
||||
For ASGI deployment (running with Uvicorn or similar), you'll want to create an ASGI application object. This approach is common in production deployments where you need more control over the server configuration:
|
||||
|
||||
```python
|
||||
# app.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
def create_app():
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.tool
|
||||
def process(data: str) -> str:
|
||||
return f"Processed: {data}"
|
||||
|
||||
return mcp.http_app()
|
||||
|
||||
app = create_app() # Uvicorn will use this
|
||||
```
|
||||
|
||||
See the [Self-Hosted Deployment](/deployment/self-hosted) guide for more ASGI deployment patterns.
|
||||
209
docs/deployment/self-hosted.mdx
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
---
|
||||
title: Self-Hosted Remote MCP
|
||||
sidebarTitle: Self-Hosted
|
||||
description: Deploy your FastMCP server as a remote MCP service accessible via URL
|
||||
icon: server
|
||||
---
|
||||
|
||||
<Tip>
|
||||
STDIO transport is perfect for local development and desktop applications. But to unlock the full potential of MCP—centralized services, multi-client access, and network availability—you need remote HTTP deployment.
|
||||
</Tip>
|
||||
|
||||
This guide walks you through deploying your FastMCP server as a remote MCP service that's accessible via a URL. Once deployed, your MCP server will be available over the network, allowing multiple clients to connect simultaneously and enabling integration with cloud-based LLM applications. This guide focuses specifically on remote MCP deployment, not local STDIO servers.
|
||||
|
||||
## Choosing Your Approach
|
||||
|
||||
FastMCP provides two ways to deploy your server as an HTTP service. Understanding the trade-offs helps you choose the right approach for your needs.
|
||||
|
||||
The **direct HTTP server** approach is simpler and perfect for getting started quickly. You modify your server's `run()` method to use HTTP transport, and FastMCP handles all the web server configuration. This approach works well for standalone deployments where you want your MCP server to be the only service running on a port.
|
||||
|
||||
The **ASGI application** approach gives you more control and flexibility. Instead of running the server directly, you create an ASGI application that can be served by production-grade servers like Uvicorn or Gunicorn. This approach is better when you need advanced server features like multiple workers, custom middleware, or when you're integrating with existing web applications.
|
||||
|
||||
### Direct HTTP Server
|
||||
|
||||
The simplest way to get your MCP server online is to use the built-in `run()` method with HTTP transport. This approach handles all the server configuration for you and is ideal when you want a standalone MCP server without additional complexity.
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My Server")
|
||||
|
||||
@mcp.tool
|
||||
def process_data(input: str) -> str:
|
||||
"""Process data on the server"""
|
||||
return f"Processed: {input}"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="http", host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
Run your server with a simple Python command:
|
||||
```bash
|
||||
python server.py
|
||||
```
|
||||
|
||||
Your server is now accessible at `http://localhost:8000/mcp/` (or use your server's actual IP address for remote access).
|
||||
|
||||
This approach is ideal when you want to get online quickly with minimal configuration. It's perfect for internal tools, development environments, or simple deployments where you don't need advanced server features. The built-in server handles all the HTTP details, letting you focus on your MCP implementation.
|
||||
|
||||
### ASGI Application
|
||||
|
||||
For production deployments, you'll often want more control over how your server runs. FastMCP can create a standard ASGI application that works with any ASGI server like Uvicorn, Gunicorn, or Hypercorn. This approach is particularly useful when you need to configure advanced server options, run multiple workers, or integrate with existing infrastructure.
|
||||
|
||||
```python app.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My Server")
|
||||
|
||||
@mcp.tool
|
||||
def process_data(input: str) -> str:
|
||||
"""Process data on the server"""
|
||||
return f"Processed: {input}"
|
||||
|
||||
# Create ASGI application
|
||||
app = mcp.http_app()
|
||||
```
|
||||
|
||||
Run with any ASGI server - here's an example with Uvicorn:
|
||||
```bash
|
||||
uvicorn app:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Your server is accessible at the same URL: `http://localhost:8000/mcp/` (or use your server's actual IP address for remote access).
|
||||
|
||||
The ASGI approach shines in production environments where you need reliability and performance. You can run multiple worker processes to handle concurrent requests, add custom middleware for logging or monitoring, integrate with existing deployment pipelines, or mount your MCP server as part of a larger application. This flexibility makes it the preferred choice for serious deployments.
|
||||
|
||||
## Configuring Your Server
|
||||
|
||||
### Custom Path
|
||||
|
||||
By default, your MCP server is accessible at `/mcp/` on your domain. You can customize this path to fit your URL structure or avoid conflicts with existing endpoints. This is particularly useful when integrating MCP into an existing application or following specific API conventions.
|
||||
|
||||
```python
|
||||
# Option 1: With mcp.run()
|
||||
mcp.run(transport="http", host="0.0.0.0", port=8000, path="/api/mcp/")
|
||||
|
||||
# Option 2: With ASGI app
|
||||
app = mcp.http_app(path="/api/mcp/")
|
||||
```
|
||||
|
||||
Now your server is accessible at `http://localhost:8000/api/mcp/`.
|
||||
|
||||
### Authentication
|
||||
|
||||
<Warning>
|
||||
Authentication is **highly recommended** for remote MCP servers. Some LLM clients require authentication for remote servers and will refuse to connect without it.
|
||||
</Warning>
|
||||
|
||||
FastMCP supports multiple authentication methods to secure your remote server. See the [Authentication Overview](/servers/auth/authentication) for complete configuration options including Bearer tokens, JWT, and OAuth.
|
||||
|
||||
### Health Checks
|
||||
|
||||
Health check endpoints are essential for monitoring your deployed server and ensuring it's responding correctly. FastMCP allows you to add custom routes alongside your MCP endpoints, making it easy to implement health checks that work with both deployment approaches.
|
||||
|
||||
```python
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
@mcp.custom_route("/health", methods=["GET"])
|
||||
async def health_check(request):
|
||||
return JSONResponse({"status": "healthy", "service": "mcp-server"})
|
||||
```
|
||||
|
||||
This health endpoint will be available at `http://localhost:8000/health` and can be used by load balancers, monitoring systems, or deployment platforms to verify your server is running.
|
||||
|
||||
## Integration with Web Frameworks
|
||||
|
||||
If you already have a web application running, you can add MCP capabilities by mounting a FastMCP server as a sub-application. This allows you to expose MCP tools alongside your existing API endpoints, sharing the same domain and infrastructure. The MCP server becomes just another route in your application, making it easy to manage and deploy.
|
||||
|
||||
For detailed integration guides, see:
|
||||
- [FastAPI Integration](/integrations/fastapi)
|
||||
- [ASGI / Starlette Integration](/integrations/asgi)
|
||||
|
||||
Here's a quick example showing how to add MCP to an existing FastAPI application:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Your existing API
|
||||
api = FastAPI()
|
||||
|
||||
@api.get("/api/status")
|
||||
def status():
|
||||
return {"status": "ok"}
|
||||
|
||||
# Create your MCP server
|
||||
mcp = FastMCP("API Tools")
|
||||
|
||||
@mcp.tool
|
||||
def query_database(query: str) -> dict:
|
||||
"""Run a database query"""
|
||||
return {"result": "data"}
|
||||
|
||||
# Mount MCP at /mcp
|
||||
api.mount("/mcp", mcp.http_app())
|
||||
|
||||
# Run with: uvicorn app:api --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Your existing API remains at `http://localhost:8000/api/` while MCP is available at `http://localhost:8000/mcp/`.
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Running with Uvicorn
|
||||
|
||||
When deploying to production, you'll want to optimize your server for performance and reliability. Uvicorn provides several options to improve your server's capabilities, including running multiple worker processes to handle concurrent requests and enabling enhanced logging for monitoring.
|
||||
|
||||
```bash
|
||||
# Install uvicorn with standard extras for better performance
|
||||
pip install 'uvicorn[standard]'
|
||||
|
||||
# Run with multiple workers for better concurrency
|
||||
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
|
||||
|
||||
# Enable detailed logging for monitoring
|
||||
uvicorn app:app --host 0.0.0.0 --port 8000 --log-level info
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Production deployments should never hardcode sensitive information like API keys or authentication tokens. Instead, use environment variables to configure your server at runtime. This keeps your code secure and makes it easy to deploy the same code to different environments with different configurations.
|
||||
|
||||
Here's an example using bearer token authentication (though OAuth is recommended for production):
|
||||
|
||||
```python
|
||||
import os
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth import BearerTokenAuth
|
||||
|
||||
# Read configuration from environment
|
||||
auth_token = os.environ.get("MCP_AUTH_TOKEN")
|
||||
if auth_token:
|
||||
auth = BearerTokenAuth(token=auth_token)
|
||||
mcp = FastMCP("Production Server", auth=auth)
|
||||
else:
|
||||
mcp = FastMCP("Production Server")
|
||||
|
||||
app = mcp.http_app()
|
||||
```
|
||||
|
||||
Deploy with your secrets safely stored in environment variables:
|
||||
```bash
|
||||
MCP_AUTH_TOKEN=secret uvicorn app:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
## Testing Your Deployment
|
||||
|
||||
Once your server is deployed, you'll need to verify it's accessible and functioning correctly. For comprehensive testing strategies including connectivity tests, client testing, and authentication testing, see the [Testing Your Server](/development/tests) guide.
|
||||
|
||||
## Hosting Your Server
|
||||
|
||||
This guide has shown you how to create an HTTP-accessible MCP server, but you'll still need a hosting provider to make it available on the internet. Your FastMCP server can run anywhere that supports Python web applications:
|
||||
|
||||
- **Cloud VMs** (AWS EC2, Google Compute Engine, Azure VMs)
|
||||
- **Container platforms** (Cloud Run, Container Instances, ECS)
|
||||
- **Platform-as-a-Service** (Railway, Render, Vercel)
|
||||
- **Edge platforms** (Cloudflare Workers)
|
||||
- **Kubernetes clusters** (self-managed or managed)
|
||||
|
||||
The key requirements are Python 3.10+ support and the ability to expose an HTTP port. Most providers will require you to package your server (requirements.txt, Dockerfile, etc.) according to their deployment format. For managed, zero-configuration deployment, see [FastMCP Cloud](/deployment/fastmcp-cloud).
|
||||
640
docs/deployment/server-configuration.mdx
Normal file
|
|
@ -0,0 +1,640 @@
|
|||
---
|
||||
title: "Project Configuration"
|
||||
sidebarTitle: "Project Configuration"
|
||||
description: Use fastmcp.json for portable, declarative project configuration
|
||||
icon: file-code
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.12.0" />
|
||||
|
||||
FastMCP supports declarative configuration through `fastmcp.json` files. This is the canonical and preferred way to configure FastMCP projects, providing a single source of truth for server settings, dependencies, and deployment options that replaces complex command-line arguments.
|
||||
|
||||
The `fastmcp.json` file is designed to be a portable description of your server configuration that can be shared across environments and teams. When running from a `fastmcp.json` file, you can override any configuration values using CLI arguments.
|
||||
|
||||
## Overview
|
||||
|
||||
The `fastmcp.json` configuration file allows you to define all aspects of your FastMCP server in a structured, shareable format. Instead of remembering command-line arguments or writing shell scripts, you declare your server's configuration once and use it everywhere.
|
||||
|
||||
When you have a `fastmcp.json` file, running your server becomes as simple as:
|
||||
|
||||
```bash
|
||||
# Run the server using the configuration
|
||||
fastmcp run fastmcp.json
|
||||
|
||||
# Or if fastmcp.json exists in the current directory
|
||||
fastmcp run
|
||||
```
|
||||
|
||||
This configuration approach ensures reproducible deployments across different environments, from local development to production servers. It works seamlessly with Claude Desktop, VS Code extensions, and any MCP-compatible client.
|
||||
|
||||
## File Structure
|
||||
|
||||
The `fastmcp.json` configuration answers three fundamental questions about your server:
|
||||
|
||||
- **Source** = WHERE does your server code live?
|
||||
- **Environment** = WHAT environment setup does it require?
|
||||
- **Deployment** = HOW should the server run?
|
||||
|
||||
This conceptual model helps you understand the purpose of each configuration section and organize your settings effectively. The configuration file maps directly to these three concerns:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
// WHERE: Location of your server code
|
||||
"type": "filesystem", // Optional, defaults to "filesystem"
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"environment": {
|
||||
// WHAT: Environment setup and dependencies
|
||||
"type": "uv", // Optional, defaults to "uv"
|
||||
"python": ">=3.10",
|
||||
"dependencies": ["pandas", "numpy"]
|
||||
},
|
||||
"deployment": {
|
||||
// HOW: Runtime configuration
|
||||
"transport": "stdio",
|
||||
"log_level": "INFO"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Only the `source` field is required. The `environment` and `deployment` sections are optional and provide additional configuration when needed.
|
||||
|
||||
### JSON Schema Support
|
||||
|
||||
FastMCP provides JSON schemas for IDE autocomplete and validation. Add the schema reference to your `fastmcp.json` for enhanced developer experience:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Two schema URLs are available:
|
||||
- **Version-specific**: `https://gofastmcp.com/public/schemas/fastmcp.json/v1.json`
|
||||
- **Latest version**: `https://gofastmcp.com/public/schemas/fastmcp.json/latest.json`
|
||||
|
||||
Modern IDEs like VS Code will automatically provide autocomplete suggestions, validation, and inline documentation when the schema is specified.
|
||||
|
||||
### Source Configuration
|
||||
|
||||
The source configuration determines **WHERE** your server code lives. It tells FastMCP how to find and load your server, whether it's a local Python file, a remote repository, or hosted in the cloud. This section is required and forms the foundation of your configuration.
|
||||
|
||||
<Card icon="code" title="Source">
|
||||
<ParamField body="source" type="object" required>
|
||||
The server source configuration that determines where your server code lives.
|
||||
|
||||
<ParamField body="type" type="string" default="filesystem">
|
||||
The source type identifier that determines which implementation to use. Currently supports `"filesystem"` for local files. Future releases will add support for `"git"` and `"cloud"` source types.
|
||||
</ParamField>
|
||||
|
||||
<Expandable title="FileSystemSource">
|
||||
When `type` is `"filesystem"` (or omitted), the source points to a local Python file containing your FastMCP server:
|
||||
|
||||
<ParamField body="path" type="string" required>
|
||||
Path to the Python file containing your FastMCP server.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="entrypoint" type="string">
|
||||
Name of the server instance or factory function within the module:
|
||||
- Can be a FastMCP server instance (e.g., `mcp = FastMCP("MyServer")`)
|
||||
- Can be a function with no arguments that returns a FastMCP server
|
||||
- If not specified, FastMCP searches for common names: `mcp`, `server`, or `app`
|
||||
</ParamField>
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
"source": {
|
||||
"type": "filesystem",
|
||||
"path": "src/server.py",
|
||||
"entrypoint": "mcp"
|
||||
}
|
||||
```
|
||||
|
||||
Note: File paths are resolved relative to the configuration file's location.
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
<Note>
|
||||
**Future Source Types**
|
||||
|
||||
Future releases will support additional source types:
|
||||
- **Git repositories** (`type: "git"`) for loading server code directly from version control
|
||||
- **FastMCP Cloud** (`type: "cloud"`) for hosted servers with automatic scaling and management
|
||||
</Note>
|
||||
|
||||
### Environment Configuration
|
||||
|
||||
The environment configuration determines **WHAT** environment setup your server requires. It controls the build-time setup of your Python environment, ensuring your server runs with the exact Python version and dependencies it requires. This section creates isolated, reproducible environments across different systems.
|
||||
|
||||
FastMCP uses an extensible environment system with a base `Environment` class that can be implemented by different environment providers. Currently, FastMCP supports the `UVEnvironment` for Python environment management using `uv`'s powerful dependency resolver.
|
||||
|
||||
<Card icon="code" title="Environment">
|
||||
<ParamField body="environment" type="object">
|
||||
Optional environment configuration. When specified, FastMCP uses the appropriate environment implementation to set up your server's runtime.
|
||||
|
||||
<ParamField body="type" type="string" default="uv">
|
||||
The environment type identifier that determines which implementation to use. Currently supports `"uv"` for Python environments managed by uv. If omitted, defaults to `"uv"`.
|
||||
</ParamField>
|
||||
|
||||
<Expandable title="UVEnvironment">
|
||||
When `type` is `"uv"` (or omitted), the environment uses uv to manage Python dependencies:
|
||||
|
||||
<ParamField body="python" type="string">
|
||||
Python version constraint. Examples:
|
||||
- Exact version: `"3.12"`
|
||||
- Minimum version: `">=3.10"`
|
||||
- Version range: `">=3.10,<3.13"`
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="dependencies" type="list[str]">
|
||||
List of pip packages with optional version specifiers (PEP 508 format).
|
||||
```json
|
||||
"dependencies": ["pandas>=2.0", "requests", "httpx"]
|
||||
```
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="requirements" type="string">
|
||||
Path to a requirements.txt file, resolved relative to the config file location.
|
||||
```json
|
||||
"requirements": "requirements.txt"
|
||||
```
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="project" type="string">
|
||||
Path to a project directory containing pyproject.toml for uv project management.
|
||||
```json
|
||||
"project": "."
|
||||
```
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="editable" type="list[string]">
|
||||
List of paths to packages to install in editable/development mode. Useful for local development when you want changes to be reflected immediately. Supports multiple packages for monorepo setups or shared libraries.
|
||||
```json
|
||||
"editable": ["."]
|
||||
```
|
||||
Or with multiple packages:
|
||||
```json
|
||||
"editable": [".", "../shared-lib", "/path/to/another-package"]
|
||||
```
|
||||
</ParamField>
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
"environment": {
|
||||
"type": "uv",
|
||||
"python": ">=3.10",
|
||||
"dependencies": ["pandas", "numpy"],
|
||||
"editable": ["."]
|
||||
}
|
||||
```
|
||||
|
||||
Note: When any UVEnvironment field is specified, FastMCP automatically creates an isolated environment using `uv` before running your server.
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
When environment configuration is provided, FastMCP:
|
||||
1. Detects the environment type (defaults to `"uv"` if not specified)
|
||||
2. Creates an isolated environment using the appropriate provider
|
||||
3. Installs the specified dependencies
|
||||
4. Runs your server in this clean environment
|
||||
|
||||
This build-time setup ensures your server always has the dependencies it needs, without polluting your system Python or conflicting with other projects.
|
||||
|
||||
<Note>
|
||||
**Future Environment Types**
|
||||
|
||||
Similar to source types, future releases may support additional environment types for different runtime requirements, such as Docker containers or language-specific environments beyond Python.
|
||||
</Note>
|
||||
|
||||
### Deployment Configuration
|
||||
|
||||
The deployment configuration controls **HOW** your server runs. It defines the runtime behavior including network settings, environment variables, and execution context. These settings determine how your server operates when it executes, from transport protocols to logging levels.
|
||||
|
||||
Environment variables are included in this section because they're runtime configuration that affects how your server behaves when it executes, not how its environment is built. The deployment configuration is applied every time your server starts, controlling its operational characteristics.
|
||||
|
||||
<Card icon="code" title="Deployment Fields">
|
||||
<ParamField body="deployment" type="object">
|
||||
Optional runtime configuration for the server.
|
||||
|
||||
<Expandable title="Deployment Fields">
|
||||
<ParamField body="transport" type="string" default="stdio">
|
||||
Protocol for client communication:
|
||||
- `"stdio"`: Standard input/output for desktop clients
|
||||
- `"http"`: Network-accessible HTTP server
|
||||
- `"sse"`: Server-sent events
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="host" type="string" default="127.0.0.1">
|
||||
Network interface to bind (HTTP transport only):
|
||||
- `"127.0.0.1"`: Local connections only
|
||||
- `"0.0.0.0"`: All network interfaces
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="port" type="integer" default="3000">
|
||||
Port number for HTTP transport.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="path" type="string" default="/mcp/">
|
||||
URL path for the MCP endpoint when using HTTP transport.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="log_level" type="string" default="INFO">
|
||||
Server logging verbosity. Options:
|
||||
- `"DEBUG"`: Detailed debugging information
|
||||
- `"INFO"`: General informational messages
|
||||
- `"WARNING"`: Warning messages
|
||||
- `"ERROR"`: Error messages only
|
||||
- `"CRITICAL"`: Critical errors only
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="env" type="object">
|
||||
Environment variables to set when running the server. Supports `${VAR_NAME}` syntax for runtime interpolation.
|
||||
```json
|
||||
"env": {
|
||||
"API_KEY": "secret-key",
|
||||
"DATABASE_URL": "postgres://${DB_USER}@${DB_HOST}/mydb"
|
||||
}
|
||||
```
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="cwd" type="string">
|
||||
Working directory for the server process. Relative paths are resolved from the config file location.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="args" type="list[str]">
|
||||
Command-line arguments to pass to the server, passed after `--` to the server's argument parser.
|
||||
```json
|
||||
"args": ["--config", "server-config.json"]
|
||||
```
|
||||
</ParamField>
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
#### Environment Variable Interpolation
|
||||
|
||||
The `env` field in deployment configuration supports runtime interpolation of environment variables using `${VAR_NAME}` syntax. This enables dynamic configuration based on your deployment environment:
|
||||
|
||||
```json
|
||||
{
|
||||
"deployment": {
|
||||
"env": {
|
||||
"API_URL": "https://api.${ENVIRONMENT}.example.com",
|
||||
"DATABASE_URL": "postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/myapp",
|
||||
"CACHE_KEY": "myapp_${ENVIRONMENT}_${VERSION}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When the server starts, FastMCP replaces `${ENVIRONMENT}`, `${DB_USER}`, etc. with values from your system's environment variables. If a variable doesn't exist, the placeholder is preserved as-is.
|
||||
|
||||
**Example**: If your system has `ENVIRONMENT=production` and `DB_HOST=db.example.com`:
|
||||
```json
|
||||
// Configuration
|
||||
{
|
||||
"deployment": {
|
||||
"env": {
|
||||
"API_URL": "https://api.${ENVIRONMENT}.example.com",
|
||||
"DB_HOST": "${DB_HOST}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Result at runtime
|
||||
{
|
||||
"API_URL": "https://api.production.example.com",
|
||||
"DB_HOST": "db.example.com"
|
||||
}
|
||||
```
|
||||
|
||||
This feature is particularly useful for:
|
||||
- Deploying the same configuration across development, staging, and production
|
||||
- Keeping sensitive values out of configuration files
|
||||
- Building dynamic URLs and connection strings
|
||||
- Creating environment-specific prefixes or suffixes
|
||||
|
||||
## Usage with CLI Commands
|
||||
|
||||
FastMCP automatically detects and uses a file specifically named `fastmcp.json` in the current directory, making server execution simple and consistent. Files with FastMCP configuration format but different names are not auto-detected and must be specified explicitly:
|
||||
|
||||
```bash
|
||||
# Auto-detect fastmcp.json in current directory
|
||||
cd my-project
|
||||
fastmcp run # No arguments needed!
|
||||
|
||||
# Or specify a configuration file explicitly
|
||||
fastmcp run prod.fastmcp.json
|
||||
|
||||
# Skip environment setup when already in a uv environment
|
||||
fastmcp run fastmcp.json --skip-env
|
||||
|
||||
# Skip source preparation when source is already prepared
|
||||
fastmcp run fastmcp.json --skip-source
|
||||
|
||||
# Skip both environment and source preparation
|
||||
fastmcp run fastmcp.json --skip-env --skip-source
|
||||
```
|
||||
|
||||
### Pre-building Environments
|
||||
|
||||
You can use `fastmcp project prepare` to create a persistent uv project with all dependencies pre-installed:
|
||||
|
||||
```bash
|
||||
# Create a persistent environment
|
||||
fastmcp project prepare fastmcp.json --output-dir ./env
|
||||
|
||||
# Use the pre-built environment to run the server
|
||||
fastmcp run fastmcp.json --project ./env
|
||||
```
|
||||
|
||||
This pattern separates environment setup (slow) from server execution (fast), useful for deployment scenarios.
|
||||
|
||||
### Using an Existing Environment
|
||||
|
||||
By default, FastMCP creates an isolated environment with `uv` based on your configuration. When you already have a suitable Python environment, use the `--skip-env` flag to skip environment creation:
|
||||
|
||||
```bash
|
||||
fastmcp run fastmcp.json --skip-env
|
||||
```
|
||||
|
||||
**When you already have an environment:**
|
||||
- You're in an activated virtual environment with all dependencies installed
|
||||
- You're inside a Docker container with pre-installed dependencies
|
||||
- You're in a CI/CD pipeline that pre-builds the environment
|
||||
- You're using a system-wide installation with all required packages
|
||||
- You're in a uv-managed environment (prevents infinite recursion)
|
||||
|
||||
This flag tells FastMCP: "I already have everything installed, just run the server."
|
||||
|
||||
### Using an Existing Source
|
||||
|
||||
When working with source types that require preparation (future support for git repositories or cloud sources), use the `--skip-source` flag when you already have the source code available:
|
||||
|
||||
```bash
|
||||
fastmcp run fastmcp.json --skip-source
|
||||
```
|
||||
|
||||
**When you already have the source:**
|
||||
- You've previously cloned a git repository and don't need to re-fetch
|
||||
- You have a cached copy of a cloud-hosted server
|
||||
- You're in a CI/CD pipeline where source checkout is a separate step
|
||||
- You're iterating locally on already-downloaded code
|
||||
|
||||
This flag tells FastMCP: "I already have the source code, skip any download/clone steps."
|
||||
|
||||
Note: For filesystem sources (local Python files), this flag has no effect since they don't require preparation.
|
||||
|
||||
The configuration file works with all FastMCP commands:
|
||||
- **`run`** - Start the server in production mode
|
||||
- **`dev`** - Launch with the Inspector UI for development
|
||||
- **`inspect`** - View server capabilities and configuration
|
||||
- **`install`** - Install to Claude Desktop, Cursor, or other MCP clients
|
||||
|
||||
When no file argument is provided, FastMCP searches the current directory for `fastmcp.json`. This means you can simply navigate to your project directory and run `fastmcp run` to start your server with all its configured settings.
|
||||
|
||||
### CLI Override Behavior
|
||||
|
||||
Command-line arguments take precedence over configuration file values, allowing ad-hoc adjustments without modifying the file:
|
||||
|
||||
```bash
|
||||
# Config specifies port 3000, CLI overrides to 8080
|
||||
fastmcp run fastmcp.json --port 8080
|
||||
|
||||
# Config specifies stdio, CLI overrides to HTTP
|
||||
fastmcp run fastmcp.json --transport http
|
||||
|
||||
# Add extra dependencies not in config
|
||||
fastmcp run fastmcp.json --with requests --with httpx
|
||||
```
|
||||
|
||||
This precedence order enables:
|
||||
- Quick testing of different settings
|
||||
- Environment-specific overrides in deployment scripts
|
||||
- Debugging with increased log levels
|
||||
- Temporary configuration changes
|
||||
|
||||
### Custom Naming Patterns
|
||||
|
||||
You can use different configuration files for different environments:
|
||||
|
||||
- `fastmcp.json` - Default configuration
|
||||
- `dev.fastmcp.json` - Development settings
|
||||
- `prod.fastmcp.json` - Production settings
|
||||
- `test_fastmcp.json` - Test configuration
|
||||
|
||||
Any file with "fastmcp.json" in the name is recognized as a configuration file.
|
||||
|
||||
## Examples
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Basic Configuration">
|
||||
|
||||
A minimal configuration for a simple server:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
}
|
||||
}
|
||||
```
|
||||
This configuration explicitly specifies the server entrypoint (`mcp`), making it clear which server instance or factory function to use. Uses all defaults: STDIO transport, no special dependencies, standard logging.
|
||||
</Tab>
|
||||
<Tab title="Development Configuration">
|
||||
|
||||
A configuration optimized for local development:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
// WHERE does the server live?
|
||||
"source": {
|
||||
"path": "src/server.py",
|
||||
"entrypoint": "app"
|
||||
},
|
||||
// WHAT dependencies does it need?
|
||||
"environment": {
|
||||
"type": "uv",
|
||||
"python": "3.12",
|
||||
"dependencies": ["fastmcp[dev]"],
|
||||
"editable": "."
|
||||
},
|
||||
// HOW should it run?
|
||||
"deployment": {
|
||||
"transport": "http",
|
||||
"host": "127.0.0.1",
|
||||
"port": 8000,
|
||||
"log_level": "DEBUG",
|
||||
"env": {
|
||||
"DEBUG": "true",
|
||||
"ENV": "development"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Production Configuration">
|
||||
|
||||
A production-ready configuration with full dependency management:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
// WHERE does the server live?
|
||||
"source": {
|
||||
"path": "app/main.py",
|
||||
"entrypoint": "mcp_server"
|
||||
},
|
||||
// WHAT dependencies does it need?
|
||||
"environment": {
|
||||
"python": "3.11",
|
||||
"requirements": "requirements/production.txt",
|
||||
"project": "."
|
||||
},
|
||||
// HOW should it run?
|
||||
"deployment": {
|
||||
"transport": "http",
|
||||
"host": "0.0.0.0",
|
||||
"port": 3000,
|
||||
"path": "/api/mcp/",
|
||||
"log_level": "INFO",
|
||||
"env": {
|
||||
"ENV": "production",
|
||||
"API_BASE_URL": "https://api.example.com",
|
||||
"DATABASE_URL": "postgresql://user:pass@db.example.com/prod"
|
||||
},
|
||||
"cwd": "/app",
|
||||
"args": ["--workers", "4"]
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Data Science Server">
|
||||
|
||||
Configuration for a data analysis server with scientific packages:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "analysis_server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"environment": {
|
||||
"python": "3.11",
|
||||
"dependencies": [
|
||||
"pandas>=2.0",
|
||||
"numpy",
|
||||
"scikit-learn",
|
||||
"matplotlib",
|
||||
"jupyterlab"
|
||||
]
|
||||
},
|
||||
"deployment": {
|
||||
"transport": "stdio",
|
||||
"env": {
|
||||
"MATPLOTLIB_BACKEND": "Agg",
|
||||
"DATA_PATH": "./datasets"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Multi-Environment Setup">
|
||||
|
||||
You can maintain multiple configuration files for different environments:
|
||||
|
||||
**dev.fastmcp.json**:
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"deployment": {
|
||||
"transport": "http",
|
||||
"log_level": "DEBUG"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**prod.fastmcp.json**:
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"environment": {
|
||||
"requirements": "requirements/production.txt"
|
||||
},
|
||||
"deployment": {
|
||||
"transport": "http",
|
||||
"host": "0.0.0.0",
|
||||
"log_level": "WARNING"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run different configurations:
|
||||
```bash
|
||||
fastmcp run dev.fastmcp.json # Development
|
||||
fastmcp run prod.fastmcp.json # Production
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Migrating from CLI Arguments
|
||||
|
||||
If you're currently using command-line arguments or shell scripts, migrating to `fastmcp.json` simplifies your workflow. Here's how common CLI patterns map to configuration:
|
||||
|
||||
**CLI Command**:
|
||||
```bash
|
||||
uv run --with pandas --with requests \
|
||||
fastmcp run server.py \
|
||||
--transport http \
|
||||
--port 8000 \
|
||||
--log-level INFO
|
||||
```
|
||||
|
||||
**Equivalent fastmcp.json**:
|
||||
```json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"environment": {
|
||||
"dependencies": ["pandas", "requests"]
|
||||
},
|
||||
"deployment": {
|
||||
"transport": "http",
|
||||
"port": 8000,
|
||||
"log_level": "INFO"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now simply run:
|
||||
```bash
|
||||
fastmcp run # Automatically finds and uses fastmcp.json
|
||||
```
|
||||
|
||||
The configuration file approach provides better documentation, easier sharing, and consistent execution across different environments while maintaining the flexibility to override settings when needed.
|
||||
187
docs/development/contributing.mdx
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
---
|
||||
title: "Contributing"
|
||||
description: "Development workflow for FastMCP contributors"
|
||||
icon: code-pull-request
|
||||
---
|
||||
|
||||
Contributing to FastMCP means joining a community that values clean, maintainable code and thoughtful API design. All contributions are valued - from fixing typos in documentation to implementing major features.
|
||||
|
||||
## Issues
|
||||
|
||||
### Issue First, Code Second
|
||||
|
||||
**Every pull request requires a corresponding issue - no exceptions.** This requirement creates a collaborative space where approach, scope, and alignment are established before code is written. Issues serve as design documents where maintainers and contributors discuss implementation strategy, identify potential conflicts with existing patterns, and ensure proposed changes advance FastMCP's vision.
|
||||
|
||||
**FastMCP is an opinionated framework, not a kitchen sink.** The maintainers have strong beliefs about what FastMCP should and shouldn't do. Just because something takes N lines of code and you want it in fewer lines doesn't mean FastMCP should take on the maintenance burden or endorse that pattern. This is judged at the maintainers' discretion.
|
||||
|
||||
Use issues to understand scope BEFORE opening PRs. The issue discussion determines whether a feature belongs in core, contrib, or not at all.
|
||||
|
||||
### Writing Good Issues
|
||||
|
||||
FastMCP is an extremely highly-trafficked repository maintained by a very small team. Issues that appear to transfer burden to maintainers without any effort to validate the problem will be closed. Please help the maintainers help you by always providing a minimal reproducible example and clearly describing the problem.
|
||||
|
||||
**LLM-generated issues will be closed immediately.** Issues that contain paragraphs of unnecessary explanation, verbose problem descriptions, or obvious LLM authorship patterns obfuscate the actual problem and transfer burden to maintainers.
|
||||
|
||||
Write clear, concise issues that:
|
||||
- State the problem directly
|
||||
- Provide a minimal reproducible example
|
||||
- Skip unnecessary background or context
|
||||
- Take responsibility for clear communication
|
||||
|
||||
Issues may be labeled "Invalid" simply due to confusion caused by verbosity or not adhering to the guidelines outlined here.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
PRs that deviate from FastMCP's core principles will be rejected regardless of implementation quality. **PRs are NOT for iterating on ideas** - they should only be opened for ideas that already have a bias toward acceptance based on issue discussion.
|
||||
|
||||
|
||||
### Development Environment
|
||||
|
||||
#### Installation
|
||||
|
||||
To contribute to FastMCP, you'll need to set up a development environment with all necessary tools and dependencies.
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/jlowin/fastmcp.git
|
||||
cd fastmcp
|
||||
|
||||
# Install all dependencies including dev tools
|
||||
uv sync
|
||||
|
||||
# Install pre-commit hooks
|
||||
uv run pre-commit install
|
||||
```
|
||||
|
||||
In addition, some development commands require [just](https://github.com/casey/just) to be installed.
|
||||
|
||||
Pre-commit hooks will run automatically on every commit to catch issues before they reach CI. If you see failures, fix them before committing - never commit broken code expecting to fix it later.
|
||||
|
||||
### Development Standards
|
||||
|
||||
#### Scope
|
||||
|
||||
Large pull requests create review bottlenecks and quality risks. Unless you're fixing a discrete bug or making an incredibly well-scoped change, keep PRs small and focused.
|
||||
|
||||
A PR that changes 50 lines across 3 files can be thoroughly reviewed in minutes. A PR that changes 500 lines across 20 files requires hours of careful analysis and often hides subtle issues.
|
||||
|
||||
Breaking large features into smaller PRs:
|
||||
- Creates better review experiences
|
||||
- Makes git history clear
|
||||
- Simplifies debugging with bisect
|
||||
- Reduces merge conflicts
|
||||
- Gets your code merged faster
|
||||
|
||||
#### Code Quality
|
||||
|
||||
FastMCP values clarity over cleverness. Every line you write will be maintained by someone else - possibly years from now, possibly without context about your decisions.
|
||||
|
||||
**PRs can be rejected for two opposing reasons:**
|
||||
1. **Insufficient quality** - Code that doesn't meet our standards for clarity, maintainability, or idiomaticity
|
||||
2. **Overengineering** - Code that is overbearing, unnecessarily complex, or tries to be too clever
|
||||
|
||||
The focus is on idiomatic, high-quality Python. FastMCP uses patterns like `NotSet` type as an alternative to `None` in certain situations - follow existing patterns.
|
||||
|
||||
#### Required Practices
|
||||
|
||||
**Full type annotations** on all functions and methods. They catch bugs before runtime and serve as inline documentation.
|
||||
|
||||
**Async/await patterns** for all I/O operations. Even if your specific use case doesn't need concurrency, consistency means users can compose features without worrying about blocking operations.
|
||||
|
||||
**Descriptive names** make code self-documenting. `auth_token` is clear; `tok` requires mental translation.
|
||||
|
||||
**Specific exception types** make error handling predictable. Catching `ValueError` tells readers exactly what error you expect. Never use bare `except` clauses.
|
||||
|
||||
#### Anti-Patterns to Avoid
|
||||
|
||||
**Complex one-liners** are hard to debug and modify. Break operations into clear steps.
|
||||
|
||||
**Mutable default arguments** cause subtle bugs. Use `None` as the default and create the mutable object inside the function.
|
||||
|
||||
**Breaking established patterns** confuses readers. If you must deviate, discuss in the issue first.
|
||||
|
||||
### Pre-Commit Checks
|
||||
|
||||
```bash
|
||||
# Runs automatically on commit, or manually:
|
||||
uv run pre-commit run --all-files
|
||||
```
|
||||
|
||||
This runs three critical tools:
|
||||
- **Ruff**: Linting and formatting
|
||||
- **ty**: Static type checking
|
||||
- **Pytest**: Core test suite
|
||||
|
||||
CI will reject PRs that fail these checks. Always run them locally first.
|
||||
|
||||
### Testing
|
||||
|
||||
Tests are documentation that shows how features work. Good tests give reviewers confidence and help future maintainers understand intent.
|
||||
|
||||
```bash
|
||||
# Run specific test directory
|
||||
uv run pytest tests/server/ -v
|
||||
|
||||
# Run all tests before submitting PR
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
Every new feature needs tests. See the [Testing Guide](/development/tests) for patterns and requirements.
|
||||
|
||||
### Documentation
|
||||
|
||||
A feature doesn't exist unless it's documented. Note that FastMCP's hosted documentation always tracks the main branch - users who want historical documentation can clone the repo, checkout a specific tag, and host it themselves.
|
||||
|
||||
```bash
|
||||
# Preview documentation locally
|
||||
just docs
|
||||
```
|
||||
|
||||
Documentation requirements:
|
||||
- **Explain concepts in prose first** - Code without context is just syntax
|
||||
- **Complete, runnable examples** - Every code block should be copy-pasteable
|
||||
- **Register in docs.json** - Makes pages appear in navigation
|
||||
- **Version badges** - Mark when features were added using `<VersionBadge />`
|
||||
|
||||
#### SDK Documentation
|
||||
|
||||
FastMCP's SDK documentation is auto-generated from the source code docstrings and type annotations. It is automatically updated on every merge to main by a GitHub Actions workflow, so users are *not* responsible for keeping the documentation up to date. However, to generate it proactively, you can use the following command:
|
||||
|
||||
```bash
|
||||
just api-ref-all
|
||||
```
|
||||
|
||||
### Submitting Your PR
|
||||
|
||||
#### Before Submitting
|
||||
|
||||
1. **Run all checks**: `uv run pre-commit run --all-files && uv run pytest`
|
||||
2. **Keep scope small**: One feature or fix per PR
|
||||
3. **Write clear description**: Your PR description becomes permanent documentation
|
||||
4. **Update docs**: Include documentation for API changes
|
||||
|
||||
#### PR Description
|
||||
|
||||
Write PR descriptions that explain:
|
||||
- What problem you're solving
|
||||
- Why you chose this approach
|
||||
- Any trade-offs or alternatives considered
|
||||
- Migration path for breaking changes
|
||||
|
||||
Focus on the "why" - the code shows the "what". Keep it concise but complete.
|
||||
|
||||
#### What We Look For
|
||||
|
||||
**Framework Philosophy**: FastMCP is NOT trying to do all things or provide all shortcuts. Features are rejected when they don't align with the framework's vision, even if perfectly implemented. The burden of proof is on the PR to demonstrate value.
|
||||
|
||||
**Code Quality**: We verify code follows existing patterns. Consistency reduces cognitive load. When every module works similarly, developers understand new code quickly.
|
||||
|
||||
**Test Coverage**: Not every line needs testing, but every behavior does. Tests document intent and protect against regressions.
|
||||
|
||||
**Breaking Changes**: May be acceptable in minor versions but must be clearly documented. See the [versioning policy](/development/releases#versioning-policy).
|
||||
|
||||
## Special Modules
|
||||
|
||||
**`contrib`**: Community-maintained patterns and utilities. Original authors maintain their contributions. Not representative of the core framework.
|
||||
|
||||
**`experimental`**: Maintainer-developed features that may preview future functionality. Can break or be deleted at any time without notice. Pin your FastMCP version when using these features.
|
||||
79
docs/development/releases.mdx
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
title: "Releases"
|
||||
description: "FastMCP versioning and release process"
|
||||
icon: "truck-fast"
|
||||
---
|
||||
|
||||
FastMCP releases frequently to deliver features quickly in the rapidly evolving MCP ecosystem. We use semantic versioning pragmatically - the Model Context Protocol is young, patterns are still emerging, and waiting for perfect stability would mean missing opportunities to empower developers with better tools.
|
||||
|
||||
## Versioning Policy
|
||||
|
||||
### Semantic Versioning
|
||||
|
||||
**Major (x.0.0)**: Complete API redesigns
|
||||
|
||||
Major versions represent fundamental shifts. FastMCP 2.x is entirely different from 1.x in both implementation and design philosophy.
|
||||
|
||||
**Minor (2.x.0)**: New features and evolution
|
||||
|
||||
<Warning>
|
||||
Unlike traditional semantic versioning, minor versions **may** include [breaking changes](#breaking-changes) when necessary for the ecosystem's evolution. This flexibility is essential in a young ecosystem where perfect backwards compatibility would prevent important improvements.
|
||||
</Warning>
|
||||
|
||||
FastMCP always targets the most current MCP Protocol version. Breaking changes in the MCP spec or MCP SDK automatically flow through to FastMCP - we prioritize staying current with the latest features and conventions over maintaining compatibility with older protocol versions.
|
||||
|
||||
**Patch (2.0.x)**: Bug fixes and refinements
|
||||
|
||||
Patch versions contain only bug fixes without breaking changes. These are safe updates you can apply with confidence.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
We permit breaking changes in minor versions because the MCP ecosystem is rapidly evolving. Refusing to break problematic APIs would accumulate design debt that eventually makes the framework unusable. Each breaking change represents a deliberate decision to keep FastMCP aligned with the ecosystem's evolution.
|
||||
|
||||
When breaking changes occur:
|
||||
- They only happen in minor versions (e.g., 2.3.x to 2.4.0)
|
||||
- Release notes explain what changed and how to migrate
|
||||
- We provide deprecation warnings at least 1 minor version in advance when possible
|
||||
- Changes must substantially benefit users to justify disruption
|
||||
|
||||
The public API is what's covered by our compatibility guarantees - these are the parts of FastMCP you can rely on to remain stable within a minor version. The public API consists of:
|
||||
- `FastMCP` server class, `Client` class, and FastMCP `Context`
|
||||
- Core MCP components: `Tool`, `Prompt`, `Resource`, `ResourceTemplate`, and transports
|
||||
- Their public methods and documented behaviors
|
||||
|
||||
Everything else (utilities, private methods, internal modules) may change without notice. This boundary lets us refactor internals and improve implementation details without breaking your code. For production stability, pin to specific versions.
|
||||
|
||||
<Warning>
|
||||
The `fastmcp.server.auth` module was introduced in 2.12.0 and is exempted from this policy temporarily, meaning it is *expected* to have breaking changes even on patch versions. This is because auth is a rapidly evolving part of the MCP spec and it would be dangerous to be beholden to old decisions. Please pin your FastMCP version if using authentication in production.
|
||||
|
||||
We expect this exemption to last through at least the 2.12.x and 2.13.x release series.
|
||||
</Warning>
|
||||
|
||||
### Production Use
|
||||
|
||||
Pin to exact versions:
|
||||
```
|
||||
fastmcp==2.11.0 # Good
|
||||
fastmcp>=2.11.0 # Bad - will install breaking changes
|
||||
```
|
||||
|
||||
## Creating Releases
|
||||
|
||||
Our release process is intentionally simple:
|
||||
|
||||
1. Create GitHub release with tag `vMAJOR.MINOR.PATCH` (e.g., `v2.11.0`)
|
||||
2. Generate release notes automatically, and curate or add additional editorial information as needed
|
||||
3. GitHub releases automatically trigger PyPI deployments
|
||||
|
||||
This automation lets maintainers focus on code quality rather than release mechanics.
|
||||
|
||||
### Release Cadence
|
||||
|
||||
We follow a feature-driven release cadence rather than a fixed schedule. Minor versions ship approximately every 3-4 weeks when significant functionality is ready.
|
||||
|
||||
Patch releases ship promptly for:
|
||||
- Critical bug fixes
|
||||
- Security updates (immediate release)
|
||||
- Regression fixes
|
||||
|
||||
This approach means you get improvements as soon as they're ready rather than waiting for arbitrary release dates.
|
||||
350
docs/development/tests.mdx
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
---
|
||||
title: "Tests"
|
||||
description: "Testing patterns and requirements for FastMCP"
|
||||
icon: vial
|
||||
---
|
||||
|
||||
Good tests are the foundation of reliable software. In FastMCP, we treat tests as first-class documentation that demonstrates how features work while protecting against regressions. Every new capability needs comprehensive tests that demonstrate correctness.
|
||||
|
||||
## FastMCP Tests
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
uv run pytest
|
||||
|
||||
# Run specific test file
|
||||
uv run pytest tests/server/test_auth.py
|
||||
|
||||
# Run with coverage
|
||||
uv run pytest --cov=fastmcp
|
||||
|
||||
# Skip integration tests for faster runs
|
||||
uv run pytest -m "not integration"
|
||||
|
||||
# Skip tests that spawn processes
|
||||
uv run pytest -m "not integration and not client_process"
|
||||
```
|
||||
|
||||
Tests should complete in under 1 second unless marked as integration tests. This speed encourages running them frequently, catching issues early.
|
||||
|
||||
### 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.
|
||||
|
||||
### Test Markers
|
||||
|
||||
We use pytest markers to categorize tests that require special resources or take longer to run:
|
||||
|
||||
```python
|
||||
@pytest.mark.integration
|
||||
async def test_github_api_integration():
|
||||
"""Test GitHub API integration with real service."""
|
||||
token = os.getenv("FASTMCP_GITHUB_TOKEN")
|
||||
if not token:
|
||||
pytest.skip("FASTMCP_GITHUB_TOKEN not available")
|
||||
|
||||
# Test against real GitHub API
|
||||
client = GitHubClient(token)
|
||||
repos = await client.list_repos("jlowin")
|
||||
assert "fastmcp" in [repo.name for repo in repos]
|
||||
|
||||
@pytest.mark.client_process
|
||||
async def test_stdio_transport():
|
||||
"""Test STDIO transport with separate process."""
|
||||
# This spawns a subprocess
|
||||
async with Client("python examples/simple_echo.py") as client:
|
||||
result = await client.call_tool("echo", {"message": "test"})
|
||||
assert result.content[0].text == "test"
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
|
||||
### Test Requirements
|
||||
|
||||
Following these practices creates maintainable, debuggable test suites that serve as both documentation and regression protection.
|
||||
|
||||
#### Single Behavior Per Test
|
||||
|
||||
Each test should verify exactly one behavior. When it fails, you need to know immediately what broke. A test that checks five things gives you five potential failure points to investigate. A test that checks one thing points directly to the problem.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Good: Atomic Test
|
||||
async def test_tool_registration():
|
||||
"""Test that tools are properly registered with the server."""
|
||||
mcp = FastMCP("test-server")
|
||||
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
tools = mcp.list_tools()
|
||||
assert len(tools) == 1
|
||||
assert tools[0].name == "add"
|
||||
```
|
||||
|
||||
```python Bad: Multi-Behavior Test
|
||||
async def test_server_functionality():
|
||||
"""Test multiple server features at once."""
|
||||
mcp = FastMCP("test-server")
|
||||
|
||||
# Tool registration
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
# Resource creation
|
||||
@mcp.resource("config://app")
|
||||
def get_config():
|
||||
return {"version": "1.0"}
|
||||
|
||||
# Authentication setup
|
||||
mcp.auth = BearerTokenProvider({"token": "user"})
|
||||
|
||||
# What exactly are we testing? If this fails, what broke?
|
||||
assert mcp.list_tools()
|
||||
assert mcp.list_resources()
|
||||
assert mcp.auth is not None
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
#### Self-Contained Setup
|
||||
|
||||
Every test must create its own setup. Tests should be runnable in any order, in parallel, or in isolation. When a test fails, you should be able to run just that test to reproduce the issue.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Good: Self-Contained
|
||||
async def test_tool_execution_with_error():
|
||||
"""Test that tool errors are properly handled."""
|
||||
mcp = FastMCP("test-server")
|
||||
|
||||
@mcp.tool
|
||||
def divide(a: int, b: int) -> float:
|
||||
if b == 0:
|
||||
raise ValueError("Cannot divide by zero")
|
||||
return a / b
|
||||
|
||||
async with Client(mcp) as client:
|
||||
with pytest.raises(Exception):
|
||||
await client.call_tool("divide", {"a": 10, "b": 0})
|
||||
```
|
||||
|
||||
```python Bad: Test Dependencies
|
||||
# Global state that tests depend on
|
||||
test_server = None
|
||||
|
||||
def test_setup_server():
|
||||
"""Setup for other tests."""
|
||||
global test_server
|
||||
test_server = FastMCP("shared-server")
|
||||
|
||||
def test_server_works():
|
||||
"""Test server functionality."""
|
||||
# Depends on test_setup_server running first
|
||||
assert test_server is not None
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
#### Clear Intent
|
||||
|
||||
Test names and assertions should make the verified behavior obvious. A developer reading your test should understand what feature it validates and how that feature should behave.
|
||||
|
||||
```python
|
||||
async def test_authenticated_tool_requires_valid_token():
|
||||
"""Test that authenticated users can access protected tools."""
|
||||
mcp = FastMCP("test-server")
|
||||
mcp.auth = BearerTokenProvider({"secret-token": "test-user"})
|
||||
|
||||
@mcp.tool
|
||||
def protected_action() -> str:
|
||||
return "success"
|
||||
|
||||
async with Client(mcp, auth=BearerAuth("secret-token")) as client:
|
||||
result = await client.call_tool("protected_action", {})
|
||||
assert result.content[0].text == "success"
|
||||
```
|
||||
|
||||
#### Using Fixtures
|
||||
|
||||
Use fixtures to create reusable data, server configurations, or other resources for your tests. Note that you should **not** open FastMCP clients in your fixtures as it can create hard-to-diagnose issues with event loops.
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from fastmcp import FastMCP, Client
|
||||
|
||||
@pytest.fixture
|
||||
def weather_server():
|
||||
server = FastMCP("WeatherServer")
|
||||
|
||||
@server.tool
|
||||
def get_temperature(city: str) -> dict:
|
||||
temps = {"NYC": 72, "LA": 85, "Chicago": 68}
|
||||
return {"city": city, "temp": temps.get(city, 70)}
|
||||
|
||||
return server
|
||||
|
||||
async def test_temperature_tool(weather_server):
|
||||
async with Client(weather_server) as client:
|
||||
result = await client.call_tool("get_temperature", {"city": "LA"})
|
||||
assert result.data == {"city": "LA", "temp": 85}
|
||||
```
|
||||
|
||||
#### Effective Assertions
|
||||
|
||||
Assertions should be specific and provide context on failure. When a test fails during CI, the assertion message should tell you exactly what went wrong.
|
||||
|
||||
```python
|
||||
# Basic assertion - minimal context on failure
|
||||
assert result.status == "success"
|
||||
|
||||
# Better - explains what was expected
|
||||
assert result.status == "success", f"Expected successful operation, got {result.status}: {result.error}"
|
||||
```
|
||||
|
||||
Try not to have too many assertions in a single test unless you truly need to check various aspects of the same behavior. In general, assertions of different behaviors should be in separate tests.
|
||||
|
||||
#### Inline Snapshots
|
||||
|
||||
FastMCP uses `inline-snapshot` for testing complex data structures. On first run of `pytest --inline-snapshot=create` with an empty `snapshot()`, pytest will auto-populate the expected value. To update snapshots after intentional changes, run `pytest --inline-snapshot=fix`. This is particularly useful for testing JSON schemas and API responses.
|
||||
|
||||
```python
|
||||
from inline_snapshot import snapshot
|
||||
|
||||
async def test_tool_schema_generation():
|
||||
"""Test that tool schemas are generated correctly."""
|
||||
mcp = FastMCP("test-server")
|
||||
|
||||
@mcp.tool
|
||||
def calculate_tax(amount: float, rate: float = 0.1) -> dict:
|
||||
"""Calculate tax on an amount."""
|
||||
return {"amount": amount, "tax": amount * rate, "total": amount * (1 + rate)}
|
||||
|
||||
tools = mcp.list_tools()
|
||||
schema = tools[0].inputSchema
|
||||
|
||||
# First run: snapshot() is empty, gets auto-populated
|
||||
# Subsequent runs: compares against stored snapshot
|
||||
assert schema == snapshot({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount": {"type": "number"},
|
||||
"rate": {"type": "number", "default": 0.1}
|
||||
},
|
||||
"required": ["amount"]
|
||||
})
|
||||
```
|
||||
|
||||
### In-Memory Testing
|
||||
|
||||
FastMCP uses in-memory transport for testing, where servers and clients communicate directly. The majority of functionality can be tested in a deterministic fashion this way. We use more complex setups only when testing transports themselves.
|
||||
|
||||
The in-memory transport runs the real MCP protocol implementation without network overhead. Instead of deploying your server or managing network connections, you pass your server instance directly to the client. Everything runs in the same Python process - you can set breakpoints anywhere and step through with your debugger.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP, Client
|
||||
|
||||
# Create your server
|
||||
server = FastMCP("WeatherServer")
|
||||
|
||||
@server.tool
|
||||
def get_temperature(city: str) -> dict:
|
||||
"""Get current temperature for a city"""
|
||||
temps = {"NYC": 72, "LA": 85, "Chicago": 68}
|
||||
return {"city": city, "temp": temps.get(city, 70)}
|
||||
|
||||
async def test_weather_operations():
|
||||
# Pass server directly - no deployment needed
|
||||
async with Client(server) as client:
|
||||
result = await client.call_tool("get_temperature", {"city": "NYC"})
|
||||
assert result.data == {"city": "NYC", "temp": 72}
|
||||
```
|
||||
|
||||
This pattern makes tests deterministic and fast - typically completing in milliseconds rather than seconds.
|
||||
|
||||
### Mocking External Dependencies
|
||||
|
||||
FastMCP servers are standard Python objects, so you can mock external dependencies using your preferred approach:
|
||||
|
||||
```python
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
async def test_database_tool():
|
||||
server = FastMCP("DataServer")
|
||||
|
||||
# Mock the database
|
||||
mock_db = AsyncMock()
|
||||
mock_db.fetch_users.return_value = [
|
||||
{"id": 1, "name": "Alice"},
|
||||
{"id": 2, "name": "Bob"}
|
||||
]
|
||||
|
||||
@server.tool
|
||||
async def list_users() -> list:
|
||||
return await mock_db.fetch_users()
|
||||
|
||||
async with Client(server) as client:
|
||||
result = await client.call_tool("list_users", {})
|
||||
assert len(result.data) == 2
|
||||
assert result.data[0]["name"] == "Alice"
|
||||
mock_db.fetch_users.assert_called_once()
|
||||
```
|
||||
|
||||
### Testing Network Transports
|
||||
|
||||
While in-memory testing covers most unit testing needs, you'll occasionally need to test actual network transports. Use the `run_server_in_process` utility to spawn a server in a separate process for testing:
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from fastmcp.utilities.tests import run_server_in_process
|
||||
from fastmcp import FastMCP, Client
|
||||
from fastmcp.client.transports import StreamableHttpTransport
|
||||
|
||||
def run_server(host: str, port: int) -> None:
|
||||
"""Function to run in subprocess."""
|
||||
server = FastMCP("TestServer")
|
||||
|
||||
@server.tool
|
||||
def greet(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
server.run(host=host, port=port)
|
||||
|
||||
@pytest.fixture
|
||||
async def http_server():
|
||||
"""Fixture that runs server in subprocess."""
|
||||
with run_server_in_process(run_server, transport="http") as url:
|
||||
yield f"{url}/mcp"
|
||||
|
||||
async def test_http_transport(http_server: str):
|
||||
"""Test actual HTTP transport behavior."""
|
||||
async with Client(
|
||||
transport=StreamableHttpTransport(http_server)
|
||||
) as client:
|
||||
result = await client.ping()
|
||||
assert result is True
|
||||
|
||||
greeting = await client.call_tool("greet", {"name": "World"})
|
||||
assert greeting.data == "Hello, World!"
|
||||
```
|
||||
|
||||
The `run_server_in_process` utility handles server lifecycle, port allocation, and cleanup automatically. This pattern is essential for testing transport-specific behavior like timeouts, headers, and authentication. Note that FastMCP often uses the `client_process` marker to isolate tests that spawn processes, as they can create contention in CI.
|
||||
|
||||
### Documentation Testing
|
||||
|
||||
Documentation requires the same validation as code. The `just docs` command launches a local Mintlify server that renders your documentation exactly as users will see it:
|
||||
|
||||
```bash
|
||||
# Start local documentation server with hot reload
|
||||
just docs
|
||||
|
||||
# Or run Mintlify directly
|
||||
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.
|
||||
462
docs/docs.json
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"appearance": {
|
||||
"default": "system",
|
||||
"strict": false
|
||||
},
|
||||
"background": {
|
||||
"color": {
|
||||
"dark": "#222831",
|
||||
"light": "#EEEEEE"
|
||||
},
|
||||
"decoration": "windows"
|
||||
},
|
||||
"banner": {
|
||||
"content": "Host your server on [FastMCP Cloud](https://fastmcp.cloud) for free!"
|
||||
},
|
||||
"colors": {
|
||||
"dark": "#f72585",
|
||||
"light": "#4cc9f0",
|
||||
"primary": "#2d00f7"
|
||||
},
|
||||
"contextual": {
|
||||
"options": [
|
||||
"copy",
|
||||
"view"
|
||||
]
|
||||
},
|
||||
"description": "The fast, Pythonic way to build MCP servers and clients.",
|
||||
"favicon": {
|
||||
"dark": "/assets/brand/favicon.svg",
|
||||
"light": "/assets/brand/favicon.svg"
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"bluesky": "https://bsky.app/profile/jlowin.dev",
|
||||
"github": "https://github.com/jlowin/fastmcp",
|
||||
"x": "https://x.com/jlowin"
|
||||
}
|
||||
},
|
||||
"integrations": {
|
||||
"ga4": {
|
||||
"measurementId": "G-64R5W1TJXG"
|
||||
}
|
||||
},
|
||||
"interaction": {
|
||||
"drilldown": false
|
||||
},
|
||||
"logo": {
|
||||
"dark": "/assets/brand/wordmark-white.png",
|
||||
"light": "/assets/brand/wordmark.png"
|
||||
},
|
||||
"name": "FastMCP",
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"href": "https://fastmcp.cloud",
|
||||
"icon": "cloud",
|
||||
"label": "FastMCP Cloud"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"href": "https://github.com/jlowin/fastmcp",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"pages": [
|
||||
"getting-started/welcome",
|
||||
"getting-started/installation",
|
||||
"getting-started/quickstart"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Servers",
|
||||
"pages": [
|
||||
"servers/server",
|
||||
{
|
||||
"group": "Core Components",
|
||||
"icon": "toolbox",
|
||||
"pages": [
|
||||
"servers/tools",
|
||||
"servers/resources",
|
||||
"servers/prompts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Advanced Features",
|
||||
"icon": "stars",
|
||||
"pages": [
|
||||
"servers/context",
|
||||
"servers/proxy",
|
||||
"servers/composition",
|
||||
"servers/elicitation",
|
||||
"servers/logging",
|
||||
"servers/progress",
|
||||
"servers/sampling",
|
||||
"servers/middleware"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "shield-check",
|
||||
"pages": [
|
||||
"servers/auth/authentication",
|
||||
"servers/auth/token-verification",
|
||||
"servers/auth/remote-oauth",
|
||||
"servers/auth/oauth-proxy",
|
||||
"servers/auth/oidc-proxy",
|
||||
"servers/auth/full-oauth-server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Deployment",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"deployment/running-server",
|
||||
"deployment/server-configuration",
|
||||
"deployment/self-hosted",
|
||||
"deployment/fastmcp-cloud"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Clients",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Essentials",
|
||||
"icon": "cube",
|
||||
"pages": [
|
||||
"clients/client",
|
||||
"clients/transports"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Core Operations",
|
||||
"icon": "handshake",
|
||||
"pages": [
|
||||
"clients/tools",
|
||||
"clients/resources",
|
||||
"clients/prompts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Advanced Features",
|
||||
"icon": "stars",
|
||||
"pages": [
|
||||
"clients/elicitation",
|
||||
"clients/logging",
|
||||
"clients/progress",
|
||||
"clients/sampling",
|
||||
"clients/messages",
|
||||
"clients/roots"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "user-shield",
|
||||
"pages": [
|
||||
"clients/auth/oauth",
|
||||
"clients/auth/bearer"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "key",
|
||||
"pages": [
|
||||
"integrations/auth0",
|
||||
"integrations/authkit",
|
||||
"integrations/aws-cognito",
|
||||
"integrations/azure",
|
||||
"integrations/descope",
|
||||
"integrations/github",
|
||||
"integrations/scalekit",
|
||||
"integrations/google",
|
||||
"integrations/workos"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Authorization",
|
||||
"icon": "shield-check",
|
||||
"pages": [
|
||||
"integrations/eunomia-authorization",
|
||||
"integrations/permit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI Assistants",
|
||||
"icon": "robot",
|
||||
"pages": [
|
||||
"integrations/chatgpt",
|
||||
"integrations/claude-code",
|
||||
"integrations/claude-desktop",
|
||||
"integrations/cursor",
|
||||
"integrations/gemini-cli",
|
||||
"integrations/mcp-json-configuration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI SDKs",
|
||||
"icon": "code",
|
||||
"pages": [
|
||||
"integrations/anthropic",
|
||||
"integrations/gemini",
|
||||
"integrations/openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Web Frameworks",
|
||||
"icon": "globe",
|
||||
"pages": [
|
||||
"integrations/asgi",
|
||||
"integrations/fastapi",
|
||||
"integrations/openapi"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Patterns",
|
||||
"pages": [
|
||||
"patterns/tool-transformation",
|
||||
"patterns/decorating-methods",
|
||||
"patterns/cli",
|
||||
"patterns/contrib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Development",
|
||||
"pages": [
|
||||
"development/contributing",
|
||||
"development/tests",
|
||||
"development/releases"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tab": "Documentation"
|
||||
},
|
||||
{
|
||||
"pages": [
|
||||
"updates",
|
||||
"changelog"
|
||||
],
|
||||
"tab": "What's New"
|
||||
},
|
||||
{
|
||||
"anchors": [
|
||||
{
|
||||
"anchor": "Python SDK",
|
||||
"icon": "python",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-exceptions",
|
||||
"python-sdk/fastmcp-mcp_config",
|
||||
"python-sdk/fastmcp-settings",
|
||||
{
|
||||
"group": "fastmcp.cli",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-cli-__init__",
|
||||
"python-sdk/fastmcp-cli-claude",
|
||||
"python-sdk/fastmcp-cli-cli",
|
||||
{
|
||||
"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-mcp_json",
|
||||
"python-sdk/fastmcp-cli-install-shared"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-cli-run"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"python-sdk/fastmcp-client-oauth_callback",
|
||||
"python-sdk/fastmcp-client-progress",
|
||||
"python-sdk/fastmcp-client-roots",
|
||||
"python-sdk/fastmcp-client-sampling",
|
||||
"python-sdk/fastmcp-client-transports"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.prompts",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-prompts-__init__",
|
||||
"python-sdk/fastmcp-prompts-prompt",
|
||||
"python-sdk/fastmcp-prompts-prompt_manager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.resources",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-resources-__init__",
|
||||
"python-sdk/fastmcp-resources-resource",
|
||||
"python-sdk/fastmcp-resources-resource_manager",
|
||||
"python-sdk/fastmcp-resources-template",
|
||||
"python-sdk/fastmcp-resources-types"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.server",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-__init__",
|
||||
{
|
||||
"group": "auth",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-__init__",
|
||||
"python-sdk/fastmcp-server-auth-auth",
|
||||
"python-sdk/fastmcp-server-auth-oauth_proxy",
|
||||
"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-bearer",
|
||||
"python-sdk/fastmcp-server-auth-providers-descope",
|
||||
"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-jwt",
|
||||
"python-sdk/fastmcp-server-auth-providers-scalekit",
|
||||
"python-sdk/fastmcp-server-auth-providers-workos"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-auth-redirect_validation"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-context",
|
||||
"python-sdk/fastmcp-server-dependencies",
|
||||
"python-sdk/fastmcp-server-elicitation",
|
||||
"python-sdk/fastmcp-server-http",
|
||||
"python-sdk/fastmcp-server-low_level",
|
||||
{
|
||||
"group": "middleware",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-middleware-__init__",
|
||||
"python-sdk/fastmcp-server-middleware-error_handling",
|
||||
"python-sdk/fastmcp-server-middleware-logging",
|
||||
"python-sdk/fastmcp-server-middleware-middleware",
|
||||
"python-sdk/fastmcp-server-middleware-rate_limiting",
|
||||
"python-sdk/fastmcp-server-middleware-timing"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-openapi",
|
||||
"python-sdk/fastmcp-server-proxy",
|
||||
"python-sdk/fastmcp-server-server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.tools",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-tools-__init__",
|
||||
"python-sdk/fastmcp-tools-tool",
|
||||
"python-sdk/fastmcp-tools-tool_manager",
|
||||
"python-sdk/fastmcp-tools-tool_transform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.utilities",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-__init__",
|
||||
"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-logging",
|
||||
"python-sdk/fastmcp-utilities-mcp_config",
|
||||
{
|
||||
"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-openapi",
|
||||
"python-sdk/fastmcp-utilities-storage",
|
||||
"python-sdk/fastmcp-utilities-tests",
|
||||
"python-sdk/fastmcp-utilities-types"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tab": "SDK Reference"
|
||||
}
|
||||
]
|
||||
},
|
||||
"redirects": [
|
||||
{
|
||||
"destination": "/servers/proxy",
|
||||
"source": "/patterns/proxy"
|
||||
},
|
||||
{
|
||||
"destination": "/servers/composition",
|
||||
"source": "/patterns/composition"
|
||||
}
|
||||
],
|
||||
"search": {
|
||||
"prompt": "Search the docs..."
|
||||
},
|
||||
"theme": "mint",
|
||||
"thumbnails": {
|
||||
"appearance": "light",
|
||||
"background": "/assets/brand/thumbnail-background.png"
|
||||
}
|
||||
}
|
||||
82
docs/getting-started/installation.mdx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
title: Installation
|
||||
icon: arrow-down-to-line
|
||||
---
|
||||
## Install FastMCP
|
||||
|
||||
We recommend using [uv](https://docs.astral.sh/uv/getting-started/installation/) to install and manage FastMCP.
|
||||
|
||||
If you plan to use FastMCP in your project, you can add it as a dependency with:
|
||||
|
||||
```bash
|
||||
uv add fastmcp
|
||||
```
|
||||
|
||||
Alternatively, you can install it directly with `pip` or `uv pip`:
|
||||
<CodeGroup>
|
||||
```bash uv
|
||||
uv pip install fastmcp
|
||||
```
|
||||
|
||||
```bash pip
|
||||
pip install fastmcp
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Verify Installation
|
||||
|
||||
To verify that FastMCP is installed correctly, you can run the following command:
|
||||
|
||||
```bash
|
||||
fastmcp version
|
||||
```
|
||||
|
||||
You should see output like the following:
|
||||
|
||||
```bash
|
||||
$ fastmcp version
|
||||
|
||||
FastMCP version: 2.11.3
|
||||
MCP version: 1.12.4
|
||||
Python version: 3.12.2
|
||||
Platform: macOS-15.3.1-arm64-arm-64bit
|
||||
FastMCP root path: ~/Developer/fastmcp
|
||||
```
|
||||
## Upgrading from the Official MCP SDK
|
||||
|
||||
Upgrading from the official MCP SDK's FastMCP 1.0 to FastMCP 2.0 is generally straightforward. The core server API is highly compatible, and in many cases, changing your import statement from `from mcp.server.fastmcp import FastMCP` to `from fastmcp import FastMCP` will be sufficient.
|
||||
|
||||
|
||||
```python {5}
|
||||
# Before
|
||||
# from mcp.server.fastmcp import FastMCP
|
||||
|
||||
# After
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Prior to `fastmcp==2.3.0` and `mcp==1.8.0`, the 2.x API always mirrored the official 1.0 API. However, as the projects diverge, this can not be guaranteed. You may see deprecation warnings if you attempt to use 1.0 APIs in FastMCP 2.x. Please refer to this documentation for details on new capabilities.
|
||||
</Warning>
|
||||
|
||||
## Versioning Policy
|
||||
|
||||
FastMCP follows semantic versioning with pragmatic adaptations for the rapidly evolving MCP ecosystem. Breaking changes may occur in minor versions (e.g., 2.3.x to 2.4.0) when necessary to stay current with the MCP Protocol.
|
||||
|
||||
For production use, always pin to exact versions:
|
||||
```
|
||||
fastmcp==2.11.0 # Good
|
||||
fastmcp>=2.11.0 # Bad - will install breaking changes
|
||||
```
|
||||
|
||||
See the full [versioning and release policy](/development/releases#versioning-policy) for details on our public API, deprecation practices, and breaking change philosophy.
|
||||
|
||||
## Contributing to FastMCP
|
||||
|
||||
Interested in contributing to FastMCP? See the [Contributing Guide](/development/contributing) for details on:
|
||||
- Setting up your development environment
|
||||
- Running tests and pre-commit hooks
|
||||
- Submitting issues and pull requests
|
||||
- Code standards and review process
|
||||
136
docs/getting-started/quickstart.mdx
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
---
|
||||
title: Quickstart
|
||||
icon: rocket-launch
|
||||
---
|
||||
|
||||
Welcome! This guide will help you quickly set up FastMCP, run your first MCP server, and deploy a server to FastMCP Cloud.
|
||||
|
||||
If you haven't already installed FastMCP, follow the [installation instructions](/getting-started/installation).
|
||||
|
||||
## Create a FastMCP Server
|
||||
|
||||
A FastMCP server is a collection of tools, resources, and other MCP components. To create a server, start by instantiating the `FastMCP` class.
|
||||
|
||||
Create a new file called `my_server.py` and add the following code:
|
||||
|
||||
```python my_server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
```
|
||||
|
||||
|
||||
That's it! You've created a FastMCP server, albeit a very boring one. Let's add a tool to make it more interesting.
|
||||
|
||||
|
||||
## Add a Tool
|
||||
|
||||
To add a tool that returns a simple greeting, write a function and decorate it with `@mcp.tool` to register it with the server:
|
||||
|
||||
```python my_server.py {5-7}
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
```
|
||||
|
||||
|
||||
## Run the Server
|
||||
|
||||
The simplest way to run your FastMCP server is to call its `run()` method. You can choose between different transports, like `stdio` for local servers, or `http` for remote access:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python my_server.py (stdio) {9, 10}
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
```python my_server.py (HTTP) {9, 10}
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("My MCP Server")
|
||||
|
||||
@mcp.tool
|
||||
def greet(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="http", port=8000)
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
This lets us run the server with `python my_server.py`. The stdio transport is the traditional way to connect MCP servers to clients, while the HTTP transport enables remote connections.
|
||||
|
||||
<Tip>
|
||||
Why do we need the `if __name__ == "__main__":` block?
|
||||
|
||||
The `__main__` block is recommended for consistency and compatibility, ensuring your server works with all MCP clients that execute your server file as a script. Users who will exclusively run their server with the FastMCP CLI can omit it, as the CLI imports the server object directly.
|
||||
</Tip>
|
||||
|
||||
### Using the FastMCP CLI
|
||||
|
||||
You can also use the `fastmcp run` command to start your server. Note that the FastMCP CLI **does not** execute the `__main__` block of your server file. Instead, it imports your server object and runs it with whatever transport and options you provide.
|
||||
|
||||
For example, to run this server with the default stdio transport (no matter how you called `mcp.run()`), you can use the following command:
|
||||
```bash
|
||||
fastmcp run my_server.py:mcp
|
||||
```
|
||||
|
||||
To run this server with the HTTP transport, you can use the following command:
|
||||
```bash
|
||||
fastmcp run my_server.py:mcp --transport http --port 8000
|
||||
```
|
||||
|
||||
## Call Your Server
|
||||
|
||||
Once your server is running with HTTP transport, you can connect to it with a FastMCP client or any LLM client that supports the MCP protocol:
|
||||
|
||||
```python my_client.py
|
||||
import asyncio
|
||||
from fastmcp import Client
|
||||
|
||||
client = Client("http://localhost:8000/mcp")
|
||||
|
||||
async def call_tool(name: str):
|
||||
async with client:
|
||||
result = await client.call_tool("greet", {"name": name})
|
||||
print(result)
|
||||
|
||||
asyncio.run(call_tool("Ford"))
|
||||
```
|
||||
|
||||
Note that:
|
||||
- FastMCP clients are asynchronous, so we need to use `asyncio.run` to run the client
|
||||
- We must enter a client context (`async with client:`) before using the client
|
||||
- You can make multiple client calls within the same context
|
||||
|
||||
## Deploy to FastMCP Cloud
|
||||
|
||||
[FastMCP Cloud](https://fastmcp.cloud) is a hosting service run by the FastMCP team at [Prefect](https://www.prefect.io/fastmcp). It is optimized to deploy authenticated FastMCP servers as quickly as possible, giving you a secure URL that you can plug into any LLM client.
|
||||
|
||||
<Info>
|
||||
FastMCP Cloud is **free for personal servers** and offers simple pay-as-you-go pricing for teams.
|
||||
</Info>
|
||||
|
||||
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 [FastMCP Cloud](https://fastmcp.cloud) 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! FastMCP Cloud will build and deploy your server, making it available at a URL like `https://your-project.fastmcp.app/mcp`. You can chat with it to test its functionality, or connect to it from any LLM client that supports the MCP protocol.
|
||||
|
||||
For more details, see the [FastMCP Cloud guide](/deployment/fastmcp-cloud).
|
||||
110
docs/getting-started/welcome.mdx
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
title: "Welcome to FastMCP 2.0!"
|
||||
sidebarTitle: "Welcome!"
|
||||
description: The fast, Pythonic way to build MCP servers and clients.
|
||||
icon: hand-wave
|
||||
---
|
||||
<img
|
||||
src="/assets/brand/f-watercolor-waves.png"
|
||||
|
||||
alt="'F' logo on a watercolor background"
|
||||
noZoom
|
||||
className="rounded-2xl block dark:hidden"
|
||||
/>
|
||||
<img
|
||||
src="/assets/brand/f-watercolor-waves-dark.png"
|
||||
alt="'F' logo on a watercolor background"
|
||||
noZoom
|
||||
className="rounded-2xl hidden dark:block"
|
||||
/>
|
||||
|
||||
|
||||
**FastMCP is the standard framework for building MCP applications.** The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) provides a standardized way to connect LLMs to tools and data, and FastMCP makes it production-ready with clean, Pythonic code:
|
||||
|
||||
```python {1}
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Demo 🚀")
|
||||
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
return a + b
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
|
||||
## Beyond Basic MCP
|
||||
|
||||
FastMCP pioneered Python MCP development, with FastMCP 1.0 being incorporated into the [official MCP SDK](https://github.com/modelcontextprotocol/python-sdk) in 2024.
|
||||
|
||||
**This is FastMCP 2.0** — the actively maintained version that extends far beyond basic protocol implementation. While the SDK provides core functionality, FastMCP 2.0 delivers everything needed for production: advanced MCP patterns (server composition, proxying, OpenAPI/FastAPI generation, tool transformation), enterprise auth (Google, GitHub, Azure, Auth0, WorkOS, and more), deployment tools, testing frameworks, and comprehensive client libraries.
|
||||
|
||||
Ready to build? Start with our [installation guide](/getting-started/installation) or jump straight to the [quickstart](/getting-started/quickstart).
|
||||
|
||||
|
||||
## What is MCP?
|
||||
|
||||
The Model Context Protocol lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. It is often described as "the USB-C port for AI", providing a uniform way to connect LLMs to resources they can use. It may be easier to think of it as an API, but specifically designed for LLM interactions. MCP servers can:
|
||||
|
||||
- Expose data through `Resources` (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
|
||||
- Provide functionality through `Tools` (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
|
||||
- Define interaction patterns through `Prompts` (reusable templates for LLM interactions)
|
||||
- And more!
|
||||
|
||||
FastMCP provides a high-level, Pythonic interface for building, managing, and interacting with these servers.
|
||||
|
||||
## Why FastMCP?
|
||||
|
||||
FastMCP handles all the complex protocol details so you can focus on building. In most cases, decorating a Python function is all you need — FastMCP handles the rest.
|
||||
|
||||
🚀 **Fast**: High-level interface means less code and faster development
|
||||
|
||||
🍀 **Simple**: Build MCP servers with minimal boilerplate
|
||||
|
||||
🐍 **Pythonic**: Feels natural to Python developers
|
||||
|
||||
🔍 **Complete**: Everything for production — enterprise auth (Google, GitHub, Azure, Auth0, WorkOS), deployment tools, testing frameworks, client libraries, and more
|
||||
|
||||
FastMCP provides the shortest path from idea to production. Deploy locally, to the cloud with [FastMCP Cloud](https://fastmcp.cloud) (free for personal servers), or to your own infrastructure.
|
||||
|
||||
FastMCP is made with 💙 by [Prefect](https://www.prefect.io/).
|
||||
|
||||
|
||||
|
||||
## LLM-Friendly Docs
|
||||
|
||||
The FastMCP documentation is available in multiple LLM-friendly formats:
|
||||
|
||||
### MCP Server
|
||||
|
||||
The FastMCP docs are accessible via MCP! The server URL is `https://gofastmcp.com/mcp`.
|
||||
|
||||
In fact, you can use FastMCP to search the FastMCP docs:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from fastmcp import Client
|
||||
|
||||
async def main():
|
||||
async with Client("https://gofastmcp.com/mcp") as client:
|
||||
result = await client.call_tool(
|
||||
name="SearchFastMcp",
|
||||
arguments={"query": "deploy a FastMCP server"}
|
||||
)
|
||||
print(result)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Plain Text Formats
|
||||
|
||||
The docs are also available in [llms.txt format](https://llmstxt.org/):
|
||||
- [llms.txt](https://gofastmcp.com/llms.txt) - A sitemap listing all documentation pages
|
||||
- [llms-full.txt](https://gofastmcp.com/llms-full.txt) - The entire documentation in one file (may exceed context windows)
|
||||
|
||||
Any page can be accessed as markdown by appending `.md` to the URL. For example, this page becomes `https://gofastmcp.com/getting-started/welcome.md`.
|
||||
|
||||
You can also copy any page as markdown by pressing "Cmd+C" (or "Ctrl+C" on Windows) on your keyboard.
|
||||
228
docs/integrations/anthropic.mdx
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
---
|
||||
title: Anthropic API 🤝 FastMCP
|
||||
sidebarTitle: Anthropic API
|
||||
description: Connect FastMCP servers to the Anthropic API
|
||||
icon: message-code
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
|
||||
Anthropic's [Messages API](https://docs.anthropic.com/en/api/messages) supports MCP servers as remote tool sources. This tutorial will show you how to create a FastMCP server and deploy it to a public URL, then how to call it from the Messages API.
|
||||
|
||||
<Tip>
|
||||
Currently, the MCP connector only accesses **tools** from MCP servers—it queries the `list_tools` endpoint and exposes those functions to Claude. Other MCP features like resources and prompts are not currently supported. You can read more about the MCP connector in the [Anthropic documentation](https://docs.anthropic.com/en/docs/agents-and-tools/mcp-connector).
|
||||
</Tip>
|
||||
|
||||
## Create a Server
|
||||
|
||||
First, create a FastMCP server with the tools you want to expose. For this example, we'll create a server with a single tool that rolls dice.
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
## Deploy the Server
|
||||
|
||||
Your server must be deployed to a public URL in order for Anthropic to access it. The MCP connector supports both SSE and Streamable HTTP transports.
|
||||
|
||||
For development, you can use tools like `ngrok` to temporarily expose a locally-running server to the internet. We'll do that for this example (you may need to install `ngrok` and create a free account), but you can use any other method to deploy your server.
|
||||
|
||||
Assuming you saved the above code as `server.py`, you can run the following two commands in two separate terminals to deploy your server and expose it to the internet:
|
||||
|
||||
<CodeGroup>
|
||||
```bash FastMCP server
|
||||
python server.py
|
||||
```
|
||||
|
||||
```bash ngrok
|
||||
ngrok http 8000
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Warning>
|
||||
This exposes your unauthenticated server to the internet. Only run this command in a safe environment if you understand the risks.
|
||||
</Warning>
|
||||
|
||||
## Call the Server
|
||||
|
||||
To use the Messages API with MCP servers, you'll need to install the Anthropic Python SDK (not included with FastMCP):
|
||||
|
||||
```bash
|
||||
pip install anthropic
|
||||
```
|
||||
|
||||
You'll also need to authenticate with Anthropic. You can do this by setting the `ANTHROPIC_API_KEY` environment variable. Consult the Anthropic SDK documentation for more information.
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
Here is an example of how to call your server from Python. Note that you'll need to replace `https://your-server-url.com` with the actual URL of your server. In addition, we use `/mcp/` as the endpoint because we deployed a streamable-HTTP server with the default path; you may need to use a different endpoint if you customized your server's deployment. **At this time you must also include the `extra_headers` parameter with the `anthropic-beta` header.**
|
||||
|
||||
```python {5, 13-22}
|
||||
import anthropic
|
||||
from rich import print
|
||||
|
||||
# Your server URL (replace with your actual URL)
|
||||
url = 'https://your-server-url.com'
|
||||
|
||||
client = anthropic.Anthropic()
|
||||
|
||||
response = client.beta.messages.create(
|
||||
model="claude-sonnet-4-20250514",
|
||||
max_tokens=1000,
|
||||
messages=[{"role": "user", "content": "Roll a few dice!"}],
|
||||
mcp_servers=[
|
||||
{
|
||||
"type": "url",
|
||||
"url": f"{url}/mcp/",
|
||||
"name": "dice-server",
|
||||
}
|
||||
],
|
||||
extra_headers={
|
||||
"anthropic-beta": "mcp-client-2025-04-04"
|
||||
}
|
||||
)
|
||||
|
||||
print(response.content)
|
||||
```
|
||||
|
||||
If you run this code, you'll see something like the following output:
|
||||
|
||||
```text
|
||||
I'll roll some dice for you! Let me use the dice rolling tool.
|
||||
|
||||
I rolled 3 dice and got: 4, 2, 6
|
||||
|
||||
The results were 4, 2, and 6. Would you like me to roll again or roll a different number of dice?
|
||||
```
|
||||
|
||||
|
||||
## Authentication
|
||||
|
||||
<VersionBadge version="2.6.0" />
|
||||
|
||||
The MCP connector supports OAuth authentication through authorization tokens, which means you can secure your server while still allowing Anthropic to access it.
|
||||
|
||||
### Server Authentication
|
||||
|
||||
The simplest way to add authentication to the server is to use a bearer token scheme.
|
||||
|
||||
For this example, we'll quickly generate our own tokens with FastMCP's `RSAKeyPair` utility, but this may not be appropriate for production use. For more details, see the complete server-side [Token Verification](/servers/auth/token-verification) documentation.
|
||||
|
||||
We'll start by creating an RSA key pair to sign and verify tokens.
|
||||
|
||||
```python
|
||||
from fastmcp.server.auth.providers.jwt import RSAKeyPair
|
||||
|
||||
key_pair = RSAKeyPair.generate()
|
||||
access_token = key_pair.create_token(audience="dice-server")
|
||||
```
|
||||
|
||||
<Warning>
|
||||
FastMCP's `RSAKeyPair` utility is for development and testing only.
|
||||
</Warning>
|
||||
|
||||
Next, we'll create a `JWTVerifier` to authenticate the server.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth import JWTVerifier
|
||||
|
||||
auth = JWTVerifier(
|
||||
public_key=key_pair.public_key,
|
||||
audience="dice-server",
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="Dice Roller", auth=auth)
|
||||
```
|
||||
|
||||
Here is a complete example that you can copy/paste. For simplicity and the purposes of this example only, it will print the token to the console. **Do NOT do this in production!**
|
||||
|
||||
```python server.py [expandable]
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth import JWTVerifier
|
||||
from fastmcp.server.auth.providers.jwt import RSAKeyPair
|
||||
import random
|
||||
|
||||
key_pair = RSAKeyPair.generate()
|
||||
access_token = key_pair.create_token(audience="dice-server")
|
||||
|
||||
auth = JWTVerifier(
|
||||
public_key=key_pair.public_key,
|
||||
audience="dice-server",
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="Dice Roller", auth=auth)
|
||||
|
||||
@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__":
|
||||
print(f"\n---\n\n🔑 Dice Roller access token:\n\n{access_token}\n\n---\n")
|
||||
mcp.run(transport="http", port=8000)
|
||||
```
|
||||
|
||||
### Client Authentication
|
||||
|
||||
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
|
||||
Error code: 400 - {
|
||||
"type": "error",
|
||||
"error": {
|
||||
"type": "invalid_request_error",
|
||||
"message": "MCP server 'dice-server' requires authentication. Please provide an authorization_token.",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
To authenticate the client, you can pass the token using the `authorization_token` parameter in your MCP server configuration:
|
||||
|
||||
```python {8, 21}
|
||||
import anthropic
|
||||
from rich import print
|
||||
|
||||
# Your server URL (replace with your actual URL)
|
||||
url = 'https://your-server-url.com'
|
||||
|
||||
# Your access token (replace with your actual token)
|
||||
access_token = 'your-access-token'
|
||||
|
||||
client = anthropic.Anthropic()
|
||||
|
||||
response = client.beta.messages.create(
|
||||
model="claude-sonnet-4-20250514",
|
||||
max_tokens=1000,
|
||||
messages=[{"role": "user", "content": "Roll a few dice!"}],
|
||||
mcp_servers=[
|
||||
{
|
||||
"type": "url",
|
||||
"url": f"{url}/mcp/",
|
||||
"name": "dice-server",
|
||||
"authorization_token": access_token
|
||||
}
|
||||
],
|
||||
extra_headers={
|
||||
"anthropic-beta": "mcp-client-2025-04-04"
|
||||
}
|
||||
)
|
||||
|
||||
print(response.content)
|
||||
```
|
||||
|
||||
You should now see the dice roll results in the output.
|
||||
213
docs/integrations/asgi.mdx
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
---
|
||||
title: ASGI / Starlette 🤝 FastMCP
|
||||
sidebarTitle: ASGI / Starlette
|
||||
description: Integrate FastMCP servers into ASGI applications
|
||||
icon: server
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.3.1" />
|
||||
|
||||
FastMCP servers can be integrated into existing ASGI applications, allowing you to add MCP functionality to your web applications. This is useful for:
|
||||
|
||||
- Adding MCP functionality to an existing website or API
|
||||
- Mounting MCP servers under specific URL paths
|
||||
- Combining multiple services in a single application
|
||||
- Leveraging existing authentication and middleware
|
||||
|
||||
## Basic Usage
|
||||
|
||||
To integrate a FastMCP server into an ASGI application, use the `http_app()` method to obtain a Starlette application instance:
|
||||
|
||||
<Tip>
|
||||
The `http_app()` method is new in FastMCP 2.3.2. In older versions, use `sse_app()` for SSE transport or `streamable_http_app()` for Streamable HTTP transport.
|
||||
</Tip>
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.tool
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
# Get a Starlette app instance for Streamable HTTP transport (recommended)
|
||||
http_app = mcp.http_app()
|
||||
|
||||
# For legacy SSE transport (deprecated)
|
||||
sse_app = mcp.http_app(transport="sse")
|
||||
```
|
||||
|
||||
The returned Starlette application can be integrated with other ASGI-compatible web frameworks. The MCP server's endpoint is mounted at `/mcp/` for Streamable HTTP transport and `/sse/` for SSE transport.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
You can customize the endpoint path and access the FastMCP server instance:
|
||||
|
||||
```python
|
||||
# Custom endpoint path
|
||||
http_app = mcp.http_app(path="/custom-mcp-path")
|
||||
|
||||
# Access the FastMCP server from middleware/routes
|
||||
# The server is available at: request.app.state.fastmcp_server
|
||||
```
|
||||
|
||||
### Adding Custom Routes
|
||||
|
||||
You can add custom web routes directly to your FastMCP server using the `@custom_route` decorator:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.custom_route("/api/status", methods=["GET"])
|
||||
async def get_status(request: Request):
|
||||
return JSONResponse({"server": "running"})
|
||||
|
||||
http_app = mcp.http_app()
|
||||
```
|
||||
|
||||
#### Health Check Endpoints
|
||||
|
||||
Health checks are commonly needed for monitoring and load balancing:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.custom_route("/health", methods=["GET"])
|
||||
async def health_check(request: Request):
|
||||
return JSONResponse({"status": "healthy"})
|
||||
|
||||
http_app = mcp.http_app()
|
||||
```
|
||||
|
||||
The health endpoint will be available at `/health` alongside your MCP endpoint at `/mcp/`.
|
||||
|
||||
## Starlette Integration
|
||||
|
||||
Mount your FastMCP server in another Starlette application:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from starlette.applications import Starlette
|
||||
from starlette.routing import Mount
|
||||
|
||||
# Create your FastMCP server
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
@mcp.tool
|
||||
def analyze(data: str) -> dict:
|
||||
return {"result": f"Analyzed: {data}"}
|
||||
|
||||
# Create the ASGI app
|
||||
mcp_app = mcp.http_app(path='/mcp')
|
||||
|
||||
# Create a Starlette app and mount the MCP server
|
||||
app = Starlette(
|
||||
routes=[
|
||||
Mount("/mcp-server", app=mcp_app),
|
||||
# Add other routes as needed
|
||||
],
|
||||
lifespan=mcp_app.lifespan,
|
||||
)
|
||||
```
|
||||
|
||||
The MCP endpoint will be available at `/mcp-server/mcp/` of the resulting Starlette app.
|
||||
|
||||
<Warning>
|
||||
For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
|
||||
</Warning>
|
||||
|
||||
### Nested Mounts
|
||||
|
||||
You can create complex routing structures by nesting mounts:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from starlette.applications import Starlette
|
||||
from starlette.routing import Mount
|
||||
|
||||
# Create your FastMCP server
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
# Create the ASGI app
|
||||
mcp_app = mcp.http_app(path='/mcp')
|
||||
|
||||
# Create nested application structure
|
||||
inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
|
||||
app = Starlette(
|
||||
routes=[Mount("/outer", app=inner_app)],
|
||||
lifespan=mcp_app.lifespan,
|
||||
)
|
||||
```
|
||||
|
||||
In this setup, the MCP server is accessible at the `/outer/inner/mcp/` path.
|
||||
|
||||
## Custom Middleware
|
||||
|
||||
<VersionBadge version="2.3.2" />
|
||||
|
||||
Add custom Starlette middleware to your FastMCP ASGI apps by passing a list of middleware instances:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from starlette.middleware import Middleware
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
|
||||
# Create your FastMCP server
|
||||
mcp = FastMCP("MyServer")
|
||||
|
||||
# Define custom middleware
|
||||
custom_middleware = [
|
||||
Middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
]
|
||||
|
||||
# Create ASGI app with middleware
|
||||
http_app = mcp.http_app(custom_middleware=custom_middleware)
|
||||
```
|
||||
|
||||
## Running the Server
|
||||
|
||||
To run your ASGI application, use an ASGI server like `uvicorn`:
|
||||
|
||||
```python
|
||||
import uvicorn
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
Or from the command line:
|
||||
|
||||
```bash
|
||||
uvicorn path.to.your.app:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
## Framework-Specific Integration
|
||||
|
||||
### FastAPI
|
||||
|
||||
For FastAPI-specific integration patterns including both mounting MCP servers into FastAPI apps and generating MCP servers from FastAPI apps, see the [FastAPI Integration guide](/integrations/fastapi).
|
||||
|
||||
### Other ASGI Frameworks
|
||||
|
||||
The patterns shown here work with any ASGI-compatible framework. The key requirements are:
|
||||
|
||||
1. Mount the FastMCP ASGI app at your desired path
|
||||
2. Pass the lifespan context to your root application
|
||||
3. Configure any necessary middleware or authentication
|
||||
|
||||
231
docs/integrations/auth0.mdx
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
---
|
||||
title: Auth0 OAuth 🤝 FastMCP
|
||||
sidebarTitle: Auth0
|
||||
description: Secure your FastMCP server with Auth0 OAuth
|
||||
icon: shield-check
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.12.4" />
|
||||
|
||||
This guide shows you how to secure your FastMCP server using **Auth0 OAuth**. While Auth0 does have support for Dynamic Client Registration, it is not enabled by default so this integration uses the [**OIDC Proxy**](/servers/auth/oidc-proxy) pattern to bridge Auth0's dynamic OIDC configuration with MCP's authentication requirements.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you will need:
|
||||
1. An **[Auth0 Account](https://auth0.com/)** with access to create Applications
|
||||
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
|
||||
|
||||
### Step 1: Create an Auth0 Application
|
||||
|
||||
Create an Application in your Auth0 settings to get the credentials needed for authentication:
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to Applications">
|
||||
Go to **Applications → Applications** in your Auth0 account.
|
||||
|
||||
Click **"+ Create Application"** to create a new application.
|
||||
</Step>
|
||||
|
||||
<Step title="Create Your Application">
|
||||
- **Name**: Choose a name users will recognize (e.g., "My FastMCP Server")
|
||||
- **Choose an application type**: Choose "Single Page Web Applications"
|
||||
- Click **Create** to create the application
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Your Application">
|
||||
Select the "Settings" tab for your application, then find the "Application URIs" section.
|
||||
|
||||
- **Allowed Callback URLs**: Your server URL + `/auth/callback` (e.g., `http://localhost:8000/auth/callback`)
|
||||
- Click **Save** to save your changes
|
||||
|
||||
<Warning>
|
||||
The callback URL must match exactly. The default path is `/auth/callback`, but you can customize it using the `redirect_path` parameter.
|
||||
</Warning>
|
||||
|
||||
<Tip>
|
||||
If you want to use a custom callback path (e.g., `/auth/auth0/callback`), make sure to set the same path in both your Auth0 Application settings and the `redirect_path` parameter when configuring the Auth0Provider.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Save Your Credentials">
|
||||
After creating the app, in the "Basic Information" section you'll see:
|
||||
|
||||
- **Client ID**: A public identifier like `tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB`
|
||||
- **Client Secret**: A private hidden value that should always be stored securely
|
||||
|
||||
<Tip>
|
||||
Store these credentials securely. Never commit them to version control. Use environment variables or a secrets manager in production.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Select Your Audience">
|
||||
Go to **Applications → APIs** in your Auth0 account.
|
||||
|
||||
- Find the API that you want to use for your application
|
||||
- **API Audience**: A URL that uniquely identifies the API
|
||||
|
||||
<Tip>
|
||||
Store this along with of the credentials above. Never commit this to version control. Use environment variables or a secrets manager in production.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 2: FastMCP Configuration
|
||||
|
||||
Create your FastMCP server using the `Auth0Provider`.
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.auth0 import Auth0Provider
|
||||
|
||||
# The Auth0Provider utilizes Auth0 OIDC configuration
|
||||
auth_provider = Auth0Provider(
|
||||
config_url="https://.../.well-known/openid-configuration", # Your Auth0 configuration URL
|
||||
client_id="tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB", # Your Auth0 application Client ID
|
||||
client_secret="vPYqbjemq...", # Your Auth0 application Client Secret
|
||||
audience="https://...", # Your Auth0 API audience
|
||||
base_url="http://localhost:8000", # Must match your application configuration
|
||||
# redirect_path="/auth/callback" # Default value, customize if needed
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="Auth0 Secured App", auth=auth_provider)
|
||||
|
||||
# Add a protected tool to test authentication
|
||||
@mcp.tool
|
||||
async def get_token_info() -> dict:
|
||||
"""Returns information about the Auth0 token."""
|
||||
from fastmcp.server.dependencies import get_access_token
|
||||
|
||||
token = get_access_token()
|
||||
|
||||
return {
|
||||
"issuer": token.claims.get("iss"),
|
||||
"audience": token.claims.get("aud"),
|
||||
"scope": token.claims.get("scope")
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Running the Server
|
||||
|
||||
Start your FastMCP server with HTTP transport to enable OAuth flows:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py --transport http --port 8000
|
||||
```
|
||||
|
||||
Your server is now running and protected by Auth0 authentication.
|
||||
|
||||
### Testing with a Client
|
||||
|
||||
Create a test client that authenticates with your Auth0-protected server:
|
||||
|
||||
```python test_client.py
|
||||
from fastmcp import Client
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
# The client will automatically handle Auth0 OAuth flows
|
||||
async with Client("http://localhost:8000/mcp/", auth="oauth") as client:
|
||||
# First-time connection will open Auth0 login in your browser
|
||||
print("✓ Authenticated with Auth0!")
|
||||
|
||||
# Test the protected tool
|
||||
result = await client.call_tool("get_token_info")
|
||||
print(f"Auth0 audience: {result['audience']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
When you run the client for the first time:
|
||||
1. Your browser will open to Auth0's authorization page
|
||||
2. After you authorize the app, you'll be redirected back
|
||||
3. The client receives the token and can make authenticated requests
|
||||
|
||||
<Info>
|
||||
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
|
||||
</Info>
|
||||
|
||||
## Environment Variables
|
||||
|
||||
For production deployments, use environment variables instead of hardcoding credentials.
|
||||
|
||||
### Provider Selection
|
||||
|
||||
Setting this environment variable allows the Auth0 provider to be used automatically without explicitly instantiating it in code.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
|
||||
Set to `fastmcp.server.auth.providers.auth0.Auth0Provider` to use Auth0 authentication.
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
### Auth0-Specific Configuration
|
||||
|
||||
These environment variables provide default values for the Auth0 provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_CONFIG_URL" required>
|
||||
Your Auth0 Application Configuration URL (e.g., `https://.../.well-known/openid-configuration`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_CLIENT_ID" required>
|
||||
Your Auth0 Application Client ID (e.g., `tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_CLIENT_SECRET" required>
|
||||
Your Auth0 Application Client Secret (e.g., `vPYqbjemq...`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_AUDIENCE" required>
|
||||
Your Auth0 API Audience
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_BASE_URL" required>
|
||||
Public URL of your FastMCP server for OAuth callbacks
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_REDIRECT_PATH" default="/auth/callback">
|
||||
Redirect path configured in your Auth0 Application
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTH0_REQUIRED_SCOPES" default='["openid"]'>
|
||||
Comma-, space-, or JSON-separated list of required AUth0 scopes (e.g., `openid email` or `["openid","email"]`)
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
Example `.env` file:
|
||||
```bash
|
||||
# Use the Auth0 provider
|
||||
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.auth0.Auth0Provider
|
||||
|
||||
# Auth0 configuration and credentials
|
||||
FASTMCP_SERVER_AUTH_AUTH0_CONFIG_URL=https://.../.well-known/openid-configuration
|
||||
FASTMCP_SERVER_AUTH_AUTH0_CLIENT_ID=tv2ObNgaZAWWhhycr7Bz1LU2mxlnsmsB
|
||||
FASTMCP_SERVER_AUTH_AUTH0_CLIENT_SECRET=vPYqbjemq...
|
||||
FASTMCP_SERVER_AUTH_AUTH0_AUDIENCE=https://...
|
||||
FASTMCP_SERVER_AUTH_AUTH0_BASE_URL=https://your-server.com
|
||||
FASTMCP_SERVER_AUTH_AUTH0_REQUIRED_SCOPES=openid,email
|
||||
```
|
||||
|
||||
With environment variables set, your server code simplifies to:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Authentication is automatically configured from environment
|
||||
mcp = FastMCP(name="Auth0 Secured App")
|
||||
|
||||
@mcp.tool
|
||||
async def search_logs() -> list[str]:
|
||||
"""Search the service logs."""
|
||||
# Your tool implementation here
|
||||
pass
|
||||
```
|
||||
133
docs/integrations/authkit.mdx
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
---
|
||||
title: AuthKit 🤝 FastMCP
|
||||
sidebarTitle: AuthKit
|
||||
description: Secure your FastMCP server with AuthKit by WorkOS
|
||||
icon: shield-check
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## 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`).
|
||||
|
||||
### Step 1: AuthKit Configuration
|
||||
|
||||
In your WorkOS Dashboard, enable AuthKit and configure the following settings:
|
||||
|
||||
<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>
|
||||
|
||||
<Step title="Note Your AuthKit Domain">
|
||||
Find your **AuthKit Domain** on the configuration page. It will look like `https://your-project-12345.authkit.app`. You'll need this for your FastMCP server configuration.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 2: FastMCP Configuration
|
||||
|
||||
Create your FastMCP server file and use the `AuthKitProvider` to handle all the OAuth integration automatically:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.workos import AuthKitProvider
|
||||
|
||||
# The AuthKitProvider automatically discovers WorkOS endpoints
|
||||
# and configures JWT token validation
|
||||
auth_provider = AuthKitProvider(
|
||||
authkit_domain="https://your-project-12345.authkit.app",
|
||||
base_url="http://localhost:8000" # Use your actual server URL
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="AuthKit Secured App", auth=auth_provider)
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py --transport http --port 8000
|
||||
```
|
||||
|
||||
Now, you can use a FastMCP client to test that you can reach your server after authenticating:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
async with Client("http://localhost:8000/mcp/", auth="oauth") as client:
|
||||
assert await client.ping()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
|
||||
## Environment Variables
|
||||
|
||||
<VersionBadge version="2.12.1" />
|
||||
|
||||
For production deployments, use environment variables instead of hardcoding credentials.
|
||||
|
||||
### Provider Selection
|
||||
|
||||
Setting this environment variable allows the AuthKit provider to be used automatically without explicitly instantiating it in code.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
|
||||
Set to `fastmcp.server.auth.providers.workos.AuthKitProvider` to use AuthKit authentication.
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
### AuthKit-Specific Configuration
|
||||
|
||||
These environment variables provide default values for the AuthKit provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_AUTHKIT_DOMAIN" required>
|
||||
Your AuthKit domain (e.g., `https://your-project-12345.authkit.app`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_BASE_URL" required>
|
||||
Public URL of your FastMCP server (e.g., `https://your-server.com` or `http://localhost:8000` for development)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_REQUIRED_SCOPES" default="[]">
|
||||
Comma-, space-, or JSON-separated list of required OAuth scopes (e.g., `openid profile email` or `["openid", "profile", "email"]`)
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
Example `.env` file:
|
||||
```bash
|
||||
# Use the AuthKit provider
|
||||
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.workos.AuthKitProvider
|
||||
|
||||
# AuthKit configuration
|
||||
FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_AUTHKIT_DOMAIN=https://your-project-12345.authkit.app
|
||||
FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_BASE_URL=https://your-server.com
|
||||
FASTMCP_SERVER_AUTH_AUTHKITPROVIDER_REQUIRED_SCOPES=openid,profile,email
|
||||
```
|
||||
|
||||
With environment variables set, your server code simplifies to:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Authentication is automatically configured from environment
|
||||
mcp = FastMCP(name="AuthKit Secured App")
|
||||
```
|
||||
322
docs/integrations/aws-cognito.mdx
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
---
|
||||
title: AWS Cognito OAuth 🤝 FastMCP
|
||||
sidebarTitle: AWS Cognito
|
||||
description: Secure your FastMCP server with AWS Cognito user pools
|
||||
icon: aws
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.12.4" />
|
||||
|
||||
This guide shows you how to secure your FastMCP server using **AWS Cognito user pools**. Since AWS Cognito doesn't support Dynamic Client Registration, this integration uses the [**OAuth Proxy**](/servers/auth/oauth-proxy) pattern to bridge AWS Cognito's traditional OAuth with MCP's authentication requirements. It also includes robust JWT token validation, ensuring enterprise-grade authentication.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you will need:
|
||||
1. An **[AWS Account](https://aws.amazon.com/)** with access to create AWS Cognito user pools
|
||||
2. Basic familiarity with AWS Cognito concepts (user pools, app clients)
|
||||
3. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
|
||||
|
||||
### Step 1: Create an AWS Cognito User Pool and App Client
|
||||
|
||||
Set up AWS Cognito user pool with an app client to get the credentials needed for authentication:
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to AWS Cognito">
|
||||
Go to the **[AWS Cognito Console](https://console.aws.amazon.com/cognito/)** and ensure you're in your desired AWS region.
|
||||
|
||||
Select **"User pools"** from the side navigation (click on the hamburger icon at the top left in case you don't see any), and click **"Create user pool"** to create a new user pool.
|
||||
</Step>
|
||||
|
||||
<Step title="Define Your Application">
|
||||
AWS Cognito now provides a streamlined setup experience:
|
||||
|
||||
1. **Application type**: Select **"Traditional web application"** (this is the correct choice for FastMCP server-side authentication)
|
||||
2. **Name your application**: Enter a descriptive name (e.g., `FastMCP Server`)
|
||||
|
||||
The traditional web application type automatically configures:
|
||||
- Server-side authentication with client secrets
|
||||
- Authorization code grant flow
|
||||
- Appropriate security settings for confidential clients
|
||||
|
||||
<Info>
|
||||
Choose "Traditional web application" rather than SPA, Mobile app, or Machine-to-machine options. This ensures proper OAuth 2.0 configuration for FastMCP.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Options">
|
||||
AWS will guide you through configuration options:
|
||||
|
||||
- **Sign-in identifiers**: Choose how users will sign in (email, username, or phone)
|
||||
- **Required attributes**: Select any additional user information you need
|
||||
- **Return URL**: Add your callback URL (e.g., `http://localhost:8000/auth/callback` for development)
|
||||
|
||||
<Tip>
|
||||
The simplified interface handles most OAuth security settings automatically based on your application type selection.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Review and Create">
|
||||
Review your configuration and click **"Create user pool"**.
|
||||
|
||||
After creation, you'll see your user pool details. Save these important values:
|
||||
- **User pool ID** (format: `eu-central-1_XXXXXXXXX`)
|
||||
- **Client ID** (found under → "Applications" → "App clients" in the side navigation → \<Your application name, e.g., `FastMCP Server`\> → "App client information")
|
||||
- **Client Secret** (found under → "Applications" → "App clients" in the side navigation → \<Your application name, e.g., `FastMCP Server`\> → "App client information")
|
||||
|
||||
<Tip>
|
||||
The user pool ID and app client credentials are all you need for FastMCP configuration.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Configure OAuth Settings">
|
||||
Under "Login pages" in your app client's settings, you can double check and adjust the OAuth configuration:
|
||||
|
||||
- **Allowed callback URLs**: Add your server URL + `/auth/callback` (e.g., `http://localhost:8000/auth/callback`)
|
||||
- **Allowed sign-out URLs**: Optional, for logout functionality
|
||||
- **OAuth 2.0 grant types**: Ensure "Authorization code grant" is selected
|
||||
- **OpenID Connect scopes**: Select scopes your application needs (e.g., `openid`, `email`, `profile`)
|
||||
|
||||
<Tip>
|
||||
For local development, you can use `http://localhost` URLs. For production, you must use HTTPS.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Pick Up AWS Cognito Domain">
|
||||
Navigate to **"Branding" → "Domain"** in the side navigation to find or configure Your AWS Cognito domain:
|
||||
|
||||
**Option 1: Use Auto-Generated Domain**
|
||||
- If AWS has already created a domain automatically, note the **domain prefix** (the part before `.auth.region.amazoncognito.com`)
|
||||
- This prefix is what you'll use in your FastMCP configuration
|
||||
|
||||
**Option 2: Create a Custom Domain Prefix**
|
||||
- If no domain exists or you want a better name, delete the existing domain and create a new one using the **"Actions"** menu
|
||||
- Under **"Domain"** → **"Cognito domain"** in the **"Create Cognito domain"** dialog, enter a meaningful prefix (e.g., `my-app`) that is available in the AWS region you are in
|
||||
- Just note the **domain prefix** you entered (e.g., `my-fastmcp-app`) - this is what you'll use in your FastMCP configuration
|
||||
|
||||
<Info>
|
||||
The FastMCP AWS Cognito provider automatically constructs the full domain from your prefix and region, simplifying configuration.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Save Your Credentials">
|
||||
After setup, you'll have:
|
||||
|
||||
- **User Pool ID**: Format like `eu-central-1_XXXXXXXXX`
|
||||
- **Client ID**: Your application's client identifier
|
||||
- **Client Secret**: Generated client secret (keep secure)
|
||||
- **Domain Prefix**: The prefix of Your AWS Cognito domain
|
||||
- **AWS Region**: Where Your AWS Cognito user pool is located
|
||||
|
||||
<Tip>
|
||||
Store these credentials securely. Never commit them to version control. Use environment variables or AWS Secrets Manager in production.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 2: FastMCP Configuration
|
||||
|
||||
Create your FastMCP server using the `AWSCognitoProvider`, which handles AWS Cognito's JWT tokens and user claims automatically:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.aws import AWSCognitoProvider
|
||||
from fastmcp.server.dependencies import get_access_token
|
||||
|
||||
# The AWSCognitoProvider handles JWT validation and user claims
|
||||
auth_provider = AWSCognitoProvider(
|
||||
user_pool_id="eu-central-1_XXXXXXXXX", # Your AWS Cognito user pool ID
|
||||
aws_region="eu-central-1", # AWS region (defaults to eu-central-1)
|
||||
client_id="your-app-client-id", # Your app client ID
|
||||
client_secret="your-app-client-secret", # Your app client Secret
|
||||
base_url="http://localhost:8000", # Must match your callback URL
|
||||
# redirect_path="/auth/callback" # Default value, customize if needed
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="AWS Cognito Secured App", auth=auth_provider)
|
||||
|
||||
# Add a protected tool to test authentication
|
||||
@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"),
|
||||
"username": token.claims.get("username"),
|
||||
"cognito:groups": token.claims.get("cognito:groups", []),
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Running the Server
|
||||
|
||||
Start your FastMCP server with HTTP transport to enable OAuth flows:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py --transport http --port 8000
|
||||
```
|
||||
|
||||
Your server is now running and protected by AWS Cognito OAuth authentication.
|
||||
|
||||
### Testing with a Client
|
||||
|
||||
Create a test client that authenticates with Your AWS Cognito-protected server:
|
||||
|
||||
```python test_client.py
|
||||
from fastmcp import Client
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
# The client will automatically handle AWS Cognito OAuth
|
||||
async with Client("http://localhost:8000/mcp/", auth="oauth") as client:
|
||||
# First-time connection will open AWS Cognito login in your browser
|
||||
print("✓ Authenticated with AWS Cognito!")
|
||||
|
||||
# Test the protected tool
|
||||
print("Calling protected tool: get_access_token_claims")
|
||||
result = await client.call_tool("get_access_token_claims")
|
||||
user_data = result.data
|
||||
print("Available access token claims:")
|
||||
print(f"- sub: {user_data.get('sub', 'N/A')}")
|
||||
print(f"- username: {user_data.get('username', 'N/A')}")
|
||||
print(f"- cognito:groups: {user_data.get('cognito:groups', [])}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
When you run the client for the first time:
|
||||
1. Your browser will open to AWS Cognito's hosted UI login page
|
||||
2. After you sign in (or sign up), you'll be redirected back to your MCP server
|
||||
3. The client receives the JWT token and can make authenticated requests
|
||||
|
||||
<Info>
|
||||
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
|
||||
</Info>
|
||||
|
||||
## Environment Variables
|
||||
|
||||
For production deployments, use environment variables instead of hardcoding credentials.
|
||||
|
||||
### Provider Selection
|
||||
|
||||
Setting this environment variable allows the AWS Cognito provider to be used automatically without explicitly instantiating it in code.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
|
||||
Set to `fastmcp.server.auth.providers.aws.AWSCognitoProvider` to use AWS Cognito authentication.
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
### AWS Cognito-Specific Configuration
|
||||
|
||||
These environment variables provide default values for the AWS Cognito provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_USER_POOL_ID" required>
|
||||
Your AWS Cognito user pool ID (e.g., `eu-central-1_XXXXXXXXX`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_AWS_REGION" default="eu-central-1">
|
||||
AWS region where your AWS Cognito user pool is located
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_ID" required>
|
||||
Your AWS Cognito app client ID
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_SECRET" required>
|
||||
Your AWS Cognito app client secret
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_BASE_URL" default="http://localhost:8000">
|
||||
Public URL of your FastMCP server for OAuth callbacks
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_REDIRECT_PATH" default="/auth/callback">
|
||||
One of the redirect paths configured in your AWS Cognito app client
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AWS_COGNITO_REQUIRED_SCOPES" default='["openid"]'>
|
||||
Comma-, space-, or JSON-separated list of required OAuth scopes (e.g., `openid email` or `["openid","email","profile"]`)
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
Example `.env` file:
|
||||
```bash
|
||||
# Use the AWS Cognito provider
|
||||
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.aws.AWSCognitoProvider
|
||||
|
||||
# AWS Cognito credentials
|
||||
FASTMCP_SERVER_AUTH_AWS_COGNITO_USER_POOL_ID=eu-central-1_XXXXXXXXX
|
||||
FASTMCP_SERVER_AUTH_AWS_COGNITO_AWS_REGION=eu-central-1
|
||||
FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_ID=your-app-client-id
|
||||
FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_SECRET=your-app-client-secret
|
||||
FASTMCP_SERVER_AUTH_AWS_COGNITO_BASE_URL=https://your-server.com
|
||||
FASTMCP_SERVER_AUTH_AWS_COGNITO_REQUIRED_SCOPES=openid,email,profile
|
||||
```
|
||||
|
||||
With environment variables set, your server code simplifies to:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.dependencies import get_access_token
|
||||
|
||||
# Authentication is automatically configured from environment
|
||||
mcp = FastMCP(name="AWS Cognito Secured App")
|
||||
|
||||
@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"),
|
||||
"username": token.claims.get("username"),
|
||||
"cognito:groups": token.claims.get("cognito:groups", []),
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### JWT Token Validation
|
||||
|
||||
The AWS Cognito provider includes robust JWT token validation:
|
||||
|
||||
- **Signature Verification**: Validates tokens against AWS Cognito's public keys (JWKS)
|
||||
- **Expiration Checking**: Automatically rejects expired tokens
|
||||
- **Issuer Validation**: Ensures tokens come from your specific AWS Cognito user pool
|
||||
- **Scope Enforcement**: Verifies required OAuth scopes are present
|
||||
|
||||
### User Claims and Groups
|
||||
|
||||
Access rich user information from AWS Cognito 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()
|
||||
user_groups = token.claims.get("cognito:groups", [])
|
||||
|
||||
if "admin" not in user_groups:
|
||||
raise ValueError("This tool requires admin access")
|
||||
|
||||
return "Admin access granted!"
|
||||
```
|
||||
|
||||
### Enterprise Integration
|
||||
|
||||
Perfect for enterprise environments with:
|
||||
|
||||
- **Single Sign-On (SSO)**: Integrate with corporate identity providers
|
||||
- **Multi-Factor Authentication (MFA)**: Leverage AWS Cognito's built-in MFA
|
||||
- **User Groups**: Role-based access control through AWS Cognito groups
|
||||
- **Custom Attributes**: Access custom user attributes defined in your AWS Cognito user pool
|
||||
- **Compliance**: Meet enterprise security and compliance requirements
|
||||
254
docs/integrations/azure.mdx
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
---
|
||||
title: Azure (Microsoft Entra) OAuth 🤝 FastMCP
|
||||
sidebarTitle: Azure
|
||||
description: Secure your FastMCP server with Azure/Microsoft Entra OAuth
|
||||
icon: microsoft
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.12.0" />
|
||||
|
||||
This guide shows you how to secure your FastMCP server using **Azure OAuth** (Microsoft Entra ID). Since Azure doesn't support Dynamic Client Registration, this integration uses the [**OAuth Proxy**](/servers/auth/oauth-proxy) pattern to bridge Azure's traditional OAuth with MCP's authentication requirements.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you will need:
|
||||
1. An **[Azure Account](https://portal.azure.com/)** with access to create App registrations
|
||||
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
|
||||
3. Your Azure tenant ID (found in Azure Portal under Microsoft Entra ID)
|
||||
|
||||
### Step 1: Create an Azure App Registration
|
||||
|
||||
Create an App registration in Azure Portal to get the credentials needed for authentication:
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to App registrations">
|
||||
Go to the [Azure Portal](https://portal.azure.com) and navigate to **Microsoft Entra ID → App registrations**.
|
||||
|
||||
Click **"New registration"** to create a new application.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Your Application">
|
||||
Fill in the application details:
|
||||
|
||||
- **Name**: Choose a name users will recognize (e.g., "My FastMCP Server")
|
||||
- **Supported account types**: Choose based on your needs:
|
||||
- **Single tenant**: Only users in your organization
|
||||
- **Multitenant**: Users in any Microsoft Entra directory
|
||||
- **Multitenant + personal accounts**: Any Microsoft account
|
||||
- **Redirect URI**: Select "Web" and enter your server URL + `/auth/callback` (e.g., `http://localhost:8000/auth/callback`)
|
||||
|
||||
<Warning>
|
||||
The redirect URI must match exactly. The default path is `/auth/callback`, but you can customize it using the `redirect_path` parameter. For local development, Azure allows `http://localhost` URLs. For production, you must use HTTPS.
|
||||
</Warning>
|
||||
|
||||
<Tip>
|
||||
If you want to use a custom callback path (e.g., `/auth/azure/callback`), make sure to set the same path in both your Azure App registration and the `redirect_path` parameter when configuring the AzureProvider.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Create Client Secret">
|
||||
After registration, navigate to **Certificates & secrets** in your app's settings.
|
||||
|
||||
- Click **"New client secret"**
|
||||
- Add a description (e.g., "FastMCP Server")
|
||||
- Choose an expiration period
|
||||
- Click **"Add"**
|
||||
|
||||
<Warning>
|
||||
Copy the secret value immediately - it won't be shown again! You'll need to create a new secret if you lose it.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Note Your Credentials">
|
||||
From the **Overview** page of your app registration, note:
|
||||
|
||||
- **Application (client) ID**: A UUID like `835f09b6-0f0f-40cc-85cb-f32c5829a149`
|
||||
- **Directory (tenant) ID**: A UUID like `08541b6e-646d-43de-a0eb-834e6713d6d5`
|
||||
- **Client Secret**: The value you copied in the previous step
|
||||
|
||||
<Tip>
|
||||
Store these credentials securely. Never commit them to version control. Use environment variables or a secrets manager in production.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 2: FastMCP Configuration
|
||||
|
||||
Create your FastMCP server using the `AzureProvider`, which handles Azure's OAuth flow automatically:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.azure import AzureProvider
|
||||
|
||||
# The AzureProvider handles Azure's token format and validation
|
||||
auth_provider = AzureProvider(
|
||||
client_id="835f09b6-0f0f-40cc-85cb-f32c5829a149", # Your Azure App Client ID
|
||||
client_secret="your-client-secret", # Your Azure App Client Secret
|
||||
tenant_id="08541b6e-646d-43de-a0eb-834e6713d6d5", # Your Azure Tenant ID (REQUIRED)
|
||||
base_url="http://localhost:8000", # Must match your App registration
|
||||
required_scopes=["User.Read", "email", "openid", "profile"], # Microsoft Graph permissions
|
||||
# redirect_path="/auth/callback" # Default value, customize if needed
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="Azure Secured App", auth=auth_provider)
|
||||
|
||||
# Add a protected tool to test authentication
|
||||
@mcp.tool
|
||||
async def get_user_info() -> dict:
|
||||
"""Returns information about the authenticated Azure user."""
|
||||
from fastmcp.server.dependencies import get_access_token
|
||||
|
||||
token = get_access_token()
|
||||
# The AzureProvider stores user data in token claims
|
||||
return {
|
||||
"azure_id": token.claims.get("sub"),
|
||||
"email": token.claims.get("email"),
|
||||
"name": token.claims.get("name"),
|
||||
"job_title": token.claims.get("job_title"),
|
||||
"office_location": token.claims.get("office_location")
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Important**: The `tenant_id` parameter is **REQUIRED**. Azure no longer supports using "common" for new applications due to security requirements. You must use one of:
|
||||
|
||||
- **Your specific tenant ID**: Found in Azure Portal (e.g., `08541b6e-646d-43de-a0eb-834e6713d6d5`)
|
||||
- **"organizations"**: For work and school accounts only
|
||||
- **"consumers"**: For personal Microsoft accounts only
|
||||
|
||||
Using your specific tenant ID is recommended for better security and control.
|
||||
</Note>
|
||||
|
||||
## Testing
|
||||
|
||||
### Running the Server
|
||||
|
||||
Start your FastMCP server with HTTP transport to enable OAuth flows:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py --transport http --port 8000
|
||||
```
|
||||
|
||||
Your server is now running and protected by Azure OAuth authentication.
|
||||
|
||||
### Testing with a Client
|
||||
|
||||
Create a test client that authenticates with your Azure-protected server:
|
||||
|
||||
```python test_client.py
|
||||
from fastmcp import Client
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
# The client will automatically handle Azure OAuth
|
||||
async with Client("http://localhost:8000/mcp/", auth="oauth") as client:
|
||||
# First-time connection will open Azure login in your browser
|
||||
print("✓ Authenticated with Azure!")
|
||||
|
||||
# Test the protected tool
|
||||
result = await client.call_tool("get_user_info")
|
||||
print(f"Azure user: {result['email']}")
|
||||
print(f"Name: {result['name']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
When you run the client for the first time:
|
||||
1. Your browser will open to Microsoft's authorization page
|
||||
2. Sign in with your Microsoft account (work, school, or personal based on your tenant configuration)
|
||||
3. Grant the requested permissions
|
||||
4. After authorization, you'll be redirected back
|
||||
5. The client receives the token and can make authenticated requests
|
||||
|
||||
<Info>
|
||||
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
|
||||
</Info>
|
||||
|
||||
## Environment Variables
|
||||
|
||||
<VersionBadge version="2.12.1" />
|
||||
|
||||
For production deployments, use environment variables instead of hardcoding credentials.
|
||||
|
||||
### Provider Selection
|
||||
|
||||
Setting this environment variable allows the Azure provider to be used automatically without explicitly instantiating it in code.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
|
||||
Set to `fastmcp.server.auth.providers.azure.AzureProvider` to use Azure authentication.
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
### Azure-Specific Configuration
|
||||
|
||||
These environment variables provide default values for the Azure provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_CLIENT_ID" required>
|
||||
Your Azure App registration Client ID (e.g., `835f09b6-0f0f-40cc-85cb-f32c5829a149`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_CLIENT_SECRET" required>
|
||||
Your Azure App registration Client Secret
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_TENANT_ID" required>
|
||||
Your Azure tenant ID (specific ID, "organizations", or "consumers")
|
||||
|
||||
<Note>
|
||||
This is **REQUIRED**. Find your tenant ID in Azure Portal under Microsoft Entra ID → Overview.
|
||||
</Note>
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_BASE_URL" default="http://localhost:8000">
|
||||
Public URL of your FastMCP server for OAuth callbacks
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_REDIRECT_PATH" default="/auth/callback">
|
||||
Redirect path configured in your Azure App registration
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_REQUIRED_SCOPES" default='["User.Read", "email", "openid", "profile"]'>
|
||||
Comma-, space-, or JSON-separated list of required Microsoft Graph scopes
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_AZURE_TIMEOUT_SECONDS" default="10">
|
||||
HTTP request timeout for Microsoft Graph API calls
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
Example `.env` file:
|
||||
```bash
|
||||
# Use the Azure provider
|
||||
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.azure.AzureProvider
|
||||
|
||||
# Azure OAuth credentials
|
||||
FASTMCP_SERVER_AUTH_AZURE_CLIENT_ID=835f09b6-0f0f-40cc-85cb-f32c5829a149
|
||||
FASTMCP_SERVER_AUTH_AZURE_CLIENT_SECRET=your-client-secret-here
|
||||
FASTMCP_SERVER_AUTH_AZURE_TENANT_ID=08541b6e-646d-43de-a0eb-834e6713d6d5
|
||||
FASTMCP_SERVER_AUTH_AZURE_BASE_URL=https://your-server.com
|
||||
FASTMCP_SERVER_AUTH_AZURE_REQUIRED_SCOPES=User.Read,email,profile
|
||||
```
|
||||
|
||||
With environment variables set, your server code simplifies to:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Authentication is automatically configured from environment
|
||||
mcp = FastMCP(name="Azure Secured App")
|
||||
|
||||
@mcp.tool
|
||||
async def protected_tool(query: str) -> str:
|
||||
"""A tool that requires Azure authentication to access."""
|
||||
# Your tool implementation here
|
||||
return f"Processing authenticated request: {query}"
|
||||
```
|
||||
|
||||
157
docs/integrations/chatgpt.mdx
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
---
|
||||
title: ChatGPT 🤝 FastMCP
|
||||
sidebarTitle: ChatGPT
|
||||
description: Connect FastMCP servers to ChatGPT in Chat and Deep Research modes
|
||||
icon: message-smile
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
ChatGPT supports MCP servers through remote HTTP connections in two modes: **Chat mode** for interactive conversations and **Deep Research mode** for comprehensive information retrieval.
|
||||
|
||||
<Tip>
|
||||
**Developer Mode Required for Chat Mode**: To use MCP servers in regular ChatGPT conversations, you must first enable Developer Mode in your ChatGPT settings. This feature is available for ChatGPT Pro, Team, Enterprise, and Edu users.
|
||||
</Tip>
|
||||
|
||||
<Note>
|
||||
OpenAI's official MCP documentation and examples are built with **FastMCP v2**! Learn more from their [MCP documentation](https://platform.openai.com/docs/mcp) and [Developer Mode guide](https://platform.openai.com/docs/guides/developer-mode).
|
||||
</Note>
|
||||
|
||||
## Build a Server
|
||||
|
||||
First, let's create a simple FastMCP server:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
import random
|
||||
|
||||
mcp = FastMCP("Demo Server")
|
||||
|
||||
@mcp.tool
|
||||
def roll_dice(sides: int = 6) -> int:
|
||||
"""Roll a dice with the specified number of sides."""
|
||||
return random.randint(1, sides)
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="http", port=8000)
|
||||
```
|
||||
|
||||
### Deploy Your Server
|
||||
|
||||
Your server must be accessible from the internet. For development, use `ngrok`:
|
||||
|
||||
<CodeGroup>
|
||||
```bash Terminal 1
|
||||
python server.py
|
||||
```
|
||||
|
||||
```bash Terminal 2
|
||||
ngrok http 8000
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Note your public URL (e.g., `https://abc123.ngrok.io`) for the next steps.
|
||||
|
||||
## Chat Mode
|
||||
|
||||
Chat mode lets you use MCP tools directly in ChatGPT conversations. See [OpenAI's Developer Mode guide](https://platform.openai.com/docs/guides/developer-mode) for the latest requirements.
|
||||
|
||||
### Add to ChatGPT
|
||||
|
||||
#### 1. Enable Developer Mode
|
||||
|
||||
1. Open ChatGPT and go to **Settings** → **Connectors**
|
||||
2. Under **Advanced**, toggle **Developer Mode** to enabled
|
||||
|
||||
#### 2. Create Connector
|
||||
|
||||
1. In **Settings** → **Connectors**, click **Create**
|
||||
2. Enter:
|
||||
- **Name**: Your server name
|
||||
- **Server URL**: `https://your-server.ngrok.io/mcp/`
|
||||
3. Check **I trust this provider**
|
||||
4. Add authentication if needed
|
||||
5. Click **Create**
|
||||
|
||||
<Note>
|
||||
**Without Developer Mode**: If you don't have search/fetch tools, ChatGPT will reject the server. With Developer Mode enabled, you don't need search/fetch tools for Chat mode.
|
||||
</Note>
|
||||
|
||||
#### 3. Use in Chat
|
||||
|
||||
1. Start a new chat
|
||||
2. Click the **+** button → **More** → **Developer Mode**
|
||||
3. **Enable your MCP server connector** (required - the connector must be explicitly added to each chat)
|
||||
4. Now you can use your tools:
|
||||
|
||||
Example usage:
|
||||
- "Roll a 20-sided dice"
|
||||
- "Roll dice" (uses default 6 sides)
|
||||
|
||||
<Tip>
|
||||
The connector must be explicitly enabled in each chat session through Developer Mode. Once added, it remains active for the entire conversation.
|
||||
</Tip>
|
||||
|
||||
### Skip Confirmations
|
||||
|
||||
Use `annotations={"readOnlyHint": True}` to skip confirmation prompts for read-only tools:
|
||||
|
||||
```python
|
||||
@mcp.tool(annotations={"readOnlyHint": True})
|
||||
def get_status() -> str:
|
||||
"""Check system status."""
|
||||
return "All systems operational"
|
||||
|
||||
@mcp.tool() # No annotation - ChatGPT may ask for confirmation
|
||||
def delete_item(id: str) -> str:
|
||||
"""Delete an item."""
|
||||
return f"Deleted {id}"
|
||||
```
|
||||
|
||||
## Deep Research Mode
|
||||
|
||||
Deep Research mode provides systematic information retrieval with citations. See [OpenAI's MCP documentation](https://platform.openai.com/docs/mcp) for the latest Deep Research specifications.
|
||||
|
||||
<Warning>
|
||||
**Search and Fetch Required**: Without Developer Mode, ChatGPT will reject any server that doesn't have both `search` and `fetch` tools. Even in Developer Mode, Deep Research only uses these two tools.
|
||||
</Warning>
|
||||
|
||||
### Tool Implementation
|
||||
|
||||
Deep Research tools must follow this pattern:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
def search(query: str) -> dict:
|
||||
"""
|
||||
Search for records matching the query.
|
||||
Must return {"ids": [list of string IDs]}
|
||||
"""
|
||||
# Your search logic
|
||||
matching_ids = ["id1", "id2", "id3"]
|
||||
return {"ids": matching_ids}
|
||||
|
||||
@mcp.tool()
|
||||
def fetch(id: str) -> dict:
|
||||
"""
|
||||
Fetch a complete record by ID.
|
||||
Return the full record data for ChatGPT to analyze.
|
||||
"""
|
||||
# Your fetch logic
|
||||
return {
|
||||
"id": id,
|
||||
"title": "Record Title",
|
||||
"content": "Full record content...",
|
||||
"metadata": {"author": "Jane Doe", "date": "2024"}
|
||||
}
|
||||
```
|
||||
|
||||
### Using Deep Research
|
||||
|
||||
1. Ensure your server is added to ChatGPT's connectors (same as Chat mode)
|
||||
2. Start a new chat
|
||||
3. Click **+** → **Deep Research**
|
||||
4. Select your MCP server as a source
|
||||
5. Ask research questions
|
||||
|
||||
ChatGPT will use your `search` and `fetch` tools to find and cite relevant information.
|
||||
|
||||
177
docs/integrations/claude-code.mdx
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
---
|
||||
title: Claude Code 🤝 FastMCP
|
||||
sidebarTitle: Claude Code
|
||||
description: Install and use FastMCP servers in Claude Code
|
||||
icon: message-smile
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { LocalFocusTip } from "/snippets/local-focus.mdx"
|
||||
|
||||
<LocalFocusTip />
|
||||
|
||||
Claude Code supports MCP servers through multiple transport methods including STDIO, SSE, and HTTP, allowing you to extend Claude's capabilities with custom tools, resources, and prompts from your FastMCP servers.
|
||||
|
||||
## Requirements
|
||||
|
||||
This integration uses STDIO transport to run your FastMCP server locally. For remote deployments, you can run your FastMCP server with HTTP or SSE transport and configure it directly using Claude Code's built-in MCP management commands.
|
||||
|
||||
## Create a Server
|
||||
|
||||
The examples in this guide will use the following simple dice-rolling server, saved as `server.py`.
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
## Install the Server
|
||||
|
||||
### FastMCP CLI
|
||||
<VersionBadge version="2.10.3" />
|
||||
|
||||
The easiest way to install a FastMCP server in Claude Code is using the `fastmcp install claude-code` command. This automatically handles the configuration, dependency management, and calls Claude Code's built-in MCP management system.
|
||||
|
||||
```bash
|
||||
fastmcp install claude-code server.py
|
||||
```
|
||||
|
||||
The install command supports the same `file.py:object` notation as the `run` command. If no object is specified, it will automatically look for a FastMCP server object named `mcp`, `server`, or `app` in your file:
|
||||
|
||||
```bash
|
||||
# These are equivalent if your server object is named 'mcp'
|
||||
fastmcp install claude-code server.py
|
||||
fastmcp install claude-code server.py:mcp
|
||||
|
||||
# Use explicit object name if your server has a different name
|
||||
fastmcp install claude-code server.py:my_custom_server
|
||||
```
|
||||
|
||||
The command will automatically configure the server with Claude Code's `claude mcp add` command.
|
||||
|
||||
#### Dependencies
|
||||
|
||||
FastMCP provides flexible dependency management options for your Claude Code servers:
|
||||
|
||||
**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-code server.py --with pandas --with requests
|
||||
```
|
||||
|
||||
**Requirements file**: If you maintain a `requirements.txt` file with all your dependencies, use `--with-requirements` to install them:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-code server.py --with-requirements requirements.txt
|
||||
```
|
||||
|
||||
**Editable packages**: For local packages under development, use `--with-editable` to install them in editable mode:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-code server.py --with-editable ./my-local-package
|
||||
```
|
||||
|
||||
Alternatively, you can use a `fastmcp.json` configuration file (recommended):
|
||||
|
||||
```json fastmcp.json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"environment": {
|
||||
"dependencies": ["pandas", "requests"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Python Version and Project Configuration
|
||||
|
||||
Control the Python environment for your server with these options:
|
||||
|
||||
**Python version**: Use `--python` to specify which Python version your server requires. This ensures compatibility when your server needs specific Python features:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-code server.py --python 3.11
|
||||
```
|
||||
|
||||
**Project directory**: Use `--project` to run your server within a specific project context. This tells `uv` to use the project's configuration files and virtual environment:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-code server.py --project /path/to/my-project
|
||||
```
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
If your server needs environment variables (like API keys), you must include them:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-code server.py --server-name "Weather Server" \
|
||||
--env API_KEY=your-api-key \
|
||||
--env DEBUG=true
|
||||
```
|
||||
|
||||
Or load them from a `.env` file:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-code server.py --server-name "Weather Server" --env-file .env
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**Claude Code must be installed**. The integration looks for the Claude Code CLI at the default installation location (`~/.claude/local/claude`) and uses the `claude mcp add` command to register servers.
|
||||
</Warning>
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
For more control over the configuration, you can manually use Claude Code's built-in MCP management commands. This gives you direct control over how your server is launched:
|
||||
|
||||
```bash
|
||||
# Add a server with custom configuration
|
||||
claude mcp add dice-roller -- uv run --with fastmcp fastmcp run server.py
|
||||
|
||||
# Add with environment variables
|
||||
claude mcp add weather-server -e API_KEY=secret -e DEBUG=true -- uv run --with fastmcp fastmcp run server.py
|
||||
|
||||
# Add with specific scope (local, user, or project)
|
||||
claude mcp add my-server --scope user -- uv run --with fastmcp fastmcp run server.py
|
||||
```
|
||||
|
||||
You can also manually specify Python versions and project directories in your Claude Code commands:
|
||||
|
||||
```bash
|
||||
# With specific Python version
|
||||
claude mcp add ml-server -- uv run --python 3.11 --with fastmcp fastmcp run server.py
|
||||
|
||||
# Within a project directory
|
||||
claude mcp add project-server -- uv run --project /path/to/project --with fastmcp fastmcp run server.py
|
||||
```
|
||||
|
||||
## Using the Server
|
||||
|
||||
Once your server is installed, you can start using your FastMCP server with Claude Code.
|
||||
|
||||
Try asking Claude something like:
|
||||
|
||||
> "Roll some dice for me"
|
||||
|
||||
Claude will automatically detect your `roll_dice` tool and use it to fulfill your request, returning something like:
|
||||
|
||||
> I'll roll some dice for you! Here are your results: [4, 2, 6]
|
||||
>
|
||||
> You rolled three dice and got a 4, a 2, and a 6!
|
||||
|
||||
Claude Code can now access all the tools, resources, and prompts you've defined in your FastMCP server.
|
||||
|
||||
If your server provides resources, you can reference them with `@` mentions using the format `@server:protocol://resource/path`. If your server provides prompts, you can use them as slash commands with `/mcp__servername__promptname`.
|
||||
298
docs/integrations/claude-desktop.mdx
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
---
|
||||
title: Claude Desktop 🤝 FastMCP
|
||||
sidebarTitle: Claude Desktop
|
||||
description: Connect FastMCP servers to Claude Desktop
|
||||
icon: message-smile
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { LocalFocusTip } from "/snippets/local-focus.mdx"
|
||||
|
||||
<LocalFocusTip />
|
||||
|
||||
Claude Desktop supports MCP servers through local STDIO connections and remote servers (beta), allowing you to extend Claude's capabilities with custom tools, resources, and prompts from your FastMCP servers.
|
||||
|
||||
<Note>
|
||||
Remote MCP server support is currently in beta and available for users on Claude Pro, Max, Team, and Enterprise plans (as of June 2025). Most users will still need to use local STDIO connections.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
This guide focuses specifically on using FastMCP servers with Claude Desktop. For general Claude Desktop MCP setup and official examples, see the [official Claude Desktop quickstart guide](https://modelcontextprotocol.io/quickstart/user).
|
||||
</Note>
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
Claude Desktop traditionally requires MCP servers to run locally using STDIO transport, where your server communicates with Claude through standard input/output rather than HTTP. However, users on certain plans now have access to remote server support as well.
|
||||
|
||||
<Tip>
|
||||
If you don't have access to remote server support or need to connect to remote servers, you can create a **proxy server** that runs locally via STDIO and forwards requests to remote HTTP servers. See the [Proxy Servers](#proxy-servers) section below.
|
||||
</Tip>
|
||||
|
||||
## Create a Server
|
||||
|
||||
The examples in this guide will use the following simple dice-rolling server, saved as `server.py`.
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
## Install the Server
|
||||
|
||||
### FastMCP CLI
|
||||
<VersionBadge version="2.10.3" />
|
||||
|
||||
The easiest way to install a FastMCP server in Claude Desktop is using the `fastmcp install claude-desktop` command. This automatically handles the configuration and dependency management.
|
||||
|
||||
<Tip>
|
||||
Prior to version 2.10.3, Claude Desktop could be managed by running `fastmcp install <path>` without specifying the client.
|
||||
</Tip>
|
||||
|
||||
```bash
|
||||
fastmcp install claude-desktop server.py
|
||||
```
|
||||
|
||||
The install command supports the same `file.py:object` notation as the `run` command. If no object is specified, it will automatically look for a FastMCP server object named `mcp`, `server`, or `app` in your file:
|
||||
|
||||
```bash
|
||||
# These are equivalent if your server object is named 'mcp'
|
||||
fastmcp install claude-desktop server.py
|
||||
fastmcp install claude-desktop server.py:mcp
|
||||
|
||||
# Use explicit object name if your server has a different name
|
||||
fastmcp install claude-desktop server.py:my_custom_server
|
||||
```
|
||||
|
||||
After installation, restart Claude Desktop completely. You should see a hammer icon (🔨) in the bottom left of the input box, indicating that MCP tools are available.
|
||||
|
||||
#### Dependencies
|
||||
|
||||
FastMCP provides several ways to manage your server's dependencies when installing in Claude Desktop:
|
||||
|
||||
**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-desktop server.py --with pandas --with requests
|
||||
```
|
||||
|
||||
**Requirements file**: If you have a `requirements.txt` file listing all your dependencies, use `--with-requirements` to install them all at once:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-desktop server.py --with-requirements requirements.txt
|
||||
```
|
||||
|
||||
**Editable packages**: For local packages in development, use `--with-editable` to install them in editable mode:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-desktop server.py --with-editable ./my-local-package
|
||||
```
|
||||
|
||||
Alternatively, you can use a `fastmcp.json` configuration file (recommended):
|
||||
|
||||
```json fastmcp.json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"environment": {
|
||||
"dependencies": ["pandas", "requests"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Python Version and Project Directory
|
||||
|
||||
FastMCP allows you to control the Python environment for your server:
|
||||
|
||||
**Python version**: Use `--python` to specify which Python version your server should run with. This is particularly useful when your server requires a specific Python version:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-desktop server.py --python 3.11
|
||||
```
|
||||
|
||||
**Project directory**: Use `--project` to run your server within a specific project directory. This ensures that `uv` will discover all `pyproject.toml`, `uv.toml`, and `.python-version` files from that project:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-desktop server.py --project /path/to/my-project
|
||||
```
|
||||
|
||||
When you specify a project directory, all relative paths in your server will be resolved from that directory, and the project's virtual environment will be used.
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
<Warning>
|
||||
Claude Desktop runs servers in a completely isolated environment with no access to your shell environment or locally installed applications. You must explicitly pass any environment variables your server needs.
|
||||
</Warning>
|
||||
|
||||
If your server needs environment variables (like API keys), you must include them:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-desktop server.py --server-name "Weather Server" \
|
||||
--env API_KEY=your-api-key \
|
||||
--env DEBUG=true
|
||||
```
|
||||
|
||||
Or load them from a `.env` file:
|
||||
|
||||
```bash
|
||||
fastmcp install claude-desktop server.py --server-name "Weather Server" --env-file .env
|
||||
```
|
||||
<Warning>
|
||||
- **`uv` must be installed and available in your system PATH**. Claude Desktop runs in its own isolated environment and needs `uv` to manage dependencies.
|
||||
- **On macOS, it is recommended to install `uv` globally with Homebrew** so that Claude Desktop will detect it: `brew install uv`. Installing `uv` with other methods may not make it accessible to Claude Desktop.
|
||||
</Warning>
|
||||
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
For more control over the configuration, you can manually edit Claude Desktop's configuration file. You can open the configuration file from Claude's developer settings, or find it in the following locations:
|
||||
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
||||
|
||||
The configuration file is a JSON object with a `mcpServers` key, which contains the configuration for each MCP server.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"dice-roller": {
|
||||
"command": "python",
|
||||
"args": ["path/to/your/server.py"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After updating the configuration file, restart Claude Desktop completely. Look for the hammer icon (🔨) to confirm your server is loaded.
|
||||
|
||||
#### Dependencies
|
||||
|
||||
If your server has dependencies, you can use `uv` or another package manager to set up the environment.
|
||||
|
||||
|
||||
When manually configuring dependencies, the recommended approach is to use `uv` with FastMCP. The configuration uses `uv run` to create an isolated environment with your specified packages:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"dice-roller": {
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"--with", "fastmcp",
|
||||
"--with", "pandas",
|
||||
"--with", "requests",
|
||||
"fastmcp",
|
||||
"run",
|
||||
"path/to/your/server.py"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also manually specify Python versions and project directories in your configuration. Add `--python` to use a specific Python version, or `--project` to run within a project directory:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"dice-roller": {
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"--python", "3.11",
|
||||
"--project", "/path/to/project",
|
||||
"--with", "fastmcp",
|
||||
"fastmcp",
|
||||
"run",
|
||||
"path/to/your/server.py"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The order of arguments matters: Python version and project settings come before package specifications, which come before the actual command to run.
|
||||
|
||||
<Warning>
|
||||
- **`uv` must be installed and available in your system PATH**. Claude Desktop runs in its own isolated environment and needs `uv` to manage dependencies.
|
||||
- **On macOS, it is recommended to install `uv` globally with Homebrew** so that Claude Desktop will detect it: `brew install uv`. Installing `uv` with other methods may not make it accessible to Claude Desktop.
|
||||
</Warning>
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
You can also specify environment variables in the configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"weather-server": {
|
||||
"command": "python",
|
||||
"args": ["path/to/weather_server.py"],
|
||||
"env": {
|
||||
"API_KEY": "your-api-key",
|
||||
"DEBUG": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<Warning>
|
||||
Claude Desktop runs servers in a completely isolated environment with no access to your shell environment or locally installed applications. You must explicitly pass any environment variables your server needs.
|
||||
</Warning>
|
||||
|
||||
|
||||
## Remote Servers
|
||||
|
||||
|
||||
Users on Claude Pro, Max, Team, and Enterprise plans have first-class remote server support via integrations. For other users, or as an alternative approach, FastMCP can create a proxy server that forwards requests to a remote HTTP server. You can install the proxy server in Claude Desktop.
|
||||
|
||||
Create a proxy server that connects to a remote HTTP server:
|
||||
|
||||
```python proxy_server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Create a proxy to a remote server
|
||||
proxy = FastMCP.as_proxy(
|
||||
"https://example.com/mcp/sse",
|
||||
name="Remote Server Proxy"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
proxy.run() # Runs via STDIO for Claude Desktop
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
For authenticated remote servers, create an authenticated client following the guidance in the [client auth documentation](/clients/auth/bearer) and pass it to the proxy:
|
||||
|
||||
```python auth_proxy_server.py {7}
|
||||
from fastmcp import FastMCP, Client
|
||||
from fastmcp.client.auth import BearerAuth
|
||||
|
||||
# Create authenticated client
|
||||
client = Client(
|
||||
"https://api.example.com/mcp/sse",
|
||||
auth=BearerAuth(token="your-access-token")
|
||||
)
|
||||
|
||||
# Create proxy using the authenticated client
|
||||
proxy = FastMCP.as_proxy(client, name="Authenticated Proxy")
|
||||
|
||||
if __name__ == "__main__":
|
||||
proxy.run()
|
||||
```
|
||||
|
||||
BIN
docs/integrations/cursor-install-mcp.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
284
docs/integrations/cursor.mdx
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
---
|
||||
title: Cursor 🤝 FastMCP
|
||||
sidebarTitle: Cursor
|
||||
description: Install and use FastMCP servers in Cursor
|
||||
icon: message-smile
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { LocalFocusTip } from "/snippets/local-focus.mdx"
|
||||
|
||||
<LocalFocusTip />
|
||||
|
||||
Cursor supports MCP servers through multiple transport methods including STDIO, SSE, and Streamable HTTP, allowing you to extend Cursor's AI assistant with custom tools, resources, and prompts from your FastMCP servers.
|
||||
|
||||
## Requirements
|
||||
|
||||
This integration uses STDIO transport to run your FastMCP server locally. For remote deployments, you can run your FastMCP server with HTTP or SSE transport and configure it directly in Cursor's settings.
|
||||
|
||||
## Create a Server
|
||||
|
||||
The examples in this guide will use the following simple dice-rolling server, saved as `server.py`.
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
## Install the Server
|
||||
|
||||
### FastMCP CLI
|
||||
<VersionBadge version="2.10.3" />
|
||||
|
||||
The easiest way to install a FastMCP server in Cursor is using the `fastmcp install cursor` command. This automatically handles the configuration, dependency management, and opens Cursor with a deeplink to install the server.
|
||||
|
||||
```bash
|
||||
fastmcp install cursor server.py
|
||||
```
|
||||
|
||||
#### Workspace Installation
|
||||
<VersionBadge version="2.12.0" />
|
||||
|
||||
By default, FastMCP installs servers globally for Cursor. You can also install servers to project-specific workspaces using the `--workspace` flag:
|
||||
|
||||
```bash
|
||||
# Install to current directory's .cursor/ folder
|
||||
fastmcp install cursor server.py --workspace .
|
||||
|
||||
# Install to specific workspace
|
||||
fastmcp install cursor server.py --workspace /path/to/project
|
||||
```
|
||||
|
||||
This creates a `.cursor/mcp.json` configuration file in the specified workspace directory, allowing different projects to have their own MCP server configurations.
|
||||
|
||||
The install command supports the same `file.py:object` notation as the `run` command. If no object is specified, it will automatically look for a FastMCP server object named `mcp`, `server`, or `app` in your file:
|
||||
|
||||
```bash
|
||||
# These are equivalent if your server object is named 'mcp'
|
||||
fastmcp install cursor server.py
|
||||
fastmcp install cursor server.py:mcp
|
||||
|
||||
# Use explicit object name if your server has a different name
|
||||
fastmcp install cursor server.py:my_custom_server
|
||||
```
|
||||
|
||||
After running the command, Cursor will open automatically and prompt you to install the server. The command will be `uv`, which is expected as this is a Python STDIO server. Click "Install" to confirm:
|
||||
|
||||

|
||||
|
||||
#### Dependencies
|
||||
|
||||
FastMCP offers multiple ways to manage dependencies for your Cursor servers:
|
||||
|
||||
**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
|
||||
|
||||
```bash
|
||||
fastmcp install cursor server.py --with pandas --with requests
|
||||
```
|
||||
|
||||
**Requirements file**: For projects with a `requirements.txt` file, use `--with-requirements` to install all dependencies at once:
|
||||
|
||||
```bash
|
||||
fastmcp install cursor server.py --with-requirements requirements.txt
|
||||
```
|
||||
|
||||
**Editable packages**: When developing local packages, use `--with-editable` to install them in editable mode:
|
||||
|
||||
```bash
|
||||
fastmcp install cursor server.py --with-editable ./my-local-package
|
||||
```
|
||||
|
||||
Alternatively, you can use a `fastmcp.json` configuration file (recommended):
|
||||
|
||||
```json fastmcp.json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"environment": {
|
||||
"dependencies": ["pandas", "requests"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Python Version and Project Configuration
|
||||
|
||||
Control your server's Python environment with these options:
|
||||
|
||||
**Python version**: Use `--python` to specify which Python version your server should use. This is essential when your server requires specific Python features:
|
||||
|
||||
```bash
|
||||
fastmcp install cursor server.py --python 3.11
|
||||
```
|
||||
|
||||
**Project directory**: Use `--project` to run your server within a specific project context. This ensures `uv` discovers all project configuration files and uses the correct virtual environment:
|
||||
|
||||
```bash
|
||||
fastmcp install cursor server.py --project /path/to/my-project
|
||||
```
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
<Warning>
|
||||
Cursor runs servers in a completely isolated environment with no access to your shell environment or locally installed applications. You must explicitly pass any environment variables your server needs.
|
||||
</Warning>
|
||||
|
||||
If your server needs environment variables (like API keys), you must include them:
|
||||
|
||||
```bash
|
||||
fastmcp install cursor server.py --server-name "Weather Server" \
|
||||
--env API_KEY=your-api-key \
|
||||
--env DEBUG=true
|
||||
```
|
||||
|
||||
Or load them from a `.env` file:
|
||||
|
||||
```bash
|
||||
fastmcp install cursor server.py --server-name "Weather Server" --env-file .env
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**`uv` must be installed and available in your system PATH**. Cursor runs in its own isolated environment and needs `uv` to manage dependencies.
|
||||
</Warning>
|
||||
|
||||
### Generate MCP JSON
|
||||
|
||||
<Note>
|
||||
**Use the first-class integration above for the best experience.** The MCP JSON generation is useful for advanced use cases, manual configuration, or integration with other tools.
|
||||
</Note>
|
||||
|
||||
You can generate MCP JSON configuration for manual use:
|
||||
|
||||
```bash
|
||||
# Generate configuration and output to stdout
|
||||
fastmcp install mcp-json server.py --server-name "Dice Roller" --with pandas
|
||||
|
||||
# Copy configuration to clipboard for easy pasting
|
||||
fastmcp install mcp-json server.py --server-name "Dice Roller" --copy
|
||||
```
|
||||
|
||||
This generates the standard `mcpServers` configuration format that can be used with any MCP-compatible client.
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
For more control over the configuration, you can manually edit Cursor's configuration file. The configuration file is located at:
|
||||
- **All platforms**: `~/.cursor/mcp.json`
|
||||
|
||||
The configuration file is a JSON object with a `mcpServers` key, which contains the configuration for each MCP server.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"dice-roller": {
|
||||
"command": "python",
|
||||
"args": ["path/to/your/server.py"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After updating the configuration file, your server should be available in Cursor.
|
||||
|
||||
#### Dependencies
|
||||
|
||||
If your server has dependencies, you can use `uv` or another package manager to set up the environment.
|
||||
|
||||
When manually configuring dependencies, the recommended approach is to use `uv` with FastMCP. The configuration should use `uv run` to create an isolated environment with your specified packages:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"dice-roller": {
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"--with", "fastmcp",
|
||||
"--with", "pandas",
|
||||
"--with", "requests",
|
||||
"fastmcp",
|
||||
"run",
|
||||
"path/to/your/server.py"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also manually specify Python versions and project directories in your configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"dice-roller": {
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"--python", "3.11",
|
||||
"--project", "/path/to/project",
|
||||
"--with", "fastmcp",
|
||||
"fastmcp",
|
||||
"run",
|
||||
"path/to/your/server.py"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that the order of arguments is important: Python version and project settings should come before package specifications.
|
||||
|
||||
<Warning>
|
||||
**`uv` must be installed and available in your system PATH**. Cursor runs in its own isolated environment and needs `uv` to manage dependencies.
|
||||
</Warning>
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
You can also specify environment variables in the configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"weather-server": {
|
||||
"command": "python",
|
||||
"args": ["path/to/weather_server.py"],
|
||||
"env": {
|
||||
"API_KEY": "your-api-key",
|
||||
"DEBUG": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Cursor runs servers in a completely isolated environment with no access to your shell environment or locally installed applications. You must explicitly pass any environment variables your server needs.
|
||||
</Warning>
|
||||
|
||||
## Using the Server
|
||||
|
||||
Once your server is installed, you can start using your FastMCP server with Cursor's AI assistant.
|
||||
|
||||
Try asking Cursor something like:
|
||||
|
||||
> "Roll some dice for me"
|
||||
|
||||
Cursor will automatically detect your `roll_dice` tool and use it to fulfill your request, returning something like:
|
||||
|
||||
> 🎲 Here are your dice rolls: 4, 6, 4
|
||||
>
|
||||
> You rolled 3 dice with a total of 14! The 6 was a nice high roll there!
|
||||
|
||||
The AI assistant can now access all the tools, resources, and prompts you've defined in your FastMCP server.
|
||||
154
docs/integrations/descope.mdx
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
---
|
||||
title: Descope 🤝 FastMCP
|
||||
sidebarTitle: Descope
|
||||
description: Secure your FastMCP server with Descope
|
||||
icon: shield-check
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.12.4" />
|
||||
|
||||
This guide shows you how to secure your FastMCP server using [**Descope**](https://www.descope.com), a complete authentication and user management solution. This integration uses the [**Remote OAuth**](/servers/auth/remote-oauth) pattern, where Descope handles user login and your FastMCP server validates the tokens.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you will need:
|
||||
1. To [sign up](https://www.descope.com/sign-up) for a Free Forever Descope account
|
||||
2. Your **Project ID** from the [Descope Console](https://app.descope.com/settings/project)
|
||||
3. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:3000`)
|
||||
|
||||
### Step 1: Configure Descope
|
||||
|
||||
<Steps>
|
||||
<Step title="Enable Dynamic Client Registration">
|
||||
1. Go to the [Inbound Apps page](https://app.descope.com/apps/inbound) of the Descope Console
|
||||
2. Click **DCR Settings**
|
||||
3. Enable **Dynamic Client Registration (DCR)**
|
||||
4. Define allowed scopes
|
||||
|
||||
<Warning>
|
||||
DCR is required for FastMCP clients to automatically register with your authentication server.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Note Your Project ID">
|
||||
Save your Project ID from [Project Settings](https://app.descope.com/settings/project):
|
||||
```
|
||||
Project ID: P2abc...123
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 2: Environment Setup
|
||||
|
||||
Create a `.env` file with your Descope configuration:
|
||||
|
||||
```bash
|
||||
DESCOPE_PROJECT_ID=P2abc...123 # Your Descope Project ID
|
||||
DESCOPE_BASE_URL=https://api.descope.com # Descope API URL
|
||||
SERVER_URL=http://localhost:3000 # Your server's base URL
|
||||
```
|
||||
|
||||
<Note>
|
||||
You can find your project's Descope Base URL in the [Multi-Region Support Guide](https://docs.descope.com/management/project-settings/multi-regional).
|
||||
</Note>
|
||||
|
||||
### Step 3: FastMCP Configuration
|
||||
|
||||
Create your FastMCP server file and use the DescopeProvider to handle all the OAuth integration automatically:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.descope import DescopeProvider
|
||||
|
||||
# The DescopeProvider automatically discovers Descope endpoints
|
||||
# and configures JWT token validation
|
||||
auth_provider = DescopeProvider(
|
||||
project_id=DESCOPE_PROJECT_ID, # Your Descope Project ID
|
||||
base_url=SERVER_URL, # Your server's public URL
|
||||
descope_base_url=DESCOPE_BASE_URL, # Descope API base URL
|
||||
)
|
||||
|
||||
# Create FastMCP server with auth
|
||||
mcp = FastMCP(name="My Descope Protected Server", auth=auth_provider)
|
||||
|
||||
```
|
||||
|
||||
## 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 `project_id`, `base_url`, and `descope_base_url` with your actual values!), you can run the following command:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py --transport http --port 8000
|
||||
```
|
||||
|
||||
Now, you can use a FastMCP client to test that you can reach your server after authenticating:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
async with Client("http://localhost:8000/mcp/", auth="oauth") as client:
|
||||
assert await client.ping()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
|
||||
For production deployments, use environment variables instead of hardcoding credentials.
|
||||
|
||||
### Provider Selection
|
||||
|
||||
Setting this environment variable allows the Descope provider to be used automatically without explicitly instantiating it in code.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
|
||||
Set to `fastmcp.server.auth.providers.descope.DescopeProvider` to use Descope authentication.
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
### Descope-Specific Configuration
|
||||
|
||||
These environment variables provide default values for the Descope provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_PROJECT_ID" required>
|
||||
Your Descope Project ID from the [Descope Console](https://app.descope.com/settings/project)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_BASE_URL" required>
|
||||
Public URL of your FastMCP server (e.g., `https://your-server.com` or `http://localhost:8000` for development)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_DESCOPE_BASE_URL" default="https://api.descope.com">
|
||||
Descope API base URL for your [region/environment](https://docs.descope.com/management/project-settings/multi-regional)
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
Example `.env` file:
|
||||
```bash
|
||||
# Use the Descope provider
|
||||
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.descope.DescopeProvider
|
||||
|
||||
# Descope configuration
|
||||
FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_PROJECT_ID=P2abc...123
|
||||
FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_BASE_URL=https://your-server.com
|
||||
FASTMCP_SERVER_AUTH_DESCOPEPROVIDER_DESCOPE_BASE_URL=https://api.descope.com
|
||||
```
|
||||
|
||||
With environment variables set, your server code simplifies to:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Authentication is automatically configured from environment
|
||||
mcp = FastMCP(name="My Descope Protected Server")
|
||||
```
|
||||
129
docs/integrations/eunomia-authorization.mdx
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
title: Eunomia Authorization 🤝 FastMCP
|
||||
sidebarTitle: Eunomia Auth
|
||||
description: Add policy-based authorization to your FastMCP servers with Eunomia
|
||||
icon: shield-check
|
||||
---
|
||||
|
||||
Add **policy-based authorization** to your FastMCP servers with one-line code addition with the **[Eunomia][eunomia-github] authorization middleware**.
|
||||
|
||||
Control which tools, resources and prompts MCP clients can view and execute on your server. Define dynamic JSON-based policies and obtain a comprehensive audit log of all access attempts and violations.
|
||||
|
||||
## How it Works
|
||||
|
||||
Exploiting FastMCP's [Middleware][fastmcp-middleare], the Eunomia middleware intercepts all MCP requests to your server and automatically maps MCP methods to authorization checks.
|
||||
|
||||
### Listing Operations
|
||||
|
||||
The middleware behaves as a filter for listing operations (`tools/list`, `resources/list`, `prompts/list`), hiding to the client components that are not authorized by the defined policies.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant MCPClient as MCP Client
|
||||
participant EunomiaMiddleware as Eunomia Middleware
|
||||
participant MCPServer as FastMCP Server
|
||||
participant EunomiaServer as Eunomia Server
|
||||
|
||||
MCPClient->>EunomiaMiddleware: MCP Listing Request (e.g., tools/list)
|
||||
EunomiaMiddleware->>MCPServer: MCP Listing Request
|
||||
MCPServer-->>EunomiaMiddleware: MCP Listing Response
|
||||
EunomiaMiddleware->>EunomiaServer: Authorization Checks
|
||||
EunomiaServer->>EunomiaMiddleware: Authorization Decisions
|
||||
EunomiaMiddleware-->>MCPClient: Filtered MCP Listing Response
|
||||
```
|
||||
|
||||
### Execution Operations
|
||||
|
||||
The middleware behaves as a firewall for execution operations (`tools/call`, `resources/read`, `prompts/get`), blocking operations that are not authorized by the defined policies.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant MCPClient as MCP Client
|
||||
participant EunomiaMiddleware as Eunomia Middleware
|
||||
participant MCPServer as FastMCP Server
|
||||
participant EunomiaServer as Eunomia Server
|
||||
|
||||
MCPClient->>EunomiaMiddleware: MCP Execution Request (e.g., tools/call)
|
||||
EunomiaMiddleware->>EunomiaServer: Authorization Check
|
||||
EunomiaServer->>EunomiaMiddleware: Authorization Decision
|
||||
EunomiaMiddleware-->>MCPClient: MCP Unauthorized Error (if denied)
|
||||
EunomiaMiddleware->>MCPServer: MCP Execution Request (if allowed)
|
||||
MCPServer-->>EunomiaMiddleware: MCP Execution Response (if allowed)
|
||||
EunomiaMiddleware-->>MCPClient: MCP Execution Response (if allowed)
|
||||
```
|
||||
|
||||
## Add Authorization to Your Server
|
||||
|
||||
<Note>
|
||||
Eunomia is an AI-specific authorization server that handles policy decisions. The server runs embedded within your MCP server by default for a zero-effort configuration, but can alternatively be run remotely for centralized policy decisions.
|
||||
|
||||
</Note>
|
||||
|
||||
### Create a Server with Authorization
|
||||
|
||||
First, install the `eunomia-mcp` package:
|
||||
|
||||
```bash
|
||||
pip install eunomia-mcp
|
||||
```
|
||||
|
||||
Then create a FastMCP server and add the Eunomia middleware in one line:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from eunomia_mcp import create_eunomia_middleware
|
||||
|
||||
# Create your FastMCP server
|
||||
mcp = FastMCP("Secure MCP Server 🔒")
|
||||
|
||||
@mcp.tool()
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
return a + b
|
||||
|
||||
# Add middleware to your server
|
||||
middleware = create_eunomia_middleware(policy_file="mcp_policies.json")
|
||||
mcp.add_middleware(middleware)
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
### Configure Access Policies
|
||||
|
||||
Use the `eunomia-mcp` CLI in your terminal to manage your authorization policies:
|
||||
|
||||
```bash
|
||||
# Create a default policy file
|
||||
eunomia-mcp init
|
||||
|
||||
# Or create a policy file customized for your FastMCP server
|
||||
eunomia-mcp init --custom-mcp "app.server:mcp"
|
||||
```
|
||||
|
||||
This creates `mcp_policies.json` file that you can further edit to your access control needs.
|
||||
|
||||
```bash
|
||||
# Once edited, validate your policy file
|
||||
eunomia-mcp validate mcp_policies.json
|
||||
```
|
||||
|
||||
### Run the Server
|
||||
|
||||
Start your FastMCP server normally:
|
||||
|
||||
```bash
|
||||
python server.py
|
||||
```
|
||||
|
||||
The middleware will now intercept all MCP requests and check them against your policies. Requests include agent identification through headers like `X-Agent-ID`, `X-User-ID`, `User-Agent`, or `Authorization` and an automatic mapping of MCP methods to authorization resources and actions.
|
||||
|
||||
<Tip>
|
||||
For detailed policy configuration, custom authentication, and remote
|
||||
deployments, visit the [Eunomia MCP Middleware
|
||||
repository][eunomia-mcp-github].
|
||||
</Tip>
|
||||
|
||||
[eunomia-github]: https://github.com/whataboutyou-ai/eunomia
|
||||
[eunomia-mcp-github]: https://github.com/whataboutyou-ai/eunomia/tree/main/pkgs/extensions/mcp
|
||||
[fastmcp-middleare]: /servers/middleware
|
||||
452
docs/integrations/fastapi.mdx
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
---
|
||||
title: FastAPI 🤝 FastMCP
|
||||
sidebarTitle: FastAPI
|
||||
description: Integrate FastMCP with FastAPI applications
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<Tip>
|
||||
**New in 2.11**: FastMCP is introducing a next-generation OpenAPI parser. The new parser has greatly improved performance and compatibility, and is also easier to maintain. To enable it, set the environment variable `FASTMCP_EXPERIMENTAL_ENABLE_NEW_OPENAPI_PARSER=true`.
|
||||
|
||||
The new parser is largely API-compatible with the existing implementation and will become the default in a future version. We encourage all users to test it and report any issues before it becomes the default.
|
||||
</Tip>
|
||||
|
||||
FastMCP provides two powerful ways to integrate with FastAPI applications:
|
||||
|
||||
1. **[Generate an MCP server FROM your FastAPI app](#generating-an-mcp-server)** - Convert existing API endpoints into MCP tools
|
||||
2. **[Mount an MCP server INTO your FastAPI app](#mounting-an-mcp-server)** - Add MCP functionality to your web application
|
||||
|
||||
|
||||
<Tip>
|
||||
Generating MCP servers from OpenAPI is a great way to get started with FastMCP, but in practice LLMs achieve **significantly better performance** with well-designed and curated MCP servers than with auto-converted OpenAPI servers. This is especially true for complex APIs with many endpoints and parameters.
|
||||
|
||||
We recommend using the FastAPI integration for bootstrapping and prototyping, not for mirroring your API to LLM clients. See the post [Stop Converting Your REST APIs to MCP](https://www.jlowin.dev/blog/stop-converting-rest-apis-to-mcp) for more details.
|
||||
</Tip>
|
||||
|
||||
|
||||
<Note>
|
||||
FastMCP does *not* include FastAPI as a dependency; you must install it separately to use this integration.
|
||||
</Note>
|
||||
|
||||
## Example FastAPI Application
|
||||
|
||||
Throughout this guide, we'll use this e-commerce API as our example (click the `Copy` button to copy it for use with other code blocks):
|
||||
|
||||
```python [expandable]
|
||||
# Copy this FastAPI server into other code blocks in this guide
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Models
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
category: str
|
||||
description: str | None = None
|
||||
|
||||
class ProductResponse(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
price: float
|
||||
category: str
|
||||
description: str | None = None
|
||||
|
||||
# Create FastAPI app
|
||||
app = FastAPI(title="E-commerce API", version="1.0.0")
|
||||
|
||||
# In-memory database
|
||||
products_db = {
|
||||
1: ProductResponse(
|
||||
id=1, name="Laptop", price=999.99, category="Electronics"
|
||||
),
|
||||
2: ProductResponse(
|
||||
id=2, name="Mouse", price=29.99, category="Electronics"
|
||||
),
|
||||
3: ProductResponse(
|
||||
id=3, name="Desk Chair", price=299.99, category="Furniture"
|
||||
),
|
||||
}
|
||||
next_id = 4
|
||||
|
||||
@app.get("/products", response_model=list[ProductResponse])
|
||||
def list_products(
|
||||
category: str | None = None,
|
||||
max_price: float | None = None,
|
||||
) -> list[ProductResponse]:
|
||||
"""List all products with optional filtering."""
|
||||
products = list(products_db.values())
|
||||
if category:
|
||||
products = [p for p in products if p.category == category]
|
||||
if max_price:
|
||||
products = [p for p in products if p.price <= max_price]
|
||||
return products
|
||||
|
||||
@app.get("/products/{product_id}", response_model=ProductResponse)
|
||||
def get_product(product_id: int):
|
||||
"""Get a specific product by ID."""
|
||||
if product_id not in products_db:
|
||||
raise HTTPException(status_code=404, detail="Product not found")
|
||||
return products_db[product_id]
|
||||
|
||||
@app.post("/products", response_model=ProductResponse)
|
||||
def create_product(product: Product):
|
||||
"""Create a new product."""
|
||||
global next_id
|
||||
product_response = ProductResponse(id=next_id, **product.model_dump())
|
||||
products_db[next_id] = product_response
|
||||
next_id += 1
|
||||
return product_response
|
||||
|
||||
@app.put("/products/{product_id}", response_model=ProductResponse)
|
||||
def update_product(product_id: int, product: Product):
|
||||
"""Update an existing product."""
|
||||
if product_id not in products_db:
|
||||
raise HTTPException(status_code=404, detail="Product not found")
|
||||
products_db[product_id] = ProductResponse(
|
||||
id=product_id,
|
||||
**product.model_dump(),
|
||||
)
|
||||
return products_db[product_id]
|
||||
|
||||
@app.delete("/products/{product_id}")
|
||||
def delete_product(product_id: int):
|
||||
"""Delete a product."""
|
||||
if product_id not in products_db:
|
||||
raise HTTPException(status_code=404, detail="Product not found")
|
||||
del products_db[product_id]
|
||||
return {"message": "Product deleted"}
|
||||
```
|
||||
|
||||
<Tip>
|
||||
All subsequent code examples in this guide assume you have the above FastAPI application code already defined. Each example builds upon this base application, `app`.
|
||||
</Tip>
|
||||
|
||||
## Generating an MCP Server
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
One of the most common ways to bootstrap an MCP server is to generate it from an existing FastAPI application. FastMCP will expose your FastAPI endpoints as MCP components (tools, by default) in order to expose your API to LLM clients.
|
||||
|
||||
|
||||
|
||||
### Basic Conversion
|
||||
|
||||
Convert the FastAPI app to an MCP server with a single line:
|
||||
|
||||
```python {5}
|
||||
# Assumes the FastAPI app from above is already defined
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Convert to MCP server
|
||||
mcp = FastMCP.from_fastapi(app=app)
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
### Adding Components
|
||||
|
||||
Your converted MCP server is a full FastMCP instance, meaning you can add new tools, resources, and other components to it just like you would with any other FastMCP instance.
|
||||
|
||||
```python {8-11}
|
||||
# Assumes the FastAPI app from above is already defined
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Convert to MCP server
|
||||
mcp = FastMCP.from_fastapi(app=app)
|
||||
|
||||
# Add a new tool
|
||||
@mcp.tool
|
||||
def get_product(product_id: int) -> ProductResponse:
|
||||
"""Get a product by ID."""
|
||||
return products_db[product_id]
|
||||
|
||||
# Run the MCP server
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Interacting with the MCP Server
|
||||
|
||||
Once you've converted your FastAPI app to an MCP server, you can interact with it using the FastMCP client to test functionality before deploying it to an LLM-based application.
|
||||
|
||||
```python {3, }
|
||||
# Assumes the FastAPI app from above is already defined
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.client import Client
|
||||
import asyncio
|
||||
|
||||
# Convert to MCP server
|
||||
mcp = FastMCP.from_fastapi(app=app)
|
||||
|
||||
async def demo():
|
||||
async with Client(mcp) as client:
|
||||
# List available tools
|
||||
tools = await client.list_tools()
|
||||
print(f"Available tools: {[t.name for t in tools]}")
|
||||
|
||||
# Create a product
|
||||
result = await client.call_tool(
|
||||
"create_product_products_post",
|
||||
{
|
||||
"name": "Wireless Keyboard",
|
||||
"price": 79.99,
|
||||
"category": "Electronics",
|
||||
"description": "Bluetooth mechanical keyboard"
|
||||
}
|
||||
)
|
||||
print(f"Created product: {result.data}")
|
||||
|
||||
# List electronics under $100
|
||||
result = await client.call_tool(
|
||||
"list_products_products_get",
|
||||
{"category": "Electronics", "max_price": 100}
|
||||
)
|
||||
print(f"Affordable electronics: {result.data}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(demo())
|
||||
```
|
||||
|
||||
### Custom Route Mapping
|
||||
|
||||
Because FastMCP's FastAPI integration is based on its [OpenAPI integration](/integrations/openapi), you can customize how endpoints are converted to MCP components in exactly the same way. For example, here we use a `RouteMap` to map all GET requests to MCP resources, and all POST/PUT/DELETE requests to MCP tools:
|
||||
|
||||
```python
|
||||
# Assumes the FastAPI app from above is already defined
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.openapi import RouteMap, MCPType
|
||||
|
||||
# If using experimental parser, import from experimental module:
|
||||
# from fastmcp.experimental.server.openapi import RouteMap, MCPType
|
||||
|
||||
# Custom mapping rules
|
||||
mcp = FastMCP.from_fastapi(
|
||||
app=app,
|
||||
route_maps=[
|
||||
# GET with path params → ResourceTemplates
|
||||
RouteMap(
|
||||
methods=["GET"],
|
||||
pattern=r".*\{.*\}.*",
|
||||
mcp_type=MCPType.RESOURCE_TEMPLATE
|
||||
),
|
||||
# Other GETs → Resources
|
||||
RouteMap(
|
||||
methods=["GET"],
|
||||
pattern=r".*",
|
||||
mcp_type=MCPType.RESOURCE
|
||||
),
|
||||
# POST/PUT/DELETE → Tools (default)
|
||||
],
|
||||
)
|
||||
|
||||
# Now:
|
||||
# - GET /products → Resource
|
||||
# - GET /products/{id} → ResourceTemplate
|
||||
# - POST/PUT/DELETE → Tools
|
||||
```
|
||||
|
||||
<Tip>
|
||||
To learn more about customizing the conversion process, see the [OpenAPI Integration guide](/integrations/openapi).
|
||||
</Tip>
|
||||
|
||||
### Authentication and Headers
|
||||
|
||||
You can configure headers and other client options via the `httpx_client_kwargs` parameter. For example, to add authentication to your FastAPI app, you can pass a `headers` dictionary to the `httpx_client_kwargs` parameter:
|
||||
|
||||
```python {27-31}
|
||||
# Assumes the FastAPI app from above is already defined
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Add authentication to your FastAPI app
|
||||
from fastapi import Depends, Header
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
|
||||
security = HTTPBearer()
|
||||
|
||||
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
||||
if credentials.credentials != "secret-token":
|
||||
raise HTTPException(status_code=401, detail="Invalid authentication")
|
||||
return credentials.credentials
|
||||
|
||||
# Add a protected endpoint
|
||||
@app.get("/admin/stats", dependencies=[Depends(verify_token)])
|
||||
def get_admin_stats():
|
||||
return {
|
||||
"total_products": len(products_db),
|
||||
"categories": list(set(p.category for p in products_db.values()))
|
||||
}
|
||||
|
||||
# Create MCP server with authentication headers
|
||||
mcp = FastMCP.from_fastapi(
|
||||
app=app,
|
||||
httpx_client_kwargs={
|
||||
"headers": {
|
||||
"Authorization": "Bearer secret-token",
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Mounting an MCP Server
|
||||
|
||||
<VersionBadge version="2.3.1" />
|
||||
|
||||
In addition to generating servers, FastMCP can facilitate adding MCP servers to your existing FastAPI application. You can do this by mounting the MCP ASGI application.
|
||||
|
||||
### Basic Mounting
|
||||
|
||||
To mount an MCP server, you can use the `http_app` method on your FastMCP instance. This will return an ASGI application that can be mounted to your FastAPI application.
|
||||
|
||||
```python {23-30}
|
||||
from fastmcp import FastMCP
|
||||
from fastapi import FastAPI
|
||||
|
||||
# Create MCP server
|
||||
mcp = FastMCP("Analytics Tools")
|
||||
|
||||
@mcp.tool
|
||||
def analyze_pricing(category: str) -> dict:
|
||||
"""Analyze pricing for a category."""
|
||||
products = [p for p in products_db.values() if p.category == category]
|
||||
if not products:
|
||||
return {"error": f"No products in {category}"}
|
||||
|
||||
prices = [p.price for p in products]
|
||||
return {
|
||||
"category": category,
|
||||
"avg_price": round(sum(prices) / len(prices), 2),
|
||||
"min": min(prices),
|
||||
"max": max(prices),
|
||||
}
|
||||
|
||||
# Create ASGI app from MCP server
|
||||
mcp_app = mcp.http_app(path='/mcp')
|
||||
|
||||
# Key: Pass lifespan to FastAPI
|
||||
app = FastAPI(title="E-commerce API", lifespan=mcp_app.lifespan)
|
||||
|
||||
# Mount the MCP server
|
||||
app.mount("/analytics", mcp_app)
|
||||
|
||||
# Now: API at /products/*, MCP at /analytics/mcp/
|
||||
```
|
||||
|
||||
## Offering an LLM-Friendly API
|
||||
|
||||
A common pattern is to generate an MCP server from your FastAPI app and serve both interfaces from the same application. This provides an LLM-optimized interface alongside your regular API:
|
||||
|
||||
```python
|
||||
# Assumes the FastAPI app from above is already defined
|
||||
from fastmcp import FastMCP
|
||||
from fastapi import FastAPI
|
||||
|
||||
# 1. Generate MCP server from your API
|
||||
mcp = FastMCP.from_fastapi(app=app, name="E-commerce MCP")
|
||||
|
||||
# 2. Create the MCP's ASGI app
|
||||
mcp_app = mcp.http_app(path='/mcp')
|
||||
|
||||
# 3. Create a new FastAPI app that combines both sets of routes
|
||||
combined_app = FastAPI(
|
||||
title="E-commerce API with MCP",
|
||||
routes=[
|
||||
*mcp_app.routes, # MCP routes
|
||||
*app.routes, # Original API routes
|
||||
],
|
||||
lifespan=mcp_app.lifespan,
|
||||
)
|
||||
|
||||
# Now you have:
|
||||
# - Regular API: http://localhost:8000/products
|
||||
# - LLM-friendly MCP: http://localhost:8000/mcp/
|
||||
# Both served from the same FastAPI application!
|
||||
```
|
||||
|
||||
This approach lets you maintain a single codebase while offering both traditional REST endpoints and MCP-compatible endpoints for LLM clients.
|
||||
|
||||
## Key Considerations
|
||||
|
||||
### Operation IDs
|
||||
|
||||
FastAPI operation IDs become MCP component names. Always specify meaningful operation IDs:
|
||||
|
||||
```python
|
||||
# Good - explicit operation_id
|
||||
@app.get("/users/{user_id}", operation_id="get_user_by_id")
|
||||
def get_user(user_id: int):
|
||||
return {"id": user_id}
|
||||
|
||||
# Less ideal - auto-generated name
|
||||
@app.get("/users/{user_id}")
|
||||
def get_user(user_id: int):
|
||||
return {"id": user_id}
|
||||
```
|
||||
|
||||
### Lifespan Management
|
||||
|
||||
When mounting MCP servers, always pass the lifespan context:
|
||||
|
||||
```python
|
||||
# Correct - lifespan passed
|
||||
mcp_app = mcp.http_app(path='/mcp')
|
||||
app = FastAPI(lifespan=mcp_app.lifespan)
|
||||
app.mount("/mcp", mcp_app)
|
||||
|
||||
# Incorrect - missing lifespan
|
||||
app = FastAPI()
|
||||
app.mount("/mcp", mcp.http_app()) # Session manager won't initialize
|
||||
```
|
||||
|
||||
### Combining Lifespans
|
||||
|
||||
If your FastAPI app already has a lifespan (for database connections, startup tasks, etc.), you can't simply replace it with the MCP lifespan. Instead, you need to create a new lifespan function that manages both contexts. This ensures that both your app's initialization logic and the MCP server's session manager run properly:
|
||||
|
||||
```python
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Your existing lifespan
|
||||
@asynccontextmanager
|
||||
async def app_lifespan(app: FastAPI):
|
||||
# Startup
|
||||
print("Starting up the app...")
|
||||
# Initialize database, cache, etc.
|
||||
yield
|
||||
# Shutdown
|
||||
print("Shutting down the app...")
|
||||
|
||||
# Create MCP server
|
||||
mcp = FastMCP("Tools")
|
||||
mcp_app = mcp.http_app(path='/mcp')
|
||||
|
||||
# Combine both lifespans
|
||||
@asynccontextmanager
|
||||
async def combined_lifespan(app: FastAPI):
|
||||
# Run both lifespans
|
||||
async with app_lifespan(app):
|
||||
async with mcp_app.lifespan(app):
|
||||
yield
|
||||
|
||||
# Use the combined lifespan
|
||||
app = FastAPI(lifespan=combined_lifespan)
|
||||
app.mount("/mcp", mcp_app)
|
||||
```
|
||||
|
||||
This pattern ensures both your app's initialization logic and the MCP server's session manager are properly managed. The key is using nested `async with` statements - the inner context (MCP) will be initialized after the outer context (your app), and cleaned up before it. This maintains the correct initialization and cleanup order for all your resources.
|
||||
|
||||
### Performance Tips
|
||||
|
||||
1. **Use in-memory transport for testing** - Pass MCP servers directly to clients
|
||||
2. **Design purpose-built MCP tools** - Better than auto-converting complex APIs
|
||||
3. **Keep tool parameters simple** - LLMs perform better with focused interfaces
|
||||
|
||||
For more details on configuration options, see the [OpenAPI Integration guide](/integrations/openapi).
|
||||
174
docs/integrations/gemini-cli.mdx
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
---
|
||||
title: Gemini CLI 🤝 FastMCP
|
||||
sidebarTitle: Gemini CLI
|
||||
description: Install and use FastMCP servers in Gemini CLI
|
||||
icon: message-smile
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { LocalFocusTip } from "/snippets/local-focus.mdx"
|
||||
|
||||
<LocalFocusTip />
|
||||
|
||||
Gemini CLI supports MCP servers through multiple transport methods including STDIO, SSE, and HTTP, allowing you to extend Gemini's capabilities with custom tools, resources, and prompts from your FastMCP servers.
|
||||
|
||||
## Requirements
|
||||
|
||||
This integration uses STDIO transport to run your FastMCP server locally. For remote deployments, you can run your FastMCP server with HTTP or SSE transport and configure it directly using Gemini CLI's built-in MCP management commands.
|
||||
|
||||
## Create a Server
|
||||
|
||||
The examples in this guide will use the following simple dice-rolling server, saved as `server.py`.
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
## Install the Server
|
||||
|
||||
### FastMCP CLI
|
||||
<VersionBadge version="2.13.0" />
|
||||
|
||||
The easiest way to install a FastMCP server in Gemini CLI is using the `fastmcp install gemini-cli` command. This automatically handles the configuration, dependency management, and calls Gemini CLI's built-in MCP management system.
|
||||
|
||||
```bash
|
||||
fastmcp install gemini-cli server.py
|
||||
```
|
||||
|
||||
The install command supports the same `file.py:object` notation as the `run` command. If no object is specified, it will automatically look for a FastMCP server object named `mcp`, `server`, or `app` in your file:
|
||||
|
||||
```bash
|
||||
# These are equivalent if your server object is named 'mcp'
|
||||
fastmcp install gemini-cli server.py
|
||||
fastmcp install gemini-cli server.py:mcp
|
||||
|
||||
# Use explicit object name if your server has a different name
|
||||
fastmcp install gemini-cli server.py:my_custom_server
|
||||
```
|
||||
|
||||
The command will automatically configure the server with Gemini CLI's `gemini mcp add` command.
|
||||
|
||||
#### Dependencies
|
||||
|
||||
FastMCP provides flexible dependency management options for your Gemini CLI servers:
|
||||
|
||||
**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
|
||||
|
||||
```bash
|
||||
fastmcp install gemini-cli server.py --with pandas --with requests
|
||||
```
|
||||
|
||||
**Requirements file**: If you maintain a `requirements.txt` file with all your dependencies, use `--with-requirements` to install them:
|
||||
|
||||
```bash
|
||||
fastmcp install gemini-cli server.py --with-requirements requirements.txt
|
||||
```
|
||||
|
||||
**Editable packages**: For local packages under development, use `--with-editable` to install them in editable mode:
|
||||
|
||||
```bash
|
||||
fastmcp install gemini-cli server.py --with-editable ./my-local-package
|
||||
```
|
||||
|
||||
Alternatively, you can use a `fastmcp.json` configuration file (recommended):
|
||||
|
||||
```json fastmcp.json
|
||||
{
|
||||
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
|
||||
"source": {
|
||||
"path": "server.py",
|
||||
"entrypoint": "mcp"
|
||||
},
|
||||
"environment": {
|
||||
"dependencies": ["pandas", "requests"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Python Version and Project Configuration
|
||||
|
||||
Control the Python environment for your server with these options:
|
||||
|
||||
**Python version**: Use `--python` to specify which Python version your server requires. This ensures compatibility when your server needs specific Python features:
|
||||
|
||||
```bash
|
||||
fastmcp install gemini-cli server.py --python 3.11
|
||||
```
|
||||
|
||||
**Project directory**: Use `--project` to run your server within a specific project context. This tells `uv` to use the project's configuration files and virtual environment:
|
||||
|
||||
```bash
|
||||
fastmcp install gemini-cli server.py --project /path/to/my-project
|
||||
```
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
If your server needs environment variables (like API keys), you must include them:
|
||||
|
||||
```bash
|
||||
fastmcp install gemini-cli server.py --server-name "Weather Server" \
|
||||
--env API_KEY=your-api-key \
|
||||
--env DEBUG=true
|
||||
```
|
||||
|
||||
Or load them from a `.env` file:
|
||||
|
||||
```bash
|
||||
fastmcp install gemini-cli server.py --server-name "Weather Server" --env-file .env
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**Gemini CLI must be installed**. The integration looks for the Gemini CLI and uses the `gemini mcp add` command to register servers.
|
||||
</Warning>
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
For more control over the configuration, you can manually use Gemini CLI's built-in MCP management commands. This gives you direct control over how your server is launched:
|
||||
|
||||
```bash
|
||||
# Add a server with custom configuration
|
||||
gemini mcp add dice-roller uv -- run --with fastmcp fastmcp run server.py
|
||||
|
||||
# Add with environment variables
|
||||
gemini mcp add weather-server -e API_KEY=secret -e DEBUG=true uv -- run --with fastmcp fastmcp run server.py
|
||||
|
||||
# Add with specific scope (user, or project)
|
||||
gemini mcp add my-server --scope user uv -- run --with fastmcp fastmcp run server.py
|
||||
```
|
||||
|
||||
You can also manually specify Python versions and project directories in your Gemini CLI commands:
|
||||
|
||||
```bash
|
||||
# With specific Python version
|
||||
gemini mcp add ml-server uv -- run --python 3.11 --with fastmcp fastmcp run server.py
|
||||
|
||||
# Within a project directory
|
||||
gemini mcp add project-server uv -- run --project /path/to/project --with fastmcp fastmcp run server.py
|
||||
```
|
||||
|
||||
## Using the Server
|
||||
|
||||
Once your server is installed, you can start using your FastMCP server with Gemini CLI.
|
||||
|
||||
Try asking Gemini something like:
|
||||
|
||||
> "Roll some dice for me"
|
||||
|
||||
Gemini will automatically detect your `roll_dice` tool and use it to fulfill your request.
|
||||
|
||||
Gemini CLI can now access all the tools and prompts you've defined in your FastMCP server.
|
||||
|
||||
If your server provides prompts, you can use them as slash commands with `/prompt_name`.
|
||||
108
docs/integrations/gemini.mdx
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
---
|
||||
title: Gemini SDK 🤝 FastMCP
|
||||
sidebarTitle: Gemini SDK
|
||||
description: Connect FastMCP servers to the Google Gemini SDK
|
||||
icon: message-code
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
Google's Gemini API includes built-in support for MCP servers in their Python and JavaScript SDKs, allowing you to connect directly to MCP servers and use their tools seamlessly with Gemini models.
|
||||
|
||||
## Gemini Python SDK
|
||||
|
||||
Google's [Gemini Python SDK](https://ai.google.dev/gemini-api/docs) can use FastMCP clients directly.
|
||||
|
||||
<Note>
|
||||
Google's MCP integration is currently experimental and available in the Python and JavaScript SDKs. The API automatically calls MCP tools when needed and can connect to both local and remote MCP servers.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
Currently, Gemini's MCP support only accesses **tools** from MCP servers—it queries the `list_tools` endpoint and exposes those functions to the AI. Other MCP features like resources and prompts are not currently supported.
|
||||
</Tip>
|
||||
|
||||
### Create a Server
|
||||
|
||||
First, create a FastMCP server with the tools you want to expose. For this example, we'll create a server with a single tool that rolls dice.
|
||||
|
||||
```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()
|
||||
```
|
||||
|
||||
### Call the Server
|
||||
|
||||
|
||||
To use the Gemini API with MCP, you'll need to install the Google Generative AI SDK:
|
||||
|
||||
```bash
|
||||
pip install google-genai
|
||||
```
|
||||
|
||||
You'll also need to authenticate with Google. You can do this by setting the `GEMINI_API_KEY` environment variable. Consult the Gemini SDK documentation for more information.
|
||||
|
||||
```bash
|
||||
export GEMINI_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
Gemini's SDK interacts directly with the MCP client session. To call the server, you'll need to instantiate a FastMCP client, enter its connection context, and pass the client session to the Gemini SDK.
|
||||
|
||||
```python {5, 9, 15}
|
||||
from fastmcp import Client
|
||||
from google import genai
|
||||
import asyncio
|
||||
|
||||
mcp_client = Client("server.py")
|
||||
gemini_client = genai.Client()
|
||||
|
||||
async def main():
|
||||
async with mcp_client:
|
||||
response = await gemini_client.aio.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents="Roll 3 dice!",
|
||||
config=genai.types.GenerateContentConfig(
|
||||
temperature=0,
|
||||
tools=[mcp_client.session], # Pass the FastMCP client session
|
||||
),
|
||||
)
|
||||
print(response.text)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
If you run this code, you'll see output like:
|
||||
|
||||
```text
|
||||
Okay, I rolled 3 dice and got a 5, 4, and 1.
|
||||
```
|
||||
|
||||
### Remote & Authenticated Servers
|
||||
|
||||
In the above example, we connected to our local server using `stdio` transport. Because we're using a FastMCP client, you can also connect to any local or remote MCP server, using any [transport](/clients/transports) or [auth](/clients/auth) method supported by FastMCP, simply by changing the client configuration.
|
||||
|
||||
For example, to connect to a remote, authenticated server, you can use the following client:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.auth import BearerAuth
|
||||
|
||||
mcp_client = Client(
|
||||
"https://my-server.com/mcp/",
|
||||
auth=BearerAuth("<your-token>"),
|
||||
)
|
||||
```
|
||||
|
||||
The rest of the code remains the same.
|
||||
|
||||
|
||||
209
docs/integrations/github.mdx
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
---
|
||||
title: GitHub OAuth 🤝 FastMCP
|
||||
sidebarTitle: GitHub
|
||||
description: Secure your FastMCP server with GitHub OAuth
|
||||
icon: github
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.12.0" />
|
||||
|
||||
This guide shows you how to secure your FastMCP server using **GitHub OAuth**. Since GitHub doesn't support Dynamic Client Registration, this integration uses the [**OAuth Proxy**](/servers/auth/oauth-proxy) pattern to bridge GitHub's traditional OAuth with MCP's authentication requirements.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you will need:
|
||||
1. A **[GitHub Account](https://github.com/)** with access to create OAuth Apps
|
||||
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
|
||||
|
||||
### Step 1: Create a GitHub OAuth App
|
||||
|
||||
Create an OAuth App in your GitHub settings to get the credentials needed for authentication:
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to OAuth Apps">
|
||||
Go to **Settings → Developer settings → OAuth Apps** in your GitHub account, or visit [github.com/settings/developers](https://github.com/settings/developers).
|
||||
|
||||
Click **"New OAuth App"** to create a new application.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Your OAuth App">
|
||||
Fill in the application details:
|
||||
|
||||
- **Application name**: Choose a name users will recognize (e.g., "My FastMCP Server")
|
||||
- **Homepage URL**: Your application's homepage or documentation URL
|
||||
- **Authorization callback URL**: Your server URL + `/auth/callback` (e.g., `http://localhost:8000/auth/callback`)
|
||||
|
||||
<Warning>
|
||||
The callback URL must match exactly. The default path is `/auth/callback`, but you can customize it using the `redirect_path` parameter. For local development, GitHub allows `http://localhost` URLs. For production, you must use HTTPS.
|
||||
</Warning>
|
||||
|
||||
<Tip>
|
||||
If you want to use a custom callback path (e.g., `/auth/github/callback`), make sure to set the same path in both your GitHub OAuth App settings and the `redirect_path` parameter when configuring the GitHubProvider.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Save Your Credentials">
|
||||
After creating the app, you'll see:
|
||||
|
||||
- **Client ID**: A public identifier like `Ov23liAbcDefGhiJkLmN`
|
||||
- **Client Secret**: Click "Generate a new client secret" and save the value securely
|
||||
|
||||
<Tip>
|
||||
Store these credentials securely. Never commit them to version control. Use environment variables or a secrets manager in production.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 2: FastMCP Configuration
|
||||
|
||||
Create your FastMCP server using the `GitHubProvider`, which handles GitHub's OAuth quirks automatically:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.github import GitHubProvider
|
||||
|
||||
# The GitHubProvider handles GitHub's token format and validation
|
||||
auth_provider = GitHubProvider(
|
||||
client_id="Ov23liAbcDefGhiJkLmN", # Your GitHub OAuth App Client ID
|
||||
client_secret="github_pat_...", # Your GitHub OAuth App Client Secret
|
||||
base_url="http://localhost:8000", # Must match your OAuth App configuration
|
||||
# redirect_path="/auth/callback" # Default value, customize if needed
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="GitHub Secured App", auth=auth_provider)
|
||||
|
||||
# Add a protected tool to test authentication
|
||||
@mcp.tool
|
||||
async def get_user_info() -> dict:
|
||||
"""Returns information about the authenticated GitHub user."""
|
||||
from fastmcp.server.dependencies import get_access_token
|
||||
|
||||
token = get_access_token()
|
||||
# The GitHubProvider stores user data in token claims
|
||||
return {
|
||||
"github_user": token.claims.get("login"),
|
||||
"name": token.claims.get("name"),
|
||||
"email": token.claims.get("email")
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Running the Server
|
||||
|
||||
Start your FastMCP server with HTTP transport to enable OAuth flows:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py --transport http --port 8000
|
||||
```
|
||||
|
||||
Your server is now running and protected by GitHub OAuth authentication.
|
||||
|
||||
### Testing with a Client
|
||||
|
||||
Create a test client that authenticates with your GitHub-protected server:
|
||||
|
||||
```python test_client.py
|
||||
from fastmcp import Client
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
# The client will automatically handle GitHub OAuth
|
||||
async with Client("http://localhost:8000/mcp/", auth="oauth") as client:
|
||||
# First-time connection will open GitHub login in your browser
|
||||
print("✓ Authenticated with GitHub!")
|
||||
|
||||
# Test the protected tool
|
||||
result = await client.call_tool("get_user_info")
|
||||
print(f"GitHub user: {result['github_user']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
When you run the client for the first time:
|
||||
1. Your browser will open to GitHub's authorization page
|
||||
2. After you authorize the app, you'll be redirected back
|
||||
3. The client receives the token and can make authenticated requests
|
||||
|
||||
<Info>
|
||||
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
|
||||
</Info>
|
||||
|
||||
## Environment Variables
|
||||
|
||||
<VersionBadge version="2.12.1" />
|
||||
|
||||
For production deployments, use environment variables instead of hardcoding credentials.
|
||||
|
||||
### Provider Selection
|
||||
|
||||
Setting this environment variable allows the GitHub provider to be used automatically without explicitly instantiating it in code.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
|
||||
Set to `fastmcp.server.auth.providers.github.GitHubProvider` to use GitHub authentication.
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
### GitHub-Specific Configuration
|
||||
|
||||
These environment variables provide default values for the GitHub provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GITHUB_CLIENT_ID" required>
|
||||
Your GitHub OAuth App Client ID (e.g., `Ov23liAbcDefGhiJkLmN`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GITHUB_CLIENT_SECRET" required>
|
||||
Your GitHub OAuth App Client Secret
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GITHUB_BASE_URL" default="http://localhost:8000">
|
||||
Public URL of your FastMCP server for OAuth callbacks
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GITHUB_REDIRECT_PATH" default="/auth/callback">
|
||||
Redirect path configured in your GitHub OAuth App
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GITHUB_REQUIRED_SCOPES" default='["user"]'>
|
||||
Comma-, space-, or JSON-separated list of required GitHub scopes (e.g., `user repo` or `["user","repo"]`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GITHUB_TIMEOUT_SECONDS" default="10">
|
||||
HTTP request timeout for GitHub API calls
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
Example `.env` file:
|
||||
```bash
|
||||
# Use the GitHub provider
|
||||
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.github.GitHubProvider
|
||||
|
||||
# GitHub OAuth credentials
|
||||
FASTMCP_SERVER_AUTH_GITHUB_CLIENT_ID=Ov23liAbcDefGhiJkLmN
|
||||
FASTMCP_SERVER_AUTH_GITHUB_CLIENT_SECRET=github_pat_...
|
||||
FASTMCP_SERVER_AUTH_GITHUB_BASE_URL=https://your-server.com
|
||||
FASTMCP_SERVER_AUTH_GITHUB_REQUIRED_SCOPES=user,repo
|
||||
```
|
||||
|
||||
With environment variables set, your server code simplifies to:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Authentication is automatically configured from environment
|
||||
mcp = FastMCP(name="GitHub Secured App")
|
||||
|
||||
@mcp.tool
|
||||
async def list_repos() -> list[str]:
|
||||
"""List the authenticated user's repositories."""
|
||||
# Your tool implementation here
|
||||
pass
|
||||
```
|
||||
222
docs/integrations/google.mdx
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
---
|
||||
title: Google OAuth 🤝 FastMCP
|
||||
sidebarTitle: Google
|
||||
description: Secure your FastMCP server with Google OAuth
|
||||
icon: google
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
||||
<VersionBadge version="2.12.0" />
|
||||
|
||||
This guide shows you how to secure your FastMCP server using **Google OAuth**. Since Google doesn't support Dynamic Client Registration, this integration uses the [**OAuth Proxy**](/servers/auth/oauth-proxy) pattern to bridge Google's traditional OAuth with MCP's authentication requirements.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you will need:
|
||||
1. A **[Google Cloud Account](https://console.cloud.google.com/)** with access to create OAuth 2.0 Client IDs
|
||||
2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)
|
||||
|
||||
### Step 1: Create a Google OAuth 2.0 Client ID
|
||||
|
||||
Create an OAuth 2.0 Client ID in your Google Cloud Console to get the credentials needed for authentication:
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to OAuth Consent Screen">
|
||||
Go to the [Google Cloud Console](https://console.cloud.google.com/apis/credentials) and select your project (or create a new one).
|
||||
|
||||
First, configure the OAuth consent screen by navigating to **APIs & Services → OAuth consent screen**. Choose "External" for testing or "Internal" for G Suite organizations.
|
||||
</Step>
|
||||
|
||||
<Step title="Create OAuth 2.0 Client ID">
|
||||
Navigate to **APIs & Services → Credentials** and click **"+ CREATE CREDENTIALS"** → **"OAuth client ID"**.
|
||||
|
||||
Configure your OAuth client:
|
||||
|
||||
- **Application type**: Web application
|
||||
- **Name**: Choose a descriptive name (e.g., "FastMCP Server")
|
||||
- **Authorized JavaScript origins**: Add your server's base URL (e.g., `http://localhost:8000`)
|
||||
- **Authorized redirect URIs**: Add your server URL + `/auth/callback` (e.g., `http://localhost:8000/auth/callback`)
|
||||
|
||||
<Warning>
|
||||
The redirect URI must match exactly. The default path is `/auth/callback`, but you can customize it using the `redirect_path` parameter. For local development, Google allows `http://localhost` URLs with various ports. For production, you must use HTTPS.
|
||||
</Warning>
|
||||
|
||||
<Tip>
|
||||
If you want to use a custom callback path (e.g., `/auth/google/callback`), make sure to set the same path in both your Google OAuth Client settings and the `redirect_path` parameter when configuring the GoogleProvider.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Save Your Credentials">
|
||||
After creating the client, you'll receive:
|
||||
|
||||
- **Client ID**: A string ending in `.apps.googleusercontent.com`
|
||||
- **Client Secret**: A string starting with `GOCSPX-`
|
||||
|
||||
Download the JSON credentials or copy these values securely.
|
||||
|
||||
<Tip>
|
||||
Store these credentials securely. Never commit them to version control. Use environment variables or a secrets manager in production.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 2: FastMCP Configuration
|
||||
|
||||
Create your FastMCP server using the `GoogleProvider`, which handles Google's OAuth flow automatically:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.server.auth.providers.google import GoogleProvider
|
||||
|
||||
# The GoogleProvider handles Google's token format and validation
|
||||
auth_provider = GoogleProvider(
|
||||
client_id="123456789.apps.googleusercontent.com", # Your Google OAuth Client ID
|
||||
client_secret="GOCSPX-abc123...", # Your Google OAuth Client Secret
|
||||
base_url="http://localhost:8000", # Must match your OAuth configuration
|
||||
required_scopes=[ # Request user information
|
||||
"openid",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
],
|
||||
# redirect_path="/auth/callback" # Default value, customize if needed
|
||||
)
|
||||
|
||||
mcp = FastMCP(name="Google Secured App", auth=auth_provider)
|
||||
|
||||
# Add a protected tool to test authentication
|
||||
@mcp.tool
|
||||
async def get_user_info() -> dict:
|
||||
"""Returns information about the authenticated Google user."""
|
||||
from fastmcp.server.dependencies import get_access_token
|
||||
|
||||
token = get_access_token()
|
||||
# The GoogleProvider stores user data in token claims
|
||||
return {
|
||||
"google_id": token.claims.get("sub"),
|
||||
"email": token.claims.get("email"),
|
||||
"name": token.claims.get("name"),
|
||||
"picture": token.claims.get("picture"),
|
||||
"locale": token.claims.get("locale")
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Running the Server
|
||||
|
||||
Start your FastMCP server with HTTP transport to enable OAuth flows:
|
||||
|
||||
```bash
|
||||
fastmcp run server.py --transport http --port 8000
|
||||
```
|
||||
|
||||
Your server is now running and protected by Google OAuth authentication.
|
||||
|
||||
### Testing with a Client
|
||||
|
||||
Create a test client that authenticates with your Google-protected server:
|
||||
|
||||
```python test_client.py
|
||||
from fastmcp import Client
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
# The client will automatically handle Google OAuth
|
||||
async with Client("http://localhost:8000/mcp/", auth="oauth") as client:
|
||||
# First-time connection will open Google login in your browser
|
||||
print("✓ Authenticated with Google!")
|
||||
|
||||
# Test the protected tool
|
||||
result = await client.call_tool("get_user_info")
|
||||
print(f"Google user: {result['email']}")
|
||||
print(f"Name: {result['name']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
When you run the client for the first time:
|
||||
1. Your browser will open to Google's authorization page
|
||||
2. Sign in with your Google account and grant the requested permissions
|
||||
3. After authorization, you'll be redirected back
|
||||
4. The client receives the token and can make authenticated requests
|
||||
|
||||
<Info>
|
||||
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
|
||||
</Info>
|
||||
|
||||
## Environment Variables
|
||||
|
||||
<VersionBadge version="2.12.1" />
|
||||
|
||||
For production deployments, use environment variables instead of hardcoding credentials.
|
||||
|
||||
### Provider Selection
|
||||
|
||||
Setting this environment variable allows the Google provider to be used automatically without explicitly instantiating it in code.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
|
||||
Set to `fastmcp.server.auth.providers.google.GoogleProvider` to use Google authentication.
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
### Google-Specific Configuration
|
||||
|
||||
These environment variables provide default values for the Google provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.
|
||||
|
||||
<Card>
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_ID" required>
|
||||
Your Google OAuth 2.0 Client ID (e.g., `123456789.apps.googleusercontent.com`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_SECRET" required>
|
||||
Your Google OAuth 2.0 Client Secret (e.g., `GOCSPX-abc123...`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GOOGLE_BASE_URL" default="http://localhost:8000">
|
||||
Public URL of your FastMCP server for OAuth callbacks
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GOOGLE_REDIRECT_PATH" default="/auth/callback">
|
||||
Redirect path configured in your Google OAuth Client
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GOOGLE_REQUIRED_SCOPES" default="[]">
|
||||
Comma-, space-, or JSON-separated list of required Google scopes (e.g., `"openid,https://www.googleapis.com/auth/userinfo.email"` or `["openid", "https://www.googleapis.com/auth/userinfo.email"]`)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="FASTMCP_SERVER_AUTH_GOOGLE_TIMEOUT_SECONDS" default="10">
|
||||
HTTP request timeout for Google API calls
|
||||
</ParamField>
|
||||
</Card>
|
||||
|
||||
Example `.env` file:
|
||||
```bash
|
||||
# Use the Google provider
|
||||
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.google.GoogleProvider
|
||||
|
||||
# Google OAuth credentials
|
||||
FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_ID=123456789.apps.googleusercontent.com
|
||||
FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_SECRET=GOCSPX-abc123...
|
||||
FASTMCP_SERVER_AUTH_GOOGLE_BASE_URL=https://your-server.com
|
||||
FASTMCP_SERVER_AUTH_GOOGLE_REQUIRED_SCOPES=openid,https://www.googleapis.com/auth/userinfo.email
|
||||
```
|
||||
|
||||
With environment variables set, your server code simplifies to:
|
||||
|
||||
```python server.py
|
||||
from fastmcp import FastMCP
|
||||
|
||||
# Authentication is automatically configured from environment
|
||||
mcp = FastMCP(name="Google Secured App")
|
||||
|
||||
@mcp.tool
|
||||
async def protected_tool(query: str) -> str:
|
||||
"""A tool that requires Google authentication to access."""
|
||||
# Your tool implementation here
|
||||
return f"Processing authenticated request: {query}"
|
||||
```
|
||||