Merge branch 'main' into elicitation

This commit is contained in:
Jeremiah Lowin 2025-06-28 08:26:55 -04:00
commit 11d2bacbb4
190 changed files with 14832 additions and 1888 deletions

13
.github/release.yml vendored
View file

@ -9,7 +9,7 @@ changelog:
- feature
exclude:
labels:
- breaking change
- contrib
- title: Enhancements 🔧
labels:
@ -17,22 +17,31 @@ changelog:
exclude:
labels:
- breaking change
- contrib
- title: Fixes 🐞
labels:
- bug
exclude:
labels:
- breaking change
- contrib
- title: Breaking Changes 🛫
labels:
- breaking change
exclude:
labels:
- contrib
- title: Docs 📚
labels:
- documentation
- title: Examples & Contrib 💡
labels:
- example
- contrib
- title: Dependencies 📦
labels:
- dependencies

View file

@ -32,4 +32,6 @@ async with Client(transport=StreamableHttpTransport(server_url)) as client:
## Development Workflow
- You must always run pre-commit if you open a PR, because it is run as part of a required check.
- When opening PRs, apply labels appropriately for bugs/breaking changes/enhancements/features. Generally, improvements are enhancements (not features) unless told otherwise.
- When opening PRs, apply labels appropriately for bugs/breaking changes/enhancements/features. Generally, improvements are enhancements (not features) unless told otherwise.
- NEVER modify files in docs/python-sdk/**, as they are auto-generated.
- Use # type: ignore[attr-defined] in unit tests when accessing an MCP result of indeterminate type instead of asserting its type

View file

@ -349,7 +349,7 @@ mcp.run(transport="stdio") # Default, so transport argument is optional
**Streamable HTTP**: Recommended for web deployments.
```python
mcp.run(transport="streamable-http", host="127.0.0.1", port=8000, path="/mcp")
mcp.run(transport="http", host="127.0.0.1", port=8000, path="/mcp")
```
**SSE**: For compatibility with existing SSE clients.

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

View file

@ -2,6 +2,103 @@
icon: "list-check"
---
<Update label="v2.9.0" description="2024-06-23">
## [v2.9.0: Stuck in the Middleware With You](https://github.com/jlowin/fastmcp/releases/tag/v2.9.0)
FastMCP 2.9 introduces two important features that push beyond the basic MCP protocol: MCP Middleware and server-side type conversion.
### MCP Middleware
MCP middleware lets you intercept and modify requests and responses at the protocol level, giving you powerful capabilities for logging, authentication, validation, and more. This is particularly useful for building production-ready MCP servers that need sophisticated request handling.
### Server-side Type Conversion
This release also introduces server-side type conversion for prompt arguments, ensuring that data is properly formatted before being passed to your functions. This reduces the burden on individual tools and prompts to handle type validation and conversion.
## What's Changed
### New Features 🎉
* Add File utility for binary data by [@gorocode](https://github.com/gorocode) in [#843](https://github.com/jlowin/fastmcp/pull/843)
* Consolidate prefix logic into FastMCP methods by [@jlowin](https://github.com/jlowin) in [#861](https://github.com/jlowin/fastmcp/pull/861)
* Add MCP Middleware by [@jlowin](https://github.com/jlowin) in [#870](https://github.com/jlowin/fastmcp/pull/870)
* Implement server-side type conversion for prompt arguments by [@jlowin](https://github.com/jlowin) in [#908](https://github.com/jlowin/fastmcp/pull/908)
### Enhancements 🔧
* Fix tool description indentation issue by [@zfflxx](https://github.com/zfflxx) in [#845](https://github.com/jlowin/fastmcp/pull/845)
* Add version parameter to FastMCP constructor by [@mkyutani](https://github.com/mkyutani) in [#842](https://github.com/jlowin/fastmcp/pull/842)
* Update version to not be positional by [@jlowin](https://github.com/jlowin) in [#848](https://github.com/jlowin/fastmcp/pull/848)
* Add key to component by [@jlowin](https://github.com/jlowin) in [#869](https://github.com/jlowin/fastmcp/pull/869)
* Add session_id property to Context for data sharing by [@jlowin](https://github.com/jlowin) in [#881](https://github.com/jlowin/fastmcp/pull/881)
* Fix CORS documentation example by [@jlowin](https://github.com/jlowin) in [#895](https://github.com/jlowin/fastmcp/pull/895)
### Fixes 🐞
* "report_progress missing passing related_request_id causes notifications not working" by [@alexsee](https://github.com/alexsee) in [#838](https://github.com/jlowin/fastmcp/pull/838)
* Fix JWT issuer validation to support string values per RFC 7519 by [@jlowin](https://github.com/jlowin) in [#892](https://github.com/jlowin/fastmcp/pull/892)
* Fix BearerAuthProvider audience type annotations by [@jlowin](https://github.com/jlowin) in [#894](https://github.com/jlowin/fastmcp/pull/894)
### Docs 📚
* Add CLAUDE.md development guidelines by [@jlowin](https://github.com/jlowin) in [#880](https://github.com/jlowin/fastmcp/pull/880)
* Update context docs for session_id property by [@jlowin](https://github.com/jlowin) in [#882](https://github.com/jlowin/fastmcp/pull/882)
* Add API reference by [@zzstoatzz](https://github.com/zzstoatzz) in [#893](https://github.com/jlowin/fastmcp/pull/893)
* Fix API ref rendering by [@zzstoatzz](https://github.com/zzstoatzz) in [#900](https://github.com/jlowin/fastmcp/pull/900)
* Simplify docs nav by [@jlowin](https://github.com/jlowin) in [#902](https://github.com/jlowin/fastmcp/pull/902)
* Add fastmcp inspect command by [@jlowin](https://github.com/jlowin) in [#904](https://github.com/jlowin/fastmcp/pull/904)
* Update client docs by [@jlowin](https://github.com/jlowin) in [#912](https://github.com/jlowin/fastmcp/pull/912)
* Update docs nav by [@jlowin](https://github.com/jlowin) in [#913](https://github.com/jlowin/fastmcp/pull/913)
* Update integration documentation for Claude Desktop, ChatGPT, and Claude Code by [@jlowin](https://github.com/jlowin) in [#915](https://github.com/jlowin/fastmcp/pull/915)
* Add http as an alias for streamable http by [@jlowin](https://github.com/jlowin) in [#917](https://github.com/jlowin/fastmcp/pull/917)
* Clean up parameter documentation by [@jlowin](https://github.com/jlowin) in [#918](https://github.com/jlowin/fastmcp/pull/918)
* Add middleware examples for timing, logging, rate limiting, and error handling by [@jlowin](https://github.com/jlowin) in [#919](https://github.com/jlowin/fastmcp/pull/919)
* ControlFlow → FastMCP rename by [@jlowin](https://github.com/jlowin) in [#922](https://github.com/jlowin/fastmcp/pull/922)
### Examples & Contrib 💡
* Add contrib.mcp_mixin support for annotations by [@rsp2k](https://github.com/rsp2k) in [#860](https://github.com/jlowin/fastmcp/pull/860)
* Add ATProto (Bluesky) MCP Server Example by [@zzstoatzz](https://github.com/zzstoatzz) in [#916](https://github.com/jlowin/fastmcp/pull/916)
* Fix path in atproto example pyproject by [@zzstoatzz](https://github.com/zzstoatzz) in [#920](https://github.com/jlowin/fastmcp/pull/920)
* Remove uv source in example by [@zzstoatzz](https://github.com/zzstoatzz) in [#921](https://github.com/jlowin/fastmcp/pull/921)
## New Contributors
* [@alexsee](https://github.com/alexsee) made their first contribution in [#838](https://github.com/jlowin/fastmcp/pull/838)
* [@zfflxx](https://github.com/zfflxx) made their first contribution in [#845](https://github.com/jlowin/fastmcp/pull/845)
* [@mkyutani](https://github.com/mkyutani) made their first contribution in [#842](https://github.com/jlowin/fastmcp/pull/842)
* [@gorocode](https://github.com/gorocode) made their first contribution in [#843](https://github.com/jlowin/fastmcp/pull/843)
* [@rsp2k](https://github.com/rsp2k) made their first contribution in [#860](https://github.com/jlowin/fastmcp/pull/860)
* [@owtaylor](https://github.com/owtaylor) made their first contribution in [#897](https://github.com/jlowin/fastmcp/pull/897)
* [@Jason-CKY](https://github.com/Jason-CKY) made their first contribution in [#906](https://github.com/jlowin/fastmcp/pull/906)
**Full Changelog**: [v2.8.1...v2.9.0](https://github.com/jlowin/fastmcp/compare/v2.8.1...v2.9.0)
</Update>
<Update label="v2.8.1" description="2024-06-15">
## [v2.8.1: Sound Judgement](https://github.com/jlowin/fastmcp/releases/tag/v2.8.1)
2.8.1 introduces audio support, as well as minor fixes and updates for deprecated features.
### Audio Support
This release adds support for audio content in MCP tools and resources, expanding FastMCP's multimedia capabilities beyond text and images.
## What's Changed
### New Features 🎉
* Add audio support by [@jlowin](https://github.com/jlowin) in [#833](https://github.com/jlowin/fastmcp/pull/833)
### Enhancements 🔧
* Add flag for disabling deprecation warnings by [@jlowin](https://github.com/jlowin) in [#802](https://github.com/jlowin/fastmcp/pull/802)
* Add examples to Tool Arg Param transformation by [@strawgate](https://github.com/strawgate) in [#806](https://github.com/jlowin/fastmcp/pull/806)
### Fixes 🐞
* Restore .settings access as deprecated by [@jlowin](https://github.com/jlowin) in [#800](https://github.com/jlowin/fastmcp/pull/800)
* Ensure handling of false http kwargs correctly; removed unused kwarg by [@jlowin](https://github.com/jlowin) in [#804](https://github.com/jlowin/fastmcp/pull/804)
* Bump mcp 1.9.4 by [@jlowin](https://github.com/jlowin) in [#835](https://github.com/jlowin/fastmcp/pull/835)
### Docs 📚
* Update changelog for 2.8.0 by [@jlowin](https://github.com/jlowin) in [#794](https://github.com/jlowin/fastmcp/pull/794)
* Update welcome docs by [@jlowin](https://github.com/jlowin) in [#808](https://github.com/jlowin/fastmcp/pull/808)
* Update headers in docs by [@jlowin](https://github.com/jlowin) in [#809](https://github.com/jlowin/fastmcp/pull/809)
* Add MCP group to tutorials by [@jlowin](https://github.com/jlowin) in [#810](https://github.com/jlowin/fastmcp/pull/810)
* Add Community section to documentation by [@zzstoatzz](https://github.com/zzstoatzz) in [#819](https://github.com/jlowin/fastmcp/pull/819)
* Add 2.8 update by [@jlowin](https://github.com/jlowin) in [#821](https://github.com/jlowin/fastmcp/pull/821)
* Embed YouTube videos in community showcase by [@zzstoatzz](https://github.com/zzstoatzz) in [#820](https://github.com/jlowin/fastmcp/pull/820)
### Other Changes 🦾
* Ensure http args are passed through by [@jlowin](https://github.com/jlowin) in [#803](https://github.com/jlowin/fastmcp/pull/803)
* Fix install link in readme by [@jlowin](https://github.com/jlowin) in [#836](https://github.com/jlowin/fastmcp/pull/836)
**Full Changelog**: [v2.8.0...v2.8.1](https://github.com/jlowin/fastmcp/compare/v2.8.0...v2.8.1)
</Update>
<Update label="v2.8.0" description="2024-06-10">
## [v2.8.0: Transform and Roll Out](https://github.com/jlowin/fastmcp/releases/tag/v2.8.0)

View file

@ -102,14 +102,14 @@ config = {
"mcpServers": {
"server_name": {
# Remote HTTP/SSE server
"transport": "streamable-http", # or "sse"
"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"
"transport": "stdio",
"command": "python",
"args": ["./server.py", "--verbose"],
"env": {"DEBUG": "true"},

View file

@ -11,7 +11,7 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
MCP servers can emit log messages to clients. The client can handle these logs through a log handler callback.
## Setting Up Log Handling
## Log Handler
Provide a `log_handler` function when creating the client:
@ -31,13 +31,27 @@ client = Client(
)
```
## LogMessage Structure
### Handler Parameters
The `log_handler` receives a `LogMessage` object with:
The `log_handler` is called every time a log message is received. It receives a `LogMessage` object:
- **`level`**: Log level (e.g., "debug", "info", "warning", "error")
- **`logger`**: Logger name (optional, may be None)
- **`data`**: The actual log message content
<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="Any">
The actual log message content
</ResponseField>
</Expandable>
</ResponseField>
</Card>
```python
async def detailed_log_handler(message: LogMessage):
@ -51,13 +65,12 @@ async def detailed_log_handler(message: LogMessage):
## Default Log Handling
If you don't provide a custom `log_handler`, FastMCP uses a default handler that emits DEBUG level logs:
If you don't provide a custom `log_handler`, FastMCP uses a default handler that emits a DEBUG-level FastMCP log for every log message received from the server, which is useful for visibility without polluting your own logs.
```python
# Without custom handler - uses default DEBUG logging
client = Client("my_mcp_server.py")
async with client:
# Server logs will be emitted at DEBUG level
# Server logs will be emitted at DEBUG level automatically
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.

View file

@ -11,7 +11,7 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
MCP servers can report progress during long-running operations. The client can receive these updates through a progress handler.
## Setting Up Progress Handling
## Progress Handler
Set a progress handler when creating the client:
@ -35,6 +35,26 @@ client = Client(
)
```
### 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:
@ -48,12 +68,3 @@ async with client:
progress_handler=my_progress_handler
)
```
## Handler Parameters
The progress handler receives:
- **`progress`** (float): Current progress value
- **`total`** (float | None): Expected total value (may be None)
- **`message`** (str | None): Optional status message (may be None)

View file

@ -5,13 +5,13 @@ description: Handle server-initiated LLM sampling requests.
icon: robot
---
import { VersionBadge } from '/snippets/version-badge.mdx'
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.
## Setting Up Sampling Handling
## Sampling Handler
Provide a `sampling_handler` function when creating the client:
@ -38,26 +38,88 @@ client = Client(
)
```
## Handler Parameters
### Handler Parameters
The sampling handler receives three parameters:
### SamplingMessage
<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>
- **`role`**: Message role (e.g., "user", "assistant", "system")
- **`content`**: Message content (usually has `.text` attribute)
<ResponseField name="content" type="TextContent | ImageContent | AudioContent">
The content of the message.
### SamplingParams
TextContent is most common, and has a `.text` attribute.
</ResponseField>
- **`systemPrompt`**: System prompt string (optional)
- **`maxTokens`**: Maximum tokens to generate (optional)
- **`temperature`**: Sampling temperature (optional)
- **`topP`**: Top-p sampling parameter (optional)
- **`stopSequences`**: List of stop sequences (optional)
</Expandable>
</ResponseField>
<ResponseField name="SamplingParams" type="Sampling Parameters Object">
<Expandable title="attributes">
<ResponseField name="messages" type="list[SamplingMessage]">
The messages to sample from
</ResponseField>
### RequestContext
<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>
- **`request_id`**: Unique identifier for the sampling request
<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
@ -75,10 +137,10 @@ async def basic_sampling_handler(
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)}"
@ -88,4 +150,3 @@ client = Client(
sampling_handler=basic_sampling_handler
)
```

View file

@ -37,10 +37,13 @@ Execute a tool using `call_tool()` with the tool name and arguments:
async with client:
# Simple tool call
result = await client.call_tool("add", {"a": 5, "b": 3})
# result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
# result -> CallToolResult with structured and unstructured data
# Access the result content
print(result[0].text) # Assuming TextContent, e.g., '8'
# 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
@ -72,21 +75,97 @@ async with client:
## Handling Results
Tool execution returns a list of content objects. The most common types are:
<VersionBadge version="2.10.0" />
- **`TextContent`**: Text-based results with a `.text` attribute
- **`ImageContent`**: Image data with image-specific attributes
- **`BlobContent`**: Binary data content
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"})
for content in result:
if hasattr(content, 'text'):
print(f"Text result: {content.text}")
elif hasattr(content, 'data'):
print(f"Binary data: {len(content.data)} bytes")
# 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
@ -101,14 +180,32 @@ from fastmcp.exceptions import ToolError
async with client:
try:
result = await client.call_tool("potentially_failing_tool", {"param": "value"})
print("Tool succeeded:", result)
print("Tool succeeded:", result.data)
except ToolError as e:
print(f"Tool failed: {e}")
```
### Manual Error Checking
For more granular control, use `call_tool_mcp()` which returns the raw MCP protocol object with an `isError` flag:
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:
@ -119,6 +216,7 @@ async with client:
print(f"Tool failed: {result.content}")
else:
print(f"Tool succeeded: {result.content}")
# Note: No automatic deserialization with call_tool_mcp()
```
## Argument Handling

View file

@ -41,7 +41,7 @@ Streamable HTTP is the recommended transport for web-based deployments, providin
- **Class:** `fastmcp.client.transports.StreamableHttpTransport`
- **Inferred From:** URLs starting with `http://` or `https://` (default for HTTP URLs since v2.3.0) that do not contain `/sse/` in the path
- **Server Compatibility:** Works with FastMCP servers running in `streamable-http` mode
- **Server Compatibility:** Works with FastMCP servers running in `http` mode
#### Basic Usage
@ -150,7 +150,7 @@ client = Client(transport)
- **Use Streamable HTTP when:**
- Setting up new deployments (recommended default)
- You need bidirectional streaming
- You're connecting to FastMCP servers running in `streamable-http` mode
- You're connecting to FastMCP servers running in `http` mode
- **Use SSE when:**
- Connecting to legacy FastMCP servers running in `sse` mode
@ -397,7 +397,7 @@ config = {
# Remote HTTP server
"weather": {
"url": "https://weather-api.example.com/mcp",
"transport": "streamable-http"
"transport": "http"
},
# Local stdio server
"assistant": {
@ -408,7 +408,7 @@ config = {
# Another remote server
"calendar": {
"url": "https://calendar-api.example.com/mcp",
"transport": "streamable-http"
"transport": "http"
}
}
}

View file

@ -1,17 +1,3 @@
/* Code highlighting -- target only inline code elements, not code blocks */
p code:not(pre code),
table 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);
}
/* Banner styling -- improve readability with better contrast */
#banner {
background: #f1f5f9 !important;
@ -79,41 +65,3 @@ h6 code:not(pre code) {
color: #f1f5f9 !important;
}
/* 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;
}

3
docs/css/python-sdk.css Normal file
View file

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

13
docs/css/style.css Normal file
View file

@ -0,0 +1,13 @@
/* Code highlighting -- target only inline code elements, not code blocks */
p code:not(pre code),
table 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

@ -105,7 +105,7 @@ When using Stdio transport, you will typically *not* run the server yourself as
Streamable HTTP is a modern, efficient transport for exposing your MCP server via HTTP. It is the recommended transport for web-based deployments.
To run a server using Streamable HTTP, you can use the `run()` method with the `transport` argument set to `"streamable-http"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and path (`/mcp/`).
To run a server using Streamable HTTP, you can use the `run()` method with the `transport` argument set to `"http"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and path (`/mcp/`).
<CodeGroup>
```python {6} server.py
from fastmcp import FastMCP
@ -113,7 +113,7 @@ from fastmcp import FastMCP
mcp = FastMCP()
if __name__ == "__main__":
mcp.run(transport="streamable-http")
mcp.run(transport="http")
```
```python {5} client.py
import asyncio
@ -128,6 +128,10 @@ if __name__ == "__main__":
```
</CodeGroup>
<Tip>
For backward compatibility, wherever `"http"` is accepted as a transport name, you can also pass `"streamable-http"` as a fully supported alias. This is particularly useful when upgrading from FastMCP 1.x in the official Python SDK and FastMCP \<= 2.9, where `"streamable-http"` was the standard name.
</Tip>
To customize the host, port, path, or log level, provide appropriate keyword arguments to the `run()` method.
<CodeGroup>
@ -138,7 +142,7 @@ mcp = FastMCP()
if __name__ == "__main__":
mcp.run(
transport="streamable-http",
transport="http",
host="127.0.0.1",
port=4200,
path="/my-custom-path",
@ -158,7 +162,6 @@ if __name__ == "__main__":
```
</CodeGroup>
### SSE
<Warning>
@ -250,7 +253,7 @@ def hello(name: str) -> str:
async def main():
# Use run_async() in async contexts
await mcp.run_async(transport="streamable-http")
await mcp.run_async(transport="http")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -109,6 +109,7 @@
"clients/logging",
"clients/progress",
"clients/sampling",
"clients/messages",
"clients/roots"
]
},
@ -124,9 +125,11 @@
"group": "Integrations",
"pages": [
"integrations/anthropic",
"integrations/chatgpt",
"integrations/claude-code",
"integrations/claude-desktop",
"integrations/openai",
"integrations/gemini",
"integrations/openai",
"integrations/contrib"
]
},
@ -155,7 +158,6 @@
"anchor": "What's New",
"pages": ["updates", "changelog"]
},
{
"anchor": "Community",
"icon": "users",
@ -243,7 +245,17 @@
"python-sdk/fastmcp-server-context",
"python-sdk/fastmcp-server-dependencies",
"python-sdk/fastmcp-server-http",
"python-sdk/fastmcp-server-middleware",
{
"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"
@ -266,10 +278,12 @@
"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-logging",
"python-sdk/fastmcp-utilities-mcp_config",
"python-sdk/fastmcp-utilities-openapi",
"python-sdk/fastmcp-utilities-tests",
"python-sdk/fastmcp-utilities-types"
]
}

View file

@ -47,7 +47,7 @@ FastMCP root path: ~/Developer/fastmcp
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 {1-5}
```python {5}
# Before
# from mcp.server.fastmcp import FastMCP
@ -56,8 +56,9 @@ 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 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.
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 and Breaking Changes

View file

@ -31,7 +31,7 @@ def roll_dice(n_dice: int) -> list[int]:
return [random.randint(1, 6) for _ in range(n_dice)]
if __name__ == "__main__":
mcp.run(transport="sse", port=8000)
mcp.run(transport="http", port=8000)
```
## Deploy the Server
@ -70,7 +70,7 @@ You'll also need to authenticate with Anthropic. You can do this by setting the
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 `/sse` as the endpoint because we deployed an SSE 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.**
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
@ -88,7 +88,7 @@ response = client.beta.messages.create(
mcp_servers=[
{
"type": "url",
"url": f"{url}/sse",
"url": f"{url}/mcp/",
"name": "dice-server",
}
],
@ -175,7 +175,7 @@ def roll_dice(n_dice: int) -> list[int]:
if __name__ == "__main__":
print(f"\n---\n\n🔑 Dice Roller access token:\n\n{access_token}\n\n---\n")
mcp.run(transport="sse", port=8000)
mcp.run(transport="http", port=8000)
```
### Client Authentication
@ -213,7 +213,7 @@ response = client.beta.messages.create(
mcp_servers=[
{
"type": "url",
"url": f"{url}/sse",
"url": f"{url}/mcp/",
"name": "dice-server",
"authorization_token": access_token
}

View file

@ -0,0 +1,158 @@
---
title: ChatGPT + FastMCP
sidebarTitle: ChatGPT
description: Connect FastMCP servers to ChatGPT Deep Research
icon: message-smile
tag: NEW
---
ChatGPT supports MCP servers through remote HTTP connections, allowing you to extend ChatGPT's capabilities with custom tools and knowledge from your FastMCP servers.
<Note>
MCP integration with ChatGPT is currently limited to **Deep Research** functionality and is not available for general chat. This feature is available for ChatGPT Pro, Team, Enterprise, and Edu users.
</Note>
<Tip>
OpenAI's official MCP documentation and examples are built with **FastMCP v2**! Check out their [sample MCP server](https://github.com/openai/mcp-server-sample) which demonstrates FastMCP in action.
</Tip>
## Deep Research
ChatGPT's Deep Research feature requires MCP servers to be internet-accessible HTTP endpoints with **exactly two specific tools**:
- **`search`**: For searching through your resources and returning matching IDs
- **`fetch`**: For retrieving the full content of specific resources by ID
<Warning>
If your server doesn't implement both `search` and `fetch` tools with the correct signatures, ChatGPT will show the error: "This MCP server doesn't implement our specification". Both tools are required.
</Warning>
### Tool Descriptions Matter
Since ChatGPT needs to understand how to use your tools effectively, **write detailed tool descriptions**. The description teaches ChatGPT how to form queries, what parameters to use, and what to expect from your data. Poor descriptions lead to poor search results.
### Create a Server
A Deep Research-compatible server must implement these two required tools:
- **`search(query: str)`** - Takes a query of any kind and returns matching record IDs
- **`fetch(id: str)`** - Takes an ID and returns the record
**Critical**: Write detailed docstrings for both tools. These descriptions teach ChatGPT how to use your tools effectively. Poor descriptions lead to poor search results.
The `search` tool should take a query (of any kind!) and return IDs. The `fetch` tool should take an ID and return the record.
Here's a reference server implementation you can adapt (see also [OpenAI's sample server](https://github.com/openai/mcp-server-sample) for comparison):
```python server.py [expandable]
import json
from pathlib import Path
from dataclasses import dataclass
from fastmcp import FastMCP
@dataclass
class Record:
id: str
title: str
text: str
metadata: dict
def create_server(
records_path: Path | str,
name: str | None = None,
instructions: str | None = None,
) -> FastMCP:
"""Create a FastMCP server that can search and fetch records from a JSON file."""
records = json.loads(Path(records_path).read_text())
RECORDS = [Record(**r) for r in records]
LOOKUP = {r.id: r for r in RECORDS}
mcp = FastMCP(name=name or "Deep Research MCP", instructions=instructions)
@mcp.tool()
async def search(query: str):
"""
Simple unranked keyword search across title, text, and metadata.
Searches for any of the query terms in the record content.
Returns a list of matching record IDs for ChatGPT to fetch.
"""
toks = query.lower().split()
ids = []
for r in RECORDS:
record_txt = " ".join(
[r.title, r.text, " ".join(r.metadata.values())]
).lower()
if any(t in record_txt for t in toks):
ids.append(r.id)
return {"ids": ids}
@mcp.tool()
async def fetch(id: str):
"""
Fetch a record by ID.
Returns the complete record data for ChatGPT to analyze and cite.
"""
if id not in LOOKUP:
raise ValueError(f"Unknown record ID: {id}")
return LOOKUP[id]
return mcp
if __name__ == "__main__":
mcp = create_server("path/to/records.json")
mcp.run(transport="http", port=8000)
```
### Deploy the Server
Your server must be deployed to a public URL in order for ChatGPT to access it.
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>
### Connect to ChatGPT
Replace `https://your-server-url.com` with the actual URL of your server (such as your ngrok URL).
1. Open ChatGPT and go to **Settings** → **Connectors**
2. Click **Add custom connector**
3. Enter your server details:
- **Name**: Library Catalog
- **URL**: Your server URL (e.g., `https://abc123.ngrok.io`)
- **Description**: A library catalog for searching and retrieving books
#### Test the Connection
1. Start a new chat in ChatGPT
2. Click **Tools** → **Run deep research**
3. Select your **Library Catalog** connector as a source
4. Ask questions like:
- "Search for Python programming books"
- "Find books about AI and machine learning"
- "Show me books by the Python Software Foundation"
ChatGPT will use your server's search and fetch tools to find relevant information and cite the sources in its response.
### Troubleshooting
#### "This MCP server doesn't implement our specification"
If you get this error, it most likely means that your server doesn't implement the required tools (`search` and `fetch`). To correct it, ensure that your server meets the service requirements.

View file

@ -0,0 +1,60 @@
---
title: Claude Code + FastMCP
sidebarTitle: Claude Code
description: Connect FastMCP servers to Claude Code
icon: message-smile
tag: NEW
---
Claude Code supports MCP servers through multiple transport methods, allowing you to extend Claude's capabilities with custom tools, resources, and prompts from your FastMCP servers.
<Note>
Claude Code supports both local and remote MCP servers with flexible configuration options. See the [Claude Code MCP documentation](https://docs.anthropic.com/en/docs/claude-code/mcp) for other transport methods.
</Note>
<Tip>
Claude Code provides built-in MCP management commands to easily add, configure, and authenticate your FastMCP servers.
</Tip>
## Create a Server
You can create FastMCP servers using STDIO transport, remote HTTP servers, or local HTTP servers. This example shows one common approach: running an HTTP server locally for development.
```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)
```
## Connect to Claude Code
Start your server and add it to Claude Code:
```bash
# Start your server first
python server.py
```
Then add it to Claude Code:
```bash
claude mcp add dice --transport http http://localhost:8000/mcp/
```
## Using Your Server
Once connected, Claude Code will automatically discover and use your server's tools when relevant:
```
Roll some dice for me
```
Claude will call your `roll_dice` tool and provide the results. If your server provides resources, you can reference them with `@` mentions like `@dice:file://path/to/resource`.

View file

@ -2,11 +2,15 @@
title: Claude Desktop + FastMCP
sidebarTitle: Claude Desktop
description: Call FastMCP servers from Claude Desktop
icon: desktop
icon: message-smile
---
Claude Desktop supports MCP servers through local STDIO connections, allowing you to extend Claude's capabilities with custom tools, resources, and prompts from your FastMCP servers.
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).
@ -15,10 +19,10 @@ This guide focuses specifically on using FastMCP servers with Claude Desktop. Fo
## Requirements
Claude Desktop requires MCP servers to run locally using STDIO transport. This means your server will communicate with Claude through standard input/output rather than HTTP.
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 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.
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
@ -181,7 +185,7 @@ Claude Desktop runs servers in a completely isolated environment with no access
## Remote Servers
Claude Desktop only supports local STDIO servers, but FastMCP can create a proxy server that forwards requests to a remote HTTP server. You can install the proxy server in Claude Desktop.
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:

View file

@ -99,7 +99,7 @@ from fastmcp import Client
from fastmcp.client.auth import BearerAuth
mcp_client = Client(
"https://my-server.com/sse",
"https://my-server.com/mcp/",
auth=BearerAuth("<your-token>"),
)
```

View file

@ -38,7 +38,7 @@ def roll_dice(n_dice: int) -> list[int]:
return [random.randint(1, 6) for _ in range(n_dice)]
if __name__ == "__main__":
mcp.run(transport="sse", port=8000)
mcp.run(transport="http", port=8000)
```
### Deploy the Server
@ -77,7 +77,7 @@ You'll also need to authenticate with OpenAI. You can do this by setting the `OP
export OPENAI_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 `/sse` as the endpoint because we deployed an SSE server with the default path; you may need to use a different endpoint if you customized your server's deployment.
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.
```python {4, 11-16}
from openai import OpenAI
@ -93,7 +93,7 @@ resp = client.responses.create(
{
"type": "mcp",
"server_label": "dice_server",
"server_url": f"{url}/sse",
"server_url": f"{url}/mcp/",
"require_approval": "never",
},
],
@ -172,7 +172,7 @@ def roll_dice(n_dice: int) -> list[int]:
if __name__ == "__main__":
print(f"\n---\n\n🔑 Dice Roller access token:\n\n{access_token}\n\n---\n")
mcp.run(transport="sse", port=8000)
mcp.run(transport="http", port=8000)
```
#### Client Authentication
@ -212,7 +212,7 @@ resp = client.responses.create(
{
"type": "mcp",
"server_label": "dice_server",
"server_url": f"{url}/sse",
"server_url": f"{url}/mcp/",
"require_approval": "never",
"headers": {
"Authorization": f"Bearer {access_token}"

View file

@ -42,11 +42,12 @@ This command runs the server directly in your current Python environment. You ar
| Option | Flag | Description |
| ------ | ---- | ----------- |
| Transport | `--transport`, `-t` | Transport protocol to use (`stdio`, `streamable-http`, or `sse`) |
| Transport | `--transport`, `-t` | Transport protocol to use (`stdio`, `http`, or `sse`) |
| Host | `--host` | Host to bind to when using http transport (default: 127.0.0.1) |
| Port | `--port`, `-p` | Port to bind to when using http transport (default: 8000) |
| Log Level | `--log-level`, `-l` | Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
#### Server Specification
<VersionBadge version="2.3.5" />
@ -79,14 +80,14 @@ if __name__ == "__main__":
You can run it with Streamable HTTP transport regardless of what's in the `__main__` block:
```bash
fastmcp run server.py --transport streamable-http --port 8000
fastmcp run server.py --transport http --port 8000
```
**Examples**
```bash
# Run a local server with Streamable HTTP transport on a custom port
fastmcp run server.py --transport streamable-http --port 8000
fastmcp run server.py --transport http --port 8000
# Connect to a remote server and proxy as a stdio server
fastmcp run https://example.com/mcp-server
@ -112,14 +113,14 @@ The `dev` command is a shortcut for testing a server over STDIO only. When the I
1. Select "STDIO" from the transport dropdown
2. Connect manually
This command does not support HTTP testing. To test a server over HTTP:
1. Start your server manually with HTTP transport using either:
This command does not support HTTP testing. To test a server over Streamable HTTP or SSE:
1. Start your server manually with the appropriate transport using either the command line:
```bash
fastmcp run server.py --transport streamable-http
fastmcp run server.py --transport http
```
or
or by setting the transport in your code:
```bash
python server.py # Assuming your __main__ block sets HTTP transport
python server.py # Assuming your __main__ block sets Streamable HTTP transport
```
2. Open the MCP Inspector separately and connect to your running server
</Warning>

View file

@ -89,6 +89,7 @@ The `Tool.from_tool()` class method is the primary way to create a transformed t
- `description`: An optional description for the new tool.
- `transform_args`: A dictionary of `ArgTransform` objects, one for each argument you want to modify.
- `transform_fn`: An optional function that will be called instead of the parent tool's logic.
- `output_schema`: Control output schema and structured outputs (see [Output Schema Control](#output-schema-control)).
- `tags`: An optional set of tags for the new tool.
- `annotations`: An optional set of `ToolAnnotations` for the new tool.
- `serializer`: An optional function that will be called to serialize the result of the new tool.
@ -439,7 +440,44 @@ mcp.add_tool(new_tool)
<Tip>
In the above example, `**kwargs` receives the renamed argument `b`, not the original argument `y`. It is therefore recommended to use with `forward()`, not `forward_raw()`.
</Tip>
</Tip>
## Output Schema Control
<VersionBadge version="2.10.0" />
Transformed tools inherit output schemas from their parent by default, but you can control this behavior:
**Inherit from Parent (Default)**
```python
Tool.from_tool(parent_tool, name="renamed_tool")
```
The transformed tool automatically uses the parent tool's output schema and structured output behavior.
**Custom Output Schema**
```python
Tool.from_tool(parent_tool, output_schema={
"type": "object",
"properties": {"status": {"type": "string"}}
})
```
Provide your own schema that differs from the parent. The tool must return data matching this schema.
**Remove Output Schema**
```python
Tool.from_tool(parent_tool, output_schema=False)
```
Removes the output schema declaration. Automatic structured content still works for object-like returns (dict, dataclass, Pydantic models) but primitive types won't be structured.
**Full Control with Transform Functions**
```python
async def custom_output(**kwargs) -> ToolResult:
result = await forward(**kwargs)
return ToolResult(content=[...], structured_content={...})
Tool.from_tool(parent_tool, transform_fn=custom_output)
```
Use a transform function returning `ToolResult` for complete control over both content blocks and structured outputs.
## Common Patterns

View file

@ -10,7 +10,7 @@ Claude app integration utilities.
## Functions
### `get_claude_config_path`
### `get_claude_config_path` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/claude.py#L14"><Icon icon="github" size="14" /></a></sup>
```python
get_claude_config_path() -> Path | None
@ -20,7 +20,7 @@ get_claude_config_path() -> Path | None
Get the Claude config directory based on platform.
### `update_claude_config`
### `update_claude_config` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/claude.py#L32"><Icon icon="github" size="14" /></a></sup>
```python
update_claude_config(file_spec: str, server_name: str) -> bool

View file

@ -10,13 +10,13 @@ FastMCP CLI tools.
## Functions
### `version`
### `version` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/cli.py#L87"><Icon icon="github" size="14" /></a></sup>
```python
version(ctx: Context)
```
### `dev`
### `dev` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/cli.py#L110"><Icon icon="github" size="14" /></a></sup>
```python
dev(server_spec: str = typer.Argument(..., help='Python file to run, optionally with :object suffix'), with_editable: Annotated[Path | None, typer.Option('--with-editable', '-e', help='Directory containing pyproject.toml to install in editable mode', exists=True, file_okay=False, resolve_path=True)] = None, with_packages: Annotated[list[str], typer.Option('--with', help='Additional packages to install')] = [], inspector_version: Annotated[str | None, typer.Option('--inspector-version', help='Version of the MCP Inspector to use')] = None, ui_port: Annotated[int | None, typer.Option('--ui-port', help='Port for the MCP Inspector UI')] = None, server_port: Annotated[int | None, typer.Option('--server-port', help='Port for the MCP Inspector Proxy server')] = None) -> None
@ -26,10 +26,10 @@ dev(server_spec: str = typer.Argument(..., help='Python file to run, optionally
Run a MCP server with the MCP Inspector.
### `run`
### `run` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/cli.py#L227"><Icon icon="github" size="14" /></a></sup>
```python
run(ctx: typer.Context, server_spec: str = typer.Argument(..., help='Python file, object specification (file:obj), or URL'), transport: Annotated[str | None, typer.Option('--transport', '-t', help='Transport protocol to use (stdio, streamable-http, or sse)')] = None, host: Annotated[str | None, typer.Option('--host', help='Host to bind to when using http transport (default: 127.0.0.1)')] = None, port: Annotated[int | None, typer.Option('--port', '-p', help='Port to bind to when using http transport (default: 8000)')] = None, log_level: Annotated[str | None, typer.Option('--log-level', '-l', help='Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)')] = None) -> None
run(ctx: typer.Context, server_spec: str = typer.Argument(..., help='Python file, object specification (file:obj), or URL'), transport: Annotated[str | None, typer.Option('--transport', '-t', help='Transport protocol to use (stdio, http, or sse)')] = None, host: Annotated[str | None, typer.Option('--host', help='Host to bind to when using http transport (default: 127.0.0.1)')] = None, port: Annotated[int | None, typer.Option('--port', '-p', help='Port to bind to when using http transport (default: 8000)')] = None, log_level: Annotated[str | None, typer.Option('--log-level', '-l', help='Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)')] = None) -> None
```
@ -51,7 +51,7 @@ Server arguments can be passed after -- :
fastmcp run server.py -- --config config.json --debug
### `install`
### `install` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/cli.py#L313"><Icon icon="github" size="14" /></a></sup>
```python
install(server_spec: str = typer.Argument(..., help='Python file to run, optionally with :object suffix'), server_name: Annotated[str | None, typer.Option('--name', '-n', help="Custom name for the server (defaults to server's name attribute or file name)")] = None, with_editable: Annotated[Path | None, typer.Option('--with-editable', '-e', help='Directory containing pyproject.toml to install in editable mode', exists=True, file_okay=False, resolve_path=True)] = None, with_packages: Annotated[list[str], typer.Option('--with', help='Additional packages to install')] = [], env_vars: Annotated[list[str], typer.Option('--env-var', '-v', help='Environment variables in KEY=VALUE format')] = [], env_file: Annotated[Path | None, typer.Option('--env-file', '-f', help='Load environment variables from a .env file', exists=True, file_okay=True, dir_okay=False, resolve_path=True)] = None) -> None
@ -63,3 +63,25 @@ Install a MCP server in the Claude desktop app.
Environment variables are preserved once added and only updated if new values
are explicitly provided.
### `inspect` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/cli.py#L444"><Icon icon="github" size="14" /></a></sup>
```python
inspect(server_spec: str = typer.Argument(..., help='Python file to inspect, optionally with :object suffix'), output: Annotated[Path, typer.Option('--output', '-o', help='Output file path for the JSON report (default: server-info.json)')] = Path('server-info.json')) -> None
```
Inspect a FastMCP server and generate a JSON report.
This command analyzes a FastMCP server (v1.x or v2.x) and generates
a comprehensive JSON report containing information about the server's
name, instructions, version, tools, prompts, resources, templates,
and capabilities.
**Examples:**
fastmcp inspect server.py
fastmcp inspect server.py -o report.json
fastmcp inspect server.py:mcp -o analysis.json
fastmcp inspect path/to/server.py:app -o /tmp/server-info.json

View file

@ -10,7 +10,7 @@ FastMCP run command implementation.
## Functions
### `is_url`
### `is_url` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/run.py#L14"><Icon icon="github" size="14" /></a></sup>
```python
is_url(path: str) -> bool
@ -20,7 +20,7 @@ is_url(path: str) -> bool
Check if a string is a URL.
### `parse_file_path`
### `parse_file_path` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/run.py#L20"><Icon icon="github" size="14" /></a></sup>
```python
parse_file_path(server_spec: str) -> tuple[Path, str | None]
@ -36,7 +36,7 @@ Parse a file path that may include a server object specification.
- Tuple of (file_path, server_object)
### `import_server`
### `import_server` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/run.py#L51"><Icon icon="github" size="14" /></a></sup>
```python
import_server(file: Path, server_object: str | None = None) -> Any
@ -53,7 +53,7 @@ Import a MCP server from a file.
- The server object
### `create_client_server`
### `create_client_server` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/run.py#L121"><Icon icon="github" size="14" /></a></sup>
```python
create_client_server(url: str) -> Any
@ -69,7 +69,7 @@ Create a FastMCP server from a client URL.
- A FastMCP server instance
### `import_server_with_args`
### `import_server_with_args` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/run.py#L141"><Icon icon="github" size="14" /></a></sup>
```python
import_server_with_args(file: Path, server_object: str | None = None, server_args: list[str] | None = None) -> Any
@ -87,7 +87,7 @@ Import a server with optional command line arguments.
- The imported server object
### `run_command`
### `run_command` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/cli/run.py#L165"><Icon icon="github" size="14" /></a></sup>
```python
run_command(server_spec: str, transport: str | None = None, host: str | None = None, port: int | None = None, log_level: str | None = None, server_args: list[str] | None = None) -> None

View file

@ -7,11 +7,11 @@ sidebarTitle: bearer
## Classes
### `BearerAuth`
### `BearerAuth` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/bearer.py#L11"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `auth_flow`
#### `auth_flow` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/bearer.py#L15"><Icon icon="github" size="14" /></a></sup>
```python
auth_flow(self, request)

View file

@ -7,13 +7,13 @@ sidebarTitle: oauth
## Functions
### `default_cache_dir`
### `default_cache_dir` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L38"><Icon icon="github" size="14" /></a></sup>
```python
default_cache_dir() -> Path
```
### `OAuth`
### `OAuth` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L295"><Icon icon="github" size="14" /></a></sup>
```python
OAuth(mcp_url: str, scopes: str | list[str] | None = None, client_name: str = 'FastMCP Client', token_storage_cache_dir: Path | None = None, additional_client_metadata: dict[str, Any] | None = None) -> _MCPOAuthClientProvider
@ -38,7 +38,7 @@ httpx.AsyncClient (or appropriate FastMCP client/transport instance)
## Classes
### `ServerOAuthMetadata`
### `ServerOAuthMetadata` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L43"><Icon icon="github" size="14" /></a></sup>
More flexible OAuth metadata model that accepts broader ranges of values
@ -48,13 +48,13 @@ This handles real-world OAuth servers like PayPal that may support
additional methods not in the MCP specification.
### `OAuthClientProvider`
### `OAuthClientProvider` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L68"><Icon icon="github" size="14" /></a></sup>
OAuth client provider with more flexible OAuth metadata discovery.
### `FileTokenStorage`
### `FileTokenStorage` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L116"><Icon icon="github" size="14" /></a></sup>
File-based token storage implementation for OAuth credentials and tokens.
@ -65,7 +65,7 @@ Each instance is tied to a specific server URL for proper token isolation.
**Methods:**
#### `get_base_url`
#### `get_base_url` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L131"><Icon icon="github" size="14" /></a></sup>
```python
get_base_url(url: str) -> str
@ -74,7 +74,7 @@ get_base_url(url: str) -> str
Extract the base URL (scheme + host) from a URL.
#### `get_cache_key`
#### `get_cache_key` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L136"><Icon icon="github" size="14" /></a></sup>
```python
get_cache_key(self) -> str
@ -83,7 +83,7 @@ get_cache_key(self) -> str
Generate a safe filesystem key from the server's base URL.
#### `clear`
#### `clear` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L208"><Icon icon="github" size="14" /></a></sup>
```python
clear(self) -> None
@ -92,7 +92,7 @@ clear(self) -> None
Clear all cached data for this server.
#### `clear_all`
#### `clear_all` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/auth/oauth.py#L217"><Icon icon="github" size="14" /></a></sup>
```python
clear_all(cls, cache_dir: Path | None = None) -> None

View file

@ -7,48 +7,48 @@ sidebarTitle: client
## Classes
### `Client`
### `Client` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/client.py#L60"><Icon icon="github" size="14" /></a></sup>
MCP client that delegates connection management to a Transport instance.
MCP client that delegates connection management to a Transport instance.
The Client class is responsible for MCP protocol logic, while the Transport
handles connection establishment and management. Client provides methods for
working with resources, prompts, tools and other MCP capabilities.
The Client class is responsible for MCP protocol logic, while the Transport
handles connection establishment and management. Client provides methods for
working with resources, prompts, tools and other MCP capabilities.
**Args:**
- `transport`: Connection source specification, which can be\:
- ClientTransport\: Direct transport instance
- FastMCP\: In-process FastMCP server
- AnyUrl | str\: URL to connect to
- Path\: File path for local socket
- MCPConfig\: MCP server configuration
- dict\: Transport configuration
- `roots`: Optional RootsList or RootsHandler for filesystem access
- `sampling_handler`: Optional handler for sampling requests
- `log_handler`: Optional handler for log messages
- `message_handler`: Optional handler for protocol messages
- `progress_handler`: Optional handler for progress notifications
- `timeout`: Optional timeout for requests (seconds or timedelta)
- `init_timeout`: Optional timeout for initial connection (seconds or timedelta).
Set to 0 to disable. If None, uses the value in the FastMCP global settings.
Args:
transport: Connection source specification, which can be:
- ClientTransport: Direct transport instance
- FastMCP: In-process FastMCP server
- AnyUrl | str: URL to connect to
- Path: File path for local socket
- MCPConfig: MCP server configuration
- dict: Transport configuration
roots: Optional RootsList or RootsHandler for filesystem access
sampling_handler: Optional handler for sampling requests
log_handler: Optional handler for log messages
message_handler: Optional handler for protocol messages
progress_handler: Optional handler for progress notifications
timeout: Optional timeout for requests (seconds or timedelta)
init_timeout: Optional timeout for initial connection (seconds or timedelta).
Set to 0 to disable. If None, uses the value in the FastMCP global settings.
**Examples:**
Examples:
```python # Connect to FastMCP server client =
Client("http://localhost:8080")
```python # Connect to FastMCP server client =
Client("http://localhost:8080")
async with client:
# List available resources resources = await client.list_resources()
async with client:
# List available resources resources = await client.list_resources()
# Call a tool result = await client.call_tool("my_tool", {"param":
"value"})
```
# Call a tool result = await client.call_tool("my_tool", {"param":
"value"})
```
**Methods:**
#### `session`
#### `session` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/client.py#L207"><Icon icon="github" size="14" /></a></sup>
```python
session(self) -> ClientSession
@ -57,7 +57,7 @@ session(self) -> ClientSession
Get the current active session. Raises RuntimeError if not connected.
#### `initialize_result`
#### `initialize_result` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/client.py#L217"><Icon icon="github" size="14" /></a></sup>
```python
initialize_result(self) -> mcp.types.InitializeResult
@ -66,7 +66,7 @@ initialize_result(self) -> mcp.types.InitializeResult
Get the result of the initialization request.
#### `set_roots`
#### `set_roots` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/client.py#L225"><Icon icon="github" size="14" /></a></sup>
```python
set_roots(self, roots: RootsList | RootsHandler) -> None
@ -75,7 +75,7 @@ set_roots(self, roots: RootsList | RootsHandler) -> None
Set the roots for the client. This does not automatically call `send_roots_list_changed`.
#### `set_sampling_callback`
#### `set_sampling_callback` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/client.py#L229"><Icon icon="github" size="14" /></a></sup>
```python
set_sampling_callback(self, sampling_callback: SamplingHandler) -> None
@ -84,7 +84,7 @@ set_sampling_callback(self, sampling_callback: SamplingHandler) -> None
Set the sampling callback for the client.
#### `is_connected`
#### `is_connected` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/client.py#L235"><Icon icon="github" size="14" /></a></sup>
```python
is_connected(self) -> bool

View file

@ -7,7 +7,7 @@ sidebarTitle: logging
## Functions
### `create_log_callback`
### `create_log_callback` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/logging.py#L20"><Icon icon="github" size="14" /></a></sup>
```python
create_log_callback(handler: LogHandler | None = None) -> LoggingFnT

View file

@ -15,7 +15,7 @@ and display styled responses to users.
## Functions
### `create_callback_html`
### `create_callback_html` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/oauth_callback.py#L25"><Icon icon="github" size="14" /></a></sup>
```python
create_callback_html(message: str, is_success: bool = True, title: str = 'FastMCP OAuth', server_url: str | None = None) -> str
@ -25,7 +25,7 @@ create_callback_html(message: str, is_success: bool = True, title: str = 'FastMC
Create a styled HTML response for OAuth callbacks.
### `create_oauth_callback_server`
### `create_oauth_callback_server` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/oauth_callback.py#L197"><Icon icon="github" size="14" /></a></sup>
```python
create_oauth_callback_server(port: int, callback_path: str = '/callback', server_url: str | None = None, response_future: asyncio.Future | None = None) -> Server
@ -46,17 +46,17 @@ Create an OAuth callback server.
## Classes
### `CallbackResponse`
### `CallbackResponse` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/oauth_callback.py#L183"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `from_dict`
#### `from_dict` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/oauth_callback.py#L190"><Icon icon="github" size="14" /></a></sup>
```python
from_dict(cls, data: dict[str, str]) -> CallbackResponse
```
#### `to_dict`
#### `to_dict` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/oauth_callback.py#L193"><Icon icon="github" size="14" /></a></sup>
```python
to_dict(self) -> dict[str, str]

View file

@ -7,13 +7,13 @@ sidebarTitle: roots
## Functions
### `convert_roots_list`
### `convert_roots_list` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/roots.py#L19"><Icon icon="github" size="14" /></a></sup>
```python
convert_roots_list(roots: RootsList) -> list[mcp.types.Root]
```
### `create_roots_callback`
### `create_roots_callback` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/roots.py#L33"><Icon icon="github" size="14" /></a></sup>
```python
create_roots_callback(handler: RootsList | RootsHandler) -> ListRootsFnT

View file

@ -7,7 +7,7 @@ sidebarTitle: sampling
## Functions
### `create_sampling_callback`
### `create_sampling_callback` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/sampling.py#L25"><Icon icon="github" size="14" /></a></sup>
```python
create_sampling_callback(sampling_handler: SamplingHandler) -> SamplingFnT

View file

@ -7,63 +7,63 @@ sidebarTitle: transports
## Functions
### `infer_transport`
### `infer_transport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L837"><Icon icon="github" size="14" /></a></sup>
```python
infer_transport(transport: ClientTransport | FastMCP | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str) -> ClientTransport
```
Infer the appropriate transport type from the given transport argument.
Infer the appropriate transport type from the given transport argument.
This function attempts to infer the correct transport type from the provided
argument, handling various input types and converting them to the appropriate
ClientTransport subclass.
This function attempts to infer the correct transport type from the provided
argument, handling various input types and converting them to the appropriate
ClientTransport subclass.
The function supports these input types:
- ClientTransport: Used directly without modification
- FastMCP or FastMCP1Server: Creates an in-memory FastMCPTransport
- Path or str (file path): Creates PythonStdioTransport (.py) or NodeStdioTransport (.js)
- AnyUrl or str (URL): Creates StreamableHttpTransport (default) or SSETransport (for /sse endpoints)
- MCPConfig or dict: Creates MCPConfigTransport, potentially connecting to multiple servers
The function supports these input types:
- ClientTransport: Used directly without modification
- FastMCP or FastMCP1Server: Creates an in-memory FastMCPTransport
- Path or str (file path): Creates PythonStdioTransport (.py) or NodeStdioTransport (.js)
- AnyUrl or str (URL): Creates StreamableHttpTransport (default) or SSETransport (for /sse endpoints)
- MCPConfig or dict: Creates MCPConfigTransport, potentially connecting to multiple servers
For HTTP URLs, they are assumed to be Streamable HTTP URLs unless they end in `/sse`.
For HTTP URLs, they are assumed to be Streamable HTTP URLs unless they end in `/sse`.
For MCPConfig with multiple servers, a composite client is created where each server
is mounted with its name as prefix. This allows accessing tools and resources from multiple
servers through a single unified client interface, using naming patterns like
`servername_toolname` for tools and `protocol://servername/path` for resources.
If the MCPConfig contains only one server, a direct connection is established without prefixing.
For MCPConfig with multiple servers, a composite client is created where each server
is mounted with its name as prefix. This allows accessing tools and resources from multiple
servers through a single unified client interface, using naming patterns like
`servername_toolname` for tools and `protocol://servername/path` for resources.
If the MCPConfig contains only one server, a direct connection is established without prefixing.
**Examples:**
Examples:
```python
# Connect to a local Python script
transport = infer_transport("my_script.py")
```python
# Connect to a local Python script
transport = infer_transport("my_script.py")
# Connect to a remote server via HTTP
transport = infer_transport("http://example.com/mcp")
# Connect to a remote server via HTTP
transport = infer_transport("http://example.com/mcp")
# Connect to multiple servers using MCPConfig
config = {
"mcpServers": {
"weather": {"url": "http://weather.example.com/mcp"},
"calendar": {"url": "http://calendar.example.com/mcp"}
}
}
transport = infer_transport(config)
```
# Connect to multiple servers using MCPConfig
config = {
"mcpServers": {
"weather": {"url": "http://weather.example.com/mcp"},
"calendar": {"url": "http://calendar.example.com/mcp"}
}
}
transport = infer_transport(config)
```
## Classes
### `SessionKwargs`
### `SessionKwargs` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L52"><Icon icon="github" size="14" /></a></sup>
Keyword arguments for the MCP ClientSession constructor.
### `ClientTransport`
### `ClientTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L63"><Icon icon="github" size="14" /></a></sup>
Abstract base class for different MCP client transport mechanisms.
@ -72,25 +72,25 @@ A Transport is responsible for establishing and managing connections
to an MCP server, and providing a ClientSession within an async context.
### `WSTransport`
### `WSTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L109"><Icon icon="github" size="14" /></a></sup>
Transport implementation that connects to an MCP server via WebSockets.
### `SSETransport`
### `SSETransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L148"><Icon icon="github" size="14" /></a></sup>
Transport implementation that connects to an MCP server via Server-Sent Events.
### `StreamableHttpTransport`
### `StreamableHttpTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L223"><Icon icon="github" size="14" /></a></sup>
Transport implementation that connects to an MCP server via Streamable HTTP Requests.
### `StdioTransport`
### `StdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L299"><Icon icon="github" size="14" /></a></sup>
Base transport for connecting to an MCP server via subprocess with stdio.
@ -99,37 +99,37 @@ This is a base class that can be subclassed for specific command-based
transports like Python, Node, Uvx, etc.
### `PythonStdioTransport`
### `PythonStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L416"><Icon icon="github" size="14" /></a></sup>
Transport for running Python scripts.
### `FastMCPStdioTransport`
### `FastMCPStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L462"><Icon icon="github" size="14" /></a></sup>
Transport for running FastMCP servers using the FastMCP CLI.
### `NodeStdioTransport`
### `NodeStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L489"><Icon icon="github" size="14" /></a></sup>
Transport for running Node.js scripts.
### `UvxStdioTransport`
### `UvxStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L531"><Icon icon="github" size="14" /></a></sup>
Transport for running commands via the uvx tool.
### `NpxStdioTransport`
### `NpxStdioTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L597"><Icon icon="github" size="14" /></a></sup>
Transport for running commands via the npx tool.
### `FastMCPTransport`
### `FastMCPTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L659"><Icon icon="github" size="14" /></a></sup>
In-memory transport for FastMCP servers.
@ -140,52 +140,53 @@ servers from the low-level MCP SDK. This is particularly useful for unit
tests or scenarios where client and server run in the same runtime.
### `MCPConfigTransport`
### `MCPConfigTransport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/client/transports.py#L713"><Icon icon="github" size="14" /></a></sup>
Transport for connecting to one or more MCP servers defined in an MCPConfig.
This transport provides a unified interface to multiple MCP servers defined in an MCPConfig
object or dictionary matching the MCPConfig schema. It supports two key scenarios:
This transport provides a unified interface to multiple MCP servers defined in an MCPConfig
object or dictionary matching the MCPConfig schema. It supports two key scenarios:
1. If the MCPConfig contains exactly one server, it creates a direct transport to that server.
2. If the MCPConfig contains multiple servers, it creates a composite client by mounting
all servers on a single FastMCP instance, with each server's name used as its mounting prefix.
1. If the MCPConfig contains exactly one server, it creates a direct transport to that server.
2. If the MCPConfig contains multiple servers, it creates a composite client by mounting
all servers on a single FastMCP instance, with each server's name used as its mounting prefix.
In the multi-server case, tools are accessible with the prefix pattern `{server_name}_{tool_name}`
and resources with the pattern `protocol://{server_name}/path/to/resource`.
In the multi-server case, tools are accessible with the prefix pattern `{server_name}_{tool_name}`
and resources with the pattern `protocol://{server_name}/path/to/resource`.
This is particularly useful for creating clients that need to interact with multiple specialized
MCP servers through a single interface, simplifying client code.
This is particularly useful for creating clients that need to interact with multiple specialized
MCP servers through a single interface, simplifying client code.
Examples:
```python
from fastmcp import Client
from fastmcp.utilities.mcp_config import MCPConfig
**Examples:**
# Create a config with multiple servers
config = {
"mcpServers": {
"weather": {
"url": "https://weather-api.example.com/mcp",
"transport": "streamable-http"
},
"calendar": {
"url": "https://calendar-api.example.com/mcp",
"transport": "streamable-http"
}
}
```python
from fastmcp import Client
from fastmcp.utilities.mcp_config import MCPConfig
# Create a config with multiple servers
config = {
"mcpServers": {
"weather": {
"url": "https://weather-api.example.com/mcp",
"transport": "http"
},
"calendar": {
"url": "https://calendar-api.example.com/mcp",
"transport": "http"
}
}
}
# Create a client with the config
client = Client(config)
# Create a client with the config
client = Client(config)
async with client:
# Access tools with prefixes
weather = await client.call_tool("weather_get_forecast", {"city": "London"})
events = await client.call_tool("calendar_list_events", {"date": "2023-06-01"})
async with client:
# Access tools with prefixes
weather = await client.call_tool("weather_get_forecast", {"city": "London"})
events = await client.call_tool("calendar_list_events", {"date": "2023-06-01"})
# Access resources with prefixed URIs
icons = await client.read_resource("weather://weather/icons/sunny")
```
# Access resources with prefixed URIs
icons = await client.read_resource("weather://weather/icons/sunny")
```

View file

@ -10,55 +10,55 @@ Custom exceptions for FastMCP.
## Classes
### `FastMCPError`
### `FastMCPError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L6"><Icon icon="github" size="14" /></a></sup>
Base error for FastMCP.
### `ValidationError`
### `ValidationError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L10"><Icon icon="github" size="14" /></a></sup>
Error in validating parameters or return values.
### `ResourceError`
### `ResourceError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L14"><Icon icon="github" size="14" /></a></sup>
Error in resource operations.
### `ToolError`
### `ToolError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L18"><Icon icon="github" size="14" /></a></sup>
Error in tool operations.
### `PromptError`
### `PromptError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L22"><Icon icon="github" size="14" /></a></sup>
Error in prompt operations.
### `InvalidSignature`
### `InvalidSignature` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L26"><Icon icon="github" size="14" /></a></sup>
Invalid signature for use with FastMCP.
### `ClientError`
### `ClientError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L30"><Icon icon="github" size="14" /></a></sup>
Error in client operations.
### `NotFoundError`
### `NotFoundError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L34"><Icon icon="github" size="14" /></a></sup>
Object not found.
### `DisabledError`
### `DisabledError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/exceptions.py#L38"><Icon icon="github" size="14" /></a></sup>
Object is disabled.

View file

@ -10,7 +10,7 @@ Base classes for FastMCP prompts.
## Functions
### `Message`
### `Message` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt.py#L32"><Icon icon="github" size="14" /></a></sup>
```python
Message(content: str | MCPContent, role: Role | None = None, **kwargs: Any) -> PromptMessage
@ -22,13 +22,13 @@ A user-friendly constructor for PromptMessage.
## Classes
### `PromptArgument`
### `PromptArgument` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt.py#L54"><Icon icon="github" size="14" /></a></sup>
An argument that can be passed to a prompt.
### `Prompt`
### `Prompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt.py#L66"><Icon icon="github" size="14" /></a></sup>
A prompt template that can be rendered with parameters.
@ -36,7 +36,7 @@ A prompt template that can be rendered with parameters.
**Methods:**
#### `to_mcp_prompt`
#### `to_mcp_prompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt.py#L73"><Icon icon="github" size="14" /></a></sup>
```python
to_mcp_prompt(self, **overrides: Any) -> MCPPrompt
@ -45,7 +45,7 @@ to_mcp_prompt(self, **overrides: Any) -> MCPPrompt
Convert the prompt to an MCP prompt.
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt.py#L91"><Icon icon="github" size="14" /></a></sup>
```python
from_function(fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, description: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionPrompt
@ -60,7 +60,7 @@ The function can return:
- A sequence of any of the above
### `FunctionPrompt`
### `FunctionPrompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt.py#L119"><Icon icon="github" size="14" /></a></sup>
A prompt that is a function.
@ -68,7 +68,7 @@ A prompt that is a function.
**Methods:**
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt.py#L125"><Icon icon="github" size="14" /></a></sup>
```python
from_function(cls, fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, description: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionPrompt

View file

@ -7,7 +7,7 @@ sidebarTitle: prompt_manager
## Classes
### `PromptManager`
### `PromptManager` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt_manager.py#L21"><Icon icon="github" size="14" /></a></sup>
Manages FastMCP prompts.
@ -15,7 +15,7 @@ Manages FastMCP prompts.
**Methods:**
#### `mount`
#### `mount` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt_manager.py#L45"><Icon icon="github" size="14" /></a></sup>
```python
mount(self, server: MountedServer) -> None
@ -24,7 +24,7 @@ mount(self, server: MountedServer) -> None
Adds a mounted server as a source for prompts.
#### `add_prompt_from_fn`
#### `add_prompt_from_fn` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt_manager.py#L114"><Icon icon="github" size="14" /></a></sup>
```python
add_prompt_from_fn(self, fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, description: str | None = None, tags: set[str] | None = None) -> FunctionPrompt
@ -33,7 +33,7 @@ add_prompt_from_fn(self, fn: Callable[..., PromptResult | Awaitable[PromptResult
Create a prompt from a function.
#### `add_prompt`
#### `add_prompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/prompts/prompt_manager.py#L134"><Icon icon="github" size="14" /></a></sup>
```python
add_prompt(self, prompt: Prompt) -> Prompt

View file

@ -10,7 +10,7 @@ Base classes and interfaces for FastMCP resources.
## Classes
### `Resource`
### `Resource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource.py#L32"><Icon icon="github" size="14" /></a></sup>
Base class for all resources.
@ -18,13 +18,13 @@ Base class for all resources.
**Methods:**
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource.py#L48"><Icon icon="github" size="14" /></a></sup>
```python
from_function(fn: Callable[[], Any], uri: str | AnyUrl, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionResource
```
#### `set_default_mime_type`
#### `set_default_mime_type` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource.py#L69"><Icon icon="github" size="14" /></a></sup>
```python
set_default_mime_type(cls, mime_type: str | None) -> str
@ -33,7 +33,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str
Set default MIME type if not provided.
#### `set_default_name`
#### `set_default_name` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource.py#L76"><Icon icon="github" size="14" /></a></sup>
```python
set_default_name(self) -> Self
@ -42,7 +42,7 @@ set_default_name(self) -> Self
Set default name from URI if not provided.
#### `to_mcp_resource`
#### `to_mcp_resource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource.py#L91"><Icon icon="github" size="14" /></a></sup>
```python
to_mcp_resource(self, **overrides: Any) -> MCPResource
@ -51,7 +51,7 @@ to_mcp_resource(self, **overrides: Any) -> MCPResource
Convert the resource to an MCPResource.
#### `key`
#### `key` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource.py#L105"><Icon icon="github" size="14" /></a></sup>
```python
key(self) -> str
@ -63,7 +63,7 @@ keys having a certain value, as the same tool loaded from different
hierarchies of servers may have different keys.
### `FunctionResource`
### `FunctionResource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource.py#L115"><Icon icon="github" size="14" /></a></sup>
A resource that defers data loading by wrapping a function.
@ -80,7 +80,7 @@ The function can return:
**Methods:**
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource.py#L131"><Icon icon="github" size="14" /></a></sup>
```python
from_function(cls, fn: Callable[[], Any], uri: str | AnyUrl, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionResource

View file

@ -10,7 +10,7 @@ Resource manager functionality.
## Classes
### `ResourceManager`
### `ResourceManager` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource_manager.py#L28"><Icon icon="github" size="14" /></a></sup>
Manages FastMCP resources.
@ -18,7 +18,7 @@ Manages FastMCP resources.
**Methods:**
#### `mount`
#### `mount` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource_manager.py#L60"><Icon icon="github" size="14" /></a></sup>
```python
mount(self, server: MountedServer) -> None
@ -27,7 +27,7 @@ mount(self, server: MountedServer) -> None
Adds a mounted server as a source for resources and templates.
#### `add_resource_or_template_from_fn`
#### `add_resource_or_template_from_fn` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource_manager.py#L182"><Icon icon="github" size="14" /></a></sup>
```python
add_resource_or_template_from_fn(self, fn: Callable[..., Any], uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> Resource | ResourceTemplate
@ -48,7 +48,7 @@ Add a resource or template to the manager from a function.
- returns the existing resource or template.
#### `add_resource_from_fn`
#### `add_resource_from_fn` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource_manager.py#L230"><Icon icon="github" size="14" /></a></sup>
```python
add_resource_from_fn(self, fn: Callable[..., Any], uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> Resource
@ -69,7 +69,7 @@ Add a resource to the manager from a function.
- returns the existing resource.
#### `add_resource`
#### `add_resource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource_manager.py#L270"><Icon icon="github" size="14" /></a></sup>
```python
add_resource(self, resource: Resource) -> Resource
@ -83,7 +83,7 @@ will be used as the storage key. To overwrite it, call
Resource.with_key() before calling this method.
#### `add_template_from_fn`
#### `add_template_from_fn` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource_manager.py#L292"><Icon icon="github" size="14" /></a></sup>
```python
add_template_from_fn(self, fn: Callable[..., Any], uri_template: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> ResourceTemplate
@ -92,7 +92,7 @@ add_template_from_fn(self, fn: Callable[..., Any], uri_template: str, name: str
Create a template from a function.
#### `add_template`
#### `add_template` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/resource_manager.py#L319"><Icon icon="github" size="14" /></a></sup>
```python
add_template(self, template: ResourceTemplate) -> ResourceTemplate

View file

@ -10,13 +10,13 @@ Resource template functionality.
## Functions
### `build_regex`
### `build_regex` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L28"><Icon icon="github" size="14" /></a></sup>
```python
build_regex(template: str) -> re.Pattern
```
### `match_uri_template`
### `match_uri_template` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L44"><Icon icon="github" size="14" /></a></sup>
```python
match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None
@ -24,7 +24,7 @@ match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None
## Classes
### `ResourceTemplate`
### `ResourceTemplate` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L52"><Icon icon="github" size="14" /></a></sup>
A template for dynamically creating resources.
@ -32,13 +32,13 @@ A template for dynamically creating resources.
**Methods:**
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L69"><Icon icon="github" size="14" /></a></sup>
```python
from_function(fn: Callable[..., Any], uri_template: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionResourceTemplate
```
#### `set_default_mime_type`
#### `set_default_mime_type` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L90"><Icon icon="github" size="14" /></a></sup>
```python
set_default_mime_type(cls, mime_type: str | None) -> str
@ -47,7 +47,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str
Set default MIME type if not provided.
#### `matches`
#### `matches` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L96"><Icon icon="github" size="14" /></a></sup>
```python
matches(self, uri: str) -> dict[str, Any] | None
@ -56,7 +56,7 @@ matches(self, uri: str) -> dict[str, Any] | None
Check if URI matches template and extract parameters.
#### `to_mcp_template`
#### `to_mcp_template` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L124"><Icon icon="github" size="14" /></a></sup>
```python
to_mcp_template(self, **overrides: Any) -> MCPResourceTemplate
@ -65,7 +65,7 @@ to_mcp_template(self, **overrides: Any) -> MCPResourceTemplate
Convert the resource template to an MCPResourceTemplate.
#### `from_mcp_template`
#### `from_mcp_template` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L135"><Icon icon="github" size="14" /></a></sup>
```python
from_mcp_template(cls, mcp_template: MCPResourceTemplate) -> ResourceTemplate
@ -74,7 +74,7 @@ from_mcp_template(cls, mcp_template: MCPResourceTemplate) -> ResourceTemplate
Creates a FastMCP ResourceTemplate from a raw MCP ResourceTemplate object.
#### `key`
#### `key` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L148"><Icon icon="github" size="14" /></a></sup>
```python
key(self) -> str
@ -86,7 +86,7 @@ keys having a certain value, as the same tool loaded from different
hierarchies of servers may have different keys.
### `FunctionResourceTemplate`
### `FunctionResourceTemplate` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L158"><Icon icon="github" size="14" /></a></sup>
A template for dynamically creating resources.
@ -94,7 +94,7 @@ A template for dynamically creating resources.
**Methods:**
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/template.py#L179"><Icon icon="github" size="14" /></a></sup>
```python
from_function(cls, fn: Callable[..., Any], uri_template: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionResourceTemplate

View file

@ -10,19 +10,19 @@ Concrete resource implementations.
## Classes
### `TextResource`
### `TextResource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L21"><Icon icon="github" size="14" /></a></sup>
A resource that reads from a string.
### `BinaryResource`
### `BinaryResource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L31"><Icon icon="github" size="14" /></a></sup>
A resource that reads from bytes.
### `FileResource`
### `FileResource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L41"><Icon icon="github" size="14" /></a></sup>
A resource that reads from a file.
@ -32,7 +32,7 @@ Set is_binary=True to read file as binary data instead of text.
**Methods:**
#### `validate_absolute_path`
#### `validate_absolute_path` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L59"><Icon icon="github" size="14" /></a></sup>
```python
validate_absolute_path(cls, path: Path) -> Path
@ -41,7 +41,7 @@ validate_absolute_path(cls, path: Path) -> Path
Ensure path is absolute.
#### `set_binary_from_mime_type`
#### `set_binary_from_mime_type` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L67"><Icon icon="github" size="14" /></a></sup>
```python
set_binary_from_mime_type(cls, is_binary: bool, info: ValidationInfo) -> bool
@ -50,13 +50,13 @@ set_binary_from_mime_type(cls, is_binary: bool, info: ValidationInfo) -> bool
Set is_binary based on mime_type if not explicitly set.
### `HttpResource`
### `HttpResource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L84"><Icon icon="github" size="14" /></a></sup>
A resource that reads from an HTTP endpoint.
### `DirectoryResource`
### `DirectoryResource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L100"><Icon icon="github" size="14" /></a></sup>
A resource that lists files in a directory.
@ -64,7 +64,7 @@ A resource that lists files in a directory.
**Methods:**
#### `validate_absolute_path`
#### `validate_absolute_path` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L116"><Icon icon="github" size="14" /></a></sup>
```python
validate_absolute_path(cls, path: Path) -> Path
@ -73,7 +73,7 @@ validate_absolute_path(cls, path: Path) -> Path
Ensure path is absolute.
#### `list_files`
#### `list_files` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/resources/types.py#L122"><Icon icon="github" size="14" /></a></sup>
```python
list_files(self) -> list[Path]

View file

@ -7,4 +7,4 @@ sidebarTitle: auth
## Classes
### `OAuthProvider`
### `OAuthProvider` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/auth.py#L14"><Icon icon="github" size="14" /></a></sup>

View file

@ -7,23 +7,23 @@ sidebarTitle: bearer
## Classes
### `JWKData`
### `JWKData` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/bearer.py#L29"><Icon icon="github" size="14" /></a></sup>
JSON Web Key data structure.
### `JWKSData`
### `JWKSData` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/bearer.py#L42"><Icon icon="github" size="14" /></a></sup>
JSON Web Key Set data structure.
### `RSAKeyPair`
### `RSAKeyPair` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/bearer.py#L49"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `generate`
#### `generate` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/bearer.py#L54"><Icon icon="github" size="14" /></a></sup>
```python
generate(cls) -> 'RSAKeyPair'
@ -35,7 +35,7 @@ Generate an RSA key pair for testing.
- (private_key_pem, public_key_pem)
#### `create_token`
#### `create_token` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/bearer.py#L88"><Icon icon="github" size="14" /></a></sup>
```python
create_token(self, subject: str = 'fastmcp-user', issuer: str = 'https://fastmcp.example.com', audience: str | list[str] | None = None, scopes: list[str] | None = None, expires_in_seconds: int = 3600, additional_claims: dict[str, Any] | None = None, kid: str | None = None) -> str
@ -57,7 +57,7 @@ Generate a test JWT token for testing purposes.
- Signed JWT token string
### `BearerAuthProvider`
### `BearerAuthProvider` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/bearer.py#L149"><Icon icon="github" size="14" /></a></sup>
Simple JWT Bearer Token validator for hosted MCP servers.

View file

@ -7,13 +7,13 @@ sidebarTitle: bearer_env
## Classes
### `EnvBearerAuthProviderSettings`
### `EnvBearerAuthProviderSettings` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/bearer_env.py#L8"><Icon icon="github" size="14" /></a></sup>
Settings for the BearerAuthProvider.
### `EnvBearerAuthProvider`
### `EnvBearerAuthProvider` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/bearer_env.py#L24"><Icon icon="github" size="14" /></a></sup>
A BearerAuthProvider that loads settings from environment variables. Any

View file

@ -7,7 +7,7 @@ sidebarTitle: in_memory
## Classes
### `InMemoryOAuthProvider`
### `InMemoryOAuthProvider` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/providers/in_memory.py#L31"><Icon icon="github" size="14" /></a></sup>
An in-memory OAuth provider for testing purposes.

View file

@ -7,7 +7,7 @@ sidebarTitle: context
## Functions
### `set_context`
### `set_context` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/context.py#L36"><Icon icon="github" size="14" /></a></sup>
```python
set_context(context: Context) -> Generator[Context, None, None]
@ -15,7 +15,7 @@ set_context(context: Context) -> Generator[Context, None, None]
## Classes
### `Context`
### `Context` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/context.py#L45"><Icon icon="github" size="14" /></a></sup>
Context object providing access to MCP capabilities.
@ -53,7 +53,7 @@ The context is optional - tools that don't need it can omit the parameter.
**Methods:**
#### `request_context`
#### `request_context` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/context.py#L98"><Icon icon="github" size="14" /></a></sup>
```python
request_context(self) -> RequestContext
@ -64,7 +64,7 @@ Access to the underlying request context.
If called outside of a request context, this will raise a ValueError.
#### `client_id`
#### `client_id` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/context.py#L168"><Icon icon="github" size="14" /></a></sup>
```python
client_id(self) -> str | None
@ -73,7 +73,7 @@ client_id(self) -> str | None
Get the client ID if available.
#### `request_id`
#### `request_id` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/context.py#L177"><Icon icon="github" size="14" /></a></sup>
```python
request_id(self) -> str
@ -82,7 +82,7 @@ request_id(self) -> str
Get the unique ID for this request.
#### `session_id`
#### `session_id` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/context.py#L182"><Icon icon="github" size="14" /></a></sup>
```python
session_id(self) -> str | None
@ -99,7 +99,7 @@ the same client session.
- for stdio and in-memory transports which don't use session IDs.
#### `session`
#### `session` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/context.py#L213"><Icon icon="github" size="14" /></a></sup>
```python
session(self)
@ -108,7 +108,7 @@ session(self)
Access to the underlying session for advanced usage.
#### `get_http_request`
#### `get_http_request` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/context.py#L282"><Icon icon="github" size="14" /></a></sup>
```python
get_http_request(self) -> Request

View file

@ -7,19 +7,19 @@ sidebarTitle: dependencies
## Functions
### `get_context`
### `get_context` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/dependencies.py#L27"><Icon icon="github" size="14" /></a></sup>
```python
get_context() -> Context
```
### `get_http_request`
### `get_http_request` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/dependencies.py#L39"><Icon icon="github" size="14" /></a></sup>
```python
get_http_request() -> Request
```
### `get_http_headers`
### `get_http_headers` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/dependencies.py#L48"><Icon icon="github" size="14" /></a></sup>
```python
get_http_headers(include_all: bool = False) -> dict[str, str]

View file

@ -7,13 +7,13 @@ sidebarTitle: http
## Functions
### `set_http_request`
### `set_http_request` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/http.py#L48"><Icon icon="github" size="14" /></a></sup>
```python
set_http_request(request: Request) -> Generator[Request, None, None]
```
### `setup_auth_middleware_and_routes`
### `setup_auth_middleware_and_routes` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/http.py#L72"><Icon icon="github" size="14" /></a></sup>
```python
setup_auth_middleware_and_routes(auth: OAuthProvider) -> tuple[list[Middleware], list[BaseRoute], list[str]]
@ -29,7 +29,7 @@ Set up authentication middleware and routes if auth is enabled.
- Tuple of (middleware, auth_routes, required_scopes)
### `create_base_app`
### `create_base_app` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/http.py#L110"><Icon icon="github" size="14" /></a></sup>
```python
create_base_app(routes: list[BaseRoute], middleware: list[Middleware], debug: bool = False, lifespan: Callable | None = None) -> StarletteWithLifespan
@ -48,7 +48,7 @@ Create a base Starlette app with common middleware and routes.
- A Starlette application
### `create_sse_app`
### `create_sse_app` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/http.py#L138"><Icon icon="github" size="14" /></a></sup>
```python
create_sse_app(server: FastMCP[LifespanResultT], message_path: str, sse_path: str, auth: OAuthProvider | None = None, debug: bool = False, routes: list[BaseRoute] | None = None, middleware: list[Middleware] | None = None) -> StarletteWithLifespan
@ -70,7 +70,7 @@ Returns:
A Starlette application with RequestContextMiddleware
### `create_streamable_http_app`
### `create_streamable_http_app` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/http.py#L246"><Icon icon="github" size="14" /></a></sup>
```python
create_streamable_http_app(server: FastMCP[LifespanResultT], streamable_http_path: str, event_store: EventStore | None = None, auth: OAuthProvider | None = None, json_response: bool = False, stateless_http: bool = False, debug: bool = False, routes: list[BaseRoute] | None = None, middleware: list[Middleware] | None = None) -> StarletteWithLifespan
@ -96,17 +96,17 @@ Return an instance of the StreamableHTTP server app.
## Classes
### `StarletteWithLifespan`
### `StarletteWithLifespan` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/http.py#L41"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `lifespan`
#### `lifespan` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/http.py#L43"><Icon icon="github" size="14" /></a></sup>
```python
lifespan(self) -> Lifespan
```
### `RequestContextMiddleware`
### `RequestContextMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/http.py#L56"><Icon icon="github" size="14" /></a></sup>
Middleware that stores each request in a ContextVar

View file

@ -0,0 +1,8 @@
---
title: __init__
sidebarTitle: __init__
---
# `fastmcp.server.middleware`
*This module is empty or contains only private/internal implementations.*

View file

@ -0,0 +1,40 @@
---
title: error_handling
sidebarTitle: error_handling
---
# `fastmcp.server.middleware.error_handling`
Error handling middleware for consistent error responses and tracking.
## Classes
### `ErrorHandlingMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/error_handling.py#L15"><Icon icon="github" size="14" /></a></sup>
Middleware that provides consistent error handling and logging.
Catches exceptions, logs them appropriately, and converts them to
proper MCP error responses. Also tracks error patterns for monitoring.
**Methods:**
#### `get_error_stats` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/error_handling.py#L121"><Icon icon="github" size="14" /></a></sup>
```python
get_error_stats(self) -> dict[str, int]
```
Get error statistics for monitoring.
### `RetryMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/error_handling.py#L126"><Icon icon="github" size="14" /></a></sup>
Middleware that implements automatic retry logic for failed requests.
Retries requests that fail with transient errors, using exponential
backoff to avoid overwhelming the server or external dependencies.

View file

@ -0,0 +1,29 @@
---
title: logging
sidebarTitle: logging
---
# `fastmcp.server.middleware.logging`
Comprehensive logging middleware for FastMCP servers.
## Classes
### `LoggingMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/logging.py#L10"><Icon icon="github" size="14" /></a></sup>
Middleware that provides comprehensive request and response logging.
Logs all MCP messages with configurable detail levels. Useful for debugging,
monitoring, and understanding server usage patterns.
### `StructuredLoggingMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/logging.py#L87"><Icon icon="github" size="14" /></a></sup>
Middleware that provides structured JSON logging for better log analysis.
Outputs structured logs that are easier to parse and analyze with log
aggregation tools like ELK stack, Splunk, or cloud logging services.

View file

@ -0,0 +1,56 @@
---
title: middleware
sidebarTitle: middleware
---
# `fastmcp.server.middleware.middleware`
## Functions
### `make_middleware_wrapper` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L106"><Icon icon="github" size="14" /></a></sup>
```python
make_middleware_wrapper(middleware: Middleware, call_next: CallNext[T, R]) -> CallNext[T, R]
```
Create a wrapper that applies a single middleware to a context. The
closure bakes in the middleware and call_next function, so it can be
passed to other functions that expect a call_next function.
## Classes
### `CallNext` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L36"><Icon icon="github" size="14" /></a></sup>
### `CallToolResult` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L56"><Icon icon="github" size="14" /></a></sup>
### `ListToolsResult` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L62"><Icon icon="github" size="14" /></a></sup>
### `ListResourcesResult` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L67"><Icon icon="github" size="14" /></a></sup>
### `ListResourceTemplatesResult` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L72"><Icon icon="github" size="14" /></a></sup>
### `ListPromptsResult` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L77"><Icon icon="github" size="14" /></a></sup>
### `ServerResultProtocol` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L82"><Icon icon="github" size="14" /></a></sup>
### `MiddlewareContext` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L87"><Icon icon="github" size="14" /></a></sup>
Unified context for all middleware operations.
**Methods:**
#### `copy` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L102"><Icon icon="github" size="14" /></a></sup>
```python
copy(self, **kwargs: Any) -> MiddlewareContext[T]
```
### `Middleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/middleware.py#L119"><Icon icon="github" size="14" /></a></sup>
Base class for FastMCP middleware with dispatching hooks.

View file

@ -0,0 +1,47 @@
---
title: rate_limiting
sidebarTitle: rate_limiting
---
# `fastmcp.server.middleware.rate_limiting`
Rate limiting middleware for protecting FastMCP servers from abuse.
## Classes
### `RateLimitError` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/rate_limiting.py#L15"><Icon icon="github" size="14" /></a></sup>
Error raised when rate limit is exceeded.
### `TokenBucketRateLimiter` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/rate_limiting.py#L22"><Icon icon="github" size="14" /></a></sup>
Token bucket implementation for rate limiting.
### `SlidingWindowRateLimiter` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/rate_limiting.py#L61"><Icon icon="github" size="14" /></a></sup>
Sliding window rate limiter implementation.
### `RateLimitingMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/rate_limiting.py#L92"><Icon icon="github" size="14" /></a></sup>
Middleware that implements rate limiting to prevent server abuse.
Uses a token bucket algorithm by default, allowing for burst traffic
while maintaining a sustainable long-term rate.
### `SlidingWindowRateLimitingMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/rate_limiting.py#L170"><Icon icon="github" size="14" /></a></sup>
Middleware that implements sliding window rate limiting.
Uses a sliding window approach which provides more precise rate limiting
but uses more memory to track individual request timestamps.

View file

@ -0,0 +1,29 @@
---
title: timing
sidebarTitle: timing
---
# `fastmcp.server.middleware.timing`
Timing middleware for measuring and logging request performance.
## Classes
### `TimingMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/timing.py#L10"><Icon icon="github" size="14" /></a></sup>
Middleware that logs the execution time of requests.
Only measures and logs timing for request messages (not notifications).
Provides insights into performance characteristics of your MCP server.
### `DetailedTimingMiddleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/middleware/timing.py#L60"><Icon icon="github" size="14" /></a></sup>
Enhanced timing middleware with per-operation breakdowns.
Provides detailed timing information for different types of MCP operations,
allowing you to identify performance bottlenecks in specific operations.

View file

@ -10,13 +10,13 @@ FastMCP server implementation for OpenAPI integration.
## Classes
### `MCPType`
### `MCPType` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/openapi.py#L76"><Icon icon="github" size="14" /></a></sup>
Type of FastMCP component to create from a route.
### `RouteType`
### `RouteType` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/openapi.py#L95"><Icon icon="github" size="14" /></a></sup>
Deprecated: Use MCPType instead.
@ -24,31 +24,31 @@ Deprecated: Use MCPType instead.
This enum is kept for backward compatibility and will be removed in a future version.
### `RouteMap`
### `RouteMap` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/openapi.py#L109"><Icon icon="github" size="14" /></a></sup>
Mapping configuration for HTTP routes to FastMCP component types.
### `OpenAPITool`
### `OpenAPITool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/openapi.py#L227"><Icon icon="github" size="14" /></a></sup>
Tool implementation for OpenAPI endpoints.
### `OpenAPIResource`
### `OpenAPIResource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/openapi.py#L478"><Icon icon="github" size="14" /></a></sup>
Resource implementation for OpenAPI endpoints.
### `OpenAPIResourceTemplate`
### `OpenAPIResourceTemplate` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/openapi.py#L597"><Icon icon="github" size="14" /></a></sup>
Resource template implementation for OpenAPI endpoints.
### `FastMCPOpenAPI`
### `FastMCPOpenAPI` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/openapi.py#L651"><Icon icon="github" size="14" /></a></sup>
FastMCP server implementation that creates components from an OpenAPI schema.

View file

@ -7,25 +7,25 @@ sidebarTitle: proxy
## Classes
### `ProxyToolManager`
### `ProxyToolManager` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L36"><Icon icon="github" size="14" /></a></sup>
A ToolManager that sources its tools from a remote client in addition to local and mounted tools.
### `ProxyResourceManager`
### `ProxyResourceManager` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L81"><Icon icon="github" size="14" /></a></sup>
A ResourceManager that sources its resources from a remote client in addition to local and mounted resources.
### `ProxyPromptManager`
### `ProxyPromptManager` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L159"><Icon icon="github" size="14" /></a></sup>
A PromptManager that sources its prompts from a remote client in addition to local and mounted prompts.
### `ProxyTool`
### `ProxyTool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L209"><Icon icon="github" size="14" /></a></sup>
A Tool that represents and executes a tool on a remote server.
@ -33,7 +33,7 @@ A Tool that represents and executes a tool on a remote server.
**Methods:**
#### `from_mcp_tool`
#### `from_mcp_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L219"><Icon icon="github" size="14" /></a></sup>
```python
from_mcp_tool(cls, client: Client, mcp_tool: mcp.types.Tool) -> ProxyTool
@ -42,7 +42,7 @@ from_mcp_tool(cls, client: Client, mcp_tool: mcp.types.Tool) -> ProxyTool
Factory method to create a ProxyTool from a raw MCP tool schema.
### `ProxyResource`
### `ProxyResource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L246"><Icon icon="github" size="14" /></a></sup>
A Resource that represents and reads a resource from a remote server.
@ -50,7 +50,7 @@ A Resource that represents and reads a resource from a remote server.
**Methods:**
#### `from_mcp_resource`
#### `from_mcp_resource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L260"><Icon icon="github" size="14" /></a></sup>
```python
from_mcp_resource(cls, client: Client, mcp_resource: mcp.types.Resource) -> ProxyResource
@ -59,7 +59,7 @@ from_mcp_resource(cls, client: Client, mcp_resource: mcp.types.Resource) -> Prox
Factory method to create a ProxyResource from a raw MCP resource schema.
### `ProxyTemplate`
### `ProxyTemplate` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L287"><Icon icon="github" size="14" /></a></sup>
A ResourceTemplate that represents and creates resources from a remote server template.
@ -67,7 +67,7 @@ A ResourceTemplate that represents and creates resources from a remote server te
**Methods:**
#### `from_mcp_template`
#### `from_mcp_template` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L297"><Icon icon="github" size="14" /></a></sup>
```python
from_mcp_template(cls, client: Client, mcp_template: mcp.types.ResourceTemplate) -> ProxyTemplate
@ -76,7 +76,7 @@ from_mcp_template(cls, client: Client, mcp_template: mcp.types.ResourceTemplate)
Factory method to create a ProxyTemplate from a raw MCP template schema.
### `ProxyPrompt`
### `ProxyPrompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L343"><Icon icon="github" size="14" /></a></sup>
A Prompt that represents and renders a prompt from a remote server.
@ -84,7 +84,7 @@ A Prompt that represents and renders a prompt from a remote server.
**Methods:**
#### `from_mcp_prompt`
#### `from_mcp_prompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L355"><Icon icon="github" size="14" /></a></sup>
```python
from_mcp_prompt(cls, client: Client, mcp_prompt: mcp.types.Prompt) -> ProxyPrompt
@ -93,7 +93,7 @@ from_mcp_prompt(cls, client: Client, mcp_prompt: mcp.types.Prompt) -> ProxyPromp
Factory method to create a ProxyPrompt from a raw MCP prompt schema.
### `FastMCPProxy`
### `FastMCPProxy` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/proxy.py#L381"><Icon icon="github" size="14" /></a></sup>
A FastMCP server that acts as a proxy to a remote MCP-compliant server.

View file

@ -10,7 +10,7 @@ FastMCP - A more ergonomic interface for MCP servers.
## Functions
### `add_resource_prefix`
### `add_resource_prefix` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1879"><Icon icon="github" size="14" /></a></sup>
```python
add_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str
@ -19,26 +19,36 @@ add_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'p
Add a prefix to a resource URI.
Args:
uri: The original resource URI
prefix: The prefix to add
**Args:**
- `uri`: The original resource URI
- `prefix`: The prefix to add
Returns:
The resource URI with the prefix added
**Returns:**
- The resource URI with the prefix added
Examples:
>>> add_resource_prefix("resource://path/to/resource", "prefix")
"resource://prefix/path/to/resource" # with new style
>>> add_resource_prefix("resource://path/to/resource", "prefix")
"prefix+resource://path/to/resource" # with legacy style
>>> add_resource_prefix("resource:///absolute/path", "prefix")
"resource://prefix//absolute/path" # with new style
**Examples:**
Raises:
ValueError: If the URI doesn't match the expected protocol://path format
With new style:
```python
add_resource_prefix("resource://path/to/resource", "prefix")
"resource://prefix/path/to/resource"
```
With legacy style:
```python
add_resource_prefix("resource://path/to/resource", "prefix")
"prefix+resource://path/to/resource"
```
With absolute path:
```python
add_resource_prefix("resource:///absolute/path", "prefix")
"resource://prefix//absolute/path"
```
### `remove_resource_prefix`
**Raises:**
- `ValueError`: If the URI doesn't match the expected protocol\://path format
### `remove_resource_prefix` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1939"><Icon icon="github" size="14" /></a></sup>
```python
remove_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str
@ -47,26 +57,37 @@ remove_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol',
Remove a prefix from a resource URI.
Args:
uri: The resource URI with a prefix
prefix: The prefix to remove
prefix_format: The format of the prefix to remove
Returns:
The resource URI with the prefix removed
**Args:**
- `uri`: The resource URI with a prefix
- `prefix`: The prefix to remove
- `prefix_format`: The format of the prefix to remove
Examples:
>>> remove_resource_prefix("resource://prefix/path/to/resource", "prefix")
"resource://path/to/resource" # with new style
>>> remove_resource_prefix("prefix+resource://path/to/resource", "prefix")
"resource://path/to/resource" # with legacy style
>>> remove_resource_prefix("resource://prefix//absolute/path", "prefix")
"resource:///absolute/path" # with new style
Returns:
The resource URI with the prefix removed
Raises:
ValueError: If the URI doesn't match the expected protocol://path format
**Examples:**
### `has_resource_prefix`
With new style:
```python
remove_resource_prefix("resource://prefix/path/to/resource", "prefix")
"resource://path/to/resource"
```
With legacy style:
```python
remove_resource_prefix("prefix+resource://path/to/resource", "prefix")
"resource://path/to/resource"
```
With absolute path:
```python
remove_resource_prefix("resource://prefix//absolute/path", "prefix")
"resource:///absolute/path"
```
**Raises:**
- `ValueError`: If the URI doesn't match the expected protocol\://path format
### `has_resource_prefix` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L2006"><Icon icon="github" size="14" /></a></sup>
```python
has_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> bool
@ -75,53 +96,63 @@ has_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'p
Check if a resource URI has a specific prefix.
Args:
uri: The resource URI to check
prefix: The prefix to look for
**Args:**
- `uri`: The resource URI to check
- `prefix`: The prefix to look for
Returns:
True if the URI has the specified prefix, False otherwise
**Returns:**
- True if the URI has the specified prefix, False otherwise
Examples:
>>> has_resource_prefix("resource://prefix/path/to/resource", "prefix")
True # with new style
>>> has_resource_prefix("prefix+resource://path/to/resource", "prefix")
True # with legacy style
>>> has_resource_prefix("resource://other/path/to/resource", "prefix")
False
**Examples:**
With new style:
```python
has_resource_prefix("resource://prefix/path/to/resource", "prefix")
True
```
With legacy style:
```python
has_resource_prefix("prefix+resource://path/to/resource", "prefix")
True
```
With other path:
```python
has_resource_prefix("resource://other/path/to/resource", "prefix")
False
```
**Raises:**
- `ValueError`: If the URI doesn't match the expected protocol\://path format
Raises:
ValueError: If the URI doesn't match the expected protocol://path format
## Classes
### `FastMCP`
### `FastMCP` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L113"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `settings`
#### `settings` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L264"><Icon icon="github" size="14" /></a></sup>
```python
settings(self) -> Settings
```
#### `name`
#### `name` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L275"><Icon icon="github" size="14" /></a></sup>
```python
name(self) -> str
```
#### `instructions`
#### `instructions` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L279"><Icon icon="github" size="14" /></a></sup>
```python
instructions(self) -> str | None
```
#### `run`
#### `run` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L304"><Icon icon="github" size="14" /></a></sup>
```python
run(self, transport: Literal['stdio', 'streamable-http', 'sse'] | None = None, **transport_kwargs: Any) -> None
run(self, transport: Transport | None = None, **transport_kwargs: Any) -> None
```
Run the FastMCP server. Note this is a synchronous function.
@ -130,13 +161,13 @@ Run the FastMCP server. Note this is a synchronous function.
- `transport`: Transport protocol to use ("stdio", "sse", or "streamable-http")
#### `add_middleware`
#### `add_middleware` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L338"><Icon icon="github" size="14" /></a></sup>
```python
add_middleware(self, middleware: Middleware) -> None
```
#### `custom_route`
#### `custom_route` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L384"><Icon icon="github" size="14" /></a></sup>
```python
custom_route(self, path: str, methods: list[str], name: str | None = None, include_in_schema: bool = True)
@ -157,7 +188,7 @@ Starlette's reverse URL lookup feature)
- `include_in_schema`: Whether to include in OpenAPI schema, defaults to True
#### `add_tool`
#### `add_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L742"><Icon icon="github" size="14" /></a></sup>
```python
add_tool(self, tool: Tool) -> None
@ -172,7 +203,7 @@ with the Context type annotation. See the @tool decorator for examples.
- `tool`: The Tool instance to register
#### `remove_tool`
#### `remove_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L754"><Icon icon="github" size="14" /></a></sup>
```python
remove_tool(self, name: str) -> None
@ -187,19 +218,19 @@ Remove a tool from the server.
- `NotFoundError`: If the tool is not found
#### `tool`
#### `tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L767"><Icon icon="github" size="14" /></a></sup>
```python
tool(self, name_or_fn: AnyFunction) -> FunctionTool
```
#### `tool`
#### `tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L780"><Icon icon="github" size="14" /></a></sup>
```python
tool(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionTool]
```
#### `tool`
#### `tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L792"><Icon icon="github" size="14" /></a></sup>
```python
tool(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool
@ -223,12 +254,37 @@ This decorator supports multiple calling patterns:
- `name`: Optional name for the tool (keyword-only, alternative to name_or_fn)
- `description`: Optional description of what the tool does
- `tags`: Optional set of tags for categorizing the tool
- `annotations`: Optional annotations about the tool's behavior (e.g. {"is_async"\: True})
- `annotations`: Optional annotations about the tool's behavior
- `exclude_args`: Optional list of argument names to exclude from the tool schema
- `enabled`: Optional boolean to enable or disable the tool
**Examples:**
#### `add_resource`
Register a tool with a custom name:
```python
@server.tool
def my_tool(x: int) -> str:
return str(x)
# Register a tool with a custom name
@server.tool
def my_tool(x: int) -> str:
return str(x)
@server.tool("custom_name")
def my_tool(x: int) -> str:
return str(x)
@server.tool(name="custom_name")
def my_tool(x: int) -> str:
return str(x)
# Direct function call
server.tool(my_function, name="custom_name")
```
#### `add_resource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L912"><Icon icon="github" size="14" /></a></sup>
```python
add_resource(self, resource: Resource) -> None
@ -240,7 +296,7 @@ Add a resource to the server.
- `resource`: A Resource instance to add
#### `add_template`
#### `add_template` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L922"><Icon icon="github" size="14" /></a></sup>
```python
add_template(self, template: ResourceTemplate) -> None
@ -252,7 +308,7 @@ Add a resource template to the server.
- `template`: A ResourceTemplate instance to add
#### `add_resource_fn`
#### `add_resource_fn` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L930"><Icon icon="github" size="14" /></a></sup>
```python
add_resource_fn(self, fn: AnyFunction, uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> None
@ -272,7 +328,7 @@ has parameters, it will be registered as a template resource.
- `tags`: Optional set of tags for categorizing the resource
#### `resource`
#### `resource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L969"><Icon icon="github" size="14" /></a></sup>
```python
resource(self, uri: str) -> Callable[[AnyFunction], Resource | ResourceTemplate]
@ -301,8 +357,36 @@ has parameters, it will be registered as a template resource.
- `tags`: Optional set of tags for categorizing the resource
- `enabled`: Optional boolean to enable or disable the resource
**Examples:**
#### `add_prompt`
Register a resource with a custom name:
```python
@server.resource("resource://my-resource")
def get_data() -> str:
return "Hello, world!"
@server.resource("resource://my-resource")
async get_data() -> str:
data = await fetch_data()
return f"Hello, world! {data}"
@server.resource("resource://{city}/weather")
def get_weather(city: str) -> str:
return f"Weather for {city}"
@server.resource("resource://{city}/weather")
def get_weather_with_context(city: str, ctx: Context) -> str:
ctx.info(f"Fetching weather for {city}")
return f"Weather for {city}"
@server.resource("resource://{city}/weather")
async def get_weather(city: str) -> str:
data = await fetch_weather(city)
return f"Weather for {city}: {data}"
```
#### `add_prompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1092"><Icon icon="github" size="14" /></a></sup>
```python
add_prompt(self, prompt: Prompt) -> None
@ -314,19 +398,19 @@ Add a prompt to the server.
- `prompt`: A Prompt instance to add
#### `prompt`
#### `prompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1102"><Icon icon="github" size="14" /></a></sup>
```python
prompt(self, name_or_fn: AnyFunction) -> FunctionPrompt
```
#### `prompt`
#### `prompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1113"><Icon icon="github" size="14" /></a></sup>
```python
prompt(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionPrompt]
```
#### `prompt`
#### `prompt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1123"><Icon icon="github" size="14" /></a></sup>
```python
prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt
@ -352,9 +436,11 @@ Decorator to register a prompt.
tags: Optional set of tags for categorizing the prompt
enabled: Optional boolean to enable or disable the prompt
Example:
Examples:
```python
@server.prompt
def analyze_table(table_name: str) -> list\[Message]:
def analyze_table(table_name: str) -> list[Message]:
schema = read_table_schema(table_name)
return [
{
@ -365,7 +451,7 @@ Decorator to register a prompt.
]
@server.prompt()
def analyze_with_context(table_name: str, ctx: Context) -> list\[Message]:
def analyze_with_context(table_name: str, ctx: Context) -> list[Message]:
ctx.info(f"Analyzing table {table_name}")
schema = read_table_schema(table_name)
return [
@ -377,7 +463,7 @@ Decorator to register a prompt.
]
@server.prompt("custom_name")
def analyze_file(path: str) -> list\[Message]:
def analyze_file(path: str) -> list[Message]:
content = await read_file(path)
return [
{
@ -393,14 +479,15 @@ Decorator to register a prompt.
]
@server.prompt(name="custom_name")
def another_prompt(data: str) -> list\[Message]:
def another_prompt(data: str) -> list[Message]:
return [{"role": "user", "content": data}]
# Direct function call
server.prompt(my_function, name="custom_name")
```
#### `sse_app`
#### `sse_app` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1344"><Icon icon="github" size="14" /></a></sup>
```python
sse_app(self, path: str | None = None, message_path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan
@ -414,7 +501,7 @@ Create a Starlette app for the SSE server.
- `middleware`: A list of middleware to apply to the app
#### `streamable_http_app`
#### `streamable_http_app` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1375"><Icon icon="github" size="14" /></a></sup>
```python
streamable_http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan
@ -427,10 +514,10 @@ Create a Starlette app for the StreamableHTTP server.
- `middleware`: A list of middleware to apply to the app
#### `http_app`
#### `http_app` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1396"><Icon icon="github" size="14" /></a></sup>
```python
http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None, json_response: bool | None = None, stateless_http: bool | None = None, transport: Literal['streamable-http', 'sse'] = 'streamable-http') -> StarletteWithLifespan
http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None, json_response: bool | None = None, stateless_http: bool | None = None, transport: Literal['http', 'streamable-http', 'sse'] = 'http') -> StarletteWithLifespan
```
Create a Starlette app using the specified HTTP transport.
@ -444,7 +531,7 @@ Create a Starlette app using the specified HTTP transport.
- A Starlette application configured with the specified transport
#### `mount`
#### `mount` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1470"><Icon icon="github" size="14" /></a></sup>
```python
mount(self, server: FastMCP[LifespanResultT], prefix: str | None = None, as_proxy: bool | None = None) -> None
@ -498,7 +585,7 @@ automatically determined based on whether the server has a custom lifespan
- `prompt_separator`: Deprecated. Separator character for prompt names.
#### `from_openapi`
#### `from_openapi` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1720"><Icon icon="github" size="14" /></a></sup>
```python
from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, tags: set[str] | None = None, **settings: Any) -> FastMCPOpenAPI
@ -507,7 +594,7 @@ from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, route
Create a FastMCP server from an OpenAPI specification.
#### `from_fastapi`
#### `from_fastapi` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1748"><Icon icon="github" size="14" /></a></sup>
```python
from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, httpx_client_kwargs: dict[str, Any] | None = None, tags: set[str] | None = None, **settings: Any) -> FastMCPOpenAPI
@ -516,7 +603,7 @@ from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap]
Create a FastMCP server from a FastAPI application.
#### `as_proxy`
#### `as_proxy` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1790"><Icon icon="github" size="14" /></a></sup>
```python
as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy
@ -524,13 +611,13 @@ as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any]
Create a FastMCP proxy server for the given backend.
The ``backend`` argument can be either an existing :class:`~fastmcp.client.Client`
instance or any value accepted as the ``transport`` argument of
:class:`~fastmcp.client.Client`. This mirrors the convenience of the
``Client`` constructor.
The `backend` argument can be either an existing `fastmcp.client.Client`
instance or any value accepted as the `transport` argument of
`fastmcp.client.Client`. This mirrors the convenience of the
`fastmcp.client.Client` constructor.
#### `from_client`
#### `from_client` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1820"><Icon icon="github" size="14" /></a></sup>
```python
from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPProxy
@ -539,4 +626,4 @@ from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPPr
Create a FastMCP proxy server from a FastMCP client.
### `MountedServer`
### `MountedServer` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/server.py#L1873"><Icon icon="github" size="14" /></a></sup>

View file

@ -7,7 +7,7 @@ sidebarTitle: settings
## Classes
### `ExtendedEnvSettingsSource`
### `ExtendedEnvSettingsSource` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/settings.py#L26"><Icon icon="github" size="14" /></a></sup>
A special EnvSettingsSource that allows for multiple env var prefixes to be used.
@ -17,15 +17,15 @@ Raises a deprecation warning if the old `FASTMCP_SERVER_` prefix is used.
**Methods:**
#### `get_field_value`
#### `get_field_value` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/settings.py#L33"><Icon icon="github" size="14" /></a></sup>
```python
get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]
```
### `ExtendedSettingsConfigDict`
### `ExtendedSettingsConfigDict` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/settings.py#L53"><Icon icon="github" size="14" /></a></sup>
### `Settings`
### `Settings` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/settings.py#L57"><Icon icon="github" size="14" /></a></sup>
FastMCP settings.
@ -33,13 +33,13 @@ FastMCP settings.
**Methods:**
#### `settings_customise_sources`
#### `settings_customise_sources` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/settings.py#L69"><Icon icon="github" size="14" /></a></sup>
```python
settings_customise_sources(cls, settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource) -> tuple[PydanticBaseSettingsSource, ...]
```
#### `settings`
#### `settings` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/settings.py#L87"><Icon icon="github" size="14" /></a></sup>
```python
settings(self) -> Self
@ -49,7 +49,7 @@ This property is for backwards compatibility with FastMCP < 2.8.0,
which accessed fastmcp.settings.settings
#### `setup_logging`
#### `setup_logging` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/settings.py#L182"><Icon icon="github" size="14" /></a></sup>
```python
setup_logging(self) -> Self

View file

@ -7,7 +7,7 @@ sidebarTitle: tool
## Functions
### `default_serializer`
### `default_serializer` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L34"><Icon icon="github" size="14" /></a></sup>
```python
default_serializer(data: Any) -> str
@ -15,7 +15,7 @@ default_serializer(data: Any) -> str
## Classes
### `Tool`
### `Tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L38"><Icon icon="github" size="14" /></a></sup>
Internal tool registration info.
@ -23,13 +23,13 @@ Internal tool registration info.
**Methods:**
#### `to_mcp_tool`
#### `to_mcp_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L49"><Icon icon="github" size="14" /></a></sup>
```python
to_mcp_tool(self, **overrides: Any) -> MCPTool
```
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L59"><Icon icon="github" size="14" /></a></sup>
```python
from_function(fn: Callable[..., Any], name: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> FunctionTool
@ -38,17 +38,17 @@ from_function(fn: Callable[..., Any], name: str | None = None, description: str
Create a Tool from a function.
#### `from_tool`
#### `from_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L86"><Icon icon="github" size="14" /></a></sup>
```python
from_tool(cls, tool: Tool, transform_fn: Callable[..., Any] | None = None, name: str | None = None, transform_args: dict[str, ArgTransform] | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> TransformedTool
```
### `FunctionTool`
### `FunctionTool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L113"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L117"><Icon icon="github" size="14" /></a></sup>
```python
from_function(cls, fn: Callable[..., Any], name: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> FunctionTool
@ -57,11 +57,11 @@ from_function(cls, fn: Callable[..., Any], name: str | None = None, description:
Create a Tool from a function.
### `ParsedFunction`
### `ParsedFunction` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L194"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `from_function`
#### `from_function` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py#L201"><Icon icon="github" size="14" /></a></sup>
```python
from_function(cls, fn: Callable[..., Any], exclude_args: list[str] | None = None, validate: bool = True) -> ParsedFunction

View file

@ -7,7 +7,7 @@ sidebarTitle: tool_manager
## Classes
### `ToolManager`
### `ToolManager` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_manager.py#L22"><Icon icon="github" size="14" /></a></sup>
Manages FastMCP tools.
@ -15,7 +15,7 @@ Manages FastMCP tools.
**Methods:**
#### `mount`
#### `mount` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_manager.py#L46"><Icon icon="github" size="14" /></a></sup>
```python
mount(self, server: MountedServer) -> None
@ -24,7 +24,7 @@ mount(self, server: MountedServer) -> None
Adds a mounted server as a source for tools.
#### `add_tool_from_fn`
#### `add_tool_from_fn` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_manager.py#L113"><Icon icon="github" size="14" /></a></sup>
```python
add_tool_from_fn(self, fn: Callable[..., Any], name: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, serializer: Callable[[Any], str] | None = None, exclude_args: list[str] | None = None) -> Tool
@ -33,7 +33,7 @@ add_tool_from_fn(self, fn: Callable[..., Any], name: str | None = None, descript
Add a tool to the server.
#### `add_tool`
#### `add_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_manager.py#L142"><Icon icon="github" size="14" /></a></sup>
```python
add_tool(self, tool: Tool) -> Tool
@ -42,7 +42,7 @@ add_tool(self, tool: Tool) -> Tool
Register a tool with the server.
#### `remove_tool`
#### `remove_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_manager.py#L159"><Icon icon="github" size="14" /></a></sup>
```python
remove_tool(self, key: str) -> None

View file

@ -7,58 +7,69 @@ sidebarTitle: tool_transform
## Classes
### `ArgTransform`
### `ArgTransform` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L85"><Icon icon="github" size="14" /></a></sup>
Configuration for transforming a parent tool's argument.
This class allows fine-grained control over how individual arguments are transformed
when creating a new tool from an existing one. You can rename arguments, change their
descriptions, add default values, or hide them from clients while passing constants.
This class allows fine-grained control over how individual arguments are transformed
when creating a new tool from an existing one. You can rename arguments, change their
descriptions, add default values, or hide them from clients while passing constants.
Attributes:
name: New name for the argument. Use None to keep original name, or ... for no change.
description: New description for the argument. Use None to remove description, or ... for no change.
default: New default value for the argument. Use ... for no change.
default_factory: Callable that returns a default value. Cannot be used with default.
type: New type for the argument. Use ... for no change.
hide: If True, hide this argument from clients but pass a constant value to parent.
required: If True, make argument required (remove default). Use ... for no change.
examples: Examples for the argument. Use ... for no change.
**Examples:**
Examples:
# Rename argument 'old_name' to 'new_name'
ArgTransform(name="new_name")
Rename argument 'old_name' to 'new_name'
```python
ArgTransform(name="new_name")
```
# Change description only
ArgTransform(description="Updated description")
Change description only
```python
ArgTransform(description="Updated description")
```
# Add a default value (makes argument optional)
ArgTransform(default=42)
Add a default value (makes argument optional)
```python
ArgTransform(default=42)
```
# Add a default factory (makes argument optional)
ArgTransform(default_factory=lambda: time.time())
Add a default factory (makes argument optional)
```python
ArgTransform(default_factory=lambda: time.time())
```
# Change the type
ArgTransform(type=str)
Change the type
```python
ArgTransform(type=str)
```
# Hide the argument entirely from clients
ArgTransform(hide=True)
Hide the argument entirely from clients
```python
ArgTransform(hide=True)
```
# Hide argument but pass a constant value to parent
ArgTransform(hide=True, default="constant_value")
Hide argument but pass a constant value to parent
```python
ArgTransform(hide=True, default="constant_value")
```
# Hide argument but pass a factory-generated value to parent
ArgTransform(hide=True, default_factory=lambda: uuid.uuid4().hex)
Hide argument but pass a factory-generated value to parent
```python
ArgTransform(hide=True, default_factory=lambda: uuid.uuid4().hex)
```
# Make an optional parameter required (removes any default)
ArgTransform(required=True)
Make an optional parameter required (removes any default)
```python
ArgTransform(required=True)
```
# Combine multiple transformations
ArgTransform(name="new_name", description="New desc", default=None, type=int)
Combine multiple transformations
```python
ArgTransform(name="new_name", description="New desc", default=None, type=int)
```
### `TransformedTool`
### `TransformedTool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L199"><Icon icon="github" size="14" /></a></sup>
A tool that is transformed from another tool.
@ -74,7 +85,7 @@ with transformed arguments.
**Methods:**
#### `from_tool`
#### `from_tool` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool_transform.py#L280"><Icon icon="github" size="14" /></a></sup>
```python
from_tool(cls, tool: Tool, name: str | None = None, description: str | None = None, tags: set[str] | None = None, transform_fn: Callable[..., Any] | None = None, transform_args: dict[str, ArgTransform] | None = None, annotations: ToolAnnotations | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> TransformedTool
@ -90,9 +101,9 @@ argument names.
- `name`: New name for the tool. Defaults to parent tool's name.
- `transform_args`: Optional transformations for parent tool arguments.
Only specified arguments are transformed, others pass through unchanged\:
- str\: Simple rename
- ArgTransform\: Complex transformation (rename/description/default/drop)
- None\: Drop the argument
- Simple rename (str)
- Complex transformation (rename/description/default/drop) (ArgTransform)
- Drop the argument (None)
- `description`: New description. Defaults to parent's description.
- `tags`: New tags. Defaults to parent's tags.
- `annotations`: New annotations. Defaults to parent's annotations.
@ -101,17 +112,28 @@ Only specified arguments are transformed, others pass through unchanged\:
**Returns:**
- TransformedTool with the specified transformations.
Examples:
- # Transform specific arguments only
- Tool.from_tool(parent, transform_args={"old": "new"}) # Others unchanged
- # Custom function with partial transforms
- async def custom(x: int, y: int) -> str:
result = await forward(x=x, y=y)
return f"Custom: {result}"
- Tool.from_tool(parent, transform_fn=custom, transform_args={"a": "x", "b": "y"})
- # Using **kwargs (gets all args, transformed and untransformed)
- async def flexible(**kwargs) -> str:
result = await forward(**kwargs)
return f"Got: {kwargs}"
- Tool.from_tool(parent, transform_fn=flexible, transform_args={"a": "x"})
**Examples:**
# Transform specific arguments only
```python
Tool.from_tool(parent, transform_args={"old": "new"}) # Others unchanged
```
# Custom function with partial transforms
```python
async def custom(x: int, y: int) -> str:
result = await forward(x=x, y=y)
return f"Custom: {result}"
Tool.from_tool(parent, transform_fn=custom, transform_args={"a": "x", "b": "y"})
```
# Using **kwargs (gets all args, transformed and untransformed)
```python
async def flexible(**kwargs) -> str:
result = await forward(**kwargs)
return f"Got: {kwargs}"
Tool.from_tool(parent, transform_fn=flexible, transform_args={"a": "x"})
```

View file

@ -7,23 +7,23 @@ sidebarTitle: cache
## Classes
### `TimedCache`
### `TimedCache` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/cache.py#L7"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `set`
#### `set` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/cache.py#L14"><Icon icon="github" size="14" /></a></sup>
```python
set(self, key: Any, value: Any) -> None
```
#### `get`
#### `get` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/cache.py#L18"><Icon icon="github" size="14" /></a></sup>
```python
get(self, key: Any) -> Any
```
#### `clear`
#### `clear` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/cache.py#L25"><Icon icon="github" size="14" /></a></sup>
```python
clear(self) -> None

View file

@ -7,7 +7,7 @@ sidebarTitle: components
## Classes
### `FastMCPComponent`
### `FastMCPComponent` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/components.py#L21"><Icon icon="github" size="14" /></a></sup>
Base class for FastMCP tools, prompts, resources, and resource templates.
@ -15,7 +15,7 @@ Base class for FastMCP tools, prompts, resources, and resource templates.
**Methods:**
#### `key`
#### `key` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/components.py#L48"><Icon icon="github" size="14" /></a></sup>
```python
key(self) -> str
@ -27,13 +27,13 @@ keys having a certain value, as the same tool loaded from different
hierarchies of servers may have different keys.
#### `with_key`
#### `with_key` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/components.py#L57"><Icon icon="github" size="14" /></a></sup>
```python
with_key(self, key: str) -> Self
```
#### `enable`
#### `enable` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/components.py#L69"><Icon icon="github" size="14" /></a></sup>
```python
enable(self) -> None
@ -42,7 +42,7 @@ enable(self) -> None
Enable the component.
#### `disable`
#### `disable` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/components.py#L73"><Icon icon="github" size="14" /></a></sup>
```python
disable(self) -> None

View file

@ -7,13 +7,13 @@ sidebarTitle: exceptions
## Functions
### `iter_exc`
### `iter_exc` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/exceptions.py#L12"><Icon icon="github" size="14" /></a></sup>
```python
iter_exc(group: BaseExceptionGroup)
```
### `get_catch_handlers`
### `get_catch_handlers` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/exceptions.py#L42"><Icon icon="github" size="14" /></a></sup>
```python
get_catch_handlers() -> Mapping[type[BaseException] | Iterable[type[BaseException]], Callable[[BaseExceptionGroup[Any]], Any]]

View file

@ -7,7 +7,7 @@ sidebarTitle: http
## Functions
### `find_available_port`
### `find_available_port` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/http.py#L4"><Icon icon="github" size="14" /></a></sup>
```python
find_available_port() -> int

View file

@ -0,0 +1,41 @@
---
title: inspect
sidebarTitle: inspect
---
# `fastmcp.utilities.inspect`
Utilities for inspecting FastMCP instances.
## Classes
### `ToolInfo` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/inspect.py#L16"><Icon icon="github" size="14" /></a></sup>
Information about a tool.
### `PromptInfo` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/inspect.py#L29"><Icon icon="github" size="14" /></a></sup>
Information about a prompt.
### `ResourceInfo` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/inspect.py#L41"><Icon icon="github" size="14" /></a></sup>
Information about a resource.
### `TemplateInfo` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/inspect.py#L54"><Icon icon="github" size="14" /></a></sup>
Information about a resource template.
### `FastMCPInfo` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/inspect.py#L67"><Icon icon="github" size="14" /></a></sup>
Information extracted from a FastMCP instance.

View file

@ -7,7 +7,7 @@ sidebarTitle: json_schema
## Functions
### `compress_schema`
### `compress_schema` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/json_schema.py#L130"><Icon icon="github" size="14" /></a></sup>
```python
compress_schema(schema: dict, prune_params: list[str] | None = None, prune_defs: bool = True, prune_additional_properties: bool = True, prune_titles: bool = False) -> dict

View file

@ -10,7 +10,7 @@ Logging utilities for FastMCP.
## Functions
### `get_logger`
### `get_logger` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/logging.py#L10"><Icon icon="github" size="14" /></a></sup>
```python
get_logger(name: str) -> logging.Logger
@ -26,7 +26,7 @@ Get a logger nested under FastMCP namespace.
- a configured logger instance
### `configure_logging`
### `configure_logging` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/logging.py#L22"><Icon icon="github" size="14" /></a></sup>
```python
configure_logging(level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] | int = 'INFO', logger: logging.Logger | None = None, enable_rich_tracebacks: bool = True) -> None

View file

@ -7,10 +7,10 @@ sidebarTitle: mcp_config
## Functions
### `infer_transport_type_from_url`
### `infer_transport_type_from_url` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/mcp_config.py#L20"><Icon icon="github" size="14" /></a></sup>
```python
infer_transport_type_from_url(url: str | AnyUrl) -> Literal['streamable-http', 'sse']
infer_transport_type_from_url(url: str | AnyUrl) -> Literal['http', 'sse']
```
@ -19,31 +19,31 @@ Infer the appropriate transport type from the given URL.
## Classes
### `StdioMCPServer`
### `StdioMCPServer` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/mcp_config.py#L40"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `to_transport`
#### `to_transport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/mcp_config.py#L47"><Icon icon="github" size="14" /></a></sup>
```python
to_transport(self) -> StdioTransport
```
### `RemoteMCPServer`
### `RemoteMCPServer` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/mcp_config.py#L58"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `to_transport`
#### `to_transport` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/mcp_config.py#L71"><Icon icon="github" size="14" /></a></sup>
```python
to_transport(self) -> StreamableHttpTransport | SSETransport
```
### `MCPConfig`
### `MCPConfig` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/mcp_config.py#L88"><Icon icon="github" size="14" /></a></sup>
**Methods:**
#### `from_dict`
#### `from_dict` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/mcp_config.py#L92"><Icon icon="github" size="14" /></a></sup>
```python
from_dict(cls, config: dict[str, Any]) -> MCPConfig

View file

@ -7,7 +7,7 @@ sidebarTitle: openapi
## Functions
### `parse_openapi_to_http_routes`
### `parse_openapi_to_http_routes` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L112"><Icon icon="github" size="14" /></a></sup>
```python
parse_openapi_to_http_routes(openapi_dict: dict[str, Any]) -> list[HTTPRoute]
@ -20,7 +20,7 @@ using the openapi-pydantic library.
Supports both OpenAPI 3.0.x and 3.1.x versions.
### `clean_schema_for_display`
### `clean_schema_for_display` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L570"><Icon icon="github" size="14" /></a></sup>
```python
clean_schema_for_display(schema: JsonSchema | None) -> JsonSchema | None
@ -30,7 +30,7 @@ clean_schema_for_display(schema: JsonSchema | None) -> JsonSchema | None
Clean up a schema dictionary for display by removing internal/complex fields.
### `generate_example_from_schema`
### `generate_example_from_schema` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L630"><Icon icon="github" size="14" /></a></sup>
```python
generate_example_from_schema(schema: JsonSchema | None) -> Any
@ -41,7 +41,7 @@ Generate a simple example value from a JSON schema dictionary.
Very basic implementation focusing on types.
### `format_json_for_description`
### `format_json_for_description` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L713"><Icon icon="github" size="14" /></a></sup>
```python
format_json_for_description(data: Any, indent: int = 2) -> str
@ -51,7 +51,7 @@ format_json_for_description(data: Any, indent: int = 2) -> str
Formats Python data as a JSON string block for markdown.
### `format_description_with_responses`
### `format_description_with_responses` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L722"><Icon icon="github" size="14" /></a></sup>
```python
format_description_with_responses(base_description: str, responses: dict[str, Any], parameters: list[ParameterInfo] | None = None, request_body: RequestBodyInfo | None = None) -> str
@ -76,31 +76,31 @@ including its description, whether it is required, and its content schema.
## Classes
### `ParameterInfo`
### `ParameterInfo` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L42"><Icon icon="github" size="14" /></a></sup>
Represents a single parameter for an HTTP operation in our IR.
### `RequestBodyInfo`
### `RequestBodyInfo` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L52"><Icon icon="github" size="14" /></a></sup>
Represents the request body for an HTTP operation in our IR.
### `ResponseInfo`
### `ResponseInfo` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L62"><Icon icon="github" size="14" /></a></sup>
Represents response information in our IR.
### `HTTPRoute`
### `HTTPRoute` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L70"><Icon icon="github" size="14" /></a></sup>
Intermediate Representation for a single OpenAPI operation.
### `OpenAPIParser`
### `OpenAPIParser` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L164"><Icon icon="github" size="14" /></a></sup>
Unified parser for OpenAPI schemas with generic type parameters to handle both 3.0 and 3.1.
@ -108,7 +108,7 @@ Unified parser for OpenAPI schemas with generic type parameters to handle both 3
**Methods:**
#### `parse`
#### `parse` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/openapi.py#L469"><Icon icon="github" size="14" /></a></sup>
```python
parse(self) -> list[HTTPRoute]

View file

@ -0,0 +1,42 @@
---
title: tests
sidebarTitle: tests
---
# `fastmcp.utilities.tests`
## Functions
### `temporary_settings` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/tests.py#L21"><Icon icon="github" size="14" /></a></sup>
```python
temporary_settings(**kwargs: Any)
```
Temporarily override FastMCP setting values.
**Args:**
- `**kwargs`: The settings to override, including nested settings.
### `run_server_in_process` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/tests.py#L74"><Icon icon="github" size="14" /></a></sup>
```python
run_server_in_process(server_fn: Callable[..., None], *args, **kwargs) -> Generator[str, None, None]
```
Context manager that runs a FastMCP server in a separate process and
returns the server URL. When the context manager is exited, the server process is killed.
**Args:**
- `server_fn`: The function that runs a FastMCP server. FastMCP servers are
not pickleable, so we need a function that creates and runs one.
- `*args`: Arguments to pass to the server function.
- `provide_host_and_port`: Whether to provide the host and port to the server function as kwargs.
- `**kwargs`: Keyword arguments to pass to the server function.
**Returns:**
- The server URL.

View file

@ -10,7 +10,7 @@ Common types used across FastMCP.
## Functions
### `get_cached_typeadapter`
### `get_cached_typeadapter` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L35"><Icon icon="github" size="14" /></a></sup>
```python
get_cached_typeadapter(cls: T) -> TypeAdapter[T]
@ -23,7 +23,7 @@ However, this isn't feasible for user-generated functions. Instead, we use a
cache to minimize the cost of creating them as much as possible.
### `issubclass_safe`
### `issubclass_safe` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L45"><Icon icon="github" size="14" /></a></sup>
```python
issubclass_safe(cls: type, base: type) -> bool
@ -33,7 +33,7 @@ issubclass_safe(cls: type, base: type) -> bool
Check if cls is a subclass of base, even if cls is a type variable.
### `is_class_member_of_type`
### `is_class_member_of_type` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L55"><Icon icon="github" size="14" /></a></sup>
```python
is_class_member_of_type(cls: type, base: type) -> bool
@ -46,7 +46,7 @@ Base can be a type, a UnionType, or an Annotated type. Generic types are not
considered members (e.g. T is not a member of list\[T]).
### `find_kwarg_by_type`
### `find_kwarg_by_type` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L77"><Icon icon="github" size="14" /></a></sup>
```python
find_kwarg_by_type(fn: Callable, kwarg_type: type) -> str | None
@ -60,13 +60,13 @@ Includes union types that contain the kwarg_type, as well as Annotated types.
## Classes
### `FastMCPBaseModel`
### `FastMCPBaseModel` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L28"><Icon icon="github" size="14" /></a></sup>
Base model for FastMCP models.
### `Image`
### `Image` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L94"><Icon icon="github" size="14" /></a></sup>
Helper class for returning images from tools.
@ -74,7 +74,7 @@ Helper class for returning images from tools.
**Methods:**
#### `to_image_content`
#### `to_image_content` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L131"><Icon icon="github" size="14" /></a></sup>
```python
to_image_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> ImageContent
@ -83,7 +83,7 @@ to_image_content(self, mime_type: str | None = None, annotations: Annotations |
Convert to MCP ImageContent.
### `Audio`
### `Audio` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L153"><Icon icon="github" size="14" /></a></sup>
Helper class for returning audio from tools.
@ -91,13 +91,13 @@ Helper class for returning audio from tools.
**Methods:**
#### `to_audio_content`
#### `to_audio_content` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L190"><Icon icon="github" size="14" /></a></sup>
```python
to_audio_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> AudioContent
```
### `File`
### `File` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L211"><Icon icon="github" size="14" /></a></sup>
Helper class for returning audio from tools.
@ -105,7 +105,7 @@ Helper class for returning audio from tools.
**Methods:**
#### `to_resource_content`
#### `to_resource_content` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py#L250"><Icon icon="github" size="14" /></a></sup>
```python
to_resource_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> EmbeddedResource

View file

@ -19,7 +19,7 @@ The [MCP specification](https://modelcontextprotocol.io/specification/2025-03-26
Bearer Token authentication is a common way to secure HTTP-based APIs. In this model, the client sends a token (usually a JSON Web Token or JWT) in the `Authorization` header with the "Bearer" scheme. The server then validates this token to grant or deny access.
FastMCP supports Bearer Token authentication for its HTTP-based transports (`streamable-http` and `sse`), allowing you to protect your server from unauthorized access.
FastMCP supports Bearer Token authentication for its HTTP-based transports (`http` and `sse`), allowing you to protect your server from unauthorized access.
## Authentication Strategy
@ -61,13 +61,27 @@ mcp = FastMCP(name="My MCP Server", auth=auth)
### Configuration Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `public_key` | `str` | If `jwks_uri` is not provided | RSA public key in PEM format for static key validation |
| `jwks_uri` | `str` | If `public_key` is not provided | URL for JSON Web Key Set endpoint |
| `issuer` | `str` | No | Expected JWT `iss` claim value |
| `audience` | `str` | No | Expected JWT `aud` claim value |
| `required_scopes` | `list[str]` | No | Global scopes required for all requests |
<Card icon="code" title="BearerAuthProvider Configuration">
<ParamField body="public_key" type="str">
RSA public key in PEM format for static key validation. Required if `jwks_uri` is not provided
</ParamField>
<ParamField body="jwks_uri" type="str">
URL for JSON Web Key Set endpoint. Required if `public_key` is not provided
</ParamField>
<ParamField body="issuer" type="str | None">
Expected JWT `iss` claim value
</ParamField>
<ParamField body="audience" type="str | None">
Expected JWT `aud` claim value
</ParamField>
<ParamField body="required_scopes" type="list[str] | None">
Global scopes required for all requests
</ParamField>
</Card>
#### Public Key
@ -141,15 +155,35 @@ print(f"Test token: {token}")
The `create_token()` method accepts these parameters:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `subject` | `str` | `"fastmcp-user"` | JWT subject claim (usually user ID) |
| `issuer` | `str` | `"https://fastmcp.example.com"` | JWT issuer claim |
| `audience` | `str` | `None` | JWT audience claim |
| `scopes` | `list[str]` | `None` | OAuth scopes to include |
| `expires_in_seconds` | `int` | `3600` | Token expiration time |
| `additional_claims` | `dict` | `None` | Extra claims to include |
| `kid` | `str` | `None` | Key ID for JWKS lookup |
<Card icon="code" title="create_token() Parameters">
<ParamField body="subject" type="str" default="fastmcp-user">
JWT subject claim (usually user ID)
</ParamField>
<ParamField body="issuer" type="str" default="https://fastmcp.example.com">
JWT issuer claim
</ParamField>
<ParamField body="audience" type="str | None">
JWT audience claim
</ParamField>
<ParamField body="scopes" type="list[str] | None">
OAuth scopes to include
</ParamField>
<ParamField body="expires_in_seconds" type="int" default="3600">
Token expiration time in seconds
</ParamField>
<ParamField body="additional_claims" type="dict | None">
Extra claims to include in the token
</ParamField>
<ParamField body="kid" type="str | None">
Key ID for JWKS lookup
</ParamField>
</Card>
## Accessing Token Claims
@ -179,10 +213,21 @@ async def get_my_data(ctx: Context) -> dict:
### AccessToken Properties
| Property | Type | Description |
|----------|------|-------------|
| `token` | `str` | The raw JWT string |
| `client_id` | `str` | Authenticated principal identifier |
| `scopes` | `list[str]` | Granted scopes |
| `expires_at` | `datetime \| None` | Token expiration timestamp |
<Card icon="code" title="AccessToken Properties">
<ParamField body="token" type="str">
The raw JWT string
</ParamField>
<ParamField body="client_id" type="str">
Authenticated principal identifier
</ParamField>
<ParamField body="scopes" type="list[str]">
Granted scopes
</ParamField>
<ParamField body="expires_at" type="datetime | None">
Token expiration timestamp
</ParamField>
</Card>

View file

@ -275,6 +275,25 @@ async def generate_example(concept: str, ctx: Context) -> str:
See [Client Sampling](/clients/client#llm-sampling) for more details on how clients handle these requests.
### Component Changes
<VersionBadge version="2.9.1" />
FastMCP automatically sends list change notifications when components (such as tools, resources, or prompts) are added, removed, enabled, or disabled. In rare cases where you need to manually trigger these notifications, you can use the context methods:
```python
@mcp.tool
async def custom_tool_management(ctx: Context) -> str:
"""Example of manual notification after custom tool changes."""
# After making custom changes to tools
await ctx.send_tool_list_changed()
await ctx.send_resource_list_changed()
await ctx.send_prompt_list_changed()
return "Notifications sent"
```
These methods are primarily used internally by FastMCP's automatic notification system and most users will not need to invoke them directly.
### Request Information
Access metadata about the current request and client.

View file

@ -78,10 +78,13 @@ When a request comes in, **multiple hooks may be called for the same request**,
2. **`on_request` or `on_notification`** - Called based on the message type
3. **Operation-specific hooks** - Called for specific MCP operations like `on_call_tool`
For example, when a client calls a tool, your middleware will receive **three separate hook calls**:
1. First: `on_message` (because it's any MCP message)
2. Second: `on_request` (because tool calls expect responses)
3. Third: `on_call_tool` (because it's specifically a tool execution)
For example, when a client calls a tool, your middleware will receive **multiple hook calls**:
1. `on_message` and `on_request` for any initial tool discovery operations (list_tools)
2. `on_message` (because it's any MCP message) for the tool call itself
3. `on_request` (because tool calls expect responses) for the tool call itself
4. `on_call_tool` (because it's specifically a tool execution) for the tool call itself
Note that the MCP SDK may perform additional operations like listing tools for caching purposes, which will trigger additional middleware calls beyond just the direct tool execution.
This hierarchy allows you to target your middleware logic with the right level of specificity. Use `on_message` for broad concerns like logging, `on_request` for authentication, and `on_call_tool` for tool-specific logic like performance monitoring.
@ -329,93 +332,246 @@ parent.mount(child, prefix="child")
When a client calls "child_tool", the request will flow through the parent's authentication middleware first, then route to the child server where it will go through the child's logging middleware.
## Examples
## Built-in Middleware Examples
### Authentication Middleware
FastMCP includes several middleware implementations that demonstrate best practices and provide immediately useful functionality. Let's explore how each type works by building simplified versions, then see how to use the full implementations.
This middleware checks for a valid authorization token on all requests:
### Timing Middleware
```python
from fastmcp.server.middleware import Middleware, MiddlewareContext
from fastmcp.exceptions import ToolError
Performance monitoring is essential for understanding your server's behavior and identifying bottlenecks. FastMCP includes timing middleware at `fastmcp.server.middleware.timing`.
class AuthenticationMiddleware(Middleware):
def __init__(self, required_token: str):
self.required_token = required_token
async def on_request(self, context: MiddlewareContext, call_next):
if hasattr(context, 'fastmcp_context') and context.fastmcp_context:
try:
request = context.fastmcp_context.get_http_request()
auth_header = request.headers.get("Authorization")
if not auth_header or not auth_header.startswith("Bearer "):
raise ToolError("Missing or invalid authorization header")
token = auth_header.split(" ", 1)[1]
if token != self.required_token:
raise ToolError("Invalid authentication token")
except Exception:
pass
return await call_next(context)
# Usage
mcp = FastMCP("SecureServer")
mcp.add_middleware(AuthenticationMiddleware("secret-token-123"))
```
### Performance Monitoring Middleware
This middleware tracks how long tools take to execute:
Here's an example of how it works:
```python
import time
import logging
from fastmcp.server.middleware import Middleware, MiddlewareContext
class PerformanceMiddleware(Middleware):
def __init__(self):
self.logger = logging.getLogger("performance")
async def on_call_tool(self, context: MiddlewareContext, call_next):
tool_name = context.message.name
start_time = time.time()
class SimpleTimingMiddleware(Middleware):
async def on_request(self, context: MiddlewareContext, call_next):
start_time = time.perf_counter()
try:
result = await call_next(context)
execution_time = time.time() - start_time
self.logger.info(
f"Tool {tool_name} completed in {execution_time:.3f}s"
)
duration_ms = (time.perf_counter() - start_time) * 1000
print(f"Request {context.method} completed in {duration_ms:.2f}ms")
return result
except Exception as e:
execution_time = time.time() - start_time
self.logger.error(
f"Tool {tool_name} failed after {execution_time:.3f}s: {e}"
)
duration_ms = (time.perf_counter() - start_time) * 1000
print(f"Request {context.method} failed after {duration_ms:.2f}ms: {e}")
raise
```
### Request Transformation Middleware
This middleware adds metadata to tool calls:
To use the full version with proper logging and configuration:
```python
class TransformationMiddleware(Middleware):
async def on_call_tool(self, context: MiddlewareContext, call_next):
if hasattr(context.message, 'arguments'):
args = context.message.arguments or {}
args['_middleware_timestamp'] = context.timestamp.isoformat()
modified_context = context.copy(
message=context.message.model_copy(update={'arguments': args})
)
else:
modified_context = context
from fastmcp.server.middleware.timing import (
TimingMiddleware,
DetailedTimingMiddleware
)
# Basic timing for all requests
mcp.add_middleware(TimingMiddleware())
# Detailed per-operation timing (tools, resources, prompts)
mcp.add_middleware(DetailedTimingMiddleware())
```
The built-in versions include custom logger support, proper formatting, and **DetailedTimingMiddleware** provides operation-specific hooks like `on_call_tool` and `on_read_resource` for granular timing.
### Logging Middleware
Request and response logging is crucial for debugging, monitoring, and understanding usage patterns in your MCP server. FastMCP provides comprehensive logging middleware at `fastmcp.server.middleware.logging`.
Here's an example of how it works:
```python
from fastmcp.server.middleware import Middleware, MiddlewareContext
class SimpleLoggingMiddleware(Middleware):
async def on_message(self, context: MiddlewareContext, call_next):
print(f"Processing {context.method} from {context.source}")
return await call_next(modified_context)
try:
result = await call_next(context)
print(f"Completed {context.method}")
return result
except Exception as e:
print(f"Failed {context.method}: {e}")
raise
```
To use the full versions with advanced features:
```python
from fastmcp.server.middleware.logging import (
LoggingMiddleware,
StructuredLoggingMiddleware
)
# Human-readable logging with payload support
mcp.add_middleware(LoggingMiddleware(
include_payloads=True,
max_payload_length=1000
))
# JSON-structured logging for log aggregation tools
mcp.add_middleware(StructuredLoggingMiddleware(include_payloads=True))
```
The built-in versions include payload logging, structured JSON output, custom logger support, payload size limits, and operation-specific hooks for granular control.
### Rate Limiting Middleware
Rate limiting is essential for protecting your server from abuse, ensuring fair resource usage, and maintaining performance under load. FastMCP includes sophisticated rate limiting middleware at `fastmcp.server.middleware.rate_limiting`.
Here's an example of how it works:
```python
import time
from collections import defaultdict
from fastmcp.server.middleware import Middleware, MiddlewareContext
from mcp import McpError
from mcp.types import ErrorData
class SimpleRateLimitMiddleware(Middleware):
def __init__(self, requests_per_minute: int = 60):
self.requests_per_minute = requests_per_minute
self.client_requests = defaultdict(list)
async def on_request(self, context: MiddlewareContext, call_next):
current_time = time.time()
client_id = "default" # In practice, extract from headers or context
# Clean old requests and check limit
cutoff_time = current_time - 60
self.client_requests[client_id] = [
req_time for req_time in self.client_requests[client_id]
if req_time > cutoff_time
]
if len(self.client_requests[client_id]) >= self.requests_per_minute:
raise McpError(ErrorData(code=-32000, message="Rate limit exceeded"))
self.client_requests[client_id].append(current_time)
return await call_next(context)
```
To use the full versions with advanced algorithms:
```python
from fastmcp.server.middleware.rate_limiting import (
RateLimitingMiddleware,
SlidingWindowRateLimitingMiddleware
)
# Token bucket rate limiting (allows controlled bursts)
mcp.add_middleware(RateLimitingMiddleware(
max_requests_per_second=10.0,
burst_capacity=20
))
# Sliding window rate limiting (precise time-based control)
mcp.add_middleware(SlidingWindowRateLimitingMiddleware(
max_requests=100,
window_minutes=1
))
```
The built-in versions include token bucket algorithms, per-client identification, global rate limiting, and async-safe implementations with configurable client identification functions.
### Error Handling Middleware
Consistent error handling and recovery is critical for robust MCP servers. FastMCP provides comprehensive error handling middleware at `fastmcp.server.middleware.error_handling`.
Here's an example of how it works:
```python
import logging
from fastmcp.server.middleware import Middleware, MiddlewareContext
class SimpleErrorHandlingMiddleware(Middleware):
def __init__(self):
self.logger = logging.getLogger("errors")
self.error_counts = {}
async def on_message(self, context: MiddlewareContext, call_next):
try:
return await call_next(context)
except Exception as error:
# Log the error and track statistics
error_key = f"{type(error).__name__}:{context.method}"
self.error_counts[error_key] = self.error_counts.get(error_key, 0) + 1
self.logger.error(f"Error in {context.method}: {type(error).__name__}: {error}")
raise
```
To use the full versions with advanced features:
```python
from fastmcp.server.middleware.error_handling import (
ErrorHandlingMiddleware,
RetryMiddleware
)
# Comprehensive error logging and transformation
mcp.add_middleware(ErrorHandlingMiddleware(
include_traceback=True,
transform_errors=True,
error_callback=my_error_callback
))
# Automatic retry with exponential backoff
mcp.add_middleware(RetryMiddleware(
max_retries=3,
retry_exceptions=(ConnectionError, TimeoutError)
))
```
The built-in versions include error transformation, custom callbacks, configurable retry logic, and proper MCP error formatting.
### Combining Middleware
These middleware work together seamlessly:
```python
from fastmcp import FastMCP
from fastmcp.server.middleware.timing import TimingMiddleware
from fastmcp.server.middleware.logging import LoggingMiddleware
from fastmcp.server.middleware.rate_limiting import RateLimitingMiddleware
from fastmcp.server.middleware.error_handling import ErrorHandlingMiddleware
mcp = FastMCP("Production Server")
# Add middleware in logical order
mcp.add_middleware(ErrorHandlingMiddleware()) # Handle errors first
mcp.add_middleware(RateLimitingMiddleware(max_requests_per_second=50))
mcp.add_middleware(TimingMiddleware()) # Time actual execution
mcp.add_middleware(LoggingMiddleware()) # Log everything
@mcp.tool
def my_tool(data: str) -> str:
return f"Processed: {data}"
```
This configuration provides comprehensive monitoring, protection, and observability for your MCP server.
### Custom Middleware Example
You can also create custom middleware by extending the base class:
```python
from fastmcp.server.middleware import Middleware, MiddlewareContext
class CustomHeaderMiddleware(Middleware):
async def on_request(self, context: MiddlewareContext, call_next):
# Add custom logic here
print(f"Processing {context.method}")
result = await call_next(context)
print(f"Completed {context.method}")
return result
mcp.add_middleware(CustomHeaderMiddleware())
```

View file

@ -57,6 +57,42 @@ def generate_code_request(language: str, task_description: str) -> PromptMessage
Functions with `*args` or `**kwargs` are not supported as prompts. This restriction exists because FastMCP needs to generate a complete parameter schema for the MCP protocol, which isn't possible with variable argument lists.
</Tip>
#### Decorator Arguments
While FastMCP infers the name and description from your function, you can override these and add additional metadata using arguments to the `@mcp.prompt` decorator:
```python
@mcp.prompt(
name="analyze_data_request", # Custom prompt name
description="Creates a request to analyze data with specific parameters", # Custom description
tags={"analysis", "data"} # Optional categorization tags
)
def data_analysis_prompt(
data_uri: str = Field(description="The URI of the resource containing the data."),
analysis_type: str = Field(default="summary", description="Type of analysis.")
) -> str:
"""This docstring is ignored when description is provided."""
return f"Please perform a '{analysis_type}' analysis on the data found at {data_uri}."
```
<Card icon="code" title="@prompt Decorator Arguments">
<ParamField body="name" type="str | None">
Sets the explicit prompt name exposed via MCP. If not provided, uses the function name
</ParamField>
<ParamField body="description" type="str | None">
Provides the description exposed via MCP. If set, the function's docstring is ignored for this purpose
</ParamField>
<ParamField body="tags" type="set[str] | None">
A set of strings used to categorize the prompt. Clients might use tags to filter or group available prompts
</ParamField>
<ParamField body="enabled" type="bool" default="True">
A boolean to enable or disable the prompt. See [Disabling Prompts](#disabling-prompts) for more information
</ParamField>
</Card>
### Argument Types
<VersionBadge version="2.9.0" />
@ -177,28 +213,6 @@ def data_analysis_prompt(
In this example, the client *must* provide `data_uri`. If `analysis_type` or `include_charts` are omitted, their default values will be used.
### Prompt Metadata
While FastMCP infers the name and description from your function, you can override these and add tags using arguments to the `@mcp.prompt` decorator:
```python
@mcp.prompt(
name="analyze_data_request", # Custom prompt name
description="Creates a request to analyze data with specific parameters", # Custom description
tags={"analysis", "data"} # Optional categorization tags
)
def data_analysis_prompt(
data_uri: str = Field(description="The URI of the resource containing the data."),
analysis_type: str = Field(default="summary", description="Type of analysis.")
) -> str:
"""This docstring is ignored when description is provided."""
return f"Please perform a '{analysis_type}' analysis on the data found at {data_uri}."
```
- **`name`**: Sets the explicit prompt name exposed via MCP.
- **`description`**: Provides the description exposed via MCP. If set, the function's docstring is ignored for this purpose.
- **`tags`**: A set of strings used to categorize the prompt. Clients *might* use tags to filter or group available prompts.
- **`enabled`**: A boolean to enable or disable the prompt (defaults to `True`). See [Disabling Prompts](#disabling-prompts) for more information.
### Disabling Prompts
<VersionBadge version="2.8.0" />
@ -224,7 +238,8 @@ def seasonal_prompt(): return "Happy Holidays!"
seasonal_prompt.disable()
seasonal_prompt.enable()
```
### Asynchronous Prompts
### Async Prompts
FastMCP seamlessly supports both standard (`def`) and asynchronous (`async def`) functions as prompts.
@ -267,7 +282,26 @@ async def generate_report_request(report_type: str, ctx: Context) -> str:
For full documentation on the Context object and all its capabilities, see the [Context documentation](/servers/context).
### Notifications
<VersionBadge version="2.9.1" />
FastMCP automatically sends `notifications/prompts/list_changed` notifications to connected clients when prompts are added, enabled, or disabled. This allows clients to stay up-to-date with the current prompt set without manually polling for changes.
```python
@mcp.prompt
def example_prompt() -> str:
return "Hello!"
# These operations trigger notifications:
mcp.add_prompt(example_prompt) # Sends prompts/list_changed notification
example_prompt.disable() # Sends prompts/list_changed notification
example_prompt.enable() # Sends prompts/list_changed notification
```
Notifications are only sent when these operations occur within an active MCP request context (e.g., when called from within a tool or other MCP operation). Operations performed during server initialization do not trigger notifications.
Clients can handle these notifications using a [message handler](/clients/messages) to automatically refresh their prompt lists or update their interfaces.
## Server Behavior

View file

@ -118,7 +118,7 @@ config = {
"mcpServers": {
"default": { # For single server configs, 'default' is commonly used
"url": "https://example.com/mcp",
"transport": "streamable-http"
"transport": "http"
}
}
}
@ -145,11 +145,11 @@ config = {
"mcpServers": {
"weather": {
"url": "https://weather-api.example.com/mcp",
"transport": "streamable-http"
"transport": "http"
},
"calendar": {
"url": "https://calendar-api.example.com/mcp",
"transport": "streamable-http"
"transport": "http"
}
}
}

View file

@ -58,18 +58,9 @@ def get_config() -> dict:
* Resource Name: Taken from the function name (`get_greeting`).
* Resource Description: Taken from the function's docstring.
### Return Values
#### Decorator Arguments
FastMCP automatically converts your function's return value into the appropriate MCP resource content:
- **`str`**: Sent as `TextResourceContents` (with `mime_type="text/plain"` by default).
- **`dict`, `list`, `pydantic.BaseModel`**: Automatically serialized to a JSON string and sent as `TextResourceContents` (with `mime_type="application/json"` by default).
- **`bytes`**: Base64 encoded and sent as `BlobResourceContents`. You should specify an appropriate `mime_type` (e.g., `"image/png"`, `"application/octet-stream"`).
- **`None`**: Results in an empty resource content list being returned.
### Resource Metadata
You can customize the resource's properties using arguments in the decorator:
You can customize the resource's properties using arguments in the `@mcp.resource` decorator:
```python
from fastmcp import FastMCP
@ -89,12 +80,40 @@ def get_application_status() -> dict:
return {"status": "ok", "uptime": 12345, "version": mcp.settings.version} # Example usage
```
- **`uri`**: The unique identifier for the resource (required).
- **`name`**: A human-readable name (defaults to function name).
- **`description`**: Explanation of the resource (defaults to docstring).
- **`mime_type`**: Specifies the content type (FastMCP often infers a default like `text/plain` or `application/json`, but explicit is better for non-text types).
- **`tags`**: A set of strings for categorization, potentially used by clients for filtering.
- **`enabled`**: A boolean to enable or disable the resource (defaults to `True`). See [Disabling Resources](#disabling-resources) for more information.
<Card icon="code" title="@resource Decorator Arguments">
<ParamField body="uri" type="str" required>
The unique identifier for the resource
</ParamField>
<ParamField body="name" type="str | None">
A human-readable name. If not provided, defaults to function name
</ParamField>
<ParamField body="description" type="str | None">
Explanation of the resource. If not provided, defaults to docstring
</ParamField>
<ParamField body="mime_type" type="str | None">
Specifies the content type. FastMCP often infers a default like `text/plain` or `application/json`, but explicit is better for non-text types
</ParamField>
<ParamField body="tags" type="set[str] | None">
A set of strings for categorization, potentially used by clients for filtering
</ParamField>
<ParamField body="enabled" type="bool" default="True">
A boolean to enable or disable the resource. See [Disabling Resources](#disabling-resources) for more information
</ParamField>
</Card>
### Return Values
FastMCP automatically converts your function's return value into the appropriate MCP resource content:
- **`str`**: Sent as `TextResourceContents` (with `mime_type="text/plain"` by default).
- **`dict`, `list`, `pydantic.BaseModel`**: Automatically serialized to a JSON string and sent as `TextResourceContents` (with `mime_type="application/json"` by default).
- **`bytes`**: Base64 encoded and sent as `BlobResourceContents`. You should specify an appropriate `mime_type` (e.g., `"image/png"`, `"application/octet-stream"`).
- **`None`**: Results in an empty resource content list being returned.
### Disabling Resources
@ -122,6 +141,7 @@ get_config.disable()
get_config.enable()
```
### Accessing MCP Context
<VersionBadge version="2.2.5" />
@ -153,7 +173,7 @@ async def get_details(name: str, ctx: Context) -> dict:
For full documentation on the Context object and all its capabilities, see the [Context documentation](/servers/context).
### Asynchronous Resources
### Async Resources
Use `async def` for resource functions that perform I/O operations (e.g., reading from a database or network) to avoid blocking the server.
@ -259,6 +279,27 @@ mcp.add_resource(special_resource, key="internal://data-v2") # Will be stored a
Note that this parameter is only available when using `add_resource()` directly and not through the `@resource` decorator, as URIs are provided explicitly when using the decorator.
### Notifications
<VersionBadge version="2.9.1" />
FastMCP automatically sends `notifications/resources/list_changed` notifications to connected clients when resources or templates are added, enabled, or disabled. This allows clients to stay up-to-date with the current resource set without manually polling for changes.
```python
@mcp.resource("data://example")
def example_resource() -> str:
return "Hello!"
# These operations trigger notifications:
mcp.add_resource(example_resource) # Sends resources/list_changed notification
example_resource.disable() # Sends resources/list_changed notification
example_resource.enable() # Sends resources/list_changed notification
```
Notifications are only sent when these operations occur within an active MCP request context (e.g., when called from within a tool or other MCP operation). Operations performed during server initialization do not trigger notifications.
Clients can handle these notifications using a [message handler](/clients/messages) to automatically refresh their resource lists or update their interfaces.
## Resource Templates
Resource Templates allow clients to request resources whose content depends on parameters embedded in the URI. Define a template using the **same `@mcp.resource` decorator**, but include `{parameter_name}` placeholders in the URI string and add corresponding arguments to your function signature.

View file

@ -31,13 +31,31 @@ mcp_with_instructions = FastMCP(
The `FastMCP` constructor accepts several arguments:
* `name`: (Optional) A human-readable name for your server. Defaults to "FastMCP".
* `instructions`: (Optional) Description of how to interact with this server. These instructions help clients understand the server's purpose and available functionality.
* `lifespan`: (Optional) An async context manager function for server startup and shutdown logic.
* `tags`: (Optional) A set of strings to tag the server itself.
* `tools`: (Optional) A list of tools (or functions to convert to tools) to add to the server. In some cases, providing tools programmatically may be more convenient than using the `@mcp.tool` decorator.
* `**settings`: Keyword arguments corresponding to additional `ServerSettings` configuration
<Card icon="code" title="FastMCP Constructor Parameters">
<ParamField body="name" type="str" default="FastMCP">
A human-readable name for your server
</ParamField>
<ParamField body="instructions" type="str | None">
Description of how to interact with this server. These instructions help clients understand the server's purpose and available functionality
</ParamField>
<ParamField body="lifespan" type="AsyncContextManager | None">
An async context manager function for server startup and shutdown logic
</ParamField>
<ParamField body="tags" type="set[str] | None">
A set of strings to tag the server itself
</ParamField>
<ParamField body="tools" type="list[Tool | Callable] | None">
A list of tools (or functions to convert to tools) to add to the server. In some cases, providing tools programmatically may be more convenient than using the `@mcp.tool` decorator
</ParamField>
<ParamField body="**settings" type="Any">
Keyword arguments corresponding to additional `ServerSettings` configuration
</ParamField>
</Card>
## Components
FastMCP servers expose several types of components to the client:
@ -158,8 +176,8 @@ if __name__ == "__main__":
# This runs the server, defaulting to STDIO transport
mcp.run()
# To use a different transport, e.g., HTTP:
# mcp.run(transport="streamable-http", host="127.0.0.1", port=9000)
# To use a different transport, e.g., Streamable HTTP:
# mcp.run(transport="http", host="127.0.0.1", port=9000)
```
FastMCP supports several transport options:
@ -235,6 +253,34 @@ mcp = FastMCP(
)
```
### Constructor Parameters
<Card icon="code" title="AdditionalFastMCP Constructor Parameters">
<ParamField body="dependencies" type="list[str] | None">
Optional server dependencies list with package specifications
</ParamField>
<ParamField body="include_tags" type="set[str] | None">
Only expose components with at least one matching tag
</ParamField>
<ParamField body="exclude_tags" type="set[str] | None">
Hide components with any matching tag
</ParamField>
<ParamField body="on_duplicate_tools" type='Literal["error", "warn", "replace"]' default="error">
How to handle duplicate tool registrations
</ParamField>
<ParamField body="on_duplicate_resources" type='Literal["error", "warn", "replace"]' default="warn">
How to handle duplicate resource registrations
</ParamField>
<ParamField body="on_duplicate_prompts" type='Literal["error", "warn", "replace"]' default="replace">
How to handle duplicate prompt registrations
</ParamField>
</Card>
### Global Settings
Global settings affect all FastMCP servers and can be configured via environment variables (prefixed with `FASTMCP_`) or in a `.env` file:
@ -260,7 +306,7 @@ Transport settings are provided when running the server and control network beha
```python
# Configure transport when running
mcp.run(
transport="streamable-http",
transport="http",
host="0.0.0.0", # Bind to all interfaces
port=9000, # Custom port
log_level="DEBUG", # Override global log level
@ -268,7 +314,7 @@ mcp.run(
# Or for async usage
await mcp.run_async(
transport="streamable-http",
transport="http",
host="127.0.0.1",
port=8080,
)

View file

@ -49,9 +49,68 @@ The way you define your Python function dictates how the tool appears and behave
Functions with `*args` or `**kwargs` are not supported as tools. This restriction exists because FastMCP needs to generate a complete parameter schema for the MCP protocol, which isn't possible with variable argument lists.
</Tip>
### Parameters
#### Decorator Arguments
#### Annotations
While FastMCP infers the name and description from your function, you can override these and add additional metadata using arguments to the `@mcp.tool` decorator:
```python
@mcp.tool(
name="find_products", # Custom tool name for the LLM
description="Search the product catalog with optional category filtering.", # Custom description
tags={"catalog", "search"}, # Optional tags for organization/filtering
)
def search_products_implementation(query: str, category: str | None = None) -> list[dict]:
"""Internal function description (ignored if description is provided above)."""
# Implementation...
print(f"Searching for '{query}' in category '{category}'")
return [{"id": 2, "name": "Another Product"}]
```
<Card icon="code" title="@tool Decorator Arguments">
<ParamField body="name" type="str | None">
Sets the explicit tool name exposed via MCP. If not provided, uses the function name
</ParamField>
<ParamField body="description" type="str | None">
Provides the description exposed via MCP. If set, the function's docstring is ignored for this purpose
</ParamField>
<ParamField body="tags" type="set[str] | None">
A set of strings to categorize the tool. Clients might use tags to filter or group available tools
</ParamField>
<ParamField body="enabled" type="bool" default="True">
A boolean to enable or disable the tool. See [Disabling Tools](#disabling-tools) for more information
</ParamField>
<ParamField body="exclude_args" type="list[str] | None">
A list of argument names to exclude from the tool schema shown to the LLM. See [Excluding Arguments](#excluding-arguments) for more information
</ParamField>
<ParamField body="annotations" type="ToolAnnotations | dict | None">
An optional `ToolAnnotations` object or dictionary to add additional metadata about the tool.
<Expandable title="ToolAnnotations attributes">
<ParamField body="title" type="str | None">
A human-readable title for the tool.
</ParamField>
<ParamField body="readOnlyHint" type="bool | None">
If true, the tool does not modify its environment.
</ParamField>
<ParamField body="destructiveHint" type="bool | None">
If true, the tool may perform destructive updates to its environment.
</ParamField>
<ParamField body="idempotentHint" type="bool | None">
If true, calling the tool repeatedly with the same arguments will have no additional effect on the its environment.
</ParamField>
<ParamField body="openWorldHint" type="bool | None">
If true, this tool may interact with an "open world" of external entities. If false, the tool's domain of interaction is closed.
</ParamField>
</Expandable>
</ParamField>
</Card>
### Tool Parameters
#### Type Annotations
Type annotations for parameters are essential for proper tool functionality. They:
1. Inform the LLM about the expected data types for each parameter
@ -150,28 +209,6 @@ def search_products(
In this example, the LLM must provide a `query` parameter, while `max_results`, `sort_by`, and `category` will use their default values if not explicitly provided.
### Metadata
While FastMCP infers the name and description from your function, you can override these and add tags using arguments to the `@mcp.tool` decorator:
```python
@mcp.tool(
name="find_products", # Custom tool name for the LLM
description="Search the product catalog with optional category filtering.", # Custom description
tags={"catalog", "search"}, # Optional tags for organization/filtering
)
def search_products_implementation(query: str, category: str | None = None) -> list[dict]:
"""Internal function description (ignored if description is provided above)."""
# Implementation...
print(f"Searching for '{query}' in category '{category}'")
return [{"id": 2, "name": "Another Product"}]
```
- **`name`**: Sets the explicit tool name exposed via MCP.
- **`description`**: Provides the description exposed via MCP. If set, the function's docstring is ignored for this purpose.
- **`tags`**: A set of strings to categorize the tool. Clients *might* use tags to filter or group available tools.
- **`enabled`**: A boolean to enable or disable the tool (defaults to `True`). See [Disabling Tools](#disabling-tools) for more information.
- **`exclude_args`**: A list of argument names to exclude from the tool schema shown to the LLM. See [Excluding Arguments](#excluding-arguments) for more information.
### Excluding Arguments
@ -251,56 +288,230 @@ Use `async def` when your tool needs to perform operations that might wait for e
### Return Values
FastMCP automatically converts the value returned by your function into the appropriate MCP content format for the client:
- **`str`**: Sent as `TextContent`.
- **`dict`, `list`, Pydantic `BaseModel`**: Serialized to a JSON string and sent as `TextContent`.
- **`bytes`**: Base64 encoded and sent as `BlobResourceContents` (often within an `EmbeddedResource`).
- **`fastmcp.utilities.types.Image`**: A helper class for easily returning image data. Sent as `ImageContent`.
- **`fastmcp.utilities.types.Audio`**: A helper class for easily returning audio data. Sent as `AudioContent`.
- **`fastmcp.utilities.types.File`**: A helper class for easily returning binary data as base64-encoded content. Sent as `EmbeddedResource`.
- **A list of any of the above**: Automatically converts each item appropriately.
- **`None`**: Results in an empty response (no content is sent back to the client).
FastMCP tools can return data in two complementary formats: **traditional content blocks** (like text and images) and **structured outputs** (machine-readable JSON). When you add return type annotations, FastMCP automatically generates **output schemas** to validate the structured data and enables clients to deserialize results back to Python objects.
FastMCP will attempt to serialize other types to a string if possible.
Understanding how these three concepts work together:
<Tip>
At this time, FastMCP responds only to your tool's return *value*, not its return *annotation*.
</Tip>
- **Return Values**: What your Python function returns (determines both content blocks and structured data)
- **Structured Outputs**: JSON data sent alongside traditional content for machine processing
- **Output Schemas**: JSON Schema declarations that describe and validate the structured output format
The following sections explain each concept in detail.
#### Content Blocks
FastMCP automatically converts tool return values into appropriate MCP content blocks:
- **`str`**: Sent as `TextContent`
- **`bytes`**: Base64 encoded and sent as `BlobResourceContents` (within an `EmbeddedResource`)
- **`fastmcp.utilities.types.Image`**: Sent as `ImageContent`
- **`fastmcp.utilities.types.Audio`**: Sent as `AudioContent`
- **`fastmcp.utilities.types.File`**: Sent as base64-encoded `EmbeddedResource`
- **A list of any of the above**: Converts each item appropriately
- **`None`**: Results in an empty response
#### Structured Output
<VersionBadge version="2.10.0" />
The 6/18/2025 MCP spec update [introduced](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content) structured content, which is a new way to return data from tools. Structured content is a JSON object that is sent alongside traditional content. FastMCP automatically creates structured outputs alongside traditional content when your tool returns data that has a JSON object representation. This provides machine-readable JSON data that clients can deserialize back to Python objects.
**Automatic Structured Content Rules:**
- **Object-like results** (`dict`, Pydantic models, dataclasses) → Always become structured content (even without output schema)
- **Non-object results** (`int`, `str`, `list`) → Only become structured content if there's an output schema to validate/serialize them
- **All results** → Always become traditional content blocks for backward compatibility
<Note>
This automatic behavior enables clients to receive machine-readable data alongside human-readable content without requiring explicit output schemas for object-like returns.
</Note>
##### Object-like Results (Automatic Structured Content)
<CodeGroup>
```python Dict Return (No Schema Needed)
@mcp.tool
def get_user_data(user_id: str) -> dict:
"""Get user data without type annotation."""
return {"name": "Alice", "age": 30, "active": True}
```
```json Traditional Content
"{\n \"name\": \"Alice\",\n \"age\": 30,\n \"active\": true\n}"
```
```json Structured Content (Automatic)
{
"name": "Alice",
"age": 30,
"active": true
}
```
</CodeGroup>
##### Non-object Results (Schema Required)
<CodeGroup>
```python Integer Return (No Schema)
@mcp.tool
def calculate_sum(a: int, b: int):
"""Calculate sum without return annotation."""
return a + b # Returns 8
```
```json Traditional Content Only
"8"
```
```python Integer Return (With Schema)
@mcp.tool
def calculate_sum(a: int, b: int) -> int:
"""Calculate sum with return annotation."""
return a + b # Returns 8
```
```json Traditional Content
"8"
```
```json Structured Content (From Schema)
{
"result": 8
}
```
</CodeGroup>
##### Complex Type Example
<CodeGroup>
```python Tool Definition
from dataclasses import dataclass
from fastmcp import FastMCP
mcp = FastMCP()
@dataclass
class Person:
name: str
age: int
email: str
@mcp.tool
def get_user_profile(user_id: str) -> Person:
"""Get a user's profile information."""
return Person(name="Alice", age=30, email="alice@example.com")
```
```json Generated Output Schema
{
"properties": {
"name": {"title": "Name", "type": "string"},
"age": {"title": "Age", "type": "integer"},
"email": {"title": "Email", "type": "string"}
},
"required": ["name", "age", "email"],
"title": "Person",
"type": "object"
}
```
```json Structured Output
{
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}
```
</CodeGroup>
#### Output Schemas
<VersionBadge version="2.10.0" />
The 6/18/2025 MCP spec update [introduced](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema) output schemas, which are a new way to describe the expected output format of a tool. When an output schema is provided, the tool *must* return structured output that matches the schema.
When you add return type annotations to your functions, FastMCP automatically generates JSON schemas that describe the expected output format. These schemas help MCP clients understand and validate the structured data they receive.
##### Primitive Type Wrapping
For primitive return types (like `int`, `str`, `bool`), FastMCP automatically wraps the result under a `"result"` key to create valid structured output:
<CodeGroup>
```python Primitive Return Type
@mcp.tool
def calculate_sum(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
```
```json Generated Schema (Wrapped)
{
"type": "object",
"properties": {
"result": {"type": "integer"}
},
"x-fastmcp-wrap-result": true
}
```
```json Structured Output
{
"result": 8
}
```
</CodeGroup>
##### Manual Schema Control
You can override the automatically generated schema by providing a custom `output_schema`:
```python
from fastmcp import FastMCP
from fastmcp.utilities.types import Image
import io
try:
from PIL import Image as PILImage
except ImportError:
raise ImportError("Please install the `pillow` library to run this example.")
mcp = FastMCP("Image Demo")
@mcp.tool
def generate_image(width: int, height: int, color: str) -> Image:
"""Generates a solid color image."""
# Create image using Pillow
img = PILImage.new("RGB", (width, height), color=color)
# Save to a bytes buffer
buffer = io.BytesIO()
img.save(buffer, format="PNG")
img_bytes = buffer.getvalue()
# Return using FastMCP's Image helper
return Image(data=img_bytes, format="png")
@mcp.tool
def do_nothing() -> None:
"""This tool performs an action but returns no data."""
print("Performing a side effect...")
return None
@mcp.tool(output_schema={
"type": "object",
"properties": {
"data": {"type": "string"},
"metadata": {"type": "object"}
}
})
def custom_schema_tool() -> dict:
"""Tool with custom output schema."""
return {"data": "Hello", "metadata": {"version": "1.0"}}
```
Schema generation works for most common types including basic types, collections, union types, Pydantic models, TypedDict structures, and dataclasses.
<Warning>
**Important Constraints**:
- Output schemas must be object types (`"type": "object"`)
- If you provide an output schema, your tool **must** return structured output that matches it
- However, you can provide structured output without an output schema (using `ToolResult`)
</Warning>
#### Full Control with ToolResult
For complete control over both traditional content and structured output, return a `ToolResult` object:
```python
from fastmcp.tools.tool import ToolResult
@mcp.tool
def advanced_tool() -> ToolResult:
"""Tool with full control over output."""
return ToolResult(
content=[TextContent(text="Human-readable summary")],
structured_content={"data": "value", "count": 42}
)
```
When returning `ToolResult`:
- You control exactly what content and structured data is sent
- Output schemas are optional - structured content can be provided without a schema
- Clients receive both traditional content blocks and structured data
<Note>
If your return type annotation cannot be converted to a JSON schema (e.g., complex custom classes without Pydantic support), the output schema will be omitted but the tool will still function normally with traditional content.
</Note>
### Error Handling
<VersionBadge version="2.4.1" />
@ -378,6 +589,28 @@ FastMCP supports these standard annotations:
Remember that annotations help make better user experiences but should be treated as advisory hints. They help client applications present appropriate UI elements and safety controls, but won't enforce security boundaries on their own. Always focus on making your annotations accurately represent what your tool actually does.
### Notifications
<VersionBadge version="2.9.1" />
FastMCP automatically sends `notifications/tools/list_changed` notifications to connected clients when tools are added, removed, enabled, or disabled. This allows clients to stay up-to-date with the current tool set without manually polling for changes.
```python
@mcp.tool
def example_tool() -> str:
return "Hello!"
# These operations trigger notifications:
mcp.add_tool(example_tool) # Sends tools/list_changed notification
example_tool.disable() # Sends tools/list_changed notification
example_tool.enable() # Sends tools/list_changed notification
mcp.remove_tool("example_tool") # Sends tools/list_changed notification
```
Notifications are only sent when these operations occur within an active MCP request context (e.g., when called from within a tool or other MCP operation). Operations performed during server initialization do not trigger notifications.
Clients can handle these notifications using a [message handler](/clients/messages) to automatically refresh their tool lists or update their interfaces.
## MCP Context
Tools can access MCP features like logging, reading resources, or reporting progress through the `Context` object. To use it, add a parameter to your tool function with the type hint `Context`.
@ -797,13 +1030,3 @@ def calculate_sum(a: int, b: int) -> int:
mcp.remove_tool("calculate_sum")
```
### Legacy JSON Parsing
<VersionBadge version="2.2.10" />
FastMCP 1.0 and < 2.2.10 relied on a crutch that attempted to work around LLM limitations by automatically parsing stringified JSON in tool arguments (e.g., converting `"[1,2,3]"` to `[1,2,3]`). As of FastMCP 2.2.10, this behavior is disabled by default because it circumvents type validation and can lead to unexpected type coercion issues (e.g. parsing "true" as a bool and attempting to call a tool that expected a string, which would fail type validation).
Most modern LLMs correctly format JSON, but if working with models that unnecessarily stringify JSON (as was the case with Claude Desktop in late 2024), you can re-enable this behavior on your server by setting the environment variable `FASTMCP_TOOL_ATTEMPT_PARSE_JSON_ARGS=1`.
We strongly recommend leaving this disabled unless necessary.

View file

@ -82,7 +82,7 @@ mcp = FastMCP.from_openapi(
)
if __name__ == "__main__":
mcp.run(transport="streamable-http", port=8000)
mcp.run(transport="http", port=8000)
```
And that's it! With just a few lines of code, you've created an MCP server that exposes the entire JSONPlaceholder API as a collection of tools.
@ -195,7 +195,7 @@ mcp = FastMCP.from_openapi(
)
if __name__ == "__main__":
mcp.run(transport="streamable-http", port=8000)
mcp.run(transport="http", port=8000)
```
With this configuration:
- `GET /users/{id}` becomes a `ResourceTemplate`.

View file

@ -5,6 +5,22 @@ icon: "sparkles"
tag: NEW
---
<Update label="FastMCP 2.9" description="June 23, 2025" tags={["Releases", "Blog Posts"]}>
<Card
title="FastMCP 2.9: MCP-Native Middleware" href="https://www.jlowin.dev/blog/fastmcp-2-9-middleware"
img="https://jlowin.dev/_image?href=%2F_astro%2Fhero.BkVTdeBk.jpg&w=1200&h=630&f=png"
cta="Read more"
>
FastMCP 2.9 is a major release that, among other things, introduces two important features that push beyond the basic MCP protocol.
🤝 *MCP Middleware* brings a flexible middleware system for intercepting and controlling server operations - think authentication, logging, rate limiting, and custom business logic without touching core protocol code.
✨ *Server-side type conversion* for prompts solves a major developer pain point: while MCP requires string arguments, your functions can now work with native Python types like lists and dictionaries, with automatic conversion handling the complexity.
These features transform FastMCP from a simple protocol implementation into a powerful framework for building sophisticated MCP applications. Combined with the new `File` utility for binary data and improvements to authentication and serialization, this release makes FastMCP significantly more flexible and developer-friendly while maintaining full protocol compliance.
</Card>
</Update>
<Update label="FastMCP 2.8" description="June 11, 2025" tags={["Releases", "Blog Posts"]}>
<Card
title="FastMCP 2.8: Transform and Roll Out" href="https://www.jlowin.dev/blog/fastmcp-2-8-tool-transformation"
@ -107,7 +123,7 @@ img="https://www.jlowin.dev/_image?href=%2F_astro%2Fhero.M_hv6gEB.png&w=1000&h=5
cta="Read more"
>
FastMCP 2.3 introduces full support for Streamable HTTP, a modern alternative to SSE that simplifies MCP deployments over the web. Its efficient, reliable, and now the default HTTP transport. Just run your server with transport="streamable-http" and connect clients via a standard URL—FastMCP handles the rest. No special setup required. This release makes deploying MCP servers easier and more portable than ever.
FastMCP 2.3 introduces full support for Streamable HTTP, a modern alternative to SSE that simplifies MCP deployments over the web. Its efficient, reliable, and now the default HTTP transport. Just run your server with transport="http" and connect clients via a standard URL—FastMCP handles the rest. No special setup required. This release makes deploying MCP servers easier and more portable than ever.
</Card>
</Update>

View file

@ -0,0 +1,156 @@
# ATProto MCP Server
This example demonstrates a FastMCP server that provides tools and resources for interacting with the AT Protocol (Bluesky).
## Features
### Resources (Read-only)
- **atproto://profile/status**: Get connection status and profile information
- **atproto://timeline**: Retrieve your timeline feed
- **atproto://notifications**: Get recent notifications
### Tools (Actions)
- **post**: Create posts with rich features (text, images, quotes, replies, links, mentions)
- **create_thread**: Post multi-part threads with automatic linking
- **search**: Search for posts by query
- **follow**: Follow users by handle
- **like**: Like posts by URI
- **repost**: Share posts by URI
## Setup
1. Create a `.env` file in the root directory with your Bluesky credentials:
```bash
ATPROTO_HANDLE=your.handle@bsky.social
ATPROTO_PASSWORD=your-app-password
ATPROTO_PDS_URL=https://bsky.social # optional, defaults to bsky.social
```
2. Install and run the server:
```bash
# Install dependencies
uv pip install -e .
# Run the server
uv run atproto-mcp
```
## The Unified Post Tool
The `post` tool is a single, flexible interface for all posting needs:
```python
async def post(
text: str, # Required: Post content
images: list[str] = None, # Optional: Image URLs (max 4)
image_alts: list[str] = None, # Optional: Alt text for images
links: list[RichTextLink] = None, # Optional: Embedded links
mentions: list[RichTextMention] = None, # Optional: User mentions
reply_to: str = None, # Optional: Reply to post URI
reply_root: str = None, # Optional: Thread root URI
quote: str = None, # Optional: Quote post URI
)
```
### Usage Examples
```python
from fastmcp import Client
from atproto_mcp.server import atproto_mcp
async def demo():
async with Client(atproto_mcp) as client:
# Simple post
await client.call_tool("post", {
"text": "Hello from FastMCP!"
})
# Post with image
await client.call_tool("post", {
"text": "Beautiful sunset! 🌅",
"images": ["https://example.com/sunset.jpg"],
"image_alts": ["Sunset over the ocean"]
})
# Reply to a post
await client.call_tool("post", {
"text": "Great point!",
"reply_to": "at://did:plc:xxx/app.bsky.feed.post/yyy"
})
# Quote post
await client.call_tool("post", {
"text": "This is important:",
"quote": "at://did:plc:xxx/app.bsky.feed.post/yyy"
})
# Rich text with links and mentions
await client.call_tool("post", {
"text": "Check out FastMCP by @alternatebuild.dev",
"links": [{"text": "FastMCP", "url": "https://github.com/jlowin/fastmcp"}],
"mentions": [{"handle": "alternatebuild.dev", "display_text": "@alternatebuild.dev"}]
})
# Advanced: Quote with image
await client.call_tool("post", {
"text": "Adding visual context:",
"quote": "at://did:plc:xxx/app.bsky.feed.post/yyy",
"images": ["https://example.com/chart.png"]
})
# Advanced: Reply with rich text
await client.call_tool("post", {
"text": "I agree! See this article for more info",
"reply_to": "at://did:plc:xxx/app.bsky.feed.post/yyy",
"links": [{"text": "this article", "url": "https://example.com/article"}]
})
# Create a thread
await client.call_tool("create_thread", {
"posts": [
{"text": "Starting a thread about Python 🧵"},
{"text": "Python is great for rapid prototyping"},
{"text": "And the ecosystem is amazing!", "images": ["https://example.com/python.jpg"]}
]
})
```
## AI Assistant Use Cases
The unified API enables natural AI assistant interactions:
- **"Reply to that post with these findings"** → Uses `reply_to` with rich text
- **"Share this article with commentary"** → Uses `quote` with the article link
- **"Post this chart with explanation"** → Uses `images` with descriptive text
- **"Start a thread about AI safety"** → Uses `create_thread` for automatic linking
## Architecture
The server is organized as:
- `server.py` - Public API with resources and tools
- `_atproto/` - Private implementation module
- `_client.py` - ATProto client management
- `_posts.py` - Unified posting logic
- `_profile.py` - Profile operations
- `_read.py` - Timeline, search, notifications
- `_social.py` - Follow, like, repost
- `types.py` - TypedDict definitions
- `settings.py` - Configuration management
## Running the Demo
```bash
# Run demo (read-only)
uv run python demo.py
# Run demo with posting enabled
uv run python demo.py --post
```
## Security Note
Store your Bluesky credentials securely in environment variables. Never commit credentials to version control.

View file

@ -0,0 +1,257 @@
"""Demo script showing all ATProto MCP server capabilities."""
import argparse
import asyncio
import json
from typing import cast
from atproto_mcp.server import atproto_mcp
from atproto_mcp.types import (
NotificationsResult,
PostResult,
ProfileInfo,
SearchResult,
TimelineResult,
)
from fastmcp import Client
async def main(enable_posting: bool = False):
print("🔵 ATProto MCP Server Demo\n")
async with Client(atproto_mcp) as client:
# 1. Check connection status (resource)
print("1. Checking connection status...")
result = await client.read_resource("atproto://profile/status")
status: ProfileInfo = (
json.loads(result[0].text) if result else cast(ProfileInfo, {})
)
if status.get("connected"):
print(f"✅ Connected as: @{status['handle']}")
print(f" Followers: {status['followers']}")
print(f" Following: {status['following']}")
print(f" Posts: {status['posts']}")
else:
print(f"❌ Connection failed: {status.get('error')}")
return
# 2. Get timeline
print("\n2. Getting timeline...")
result = await client.read_resource("atproto://timeline")
timeline: TimelineResult = (
json.loads(result[0].text) if result else cast(TimelineResult, {})
)
if timeline.get("success") and timeline["posts"]:
print(f"✅ Found {timeline['count']} posts")
post = timeline["posts"][0]
print(f" Latest by @{post['author']}: {post['text'][:80]}...")
save_uri = post["uri"] # Save for later interactions
else:
print("❌ No posts in timeline")
save_uri = None
# 3. Search for posts
print("\n3. Searching for posts about 'Bluesky'...")
result = await client.call_tool("search", {"query": "Bluesky", "limit": 5})
search: SearchResult = (
json.loads(result[0].text) if result else cast(SearchResult, {})
)
if search.get("success") and search["posts"]:
print(f"✅ Found {search['count']} posts")
print(f" Sample: {search['posts'][0]['text'][:80]}...")
# 4. Get notifications
print("\n4. Checking notifications...")
result = await client.read_resource("atproto://notifications")
notifs: NotificationsResult = (
json.loads(result[0].text) if result else cast(NotificationsResult, {})
)
if notifs.get("success"):
print(f"✅ You have {notifs['count']} notifications")
unread = sum(1 for n in notifs["notifications"] if not n["is_read"])
if unread:
print(f" ({unread} unread)")
# 5. Demo posting capabilities
if enable_posting:
print("\n5. Demonstrating posting capabilities...")
# a. Simple post
print("\n a) Creating a simple post...")
result = await client.call_tool(
"post",
{"text": "🧪 Testing the unified ATProto MCP post tool! #FastMCP"},
)
post_result: PostResult = json.loads(result[0].text) if result else {}
if post_result.get("success"):
print(" ✅ Posted successfully!")
simple_uri = post_result["uri"]
else:
print(f" ❌ Failed: {post_result.get('error')}")
simple_uri = None
# b. Post with rich text (link and mention)
print("\n b) Creating a post with rich text...")
result = await client.call_tool(
"post",
{
"text": "Check out FastMCP and follow @alternatebuild.dev for updates!",
"links": [
{"text": "FastMCP", "url": "https://github.com/jlowin/fastmcp"}
],
"mentions": [
{
"handle": "alternatebuild.dev",
"display_text": "@alternatebuild.dev",
}
],
},
)
if json.loads(result[0].text).get("success"):
print(" ✅ Rich text post created!")
# c. Reply to a post
if save_uri:
print("\n c) Replying to a post...")
result = await client.call_tool(
"post", {"text": "Great post! 👍", "reply_to": save_uri}
)
if json.loads(result[0].text).get("success"):
print(" ✅ Reply posted!")
# d. Quote post
if simple_uri:
print("\n d) Creating a quote post...")
result = await client.call_tool(
"post",
{
"text": "Quoting my own test post for demo purposes 🔄",
"quote": simple_uri,
},
)
if json.loads(result[0].text).get("success"):
print(" ✅ Quote post created!")
# e. Post with image
print("\n e) Creating a post with image...")
result = await client.call_tool(
"post",
{
"text": "Here's a test image post! 📸",
"images": ["https://picsum.photos/400/300"],
"image_alts": ["Random test image"],
},
)
if json.loads(result[0].text).get("success"):
print(" ✅ Image post created!")
# f. Quote with image (advanced)
if simple_uri:
print("\n f) Creating a quote post with image...")
result = await client.call_tool(
"post",
{
"text": "Quote + image combo! 🎨",
"quote": simple_uri,
"images": ["https://picsum.photos/300/200"],
"image_alts": ["Another test image"],
},
)
if json.loads(result[0].text).get("success"):
print(" ✅ Quote with image created!")
# g. Social actions
if save_uri:
print("\n g) Demonstrating social actions...")
# Like
result = await client.call_tool("like", {"uri": save_uri})
if json.loads(result[0].text).get("success"):
print(" ✅ Liked a post!")
# Repost
result = await client.call_tool("repost", {"uri": save_uri})
if json.loads(result[0].text).get("success"):
print(" ✅ Reposted!")
# Follow
result = await client.call_tool(
"follow", {"handle": "alternatebuild.dev"}
)
if json.loads(result[0].text).get("success"):
print(" ✅ Followed @alternatebuild.dev!")
# h. Thread creation (new!)
print("\n h) Creating a thread...")
result = await client.call_tool(
"create_thread",
{
"posts": [
{
"text": "Let me share some thoughts about the ATProto MCP server 🧵"
},
{
"text": "First, it makes posting from the terminal incredibly smooth"
},
{
"text": "The unified post API means one tool handles everything",
"links": [
{
"text": "everything",
"url": "https://github.com/jlowin/fastmcp",
}
],
},
{
"text": "And now with create_thread, multi-post threads are trivial!"
},
]
},
)
if json.loads(result[0].text).get("success"):
thread_result = json.loads(result[0].text)
print(f" ✅ Thread created with {thread_result['post_count']} posts!")
else:
print("\n5. Posting capabilities (not enabled):")
print(" To test posting, run with --post flag")
print(" Example: python demo.py --post")
# 6. Show available capabilities
print("\n6. Available capabilities:")
print("\n Resources (read-only):")
print(" - atproto://profile/status")
print(" - atproto://timeline")
print(" - atproto://notifications")
print("\n Tools (actions):")
print(" - post: Unified posting with rich features")
print(" • Simple text posts")
print(" • Images (up to 4)")
print(" • Rich text (links, mentions)")
print(" • Replies and threads")
print(" • Quote posts")
print(" • Combinations (quote + image, reply + rich text, etc.)")
print(" - search: Search for posts")
print(" - create_thread: Post multi-part threads")
print(" - follow: Follow users")
print(" - like: Like posts")
print(" - repost: Share posts")
print("\n✨ Demo complete!")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="ATProto MCP Server Demo")
parser.add_argument(
"--post",
action="store_true",
help="Enable posting test messages to Bluesky",
)
args = parser.parse_args()
asyncio.run(main(enable_posting=args.post))

View file

@ -0,0 +1,24 @@
[project]
name = "atproto-mcp"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [{ name = "zzstoatzz", email = "thrast36@gmail.com" }]
requires-python = ">=3.10"
dependencies = [
"fastmcp>=0.8.0",
"atproto@git+https://github.com/MarshalX/atproto.git@refs/pull/605/head",
"pydantic-settings>=2.0.0",
"websockets>=15.0.1",
"httpx>=0.27.0",
]
[project.scripts]
atproto-mcp = "atproto_mcp.__main__:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true

View file

@ -0,0 +1,3 @@
from atproto_mcp.settings import settings
__all__ = ["settings"]

View file

@ -0,0 +1,9 @@
from atproto_mcp.server import atproto_mcp
def main():
atproto_mcp.run()
if __name__ == "__main__":
main()

View file

@ -0,0 +1,20 @@
"""Private ATProto implementation module."""
from ._client import get_client
from ._posts import create_post, create_thread
from ._profile import get_profile_info
from ._read import fetch_notifications, fetch_timeline, search_for_posts
from ._social import follow_user_by_handle, like_post_by_uri, repost_by_uri
__all__ = [
"get_client",
"get_profile_info",
"create_post",
"create_thread",
"fetch_timeline",
"search_for_posts",
"fetch_notifications",
"follow_user_by_handle",
"like_post_by_uri",
"repost_by_uri",
]

View file

@ -0,0 +1,16 @@
"""ATProto client management."""
from atproto import Client
from atproto_mcp.settings import settings
_client: Client | None = None
def get_client() -> Client:
"""Get or create an authenticated ATProto client."""
global _client
if _client is None:
_client = Client()
_client.login(settings.atproto_handle, settings.atproto_password)
return _client

View file

@ -0,0 +1,385 @@
"""Unified posting functionality."""
import time
from datetime import datetime
from atproto import models
from atproto_mcp.types import (
PostResult,
RichTextLink,
RichTextMention,
ThreadPost,
ThreadResult,
)
from ._client import get_client
def create_post(
text: str,
images: list[str] | None = None,
image_alts: list[str] | None = None,
links: list[RichTextLink] | None = None,
mentions: list[RichTextMention] | None = None,
reply_to: str | None = None,
reply_root: str | None = None,
quote: str | None = None,
) -> PostResult:
"""Create a unified post with optional features.
Args:
text: Post text (max 300 chars)
images: URLs of images to attach (max 4)
image_alts: Alt text for images
links: Links to embed in rich text
mentions: User mentions to embed
reply_to: URI of post to reply to
reply_root: URI of thread root (defaults to reply_to)
quote: URI of post to quote
"""
try:
client = get_client()
facets = []
embed = None
reply_ref = None
# Handle rich text facets (links and mentions)
if links or mentions:
facets = _build_facets(text, links, mentions, client)
# Handle replies
if reply_to:
reply_ref = _build_reply_ref(reply_to, reply_root, client)
# Handle quotes and images
if quote and images:
# Quote with images - create record with media embed
embed = _build_quote_with_images_embed(quote, images, image_alts, client)
elif quote:
# Quote only
embed = _build_quote_embed(quote, client)
elif images:
# Images only - use send_images for proper handling
return _send_images(text, images, image_alts, facets, reply_ref, client)
# Send the post
post = client.send_post(
text=text,
facets=facets if facets else None,
embed=embed,
reply_to=reply_ref,
)
return PostResult(
success=True,
uri=post.uri,
cid=post.cid,
text=text,
created_at=datetime.now().isoformat(),
error=None,
)
except Exception as e:
return PostResult(
success=False,
uri=None,
cid=None,
text=None,
created_at=None,
error=str(e),
)
def _build_facets(
text: str,
links: list[RichTextLink] | None,
mentions: list[RichTextMention] | None,
client,
):
"""Build facets for rich text formatting."""
facets = []
# Process links
if links:
for link in links:
start = text.find(link["text"])
if start == -1:
continue
end = start + len(link["text"])
facets.append(
models.AppBskyRichtextFacet.Main(
features=[models.AppBskyRichtextFacet.Link(uri=link["url"])],
index=models.AppBskyRichtextFacet.ByteSlice(
byte_start=len(text[:start].encode("UTF-8")),
byte_end=len(text[:end].encode("UTF-8")),
),
)
)
# Process mentions
if mentions:
for mention in mentions:
display_text = mention.get("display_text") or f"@{mention['handle']}"
start = text.find(display_text)
if start == -1:
continue
end = start + len(display_text)
# Resolve handle to DID
resolved = client.app.bsky.actor.search_actors(
params={"q": mention["handle"], "limit": 1}
)
if not resolved.actors:
continue
did = resolved.actors[0].did
facets.append(
models.AppBskyRichtextFacet.Main(
features=[models.AppBskyRichtextFacet.Mention(did=did)],
index=models.AppBskyRichtextFacet.ByteSlice(
byte_start=len(text[:start].encode("UTF-8")),
byte_end=len(text[:end].encode("UTF-8")),
),
)
)
return facets
def _build_reply_ref(reply_to: str, reply_root: str | None, client):
"""Build reply reference."""
# Get parent post to extract CID
parent_post = client.app.bsky.feed.get_posts(params={"uris": [reply_to]})
if not parent_post.posts:
raise ValueError("Parent post not found")
parent_cid = parent_post.posts[0].cid
parent_ref = models.ComAtprotoRepoStrongRef.Main(uri=reply_to, cid=parent_cid)
# If no root_uri provided, parent is the root
if reply_root is None:
root_ref = parent_ref
else:
# Get root post CID
root_post = client.app.bsky.feed.get_posts(params={"uris": [reply_root]})
if not root_post.posts:
raise ValueError("Root post not found")
root_cid = root_post.posts[0].cid
root_ref = models.ComAtprotoRepoStrongRef.Main(uri=reply_root, cid=root_cid)
return models.AppBskyFeedPost.ReplyRef(parent=parent_ref, root=root_ref)
def _build_quote_embed(quote_uri: str, client):
"""Build quote embed."""
# Get the post to quote
quoted_post = client.app.bsky.feed.get_posts(params={"uris": [quote_uri]})
if not quoted_post.posts:
raise ValueError("Quoted post not found")
# Create strong ref for the quoted post
quoted_cid = quoted_post.posts[0].cid
quoted_ref = models.ComAtprotoRepoStrongRef.Main(uri=quote_uri, cid=quoted_cid)
# Create the embed
return models.AppBskyEmbedRecord.Main(record=quoted_ref)
def _build_quote_with_images_embed(
quote_uri: str, image_urls: list[str], image_alts: list[str] | None, client
):
"""Build quote embed with images."""
import httpx
# Get the quoted post
quoted_post = client.app.bsky.feed.get_posts(params={"uris": [quote_uri]})
if not quoted_post.posts:
raise ValueError("Quoted post not found")
quoted_cid = quoted_post.posts[0].cid
quoted_ref = models.ComAtprotoRepoStrongRef.Main(uri=quote_uri, cid=quoted_cid)
# Download and upload images
images = []
alts = image_alts or [""] * len(image_urls)
for i, url in enumerate(image_urls[:4]):
response = httpx.get(url, follow_redirects=True)
response.raise_for_status()
# Upload to blob storage
upload = client.upload_blob(response.content)
images.append(
models.AppBskyEmbedImages.Image(
alt=alts[i] if i < len(alts) else "",
image=upload.blob,
)
)
# Create record with media embed
return models.AppBskyEmbedRecordWithMedia.Main(
record=models.AppBskyEmbedRecord.Main(record=quoted_ref),
media=models.AppBskyEmbedImages.Main(images=images),
)
def _send_images(
text: str,
image_urls: list[str],
image_alts: list[str] | None,
facets,
reply_ref,
client,
):
"""Send post with images using the client's send_images method."""
import httpx
# Ensure alt_texts has same length as images
if image_alts is None:
image_alts = [""] * len(image_urls)
elif len(image_alts) < len(image_urls):
image_alts.extend([""] * (len(image_urls) - len(image_alts)))
image_data = []
alts = []
for i, url in enumerate(image_urls[:4]): # Max 4 images
# Download image (follow redirects)
response = httpx.get(url, follow_redirects=True)
response.raise_for_status()
image_data.append(response.content)
alts.append(image_alts[i] if i < len(image_alts) else "")
# Send post with images
# Note: send_images doesn't support facets or reply_to directly
# So we need to use send_post with manual image upload if we have those
if facets or reply_ref:
# Manual image upload
images = []
for i, data in enumerate(image_data):
upload = client.upload_blob(data)
images.append(
models.AppBskyEmbedImages.Image(
alt=alts[i],
image=upload.blob,
)
)
embed = models.AppBskyEmbedImages.Main(images=images)
post = client.send_post(
text=text,
facets=facets if facets else None,
embed=embed,
reply_to=reply_ref,
)
else:
# Use simple send_images
post = client.send_images(
text=text,
images=image_data,
image_alts=alts,
)
return PostResult(
success=True,
uri=post.uri,
cid=post.cid,
text=text,
created_at=datetime.now().isoformat(),
error=None,
)
def create_thread(posts: list[ThreadPost]) -> ThreadResult:
"""Create a thread of posts with automatic linking.
Args:
posts: List of posts to create as a thread. First post is the root.
"""
if not posts:
return ThreadResult(
success=False,
thread_uri=None,
post_uris=[],
post_count=0,
error="No posts provided",
)
try:
post_uris = []
root_uri = None
parent_uri = None
for i, post_data in enumerate(posts):
# First post is the root
if i == 0:
result = create_post(
text=post_data["text"],
images=post_data.get("images"),
image_alts=post_data.get("image_alts"),
links=post_data.get("links"),
mentions=post_data.get("mentions"),
quote=post_data.get("quote"),
)
if not result["success"]:
return ThreadResult(
success=False,
thread_uri=None,
post_uris=post_uris,
post_count=len(post_uris),
error=f"Failed to create root post: {result['error']}",
)
root_uri = result["uri"]
parent_uri = root_uri
post_uris.append(root_uri)
# Small delay to ensure post is indexed
time.sleep(0.5)
else:
# Subsequent posts reply to the previous one
result = create_post(
text=post_data["text"],
images=post_data.get("images"),
image_alts=post_data.get("image_alts"),
links=post_data.get("links"),
mentions=post_data.get("mentions"),
quote=post_data.get("quote"),
reply_to=parent_uri,
reply_root=root_uri,
)
if not result["success"]:
return ThreadResult(
success=False,
thread_uri=root_uri,
post_uris=post_uris,
post_count=len(post_uris),
error=f"Failed to create post {i + 1}: {result['error']}",
)
parent_uri = result["uri"]
post_uris.append(parent_uri)
# Small delay between posts
if i < len(posts) - 1:
time.sleep(0.5)
return ThreadResult(
success=True,
thread_uri=root_uri,
post_uris=post_uris,
post_count=len(post_uris),
error=None,
)
except Exception as e:
return ThreadResult(
success=False,
thread_uri=None,
post_uris=post_uris,
post_count=len(post_uris),
error=str(e),
)

View file

@ -0,0 +1,33 @@
"""Profile-related operations."""
from atproto_mcp.types import ProfileInfo
from ._client import get_client
def get_profile_info() -> ProfileInfo:
"""Get profile information for the authenticated user."""
try:
client = get_client()
profile = client.get_profile(client.me.did)
return ProfileInfo(
connected=True,
handle=profile.handle,
display_name=profile.display_name,
did=client.me.did,
followers=profile.followers_count,
following=profile.follows_count,
posts=profile.posts_count,
error=None,
)
except Exception as e:
return ProfileInfo(
connected=False,
handle=None,
display_name=None,
did=None,
followers=None,
following=None,
posts=None,
error=str(e),
)

View file

@ -0,0 +1,124 @@
"""Read-only operations for timeline, search, and notifications."""
from atproto_mcp.types import (
Notification,
NotificationsResult,
Post,
SearchResult,
TimelineResult,
)
from ._client import get_client
def fetch_timeline(limit: int = 10) -> TimelineResult:
"""Fetch the authenticated user's timeline."""
try:
client = get_client()
timeline = client.get_timeline(limit=limit)
posts = []
for feed_view in timeline.feed:
post = feed_view.post
posts.append(
Post(
uri=post.uri,
cid=post.cid,
text=post.record.text if hasattr(post.record, "text") else "",
author=post.author.handle,
created_at=post.record.created_at,
likes=post.like_count or 0,
reposts=post.repost_count or 0,
replies=post.reply_count or 0,
)
)
return TimelineResult(
success=True,
posts=posts,
count=len(posts),
error=None,
)
except Exception as e:
return TimelineResult(
success=False,
posts=[],
count=0,
error=str(e),
)
def search_for_posts(query: str, limit: int = 10) -> SearchResult:
"""Search for posts containing specific text."""
try:
client = get_client()
search_results = client.app.bsky.feed.search_posts(
params={"q": query, "limit": limit}
)
posts = []
for post in search_results.posts:
posts.append(
Post(
uri=post.uri,
cid=post.cid,
text=post.record.text if hasattr(post.record, "text") else "",
author=post.author.handle,
created_at=post.record.created_at,
likes=post.like_count or 0,
reposts=post.repost_count or 0,
replies=post.reply_count or 0,
)
)
return SearchResult(
success=True,
query=query,
posts=posts,
count=len(posts),
error=None,
)
except Exception as e:
return SearchResult(
success=False,
query=query,
posts=[],
count=0,
error=str(e),
)
def fetch_notifications(limit: int = 10) -> NotificationsResult:
"""Fetch recent notifications."""
try:
client = get_client()
notifs = client.app.bsky.notification.list_notifications(
params={"limit": limit}
)
notifications = []
for notif in notifs.notifications:
notifications.append(
Notification(
uri=notif.uri,
cid=notif.cid,
author=notif.author.handle,
reason=notif.reason,
is_read=notif.is_read,
indexed_at=notif.indexed_at,
)
)
return NotificationsResult(
success=True,
notifications=notifications,
count=len(notifications),
error=None,
)
except Exception as e:
return NotificationsResult(
success=False,
notifications=[],
count=0,
error=str(e),
)

View file

@ -0,0 +1,108 @@
"""Social actions like follow, like, and repost."""
from atproto_mcp.types import FollowResult, LikeResult, RepostResult
from ._client import get_client
def follow_user_by_handle(handle: str) -> FollowResult:
"""Follow a user by their handle."""
try:
client = get_client()
# Search for the user to get their DID
results = client.app.bsky.actor.search_actors(params={"q": handle, "limit": 1})
if not results.actors:
return FollowResult(
success=False,
did=None,
handle=None,
uri=None,
error=f"User @{handle} not found",
)
actor = results.actors[0]
# Create the follow
follow = client.follow(actor.did)
return FollowResult(
success=True,
did=actor.did,
handle=actor.handle,
uri=follow.uri,
error=None,
)
except Exception as e:
return FollowResult(
success=False,
did=None,
handle=None,
uri=None,
error=str(e),
)
def like_post_by_uri(uri: str) -> LikeResult:
"""Like a post by its AT URI."""
try:
client = get_client()
# Parse the URI to get the components
# URI format: at://did:plc:xxx/app.bsky.feed.post/yyy
parts = uri.replace("at://", "").split("/")
if len(parts) != 3 or parts[1] != "app.bsky.feed.post":
raise ValueError("Invalid post URI format")
# Get the post to retrieve its CID
post = client.app.bsky.feed.get_posts(params={"uris": [uri]})
if not post.posts:
raise ValueError("Post not found")
cid = post.posts[0].cid
# Now like the post with both URI and CID
like = client.like(uri, cid)
return LikeResult(
success=True,
liked_uri=uri,
like_uri=like.uri,
error=None,
)
except Exception as e:
return LikeResult(
success=False,
liked_uri=None,
like_uri=None,
error=str(e),
)
def repost_by_uri(uri: str) -> RepostResult:
"""Repost a post by its AT URI."""
try:
client = get_client()
# Parse the URI to get the components
# URI format: at://did:plc:xxx/app.bsky.feed.post/yyy
parts = uri.replace("at://", "").split("/")
if len(parts) != 3 or parts[1] != "app.bsky.feed.post":
raise ValueError("Invalid post URI format")
# Get the post to retrieve its CID
post = client.app.bsky.feed.get_posts(params={"uris": [uri]})
if not post.posts:
raise ValueError("Post not found")
cid = post.posts[0].cid
# Now repost with both URI and CID
repost = client.repost(uri, cid)
return RepostResult(
success=True,
reposted_uri=uri,
repost_uri=repost.uri,
error=None,
)
except Exception as e:
return RepostResult(
success=False,
reposted_uri=None,
repost_uri=None,
error=str(e),
)

View file

@ -0,0 +1,154 @@
"""ATProto MCP Server - Public API exposing Bluesky tools and resources."""
from typing import Annotated
from pydantic import Field
from atproto_mcp import _atproto
from atproto_mcp.settings import settings
from atproto_mcp.types import (
FollowResult,
LikeResult,
NotificationsResult,
PostResult,
ProfileInfo,
RepostResult,
RichTextLink,
RichTextMention,
SearchResult,
ThreadPost,
ThreadResult,
TimelineResult,
)
from fastmcp import FastMCP
atproto_mcp = FastMCP(
"ATProto MCP Server",
dependencies=[
"atproto_mcp@git+https://github.com/jlowin/fastmcp.git#subdirectory=examples/atproto_mcp",
],
)
# Resources - read-only operations
@atproto_mcp.resource("atproto://profile/status")
def atproto_status() -> ProfileInfo:
"""Check the status of the ATProto connection and current user profile."""
return _atproto.get_profile_info()
@atproto_mcp.resource("atproto://timeline")
def get_timeline() -> TimelineResult:
"""Get the authenticated user's timeline feed."""
return _atproto.fetch_timeline(settings.atproto_timeline_default_limit)
@atproto_mcp.resource("atproto://notifications")
def get_notifications() -> NotificationsResult:
"""Get recent notifications for the authenticated user."""
return _atproto.fetch_notifications(settings.atproto_notifications_default_limit)
# Tools - actions that modify state
@atproto_mcp.tool
def post(
text: Annotated[
str, Field(max_length=300, description="The text content of the post")
],
images: Annotated[
list[str] | None,
Field(max_length=4, description="URLs of images to attach (max 4)"),
] = None,
image_alts: Annotated[
list[str] | None, Field(description="Alt text for each image")
] = None,
links: Annotated[
list[RichTextLink] | None, Field(description="Links to embed in the text")
] = None,
mentions: Annotated[
list[RichTextMention] | None, Field(description="User mentions to embed")
] = None,
reply_to: Annotated[
str | None, Field(description="AT URI of post to reply to")
] = None,
reply_root: Annotated[
str | None, Field(description="AT URI of thread root (defaults to reply_to)")
] = None,
quote: Annotated[str | None, Field(description="AT URI of post to quote")] = None,
) -> PostResult:
"""Create a post with optional rich features like images, quotes, replies, and rich text.
Examples:
- Simple post: post("Hello world!")
- With image: post("Check this out!", images=["https://example.com/img.jpg"])
- Reply: post("I agree!", reply_to="at://did/app.bsky.feed.post/123")
- Quote: post("Great point!", quote="at://did/app.bsky.feed.post/456")
- Rich text: post("Check out example.com", links=[{"text": "example.com", "url": "https://example.com"}])
"""
return _atproto.create_post(
text, images, image_alts, links, mentions, reply_to, reply_root, quote
)
@atproto_mcp.tool
def follow(
handle: Annotated[
str,
Field(
description="The handle of the user to follow (e.g., 'user.bsky.social')"
),
],
) -> FollowResult:
"""Follow a user by their handle."""
return _atproto.follow_user_by_handle(handle)
@atproto_mcp.tool
def like(
uri: Annotated[str, Field(description="The AT URI of the post to like")],
) -> LikeResult:
"""Like a post by its AT URI."""
return _atproto.like_post_by_uri(uri)
@atproto_mcp.tool
def repost(
uri: Annotated[str, Field(description="The AT URI of the post to repost")],
) -> RepostResult:
"""Repost a post by its AT URI."""
return _atproto.repost_by_uri(uri)
@atproto_mcp.tool
def search(
query: Annotated[str, Field(description="Search query for posts")],
limit: Annotated[
int, Field(ge=1, le=100, description="Number of results to return")
] = settings.atproto_search_default_limit,
) -> SearchResult:
"""Search for posts containing specific text."""
return _atproto.search_for_posts(query, limit)
@atproto_mcp.tool
def create_thread(
posts: Annotated[
list[ThreadPost],
Field(
description="List of posts to create as a thread. Each post can have text, images, links, mentions, and quotes."
),
],
) -> ThreadResult:
"""Create a thread of posts with automatic linking.
The first post becomes the root of the thread, and each subsequent post
replies to the previous one, maintaining the thread structure.
Example:
create_thread([
{"text": "Starting a thread about Python 🧵"},
{"text": "Python is great for rapid development"},
{"text": "And the ecosystem is amazing!", "images": ["https://example.com/python.jpg"]}
])
"""
return _atproto.create_thread(posts)

View file

@ -0,0 +1,17 @@
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=[".env"], extra="ignore")
atproto_handle: str = Field(default=...)
atproto_password: str = Field(default=...)
atproto_pds_url: str = Field(default="https://bsky.social")
atproto_notifications_default_limit: int = Field(default=10)
atproto_timeline_default_limit: int = Field(default=10)
atproto_search_default_limit: int = Field(default=10)
settings = Settings()

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