Improve env vars for marvin (#1972)

This commit is contained in:
Jeremiah Lowin 2025-09-30 16:38:51 -04:00 committed by claude[bot]
commit a3fced51df
629 changed files with 133244 additions and 0 deletions

7
.ccignore Normal file
View file

@ -0,0 +1,7 @@
.pre-commit-config.yaml
.github/
docs/changelog.mdx
docs/python-sdk/
examples/
src/fastmcp/contrib/
tests/contrib/

View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
../AGENTS.md

20
.github/dependabot.yml vendored Normal file
View 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
View 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
View 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:
- "*"

View 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 }}

View 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 }}"
}
}

View 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
View 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
View 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
View 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
View 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 }}

View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
3.12

265
AGENTS.md Normal file
View 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
View file

@ -0,0 +1 @@
AGENTS.md

128
CODE_OF_CONDUCT.md Normal file
View 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
View 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
View 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/)*
[![Docs](https://img.shields.io/badge/docs-gofastmcp.com-blue)](https://gofastmcp.com)
[![PyPI - Version](https://img.shields.io/pypi/v/fastmcp.svg)](https://pypi.org/project/fastmcp)
[![Tests](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml/badge.svg)](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
[![License](https://img.shields.io/github/license/jlowin/fastmcp.svg)](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
View 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
View 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
View file

@ -0,0 +1,2 @@
changelog.mdx
python-sdk/

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

View 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"
}

View 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"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

2013
docs/changelog.mdx Normal file

File diff suppressed because it is too large Load diff

View 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
View 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
View 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>

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View file

@ -0,0 +1,3 @@
a:has(svg.icon) {
border: none !important;
}

20
docs/css/style.css Normal file
View 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);
}

View 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;
}

View 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" />

View 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.

View 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).

View 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.

View 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.

View 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
View 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
View 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"
}
}

View 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

View 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).

View 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.

View 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
View 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
View 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
```

View 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.
![Enable Dynamic Client Registration](./images/authkit/enable_dcr.png)
</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")
```

View 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
View 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}"
```

View 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.

View 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`.

View 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()
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View 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:
![Cursor install prompt](./cursor-install-mcp.png)
#### 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.

View 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")
```

View 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

View 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).

View 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`.

View 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.

View 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
```

View 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}"
```

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