mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-22 05:24:18 +02:00
Merge branch 'protocol-update' into output-schema
This commit is contained in:
commit
0142bb2af1
112 changed files with 6315 additions and 736 deletions
|
|
@ -1,4 +1,4 @@
|
|||
fail_fast: true
|
||||
fail_fast: false
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
|
|
|
|||
|
|
@ -27,4 +27,9 @@ Only use HTTP transport when testing network-specific features. Prefer Streamabl
|
|||
# Only when network testing is required
|
||||
async with Client(transport=StreamableHttpTransport(server_url)) as client:
|
||||
result = await client.ping()
|
||||
```
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
mode: center
|
||||
icon: "list-check"
|
||||
---
|
||||
|
||||
<Update label="v2.8.0" description="2024-06-10">
|
||||
|
|
|
|||
|
|
@ -1,152 +0,0 @@
|
|||
---
|
||||
title: Advanced Features
|
||||
sidebarTitle: Advanced Features
|
||||
description: Learn about the advanced features of the FastMCP Client.
|
||||
icon: stars
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
In addition to basic server interaction, FastMCP clients can also handle more advanced features and server interaction patterns. The `Client` constructor accepts additional configuration to handle these server requests.
|
||||
|
||||
<Tip>
|
||||
To enable many of these features, you must provide an appropriate handler or callback function. For example. In most cases, if you do not provide a handler, FastMCP's default handler will emit a `DEBUG` level log.
|
||||
</Tip>
|
||||
|
||||
## Logging and Notifications
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
MCP servers can emit logs to clients. To process these logs, you can provide a `log_handler` to the client.
|
||||
|
||||
The `log_handler` must be an async function that accepts a single argument, which is an instance of `fastmcp.client.logging.LogMessage`. This has attributes like `level`, `logger`, and `data`.
|
||||
|
||||
```python {2, 12}
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.logging import LogMessage
|
||||
|
||||
async def log_handler(message: LogMessage):
|
||||
level = message.level.upper()
|
||||
logger = message.logger or 'default'
|
||||
data = message.data
|
||||
print(f"[Server Log - {level}] {logger}: {data}")
|
||||
|
||||
client_with_logging = Client(
|
||||
...,
|
||||
log_handler=log_handler,
|
||||
)
|
||||
```
|
||||
## Progress Monitoring
|
||||
|
||||
<VersionBadge version="2.3.5" />
|
||||
|
||||
MCP servers can report progress during long-running operations. The client can set a progress handler to receive and process these updates.
|
||||
|
||||
```python {2, 13}
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.progress import ProgressHandler
|
||||
|
||||
async def my_progress_handler(
|
||||
progress: float,
|
||||
total: float | None,
|
||||
message: str | None
|
||||
) -> None:
|
||||
print(f"Progress: {progress} / {total} ({message})")
|
||||
|
||||
client = Client(
|
||||
...,
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
|
||||
By default, FastMCP uses a handler that logs progress updates at the debug level. This default handler properly handles cases where `total` or `message` might be None.
|
||||
|
||||
You can override the progress handler for specific tool calls:
|
||||
|
||||
```python
|
||||
# Client uses the default debug logger for progress
|
||||
client = Client(...)
|
||||
|
||||
async with client:
|
||||
# Use default progress handler (debug logging)
|
||||
result1 = await client.call_tool("long_task", {"param": "value"})
|
||||
|
||||
# Override with custom progress handler just for this call
|
||||
result2 = await client.call_tool(
|
||||
"another_task",
|
||||
{"param": "value"},
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
|
||||
A typical progress update includes:
|
||||
- Current progress value (e.g., 2 of 5 steps completed)
|
||||
- Total expected value (may be None)
|
||||
- Status message (may be None)
|
||||
|
||||
## LLM Sampling
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
MCP Servers can request LLM completions from clients. The client can provide a `sampling_handler` to handle these requests. The sampling handler receives a list of messages and other parameters from the server, and should return a string completion.
|
||||
|
||||
The following example uses the `marvin` library to generate a completion:
|
||||
|
||||
```python {8-17, 21}
|
||||
import marvin
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.sampling import (
|
||||
SamplingMessage,
|
||||
SamplingParams,
|
||||
RequestContext,
|
||||
)
|
||||
|
||||
async def sampling_handler(
|
||||
messages: list[SamplingMessage],
|
||||
params: SamplingParams,
|
||||
context: RequestContext
|
||||
) -> str:
|
||||
return await marvin.say_async(
|
||||
message=[m.content.text for m in messages],
|
||||
instructions=params.systemPrompt,
|
||||
)
|
||||
|
||||
client = Client(
|
||||
...,
|
||||
sampling_handler=sampling_handler,
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Roots
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Roots are a way for clients to inform servers about the resources they have access to or certain boundaries on their access. The server can use this information to adjust behavior or provide more accurate responses.
|
||||
|
||||
Servers can request roots from clients, and clients can notify servers when their roots change.
|
||||
|
||||
To set the roots when creating a client, users can either provide a list of roots (which can be a list of strings) or an async function that returns a list of roots.
|
||||
|
||||
<CodeGroup>
|
||||
```python Static Roots {5}
|
||||
from fastmcp import Client
|
||||
|
||||
client = Client(
|
||||
...,
|
||||
roots=["/path/to/root1", "/path/to/root2"],
|
||||
)
|
||||
```
|
||||
```python Dynamic Roots Callback {4-6, 10}
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.roots import RequestContext
|
||||
|
||||
async def roots_callback(context: RequestContext) -> list[str]:
|
||||
print(f"Server requested roots (Request ID: {context.request_id})")
|
||||
return ["/path/to/root1", "/path/to/root2"]
|
||||
|
||||
client = Client(
|
||||
...,
|
||||
roots=roots_callback,
|
||||
)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
|
@ -3,7 +3,7 @@ title: Bearer Token Authentication
|
|||
sidebarTitle: Bearer Auth
|
||||
description: Authenticate your FastMCP client with a Bearer token.
|
||||
icon: key
|
||||
tag: "New!"
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ title: OAuth Authentication
|
|||
sidebarTitle: OAuth
|
||||
description: Authenticate your FastMCP client via OAuth 2.1.
|
||||
icon: window
|
||||
tag: "New!"
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Client Overview
|
||||
title: The FastMCP Client
|
||||
sidebarTitle: Overview
|
||||
description: Learn how to use the FastMCP Client to interact with MCP servers.
|
||||
description: Programmatic client for interacting with MCP servers through a well-typed, Pythonic interface.
|
||||
icon: user-robot
|
||||
---
|
||||
|
||||
|
|
@ -9,270 +9,213 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
|
|||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
The `fastmcp.Client` provides a high-level, asynchronous interface for interacting with any Model Context Protocol (MCP) server, whether it's built with FastMCP or another implementation. It simplifies communication by handling protocol details and connection management.
|
||||
The central piece of MCP client applications is the `fastmcp.Client` class. This class provides a **programmatic interface** for interacting with any Model Context Protocol (MCP) server, handling protocol details and connection management automatically.
|
||||
|
||||
## FastMCP Client
|
||||
The FastMCP Client is designed for deterministic, controlled interactions rather than autonomous behavior, making it ideal for:
|
||||
|
||||
The FastMCP Client architecture separates the protocol logic (`Client`) from the connection mechanism (`Transport`).
|
||||
- **Testing MCP servers** during development
|
||||
- **Building deterministic applications** that need reliable MCP interactions
|
||||
- **Creating the foundation for agentic or LLM-based clients** with structured, type-safe operations
|
||||
|
||||
- **`Client`**: Handles sending MCP requests (like `tools/call`, `resources/read`), receiving responses, and managing callbacks.
|
||||
- **`Transport`**: Responsible for establishing and maintaining the connection to the server (e.g., via WebSockets, SSE, Stdio, or in-memory).
|
||||
All client operations require using the `async with` context manager for proper connection lifecycle management.
|
||||
|
||||
### Transports
|
||||
|
||||
Clients must be initialized with a `transport`. You can either provide an already instantiated transport object, or provide a transport source and let FastMCP attempt to infer the correct transport to use.
|
||||
<Note>
|
||||
This is not an agentic client - it requires explicit function calls and provides direct control over all MCP operations. Use it as a building block for higher-level systems.
|
||||
</Note>
|
||||
|
||||
The following inference rules are used to determine the appropriate `ClientTransport` based on the input type:
|
||||
## Creating a Client
|
||||
|
||||
1. **`ClientTransport` Instance**: If you provide an already instantiated transport object, it's used directly.
|
||||
2. **`FastMCP` Instance**: Creates a `FastMCPTransport` for efficient in-memory communication (ideal for testing). This also works with a **FastMCP 1.0 server** created via `mcp.server.fastmcp.FastMCP`.
|
||||
3. **`Path` or `str` pointing to an existing file**:
|
||||
* If it ends with `.py`: Creates a `PythonStdioTransport` to run the script using `python`.
|
||||
* If it ends with `.js`: Creates a `NodeStdioTransport` to run the script using `node`.
|
||||
4. **`AnyUrl` or `str` pointing to a URL that begins with `http://` or `https://`**:
|
||||
* Creates a `StreamableHttpTransport`
|
||||
5. **`MCPConfig` or dictionary matching MCPConfig schema**: Creates a client that connects to one or more MCP servers specified in the config.
|
||||
6. **Other**: Raises a `ValueError` if the type cannot be inferred.
|
||||
Creating a client is straightforward. You provide a server source and the client automatically infers the appropriate transport mechanism.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from fastmcp import Client, FastMCP
|
||||
|
||||
# Example transports (more details in Transports page)
|
||||
server_instance = FastMCP(name="TestServer") # In-memory server
|
||||
http_url = "https://example.com/mcp" # HTTP server URL
|
||||
server_script = "my_mcp_server.py" # Path to a Python server file
|
||||
# In-memory server (ideal for testing)
|
||||
server = FastMCP("TestServer")
|
||||
client = Client(server)
|
||||
|
||||
# Client automatically infers the transport type
|
||||
client_in_memory = Client(server_instance)
|
||||
client_http = Client(http_url)
|
||||
# HTTP server
|
||||
client = Client("https://example.com/mcp")
|
||||
|
||||
client_stdio = Client(server_script)
|
||||
# Local Python script
|
||||
client = Client("my_mcp_server.py")
|
||||
|
||||
print(client_in_memory.transport)
|
||||
print(client_http.transport)
|
||||
print(client_stdio.transport)
|
||||
async def main():
|
||||
async with client:
|
||||
# Basic server interaction
|
||||
await client.ping()
|
||||
|
||||
# List available operations
|
||||
tools = await client.list_tools()
|
||||
resources = await client.list_resources()
|
||||
prompts = await client.list_prompts()
|
||||
|
||||
# Execute operations
|
||||
result = await client.call_tool("example_tool", {"param": "value"})
|
||||
print(result)
|
||||
|
||||
# Expected Output (types may vary slightly based on environment):
|
||||
# <FastMCP(server='TestServer')>
|
||||
# <StreamableHttp(url='https://example.com/mcp')>
|
||||
# <PythonStdioTransport(command='python', args=['/path/to/your/my_mcp_server.py'])>
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
You can also initialize a client from an MCP configuration dictionary or `MCPConfig` file:
|
||||
## Client-Transport Architecture
|
||||
|
||||
The FastMCP Client separates concerns between protocol and connection:
|
||||
|
||||
- **`Client`**: Handles MCP protocol operations (tools, resources, prompts) and manages callbacks
|
||||
- **`Transport`**: Establishes and maintains the connection (WebSockets, HTTP, Stdio, in-memory)
|
||||
|
||||
### Transport Inference
|
||||
|
||||
The client automatically infers the appropriate transport based on the input:
|
||||
|
||||
1. **`FastMCP` instance** → In-memory transport (perfect for testing)
|
||||
2. **File path ending in `.py`** → Python Stdio transport
|
||||
3. **File path ending in `.js`** → Node.js Stdio transport
|
||||
4. **URL starting with `http://` or `https://`** → HTTP transport
|
||||
5. **`MCPConfig` dictionary** → Multi-server client
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp import Client, FastMCP
|
||||
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"local": {"command": "python", "args": ["local_server.py"]},
|
||||
"remote": {"url": "https://example.com/mcp"},
|
||||
}
|
||||
}
|
||||
|
||||
client_config = Client(config)
|
||||
# Examples of transport inference
|
||||
client_memory = Client(FastMCP("TestServer"))
|
||||
client_script = Client("./server.py")
|
||||
client_http = Client("https://api.example.com/mcp")
|
||||
```
|
||||
|
||||
<Tip>
|
||||
For more control over connection details (like headers for SSE, environment variables for Stdio), you can instantiate the specific `ClientTransport` class yourself and pass it to the `Client`. See the [Transports](/clients/transports) page for details.
|
||||
For testing and development, always prefer the in-memory transport by passing a `FastMCP` server directly to the client. This eliminates network complexity and separate processes.
|
||||
</Tip>
|
||||
|
||||
### Multi-Server Clients
|
||||
## Configuration-Based Clients
|
||||
|
||||
<VersionBadge version="2.4.0" />
|
||||
|
||||
FastMCP supports creating clients that connect to multiple MCP servers through a single client interface using a standard MCP configuration format (`MCPConfig`). This configuration approach makes it easy to connect to multiple specialized servers or create composable systems with a simple, declarative syntax.
|
||||
Create clients from MCP configuration dictionaries, which can include multiple servers. While there is no official standard for MCP configuration format, FastMCP follows established conventions used by tools like Claude Desktop.
|
||||
|
||||
<Note>
|
||||
The MCP configuration format follows an emerging standard and may evolve as the specification matures. FastMCP will strive to maintain compatibility with future versions, but be aware that field names or structure might change.
|
||||
</Note>
|
||||
|
||||
When you create a client with an `MCPConfig` containing multiple servers:
|
||||
|
||||
1. FastMCP creates a composite client that internally mounts all servers using their config names as prefixes
|
||||
2. Tools and resources from each server are accessible with appropriate prefixes in the format `servername_toolname` and `protocol://servername/resource/path`
|
||||
3. You interact with this as a single unified client, with requests automatically routed to the appropriate server
|
||||
### Configuration Format
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
# Create a standard MCP configuration with multiple servers
|
||||
config = {
|
||||
"mcpServers": {
|
||||
# A remote HTTP server
|
||||
"weather": {
|
||||
"url": "https://weather-api.example.com/mcp",
|
||||
"transport": "streamable-http"
|
||||
"server_name": {
|
||||
# Remote HTTP/SSE server
|
||||
"transport": "streamable-http", # or "sse"
|
||||
"url": "https://api.example.com/mcp",
|
||||
"headers": {"Authorization": "Bearer token"},
|
||||
"auth": "oauth" # or bearer token string
|
||||
},
|
||||
# A local server running via stdio
|
||||
"assistant": {
|
||||
"local_server": {
|
||||
# Local stdio server
|
||||
"transport": "stdio"
|
||||
"command": "python",
|
||||
"args": ["./my_assistant_server.py"],
|
||||
"env": {"DEBUG": "true"}
|
||||
"args": ["./server.py", "--verbose"],
|
||||
"env": {"DEBUG": "true"},
|
||||
"cwd": "/path/to/server",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Server Example
|
||||
|
||||
```python
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {"url": "https://weather-api.example.com/mcp"},
|
||||
"assistant": {"command": "python", "args": ["./assistant_server.py"]}
|
||||
}
|
||||
}
|
||||
|
||||
# Create a client that connects to both servers
|
||||
client = Client(config)
|
||||
|
||||
async def main():
|
||||
async with client:
|
||||
# Access tools from different servers with prefixes
|
||||
weather_data = await client.call_tool("weather_get_forecast", {"city": "London"})
|
||||
response = await client.call_tool("assistant_answer_question", {"question": "What's the capital of France?"})
|
||||
|
||||
# Access resources with prefixed URIs
|
||||
weather_icons = await client.read_resource("weather://weather/icons/sunny")
|
||||
templates = await client.read_resource("resource://assistant/templates/list")
|
||||
|
||||
print(f"Weather: {weather_data}")
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
async with client:
|
||||
# Tools are prefixed with server names
|
||||
weather_data = await client.call_tool("weather_get_forecast", {"city": "London"})
|
||||
response = await client.call_tool("assistant_answer_question", {"question": "What's the capital of France?"})
|
||||
|
||||
# Resources use prefixed URIs
|
||||
icons = await client.read_resource("weather://weather/icons/sunny")
|
||||
templates = await client.read_resource("resource://assistant/templates/list")
|
||||
```
|
||||
|
||||
If your configuration has only a single server, FastMCP will create a direct client to that server without any prefixing.
|
||||
## Connection Lifecycle
|
||||
|
||||
## Client Usage
|
||||
|
||||
### Connection Lifecycle
|
||||
|
||||
The client operates asynchronously and must be used within an `async with` block. This context manager handles establishing the connection, initializing the MCP session, and cleaning up resources upon exit.
|
||||
The client operates asynchronously and uses context managers for connection management:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from fastmcp import Client
|
||||
|
||||
client = Client("my_mcp_server.py") # Assumes my_mcp_server.py exists
|
||||
|
||||
async def main():
|
||||
# Connection is established here
|
||||
async def example():
|
||||
client = Client("my_mcp_server.py")
|
||||
|
||||
# Connection established here
|
||||
async with client:
|
||||
print(f"Client connected: {client.is_connected()}")
|
||||
|
||||
# Make MCP calls within the context
|
||||
print(f"Connected: {client.is_connected()}")
|
||||
|
||||
# Make multiple calls within the same session
|
||||
tools = await client.list_tools()
|
||||
print(f"Available tools: {tools}")
|
||||
|
||||
if any(tool.name == "greet" for tool in tools):
|
||||
result = await client.call_tool("greet", {"name": "World"})
|
||||
print(f"Greet result: {result}")
|
||||
|
||||
# Connection is closed automatically here
|
||||
print(f"Client connected: {client.is_connected()}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
result = await client.call_tool("greet", {"name": "World"})
|
||||
|
||||
# Connection closed automatically here
|
||||
print(f"Connected: {client.is_connected()}")
|
||||
```
|
||||
|
||||
You can make multiple calls to the server within the same `async with` block using the established session.
|
||||
## Operations
|
||||
|
||||
### Client Methods
|
||||
FastMCP clients can interact with several types of server components:
|
||||
|
||||
The `Client` provides methods corresponding to standard MCP requests:
|
||||
### Tools
|
||||
|
||||
<Warning>
|
||||
The standard client methods return user-friendly representations that may change as the protocol evolves. For consistent access to the complete data structure, use the `*_mcp` methods described later.
|
||||
</Warning>
|
||||
|
||||
#### Tool Operations
|
||||
|
||||
* **`list_tools()`**: Retrieves a list of tools available on the server.
|
||||
```python
|
||||
tools = await client.list_tools()
|
||||
# tools -> list[mcp.types.Tool]
|
||||
```
|
||||
* **`call_tool(name: str, arguments: dict[str, Any] | None = None, timeout: float | None = None, progress_handler: ProgressHandler | None = None)`**: Executes a tool on the server.
|
||||
```python
|
||||
result = await client.call_tool("add", {"a": 5, "b": 3})
|
||||
# result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
|
||||
print(result[0].text) # Assuming TextContent, e.g., '8'
|
||||
|
||||
# With timeout (aborts if execution takes longer than 2 seconds)
|
||||
result = await client.call_tool("long_running_task", {"param": "value"}, timeout=2.0)
|
||||
|
||||
# With progress handler (to track execution progress)
|
||||
result = await client.call_tool(
|
||||
"long_running_task",
|
||||
{"param": "value"},
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
* Arguments are passed as a dictionary. FastMCP servers automatically handle JSON string parsing for complex types if needed.
|
||||
* Returns a list of content objects (usually `TextContent` or `ImageContent`).
|
||||
* The optional `timeout` parameter limits the maximum execution time (in seconds) for this specific call, overriding any client-level timeout.
|
||||
* The optional `progress_handler` parameter receives progress updates during execution, overriding any client-level progress handler.
|
||||
|
||||
#### Resource Operations
|
||||
|
||||
* **`list_resources()`**: Retrieves a list of static resources.
|
||||
```python
|
||||
resources = await client.list_resources()
|
||||
# resources -> list[mcp.types.Resource]
|
||||
```
|
||||
* **`list_resource_templates()`**: Retrieves a list of resource templates.
|
||||
```python
|
||||
templates = await client.list_resource_templates()
|
||||
# templates -> list[mcp.types.ResourceTemplate]
|
||||
```
|
||||
* **`read_resource(uri: str | AnyUrl)`**: Reads the content of a resource or a resolved template.
|
||||
```python
|
||||
# Read a static resource
|
||||
readme_content = await client.read_resource("file:///path/to/README.md")
|
||||
# readme_content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
|
||||
print(readme_content[0].text) # Assuming text
|
||||
|
||||
# Read a resource generated from a template
|
||||
weather_content = await client.read_resource("data://weather/london")
|
||||
print(weather_content[0].text) # Assuming text JSON
|
||||
```
|
||||
|
||||
#### Prompt Operations
|
||||
|
||||
* **`list_prompts()`**: Retrieves available prompt templates.
|
||||
* **`get_prompt(name: str, arguments: dict[str, Any] | None = None)`**: Retrieves a rendered prompt message list.
|
||||
|
||||
### Raw MCP Protocol Objects
|
||||
|
||||
<VersionBadge version="2.2.7" />
|
||||
|
||||
The FastMCP client attempts to provide a "friendly" interface to the MCP protocol, but sometimes you may need access to the raw MCP protocol objects. Each of the main client methods that returns data has a corresponding `*_mcp` method that returns the raw MCP protocol objects directly.
|
||||
|
||||
<Warning>
|
||||
The standard client methods (without `_mcp`) return user-friendly representations of MCP data, while `*_mcp` methods will always return the complete MCP protocol objects. As the protocol evolves, changes to these user-friendly representations may occur and could potentially be breaking. If you need consistent, stable access to the full data structure, prefer using the `*_mcp` methods.
|
||||
</Warning>
|
||||
Tools are server-side functions that the client can execute with arguments.
|
||||
|
||||
```python
|
||||
# Standard method - returns just the list of tools
|
||||
tools = await client.list_tools()
|
||||
# tools -> list[mcp.types.Tool]
|
||||
|
||||
# Raw MCP method - returns the full protocol object
|
||||
result = await client.list_tools_mcp()
|
||||
# result -> mcp.types.ListToolsResult
|
||||
tools = result.tools
|
||||
async with client:
|
||||
# List available tools
|
||||
tools = await client.list_tools()
|
||||
|
||||
# Execute a tool
|
||||
result = await client.call_tool("multiply", {"a": 5, "b": 3})
|
||||
print(result[0].text) # "15"
|
||||
```
|
||||
|
||||
Available raw MCP methods:
|
||||
See [Tools](/clients/tools) for detailed documentation.
|
||||
|
||||
* **`list_tools_mcp()`**: Returns `mcp.types.ListToolsResult`
|
||||
* **`call_tool_mcp(name, arguments)`**: Returns `mcp.types.CallToolResult`
|
||||
* **`list_resources_mcp()`**: Returns `mcp.types.ListResourcesResult`
|
||||
* **`list_resource_templates_mcp()`**: Returns `mcp.types.ListResourceTemplatesResult`
|
||||
* **`read_resource_mcp(uri)`**: Returns `mcp.types.ReadResourceResult`
|
||||
* **`list_prompts_mcp()`**: Returns `mcp.types.ListPromptsResult`
|
||||
* **`get_prompt_mcp(name, arguments)`**: Returns `mcp.types.GetPromptResult`
|
||||
* **`complete_mcp(ref, argument)`**: Returns `mcp.types.CompleteResult`
|
||||
### Resources
|
||||
|
||||
These methods are especially useful for debugging or when you need to access metadata or fields that aren't exposed by the simplified methods.
|
||||
Resources are data sources that the client can read, either static or templated.
|
||||
|
||||
### Additional Features
|
||||
```python
|
||||
async with client:
|
||||
# List available resources
|
||||
resources = await client.list_resources()
|
||||
|
||||
# Read a resource
|
||||
content = await client.read_resource("file:///config/settings.json")
|
||||
print(content[0].text)
|
||||
```
|
||||
|
||||
#### Pinging the Server
|
||||
See [Resources](/clients/resources) for detailed documentation.
|
||||
|
||||
The client can be used to ping the server to verify connectivity.
|
||||
### Prompts
|
||||
|
||||
Prompts are reusable message templates that can accept arguments.
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# List available prompts
|
||||
prompts = await client.list_prompts()
|
||||
|
||||
# Get a rendered prompt
|
||||
messages = await client.get_prompt("analyze_data", {"data": [1, 2, 3]})
|
||||
print(messages.messages)
|
||||
```
|
||||
|
||||
See [Prompts](/clients/prompts) for detailed documentation.
|
||||
|
||||
### Server Connectivity
|
||||
|
||||
Use `ping()` to verify the server is reachable:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
|
|
@ -280,93 +223,69 @@ async with client:
|
|||
print("Server is reachable")
|
||||
```
|
||||
|
||||
#### Session Management
|
||||
## Client Configuration
|
||||
|
||||
When using stdio transports, clients support a `keep_alive` feature (enabled by default) that maintains subprocess sessions between connection contexts. You can manually control this behavior using the client's `close()` method.
|
||||
Clients can be configured with additional handlers and settings for specialized use cases.
|
||||
|
||||
When `keep_alive=False`, the client will automatically close the session when the context manager exits.
|
||||
### Callback Handlers
|
||||
|
||||
The client supports several callback handlers for advanced server interactions:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.logging import LogMessage
|
||||
|
||||
client = Client("my_mcp_server.py") # keep_alive=True by default
|
||||
async def log_handler(message: LogMessage):
|
||||
print(f"Server log: {message.data}")
|
||||
|
||||
async def example():
|
||||
async with client:
|
||||
await client.ping()
|
||||
|
||||
async with client:
|
||||
await client.ping() # Same subprocess as above
|
||||
```
|
||||
async def progress_handler(progress: float, total: float | None, message: str | None):
|
||||
print(f"Progress: {progress}/{total} - {message}")
|
||||
|
||||
<Note>
|
||||
For detailed examples and configuration options, see [Session Management in Transports](/clients/transports#session-management).
|
||||
</Note>
|
||||
async def sampling_handler(messages, params, context):
|
||||
# Integrate with your LLM service here
|
||||
return "Generated response"
|
||||
|
||||
#### Timeouts
|
||||
|
||||
<VersionBadge version="2.3.4" />
|
||||
|
||||
You can control request timeouts at both the client level and individual request level:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.exceptions import McpError
|
||||
|
||||
# Client with a global 5-second timeout for all requests
|
||||
client = Client(
|
||||
my_mcp_server,
|
||||
timeout=5.0 # Default timeout in seconds
|
||||
"my_mcp_server.py",
|
||||
log_handler=log_handler,
|
||||
progress_handler=progress_handler,
|
||||
sampling_handler=sampling_handler,
|
||||
timeout=30.0
|
||||
)
|
||||
|
||||
async with client:
|
||||
# This uses the global 5-second timeout
|
||||
result1 = await client.call_tool("quick_task", {"param": "value"})
|
||||
|
||||
# This specifies a 10-second timeout for this specific call
|
||||
result2 = await client.call_tool("slow_task", {"param": "value"}, timeout=10.0)
|
||||
|
||||
try:
|
||||
# This will likely timeout
|
||||
result3 = await client.call_tool("medium_task", {"param": "value"}, timeout=0.01)
|
||||
except McpError as e:
|
||||
# Handle timeout error
|
||||
print(f"The task timed out: {e}")
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Timeout behavior varies between transport types:
|
||||
The `Client` constructor accepts several configuration options:
|
||||
|
||||
- With **SSE** transport, the per-request (tool call) timeout **always** takes precedence, regardless of which is lower.
|
||||
- With **HTTP** transport, the **lower** of the two timeouts (client or tool call) takes precedence.
|
||||
- `transport`: Transport instance or source for automatic inference
|
||||
- `log_handler`: Handle server log messages
|
||||
- `progress_handler`: Monitor long-running operations
|
||||
- `sampling_handler`: Respond to server LLM requests
|
||||
- `roots`: Provide local context to servers
|
||||
- `timeout`: Default timeout for requests (in seconds)
|
||||
|
||||
For consistent behavior across all transports, we recommend explicitly setting timeouts at the individual tool call level when needed, rather than relying on client-level timeouts.
|
||||
</Warning>
|
||||
### Transport Configuration
|
||||
|
||||
#### Error Handling
|
||||
For detailed transport configuration (headers, authentication, environment variables), see the [Transports](/clients/transports) documentation.
|
||||
|
||||
When a `call_tool` request results in an error on the server (e.g., the tool function raised an exception), the `client.call_tool()` method will raise a `fastmcp.exceptions.ClientError`.
|
||||
## Next Steps
|
||||
|
||||
```python
|
||||
async def safe_call_tool():
|
||||
async with client:
|
||||
try:
|
||||
# Assume 'divide' tool exists and might raise ZeroDivisionError
|
||||
result = await client.call_tool("divide", {"a": 10, "b": 0})
|
||||
print(f"Result: {result}")
|
||||
except ClientError as e:
|
||||
print(f"Tool call failed: {e}")
|
||||
except ConnectionError as e:
|
||||
print(f"Connection failed: {e}")
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
Explore the detailed documentation for each operation type:
|
||||
|
||||
# Example Output if division by zero occurs:
|
||||
# Tool call failed: Division by zero is not allowed.
|
||||
```
|
||||
### Core Operations
|
||||
- **[Tools](/clients/tools)** - Execute server-side functions and handle results
|
||||
- **[Resources](/clients/resources)** - Access static and templated resources
|
||||
- **[Prompts](/clients/prompts)** - Work with message templates and argument serialization
|
||||
|
||||
Other errors, like connection failures, will raise standard Python exceptions (e.g., `ConnectionError`, `TimeoutError`).
|
||||
### Advanced Features
|
||||
- **[Logging](/clients/logging)** - Handle server log messages
|
||||
- **[Progress](/clients/progress)** - Monitor long-running operations
|
||||
- **[Sampling](/clients/sampling)** - Respond to server LLM requests
|
||||
- **[Roots](/clients/roots)** - Provide local context to servers
|
||||
|
||||
### Connection Details
|
||||
- **[Transports](/clients/transports)** - Configure connection methods and parameters
|
||||
- **[Authentication](/clients/auth/oauth)** - Set up OAuth and bearer token authentication
|
||||
|
||||
<Tip>
|
||||
The client transport often has its own error-handling mechanisms, so you can not always trap errors like those raised by `call_tool` outside of the `async with` block. Instead, you can use `call_tool_mcp()` to get the raw `mcp.types.CallToolResult` object and handle errors yourself by checking its `isError` attribute.
|
||||
</Tip>
|
||||
The FastMCP Client is designed as a foundational tool. Use it directly for deterministic operations, or build higher-level agentic systems on top of its reliable, type-safe interface.
|
||||
</Tip>
|
||||
63
docs/clients/logging.mdx
Normal file
63
docs/clients/logging.mdx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
title: Server Logging
|
||||
sidebarTitle: Logging
|
||||
description: Receive and handle log messages from MCP servers.
|
||||
icon: receipt
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
MCP servers can emit log messages to clients. The client can handle these logs through a log handler callback.
|
||||
|
||||
## Setting Up Log Handling
|
||||
|
||||
Provide a `log_handler` function when creating the client:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.logging import LogMessage
|
||||
|
||||
async def log_handler(message: LogMessage):
|
||||
level = message.level.upper()
|
||||
logger = message.logger or 'server'
|
||||
data = message.data
|
||||
print(f"[{level}] {logger}: {data}")
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
log_handler=log_handler,
|
||||
)
|
||||
```
|
||||
|
||||
## LogMessage Structure
|
||||
|
||||
The `log_handler` receives a `LogMessage` object with:
|
||||
|
||||
- **`level`**: Log level (e.g., "debug", "info", "warning", "error")
|
||||
- **`logger`**: Logger name (optional, may be None)
|
||||
- **`data`**: The actual log message content
|
||||
|
||||
```python
|
||||
async def detailed_log_handler(message: LogMessage):
|
||||
if message.level == "error":
|
||||
print(f"ERROR: {message.data}")
|
||||
elif message.level == "warning":
|
||||
print(f"WARNING: {message.data}")
|
||||
else:
|
||||
print(f"{message.level.upper()}: {message.data}")
|
||||
```
|
||||
|
||||
## Default Log Handling
|
||||
|
||||
If you don't provide a custom `log_handler`, FastMCP uses a default handler that emits DEBUG level 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
|
||||
await client.call_tool("some_tool")
|
||||
```
|
||||
59
docs/clients/progress.mdx
Normal file
59
docs/clients/progress.mdx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
title: Progress Monitoring
|
||||
sidebarTitle: Progress
|
||||
description: Handle progress notifications from long-running server operations.
|
||||
icon: bars-progress
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.3.5" />
|
||||
|
||||
MCP servers can report progress during long-running operations. The client can receive these updates through a progress handler.
|
||||
|
||||
## Setting Up Progress Handling
|
||||
|
||||
Set a progress handler when creating the client:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
async def my_progress_handler(
|
||||
progress: float,
|
||||
total: float | None,
|
||||
message: str | None
|
||||
) -> None:
|
||||
if total is not None:
|
||||
percentage = (progress / total) * 100
|
||||
print(f"Progress: {percentage:.1f}% - {message or ''}")
|
||||
else:
|
||||
print(f"Progress: {progress} - {message or ''}")
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
|
||||
## Per-Call Progress Handler
|
||||
|
||||
Override the progress handler for specific tool calls:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Override with specific progress handler for this call
|
||||
result = await client.call_tool(
|
||||
"long_running_task",
|
||||
{"param": "value"},
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
|
||||
## 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)
|
||||
|
||||
187
docs/clients/prompts.mdx
Normal file
187
docs/clients/prompts.mdx
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
---
|
||||
title: Prompts
|
||||
sidebarTitle: Prompts
|
||||
description: Use server-side prompt templates with automatic argument serialization.
|
||||
icon: message-lines
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Prompts are reusable message templates exposed by MCP servers. They can accept arguments to generate personalized message sequences for LLM interactions.
|
||||
|
||||
## Listing Prompts
|
||||
|
||||
Use `list_prompts()` to retrieve all available prompt templates:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
prompts = await client.list_prompts()
|
||||
# prompts -> list[mcp.types.Prompt]
|
||||
|
||||
for prompt in prompts:
|
||||
print(f"Prompt: {prompt.name}")
|
||||
print(f"Description: {prompt.description}")
|
||||
if prompt.arguments:
|
||||
print(f"Arguments: {[arg.name for arg in prompt.arguments]}")
|
||||
```
|
||||
|
||||
## Using Prompts
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Request a rendered prompt using `get_prompt()` with the prompt name and arguments:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Simple prompt without arguments
|
||||
result = await client.get_prompt("welcome_message")
|
||||
# result -> mcp.types.GetPromptResult
|
||||
|
||||
# Access the generated messages
|
||||
for message in result.messages:
|
||||
print(f"Role: {message.role}")
|
||||
print(f"Content: {message.content}")
|
||||
```
|
||||
|
||||
### Prompts with Arguments
|
||||
|
||||
Pass arguments as a dictionary to customize the prompt:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Prompt with simple arguments
|
||||
result = await client.get_prompt("user_greeting", {
|
||||
"name": "Alice",
|
||||
"role": "administrator"
|
||||
})
|
||||
|
||||
# Access the personalized messages
|
||||
for message in result.messages:
|
||||
print(f"Generated message: {message.content}")
|
||||
```
|
||||
|
||||
## Automatic Argument Serialization
|
||||
|
||||
<VersionBadge version="2.9.0" />
|
||||
|
||||
FastMCP automatically serializes complex arguments to JSON strings as required by the MCP specification. This allows you to pass typed objects directly:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class UserData:
|
||||
name: str
|
||||
age: int
|
||||
|
||||
async with client:
|
||||
# Complex arguments are automatically serialized
|
||||
result = await client.get_prompt("analyze_user", {
|
||||
"user": UserData(name="Alice", age=30), # Automatically serialized to JSON
|
||||
"preferences": {"theme": "dark"}, # Dict serialized to JSON string
|
||||
"scores": [85, 92, 78], # List serialized to JSON string
|
||||
"simple_name": "Bob" # Strings passed through unchanged
|
||||
})
|
||||
```
|
||||
|
||||
The client handles serialization using `pydantic_core.to_json()` for consistent formatting. FastMCP servers can automatically deserialize these JSON strings back to the expected types.
|
||||
|
||||
### Serialization Examples
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.get_prompt("data_analysis", {
|
||||
# These will be automatically serialized to JSON strings:
|
||||
"config": {
|
||||
"format": "csv",
|
||||
"include_headers": True,
|
||||
"delimiter": ","
|
||||
},
|
||||
"filters": [
|
||||
{"field": "age", "operator": ">", "value": 18},
|
||||
{"field": "status", "operator": "==", "value": "active"}
|
||||
],
|
||||
# This remains a string:
|
||||
"report_title": "Monthly Analytics Report"
|
||||
})
|
||||
```
|
||||
|
||||
## Working with Prompt Results
|
||||
|
||||
The `get_prompt()` method returns a `GetPromptResult` object containing a list of messages:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.get_prompt("conversation_starter", {"topic": "climate"})
|
||||
|
||||
# Access individual messages
|
||||
for i, message in enumerate(result.messages):
|
||||
print(f"Message {i + 1}:")
|
||||
print(f" Role: {message.role}")
|
||||
print(f" Content: {message.content.text if hasattr(message.content, 'text') else message.content}")
|
||||
```
|
||||
|
||||
## Raw MCP Protocol Access
|
||||
|
||||
For access to the complete MCP protocol objects, use the `*_mcp` methods:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Raw MCP method returns full protocol object
|
||||
prompts_result = await client.list_prompts_mcp()
|
||||
# prompts_result -> mcp.types.ListPromptsResult
|
||||
|
||||
prompt_result = await client.get_prompt_mcp("example_prompt", {"arg": "value"})
|
||||
# prompt_result -> mcp.types.GetPromptResult
|
||||
```
|
||||
|
||||
## Multi-Server Clients
|
||||
|
||||
When using multi-server clients, prompts are accessible without prefixing (unlike tools):
|
||||
|
||||
```python
|
||||
async with client: # Multi-server client
|
||||
# Prompts from any server are directly accessible
|
||||
result1 = await client.get_prompt("weather_prompt", {"city": "London"})
|
||||
result2 = await client.get_prompt("assistant_prompt", {"query": "help"})
|
||||
```
|
||||
|
||||
## Common Prompt Patterns
|
||||
|
||||
### System Messages
|
||||
|
||||
Many prompts generate system messages for LLM configuration:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.get_prompt("system_configuration", {
|
||||
"role": "helpful assistant",
|
||||
"expertise": "python programming"
|
||||
})
|
||||
|
||||
# Typically returns messages with role="system"
|
||||
system_message = result.messages[0]
|
||||
print(f"System prompt: {system_message.content}")
|
||||
```
|
||||
|
||||
### Conversation Templates
|
||||
|
||||
Prompts can generate multi-turn conversation templates:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.get_prompt("interview_template", {
|
||||
"candidate_name": "Alice",
|
||||
"position": "Senior Developer"
|
||||
})
|
||||
|
||||
# Multiple messages for a conversation flow
|
||||
for message in result.messages:
|
||||
print(f"{message.role}: {message.content}")
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Prompt arguments and their expected types depend on the specific prompt implementation. Check the server's documentation or use `list_prompts()` to see available arguments for each prompt.
|
||||
</Tip>
|
||||
171
docs/clients/resources.mdx
Normal file
171
docs/clients/resources.mdx
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
---
|
||||
title: Resource Operations
|
||||
sidebarTitle: Resources
|
||||
description: Access static and templated resources from MCP servers.
|
||||
icon: folder-open
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Resources are data sources exposed by MCP servers. They can be static files or dynamic templates that generate content based on parameters.
|
||||
|
||||
## Types of Resources
|
||||
|
||||
MCP servers expose two types of resources:
|
||||
|
||||
- **Static Resources**: Fixed content accessible via URI (e.g., configuration files, documentation)
|
||||
- **Resource Templates**: Dynamic resources that accept parameters to generate content (e.g., API endpoints, database queries)
|
||||
|
||||
## Listing Resources
|
||||
|
||||
### Static Resources
|
||||
|
||||
Use `list_resources()` to retrieve all static resources available on the server:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
resources = await client.list_resources()
|
||||
# resources -> list[mcp.types.Resource]
|
||||
|
||||
for resource in resources:
|
||||
print(f"Resource URI: {resource.uri}")
|
||||
print(f"Name: {resource.name}")
|
||||
print(f"Description: {resource.description}")
|
||||
print(f"MIME Type: {resource.mimeType}")
|
||||
```
|
||||
|
||||
### Resource Templates
|
||||
|
||||
Use `list_resource_templates()` to retrieve available resource templates:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
templates = await client.list_resource_templates()
|
||||
# templates -> list[mcp.types.ResourceTemplate]
|
||||
|
||||
for template in templates:
|
||||
print(f"Template URI: {template.uriTemplate}")
|
||||
print(f"Name: {template.name}")
|
||||
print(f"Description: {template.description}")
|
||||
```
|
||||
|
||||
## Reading Resources
|
||||
|
||||
### Static Resources
|
||||
|
||||
Read a static resource using its URI:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Read a static resource
|
||||
content = await client.read_resource("file:///path/to/README.md")
|
||||
# content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
|
||||
|
||||
# Access text content
|
||||
if hasattr(content[0], 'text'):
|
||||
print(content[0].text)
|
||||
|
||||
# Access binary content
|
||||
if hasattr(content[0], 'blob'):
|
||||
print(f"Binary data: {len(content[0].blob)} bytes")
|
||||
```
|
||||
|
||||
### Resource Templates
|
||||
|
||||
Read from a resource template by providing the URI with parameters:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Read a resource generated from a template
|
||||
# For example, a template like "weather://{{city}}/current"
|
||||
weather_content = await client.read_resource("weather://london/current")
|
||||
|
||||
# Access the generated content
|
||||
print(weather_content[0].text) # Assuming text JSON response
|
||||
```
|
||||
|
||||
## Content Types
|
||||
|
||||
Resources can return different content types:
|
||||
|
||||
### Text Resources
|
||||
|
||||
```python
|
||||
async with client:
|
||||
content = await client.read_resource("resource://config/settings.json")
|
||||
|
||||
for item in content:
|
||||
if hasattr(item, 'text'):
|
||||
print(f"Text content: {item.text}")
|
||||
print(f"MIME type: {item.mimeType}")
|
||||
```
|
||||
|
||||
### Binary Resources
|
||||
|
||||
```python
|
||||
async with client:
|
||||
content = await client.read_resource("resource://images/logo.png")
|
||||
|
||||
for item in content:
|
||||
if hasattr(item, 'blob'):
|
||||
print(f"Binary content: {len(item.blob)} bytes")
|
||||
print(f"MIME type: {item.mimeType}")
|
||||
|
||||
# Save to file
|
||||
with open("downloaded_logo.png", "wb") as f:
|
||||
f.write(item.blob)
|
||||
```
|
||||
|
||||
## Working with Multi-Server Clients
|
||||
|
||||
When using multi-server clients, resource URIs are automatically prefixed with the server name:
|
||||
|
||||
```python
|
||||
async with client: # Multi-server client
|
||||
# Access resources from different servers
|
||||
weather_icons = await client.read_resource("weather://weather/icons/sunny")
|
||||
templates = await client.read_resource("resource://assistant/templates/list")
|
||||
|
||||
print(f"Weather icon: {weather_icons[0].blob}")
|
||||
print(f"Templates: {templates[0].text}")
|
||||
```
|
||||
|
||||
## Raw MCP Protocol Access
|
||||
|
||||
For access to the complete MCP protocol objects, use the `*_mcp` methods:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Raw MCP methods return full protocol objects
|
||||
resources_result = await client.list_resources_mcp()
|
||||
# resources_result -> mcp.types.ListResourcesResult
|
||||
|
||||
templates_result = await client.list_resource_templates_mcp()
|
||||
# templates_result -> mcp.types.ListResourceTemplatesResult
|
||||
|
||||
content_result = await client.read_resource_mcp("resource://example")
|
||||
# content_result -> mcp.types.ReadResourceResult
|
||||
```
|
||||
|
||||
## Common Resource URI Patterns
|
||||
|
||||
Different MCP servers may use various URI schemes:
|
||||
|
||||
```python
|
||||
# File system resources
|
||||
"file:///path/to/file.txt"
|
||||
|
||||
# Custom protocol resources
|
||||
"weather://london/current"
|
||||
"database://users/123"
|
||||
|
||||
# Generic resource protocol
|
||||
"resource://config/settings"
|
||||
"resource://templates/email"
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Resource URIs and their formats depend on the specific MCP server implementation. Check the server's documentation for available resources and their URI patterns.
|
||||
</Tip>
|
||||
42
docs/clients/roots.mdx
Normal file
42
docs/clients/roots.mdx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
title: Client Roots
|
||||
sidebarTitle: Roots
|
||||
description: Provide local context and resource boundaries to MCP servers.
|
||||
icon: folder-tree
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Roots are a way for clients to inform servers about the resources they have access to. Servers can use this information to adjust behavior or provide more relevant responses.
|
||||
|
||||
## Setting Static Roots
|
||||
|
||||
Provide a list of roots when creating the client:
|
||||
|
||||
<CodeGroup>
|
||||
```python Static Roots
|
||||
from fastmcp import Client
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
roots=["/path/to/root1", "/path/to/root2"]
|
||||
)
|
||||
```
|
||||
|
||||
```python Dynamic Roots Callback
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.roots import RequestContext
|
||||
|
||||
async def roots_callback(context: RequestContext) -> list[str]:
|
||||
print(f"Server requested roots (Request ID: {context.request_id})")
|
||||
return ["/path/to/root1", "/path/to/root2"]
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
roots=roots_callback
|
||||
)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
91
docs/clients/sampling.mdx
Normal file
91
docs/clients/sampling.mdx
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
---
|
||||
title: LLM Sampling
|
||||
sidebarTitle: Sampling
|
||||
description: Handle server-initiated LLM sampling requests.
|
||||
icon: robot
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
MCP servers can request LLM completions from clients. The client handles these requests through a sampling handler callback.
|
||||
|
||||
## Setting Up Sampling Handling
|
||||
|
||||
Provide a `sampling_handler` function when creating the client:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.sampling import (
|
||||
SamplingMessage,
|
||||
SamplingParams,
|
||||
RequestContext,
|
||||
)
|
||||
|
||||
async def sampling_handler(
|
||||
messages: list[SamplingMessage],
|
||||
params: SamplingParams,
|
||||
context: RequestContext
|
||||
) -> str:
|
||||
# Your LLM integration logic here
|
||||
# Extract text from messages and generate a response
|
||||
return "Generated response based on the messages"
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
sampling_handler=sampling_handler,
|
||||
)
|
||||
```
|
||||
|
||||
## Handler Parameters
|
||||
|
||||
The sampling handler receives three parameters:
|
||||
|
||||
### SamplingMessage
|
||||
|
||||
- **`role`**: Message role (e.g., "user", "assistant", "system")
|
||||
- **`content`**: Message content (usually has `.text` attribute)
|
||||
|
||||
### SamplingParams
|
||||
|
||||
- **`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)
|
||||
|
||||
### RequestContext
|
||||
|
||||
- **`request_id`**: Unique identifier for the sampling request
|
||||
|
||||
## Basic Example
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.sampling import SamplingMessage, SamplingParams, RequestContext
|
||||
|
||||
async def basic_sampling_handler(
|
||||
messages: list[SamplingMessage],
|
||||
params: SamplingParams,
|
||||
context: RequestContext
|
||||
) -> str:
|
||||
# Extract message content
|
||||
conversation = []
|
||||
for message in messages:
|
||||
content = message.content.text if hasattr(message.content, 'text') else str(message.content)
|
||||
conversation.append(f"{message.role}: {content}")
|
||||
|
||||
# Use the system prompt if provided
|
||||
system_prompt = params.systemPrompt or "You are a helpful assistant."
|
||||
|
||||
# Here you would integrate with your preferred LLM service
|
||||
# This is just a placeholder response
|
||||
return f"Response based on conversation: {' | '.join(conversation)}"
|
||||
|
||||
client = Client(
|
||||
"my_mcp_server.py",
|
||||
sampling_handler=basic_sampling_handler
|
||||
)
|
||||
```
|
||||
|
||||
143
docs/clients/tools.mdx
Normal file
143
docs/clients/tools.mdx
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
---
|
||||
title: Tool Operations
|
||||
sidebarTitle: Tools
|
||||
description: Discover and execute server-side tools with the FastMCP client.
|
||||
icon: wrench
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="2.0.0" />
|
||||
|
||||
Tools are executable functions exposed by MCP servers. The FastMCP client provides methods to discover available tools and execute them with arguments.
|
||||
|
||||
## Discovering Tools
|
||||
|
||||
Use `list_tools()` to retrieve all tools available on the server:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
tools = await client.list_tools()
|
||||
# tools -> list[mcp.types.Tool]
|
||||
|
||||
for tool in tools:
|
||||
print(f"Tool: {tool.name}")
|
||||
print(f"Description: {tool.description}")
|
||||
if tool.inputSchema:
|
||||
print(f"Parameters: {tool.inputSchema}")
|
||||
```
|
||||
|
||||
## Executing Tools
|
||||
|
||||
### Basic Execution
|
||||
|
||||
Execute a tool using `call_tool()` with the tool name and arguments:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Simple tool call
|
||||
result = await client.call_tool("add", {"a": 5, "b": 3})
|
||||
# result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
|
||||
|
||||
# Access the result content
|
||||
print(result[0].text) # Assuming TextContent, e.g., '8'
|
||||
```
|
||||
|
||||
### Advanced Execution Options
|
||||
|
||||
The `call_tool()` method supports additional parameters for timeout control and progress monitoring:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# With timeout (aborts if execution takes longer than 2 seconds)
|
||||
result = await client.call_tool(
|
||||
"long_running_task",
|
||||
{"param": "value"},
|
||||
timeout=2.0
|
||||
)
|
||||
|
||||
# With progress handler (to track execution progress)
|
||||
result = await client.call_tool(
|
||||
"long_running_task",
|
||||
{"param": "value"},
|
||||
progress_handler=my_progress_handler
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `name`: The tool name (string)
|
||||
- `arguments`: Dictionary of arguments to pass to the tool (optional)
|
||||
- `timeout`: Maximum execution time in seconds (optional, overrides client-level timeout)
|
||||
- `progress_handler`: Progress callback function (optional, overrides client-level handler)
|
||||
|
||||
## Handling Results
|
||||
|
||||
Tool execution returns a list of content objects. The most common types are:
|
||||
|
||||
- **`TextContent`**: Text-based results with a `.text` attribute
|
||||
- **`ImageContent`**: Image data with image-specific attributes
|
||||
- **`BlobContent`**: Binary data content
|
||||
|
||||
```python
|
||||
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")
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Exception-Based Error Handling
|
||||
|
||||
By default, `call_tool()` raises a `ToolError` if the tool execution fails:
|
||||
|
||||
```python
|
||||
from fastmcp.exceptions import ToolError
|
||||
|
||||
async with client:
|
||||
try:
|
||||
result = await client.call_tool("potentially_failing_tool", {"param": "value"})
|
||||
print("Tool succeeded:", result)
|
||||
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:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
result = await client.call_tool_mcp("potentially_failing_tool", {"param": "value"})
|
||||
# result -> mcp.types.CallToolResult
|
||||
|
||||
if result.isError:
|
||||
print(f"Tool failed: {result.content}")
|
||||
else:
|
||||
print(f"Tool succeeded: {result.content}")
|
||||
```
|
||||
|
||||
## Argument Handling
|
||||
|
||||
Arguments are passed as a dictionary to the tool:
|
||||
|
||||
```python
|
||||
async with client:
|
||||
# Simple arguments
|
||||
result = await client.call_tool("greet", {"name": "World"})
|
||||
|
||||
# Complex arguments
|
||||
result = await client.call_tool("process_data", {
|
||||
"config": {"format": "json", "validate": True},
|
||||
"items": [1, 2, 3, 4, 5],
|
||||
"metadata": {"source": "api", "version": "1.0"}
|
||||
})
|
||||
```
|
||||
|
||||
<Tip>
|
||||
For multi-server clients, tool names are automatically prefixed with the server name (e.g., `weather_get_forecast` for a tool named `get_forecast` on the `weather` server).
|
||||
</Tip>
|
||||
|
|
@ -48,7 +48,7 @@ Both approaches return a Starlette application that can be integrated with other
|
|||
The returned app stores the `FastMCP` instance on `app.state.fastmcp_server`, so you
|
||||
can access it from custom middleware or routes via `request.app.state.fastmcp_server`.
|
||||
|
||||
The MCP server's endpoint is mounted at the root path `/mcp` for Streamable HTTP transport, and `/sse` for SSE transport, though you can change these paths by passing a `path` argument to the `http_app()` method:
|
||||
The MCP server's endpoint is mounted at the root path `/mcp/` for Streamable HTTP transport, and `/sse/` for SSE transport, though you can change these paths by passing a `path` argument to the `http_app()` method:
|
||||
|
||||
```python
|
||||
# For Streamable HTTP transport
|
||||
|
|
@ -96,7 +96,13 @@ mcp = FastMCP("MyServer")
|
|||
|
||||
# Define custom middleware
|
||||
custom_middleware = [
|
||||
Middleware(CORSMiddleware, allow_origins=["*"]),
|
||||
Middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["https://example.com", "https://app.example.com"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "OPTIONS"],
|
||||
allow_headers=["Content-Type", "Authorization"],
|
||||
),
|
||||
]
|
||||
|
||||
# Create ASGI app with custom middleware
|
||||
|
|
@ -131,7 +137,7 @@ app = Starlette(
|
|||
)
|
||||
```
|
||||
|
||||
The MCP endpoint will be available at `/mcp-server/mcp` of the resulting Starlette app.
|
||||
The MCP endpoint will be available at `/mcp-server/mcp/` of the resulting Starlette app.
|
||||
|
||||
<Warning>
|
||||
For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
|
||||
|
|
@ -161,7 +167,7 @@ app = Starlette(
|
|||
)
|
||||
```
|
||||
|
||||
In this setup, the MCP server is accessible at the `/outer/inner/mcp` path of the resulting Starlette app.
|
||||
In this setup, the MCP server is accessible at the `/outer/inner/mcp/` path of the resulting Starlette app.
|
||||
|
||||
<Warning>
|
||||
For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the *outer* Starlette app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
|
||||
|
|
@ -188,7 +194,7 @@ app = FastAPI(lifespan=mcp_app.lifespan)
|
|||
app.mount("/mcp-server", mcp_app)
|
||||
```
|
||||
|
||||
The MCP endpoint will be available at `/mcp-server/mcp` of the resulting FastAPI app.
|
||||
The MCP endpoint will be available at `/mcp-server/mcp/` of the resulting FastAPI app.
|
||||
|
||||
<Warning>
|
||||
For Streamable HTTP transport, you **must** pass the lifespan context from the FastMCP app to the resulting FastAPI app, as nested lifespans are not recognized. Otherwise, the FastMCP server's session manager will not be properly initialized.
|
||||
|
|
|
|||
|
|
@ -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 `"streamable-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
|
||||
|
|
@ -120,7 +120,7 @@ import asyncio
|
|||
from fastmcp import Client
|
||||
|
||||
async def example():
|
||||
async with Client("http://127.0.0.1:8000/mcp") as client:
|
||||
async with Client("http://127.0.0.1:8000/mcp/") as client:
|
||||
await client.ping()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -168,7 +168,7 @@ New applications should use Streamable HTTP transport instead.
|
|||
|
||||
Server-Sent Events (SSE) is an HTTP-based protocol for server-to-client streaming. While FastMCP still supports SSE, it is deprecated and Streamable HTTP is preferred for new projects.
|
||||
|
||||
To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse`) and message path (`/messages/`).
|
||||
To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse/`) and message path (`/messages/`).
|
||||
|
||||
<CodeGroup>
|
||||
```python {6} server.py
|
||||
|
|
@ -186,7 +186,7 @@ from fastmcp.client.transports import SSETransport
|
|||
|
||||
async def example():
|
||||
async with Client(
|
||||
transport=SSETransport("http://127.0.0.1:8000/sse")
|
||||
transport=SSETransport("http://127.0.0.1:8000/sse/")
|
||||
) as client:
|
||||
await client.ping()
|
||||
|
||||
|
|
|
|||
456
docs/docs.json
456
docs/docs.json
|
|
@ -1,178 +1,296 @@
|
|||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"appearance": {
|
||||
"default": "system",
|
||||
"strict": false
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"appearance": {
|
||||
"default": "system",
|
||||
"strict": false
|
||||
},
|
||||
"background": {
|
||||
"color": {
|
||||
"dark": "#222831",
|
||||
"light": "#EEEEEE"
|
||||
},
|
||||
"background": {
|
||||
"color": {
|
||||
"dark": "#222831",
|
||||
"light": "#EEEEEE"
|
||||
},
|
||||
"decoration": "windows"
|
||||
},
|
||||
"banner": {
|
||||
"content": "[FastMCP Cloud](https://fastmcp.link/x0Kyhy2) is coming!"
|
||||
},
|
||||
"colors": {
|
||||
"dark": "#f72585",
|
||||
"light": "#4cc9f0",
|
||||
"primary": "#2d00f7"
|
||||
},
|
||||
"description": "The fast, Pythonic way to build MCP servers and clients.",
|
||||
"favicon": {
|
||||
"dark": "/assets/favicon.ico",
|
||||
"light": "/assets/favicon.ico"
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"bluesky": "https://bsky.app/profile/jlowin.dev",
|
||||
"github": "https://github.com/jlowin/fastmcp",
|
||||
"x": "https://x.com/jlowin"
|
||||
}
|
||||
},
|
||||
"integrations": {
|
||||
"ga4": {
|
||||
"measurementId": "G-64R5W1TJXG"
|
||||
}
|
||||
},
|
||||
"name": "FastMCP",
|
||||
"navbar": {
|
||||
"primary": {
|
||||
"href": "https://github.com/jlowin/fastmcp",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"decoration": "windows"
|
||||
},
|
||||
"banner": {
|
||||
"content": "[FastMCP Cloud](https://fastmcp.link/x0Kyhy2) is coming!"
|
||||
},
|
||||
"colors": {
|
||||
"dark": "#f72585",
|
||||
"light": "#4cc9f0",
|
||||
"primary": "#2d00f7"
|
||||
},
|
||||
"description": "The fast, Pythonic way to build MCP servers and clients.",
|
||||
"favicon": {
|
||||
"dark": "/assets/favicon.ico",
|
||||
"light": "/assets/favicon.ico"
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"bluesky": "https://bsky.app/profile/jlowin.dev",
|
||||
"github": "https://github.com/jlowin/fastmcp",
|
||||
"x": "https://x.com/jlowin"
|
||||
}
|
||||
},
|
||||
"integrations": {
|
||||
"ga4": {
|
||||
"measurementId": "G-64R5W1TJXG"
|
||||
}
|
||||
},
|
||||
"name": "FastMCP",
|
||||
"navbar": {
|
||||
"primary": {
|
||||
"href": "https://github.com/jlowin/fastmcp",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Documentation",
|
||||
"anchors": [
|
||||
{
|
||||
"anchor": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"pages": [
|
||||
"getting-started/welcome",
|
||||
"getting-started/installation",
|
||||
"getting-started/quickstart",
|
||||
"updates"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Servers",
|
||||
"pages": [
|
||||
"servers/fastmcp",
|
||||
{
|
||||
"group": "Core Components",
|
||||
"icon": "toolbox",
|
||||
"pages": [
|
||||
"servers/tools",
|
||||
"servers/resources",
|
||||
"servers/prompts",
|
||||
"servers/context"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "shield-check",
|
||||
"pages": [
|
||||
"servers/auth/bearer"
|
||||
]
|
||||
},
|
||||
"servers/middleware",
|
||||
"servers/openapi",
|
||||
"servers/proxy",
|
||||
"servers/composition",
|
||||
{
|
||||
"group": "Deployment",
|
||||
"icon": "upload",
|
||||
"pages": [
|
||||
"deployment/running-server",
|
||||
"deployment/asgi"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Clients",
|
||||
"pages": [
|
||||
"clients/client",
|
||||
"clients/transports",
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "user-shield",
|
||||
"pages": [
|
||||
"clients/auth/oauth",
|
||||
"clients/auth/bearer"
|
||||
]
|
||||
},
|
||||
"clients/advanced-features"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"integrations/anthropic",
|
||||
"integrations/claude-desktop",
|
||||
"integrations/openai",
|
||||
"integrations/gemini",
|
||||
"integrations/contrib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Patterns",
|
||||
"pages": [
|
||||
"patterns/tool-transformation",
|
||||
"patterns/decorating-methods",
|
||||
"patterns/http-requests",
|
||||
"patterns/testing",
|
||||
"patterns/cli"
|
||||
]
|
||||
}
|
||||
],
|
||||
"icon": "book"
|
||||
},
|
||||
{
|
||||
"anchor": "Tutorials",
|
||||
"groups": [
|
||||
{
|
||||
"group": "MCP",
|
||||
"pages": [
|
||||
"tutorials/mcp",
|
||||
"tutorials/create-mcp-server",
|
||||
"tutorials/rest-api"
|
||||
]
|
||||
}
|
||||
],
|
||||
"icon": "graduation-cap"
|
||||
},
|
||||
{
|
||||
"anchor": "Changelog",
|
||||
"icon": "list-check",
|
||||
{
|
||||
"anchor": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"pages": [
|
||||
"changelog"
|
||||
"getting-started/welcome",
|
||||
"getting-started/installation",
|
||||
"getting-started/quickstart"
|
||||
]
|
||||
},
|
||||
{
|
||||
"anchor": "Community",
|
||||
"icon": "users",
|
||||
},
|
||||
{
|
||||
"group": "Servers",
|
||||
"pages": [
|
||||
"community/showcase"
|
||||
"servers/server",
|
||||
{
|
||||
"group": "Core Components",
|
||||
"icon": "toolbox",
|
||||
"pages": [
|
||||
"servers/tools",
|
||||
"servers/resources",
|
||||
"servers/prompts",
|
||||
"servers/context"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "shield-check",
|
||||
"pages": ["servers/auth/bearer"]
|
||||
},
|
||||
"servers/middleware",
|
||||
"servers/openapi",
|
||||
"servers/proxy",
|
||||
"servers/composition",
|
||||
{
|
||||
"group": "Deployment",
|
||||
"icon": "upload",
|
||||
"pages": ["deployment/running-server", "deployment/asgi"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"group": "Clients",
|
||||
"pages": [
|
||||
"clients/client",
|
||||
{
|
||||
"group": "Core Operations",
|
||||
"icon": "handshake",
|
||||
"pages": [
|
||||
"clients/tools",
|
||||
"clients/resources",
|
||||
"clients/prompts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Advanced Features",
|
||||
"icon": "stars",
|
||||
"pages": [
|
||||
"clients/logging",
|
||||
"clients/progress",
|
||||
"clients/sampling",
|
||||
"clients/roots"
|
||||
]
|
||||
},
|
||||
"clients/transports",
|
||||
{
|
||||
"group": "Authentication",
|
||||
"icon": "user-shield",
|
||||
"pages": ["clients/auth/oauth", "clients/auth/bearer"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"integrations/anthropic",
|
||||
"integrations/claude-desktop",
|
||||
"integrations/openai",
|
||||
"integrations/gemini",
|
||||
"integrations/contrib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Patterns",
|
||||
"pages": [
|
||||
"patterns/tool-transformation",
|
||||
"patterns/decorating-methods",
|
||||
"patterns/http-requests",
|
||||
"patterns/testing",
|
||||
"patterns/cli"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Tutorials",
|
||||
"pages": [
|
||||
"tutorials/mcp",
|
||||
"tutorials/create-mcp-server",
|
||||
"tutorials/rest-api"
|
||||
]
|
||||
}
|
||||
],
|
||||
"icon": "book"
|
||||
},
|
||||
{
|
||||
"anchor": "What's New",
|
||||
"pages": ["updates", "changelog"]
|
||||
},
|
||||
|
||||
{
|
||||
"anchor": "Community",
|
||||
"icon": "users",
|
||||
"pages": ["community/showcase"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "SDK Reference",
|
||||
"anchors": [
|
||||
{
|
||||
"anchor": "Python SDK",
|
||||
"icon": "python",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-exceptions",
|
||||
"python-sdk/fastmcp-settings",
|
||||
{
|
||||
"group": "fastmcp.cli",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-cli-__init__",
|
||||
"python-sdk/fastmcp-cli-claude",
|
||||
"python-sdk/fastmcp-cli-cli",
|
||||
"python-sdk/fastmcp-cli-run"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.client",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-__init__",
|
||||
{
|
||||
"group": "auth",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-client-auth-__init__",
|
||||
"python-sdk/fastmcp-client-auth-bearer",
|
||||
"python-sdk/fastmcp-client-auth-oauth"
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-client-client",
|
||||
"python-sdk/fastmcp-client-logging",
|
||||
"python-sdk/fastmcp-client-oauth_callback",
|
||||
"python-sdk/fastmcp-client-progress",
|
||||
"python-sdk/fastmcp-client-roots",
|
||||
"python-sdk/fastmcp-client-sampling",
|
||||
"python-sdk/fastmcp-client-transports"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.prompts",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-prompts-__init__",
|
||||
"python-sdk/fastmcp-prompts-prompt",
|
||||
"python-sdk/fastmcp-prompts-prompt_manager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.resources",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-resources-__init__",
|
||||
"python-sdk/fastmcp-resources-resource",
|
||||
"python-sdk/fastmcp-resources-resource_manager",
|
||||
"python-sdk/fastmcp-resources-template",
|
||||
"python-sdk/fastmcp-resources-types"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.server",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-__init__",
|
||||
{
|
||||
"group": "auth",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-__init__",
|
||||
"python-sdk/fastmcp-server-auth-auth",
|
||||
{
|
||||
"group": "providers",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-server-auth-providers-__init__",
|
||||
"python-sdk/fastmcp-server-auth-providers-bearer",
|
||||
"python-sdk/fastmcp-server-auth-providers-bearer_env",
|
||||
"python-sdk/fastmcp-server-auth-providers-in_memory"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"python-sdk/fastmcp-server-context",
|
||||
"python-sdk/fastmcp-server-dependencies",
|
||||
"python-sdk/fastmcp-server-http",
|
||||
"python-sdk/fastmcp-server-middleware",
|
||||
"python-sdk/fastmcp-server-openapi",
|
||||
"python-sdk/fastmcp-server-proxy",
|
||||
"python-sdk/fastmcp-server-server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.tools",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-tools-__init__",
|
||||
"python-sdk/fastmcp-tools-tool",
|
||||
"python-sdk/fastmcp-tools-tool_manager",
|
||||
"python-sdk/fastmcp-tools-tool_transform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "fastmcp.utilities",
|
||||
"pages": [
|
||||
"python-sdk/fastmcp-utilities-__init__",
|
||||
"python-sdk/fastmcp-utilities-cache",
|
||||
"python-sdk/fastmcp-utilities-components",
|
||||
"python-sdk/fastmcp-utilities-exceptions",
|
||||
"python-sdk/fastmcp-utilities-http",
|
||||
"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-types"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"redirects": [
|
||||
{
|
||||
"destination": "/servers/proxy",
|
||||
"source": "/patterns/proxy"
|
||||
},
|
||||
"redirects": [
|
||||
{
|
||||
"destination": "/servers/proxy",
|
||||
"source": "/patterns/proxy"
|
||||
},
|
||||
{
|
||||
"destination": "/servers/composition",
|
||||
"source": "/patterns/composition"
|
||||
}
|
||||
],
|
||||
"search": {
|
||||
"prompt": "Search the docs..."
|
||||
},
|
||||
"theme": "mint"
|
||||
}
|
||||
{
|
||||
"destination": "/servers/composition",
|
||||
"source": "/patterns/composition"
|
||||
}
|
||||
],
|
||||
"search": {
|
||||
"prompt": "Search the docs..."
|
||||
},
|
||||
"theme": "mint"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ title: Anthropic API + FastMCP
|
|||
sidebarTitle: Anthropic API
|
||||
description: Call FastMCP servers from the Anthropic API
|
||||
icon: message-smile
|
||||
tag: "New!"
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ title: Gemini SDK + FastMCP
|
|||
sidebarTitle: Gemini SDK
|
||||
description: Call FastMCP servers from the Google Gemini SDK
|
||||
icon: message-smile
|
||||
tag: "New!"
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ title: OpenAI API + FastMCP
|
|||
sidebarTitle: OpenAI API
|
||||
description: Call FastMCP servers from the OpenAI API
|
||||
icon: message-smile
|
||||
tag: "New!"
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ fastmcp --help
|
|||
| `run` | Run a FastMCP server directly | Uses your current environment; you are responsible for ensuring all dependencies are available |
|
||||
| `dev` | Run a server with the MCP Inspector for testing | Creates an isolated environment; dependencies must be explicitly specified with `--with` and/or `--with-editable` |
|
||||
| `install` | Install a server in the Claude desktop app | Creates an isolated environment; dependencies must be explicitly specified with `--with` and/or `--with-editable` |
|
||||
| `inspect` | Generate a JSON report about a FastMCP server | Uses your current environment; you are responsible for ensuring all dependencies are available |
|
||||
| `version` | Display version information | N/A |
|
||||
|
||||
## Command Details
|
||||
|
|
@ -179,6 +180,29 @@ fastmcp install server.py:my_server
|
|||
fastmcp install server.py:my_server -n "My Analysis Server" --with pandas
|
||||
```
|
||||
|
||||
### `inspect`
|
||||
|
||||
<VersionBadge version="2.9.0" />
|
||||
|
||||
Generate a detailed JSON report about a FastMCP server, including information about its tools, prompts, resources, and capabilities.
|
||||
|
||||
```bash
|
||||
fastmcp inspect server.py
|
||||
```
|
||||
|
||||
The command supports the same server specification format as `run` and `install`:
|
||||
|
||||
```bash
|
||||
# Auto-detect server object
|
||||
fastmcp inspect server.py
|
||||
|
||||
# Specify server object
|
||||
fastmcp inspect server.py:my_server
|
||||
|
||||
# Custom output location
|
||||
fastmcp inspect server.py --output analysis.json
|
||||
```
|
||||
|
||||
### `version`
|
||||
|
||||
Display version information about FastMCP and related components.
|
||||
|
|
|
|||
9
docs/python-sdk/fastmcp-cli-__init__.mdx
Normal file
9
docs/python-sdk/fastmcp-cli-__init__.mdx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.cli`
|
||||
|
||||
|
||||
FastMCP CLI package.
|
||||
43
docs/python-sdk/fastmcp-cli-claude.mdx
Normal file
43
docs/python-sdk/fastmcp-cli-claude.mdx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
title: claude
|
||||
sidebarTitle: claude
|
||||
---
|
||||
|
||||
# `fastmcp.cli.claude`
|
||||
|
||||
|
||||
Claude app integration utilities.
|
||||
|
||||
## Functions
|
||||
|
||||
### `get_claude_config_path`
|
||||
|
||||
```python
|
||||
get_claude_config_path() -> Path | None
|
||||
```
|
||||
|
||||
|
||||
Get the Claude config directory based on platform.
|
||||
|
||||
|
||||
### `update_claude_config`
|
||||
|
||||
```python
|
||||
update_claude_config(file_spec: str, server_name: str) -> bool
|
||||
```
|
||||
|
||||
|
||||
Add or update a FastMCP server in Claude's configuration.
|
||||
|
||||
**Args:**
|
||||
- `file_spec`: Path to the server file, optionally with \:object suffix
|
||||
- `server_name`: Name for the server in Claude's config
|
||||
- `with_editable`: Optional directory to install in editable mode
|
||||
- `with_packages`: Optional list of additional packages to install
|
||||
- `env_vars`: Optional dictionary of environment variables. These are merged with
|
||||
any existing variables, with new values taking precedence.
|
||||
|
||||
**Raises:**
|
||||
- `RuntimeError`: If Claude Desktop's config directory is not found, indicating
|
||||
Claude Desktop may not be installed or properly set up.
|
||||
|
||||
65
docs/python-sdk/fastmcp-cli-cli.mdx
Normal file
65
docs/python-sdk/fastmcp-cli-cli.mdx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
title: cli
|
||||
sidebarTitle: cli
|
||||
---
|
||||
|
||||
# `fastmcp.cli.cli`
|
||||
|
||||
|
||||
FastMCP CLI tools.
|
||||
|
||||
## Functions
|
||||
|
||||
### `version`
|
||||
|
||||
```python
|
||||
version(ctx: Context)
|
||||
```
|
||||
|
||||
### `dev`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
|
||||
Run a MCP server with the MCP Inspector.
|
||||
|
||||
|
||||
### `run`
|
||||
|
||||
```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 a MCP server or connect to a remote one.
|
||||
|
||||
The server can be specified in three ways:
|
||||
1. Module approach: server.py - runs the module directly, looking for an object named mcp/server/app.
|
||||
|
||||
2. Import approach: server.py:app - imports and runs the specified server object.
|
||||
|
||||
3. URL approach: http://server-url - connects to a remote server and creates a proxy.
|
||||
|
||||
|
||||
|
||||
Note: This command runs the server directly. You are responsible for ensuring
|
||||
all dependencies are available.
|
||||
|
||||
Server arguments can be passed after -- :
|
||||
fastmcp run server.py -- --config config.json --debug
|
||||
|
||||
|
||||
### `install`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
|
||||
Install a MCP server in the Claude desktop app.
|
||||
|
||||
Environment variables are preserved once added and only updated if new values
|
||||
are explicitly provided.
|
||||
|
||||
106
docs/python-sdk/fastmcp-cli-run.mdx
Normal file
106
docs/python-sdk/fastmcp-cli-run.mdx
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
title: run
|
||||
sidebarTitle: run
|
||||
---
|
||||
|
||||
# `fastmcp.cli.run`
|
||||
|
||||
|
||||
FastMCP run command implementation.
|
||||
|
||||
## Functions
|
||||
|
||||
### `is_url`
|
||||
|
||||
```python
|
||||
is_url(path: str) -> bool
|
||||
```
|
||||
|
||||
|
||||
Check if a string is a URL.
|
||||
|
||||
|
||||
### `parse_file_path`
|
||||
|
||||
```python
|
||||
parse_file_path(server_spec: str) -> tuple[Path, str | None]
|
||||
```
|
||||
|
||||
|
||||
Parse a file path that may include a server object specification.
|
||||
|
||||
**Args:**
|
||||
- `server_spec`: Path to file, optionally with \:object suffix
|
||||
|
||||
**Returns:**
|
||||
- Tuple of (file_path, server_object)
|
||||
|
||||
|
||||
### `import_server`
|
||||
|
||||
```python
|
||||
import_server(file: Path, server_object: str | None = None) -> Any
|
||||
```
|
||||
|
||||
|
||||
Import a MCP server from a file.
|
||||
|
||||
**Args:**
|
||||
- `file`: Path to the file
|
||||
- `server_object`: Optional object name in format "module\:object" or just "object"
|
||||
|
||||
**Returns:**
|
||||
- The server object
|
||||
|
||||
|
||||
### `create_client_server`
|
||||
|
||||
```python
|
||||
create_client_server(url: str) -> Any
|
||||
```
|
||||
|
||||
|
||||
Create a FastMCP server from a client URL.
|
||||
|
||||
**Args:**
|
||||
- `url`: The URL to connect to
|
||||
|
||||
**Returns:**
|
||||
- A FastMCP server instance
|
||||
|
||||
|
||||
### `import_server_with_args`
|
||||
|
||||
```python
|
||||
import_server_with_args(file: Path, server_object: str | None = None, server_args: list[str] | None = None) -> Any
|
||||
```
|
||||
|
||||
|
||||
Import a server with optional command line arguments.
|
||||
|
||||
**Args:**
|
||||
- `file`: Path to the server file
|
||||
- `server_object`: Optional server object name
|
||||
- `server_args`: Optional command line arguments to inject
|
||||
|
||||
**Returns:**
|
||||
- The imported server object
|
||||
|
||||
|
||||
### `run_command`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
|
||||
Run a MCP server or connect to a remote one.
|
||||
|
||||
**Args:**
|
||||
- `server_spec`: Python file, object specification (file\:obj), or URL
|
||||
- `transport`: Transport protocol to use
|
||||
- `host`: Host to bind to when using http transport
|
||||
- `port`: Port to bind to when using http transport
|
||||
- `log_level`: Log level
|
||||
- `server_args`: Additional arguments to pass to the server
|
||||
|
||||
8
docs/python-sdk/fastmcp-client-__init__.mdx
Normal file
8
docs/python-sdk/fastmcp-client-__init__.mdx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.client`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
8
docs/python-sdk/fastmcp-client-auth-__init__.mdx
Normal file
8
docs/python-sdk/fastmcp-client-auth-__init__.mdx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.client.auth`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
18
docs/python-sdk/fastmcp-client-auth-bearer.mdx
Normal file
18
docs/python-sdk/fastmcp-client-auth-bearer.mdx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: bearer
|
||||
sidebarTitle: bearer
|
||||
---
|
||||
|
||||
# `fastmcp.client.auth.bearer`
|
||||
|
||||
## Classes
|
||||
|
||||
### `BearerAuth`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `auth_flow`
|
||||
|
||||
```python
|
||||
auth_flow(self, request)
|
||||
```
|
||||
102
docs/python-sdk/fastmcp-client-auth-oauth.mdx
Normal file
102
docs/python-sdk/fastmcp-client-auth-oauth.mdx
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
title: oauth
|
||||
sidebarTitle: oauth
|
||||
---
|
||||
|
||||
# `fastmcp.client.auth.oauth`
|
||||
|
||||
## Functions
|
||||
|
||||
### `default_cache_dir`
|
||||
|
||||
```python
|
||||
default_cache_dir() -> Path
|
||||
```
|
||||
|
||||
### `OAuth`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
|
||||
Create an OAuthClientProvider for an MCP server.
|
||||
|
||||
This is intended to be provided to the `auth` parameter of an
|
||||
httpx.AsyncClient (or appropriate FastMCP client/transport instance)
|
||||
|
||||
**Args:**
|
||||
- `mcp_url`: Full URL to the MCP endpoint (e.g. "http\://host/mcp/sse/")
|
||||
- `scopes`: OAuth scopes to request. Can be a
|
||||
- `client_name`: Name for this client during registration
|
||||
- `token_storage_cache_dir`: Directory for FileTokenStorage
|
||||
- `additional_client_metadata`: Extra fields for OAuthClientMetadata
|
||||
|
||||
**Returns:**
|
||||
- OAuthClientProvider
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `ServerOAuthMetadata`
|
||||
|
||||
|
||||
More flexible OAuth metadata model that accepts broader ranges of values
|
||||
than the restrictive MCP standard model.
|
||||
|
||||
This handles real-world OAuth servers like PayPal that may support
|
||||
additional methods not in the MCP specification.
|
||||
|
||||
|
||||
### `OAuthClientProvider`
|
||||
|
||||
|
||||
OAuth client provider with more flexible OAuth metadata discovery.
|
||||
|
||||
|
||||
### `FileTokenStorage`
|
||||
|
||||
|
||||
File-based token storage implementation for OAuth credentials and tokens.
|
||||
Implements the mcp.client.auth.TokenStorage protocol.
|
||||
|
||||
Each instance is tied to a specific server URL for proper token isolation.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `get_base_url`
|
||||
|
||||
```python
|
||||
get_base_url(url: str) -> str
|
||||
```
|
||||
|
||||
Extract the base URL (scheme + host) from a URL.
|
||||
|
||||
|
||||
#### `get_cache_key`
|
||||
|
||||
```python
|
||||
get_cache_key(self) -> str
|
||||
```
|
||||
|
||||
Generate a safe filesystem key from the server's base URL.
|
||||
|
||||
|
||||
#### `clear`
|
||||
|
||||
```python
|
||||
clear(self) -> None
|
||||
```
|
||||
|
||||
Clear all cached data for this server.
|
||||
|
||||
|
||||
#### `clear_all`
|
||||
|
||||
```python
|
||||
clear_all(cls, cache_dir: Path | None = None) -> None
|
||||
```
|
||||
|
||||
Clear all cached data for all servers.
|
||||
|
||||
94
docs/python-sdk/fastmcp-client-client.mdx
Normal file
94
docs/python-sdk/fastmcp-client-client.mdx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
---
|
||||
title: client
|
||||
sidebarTitle: client
|
||||
---
|
||||
|
||||
# `fastmcp.client.client`
|
||||
|
||||
## Classes
|
||||
|
||||
### `Client`
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
```python # Connect to FastMCP server client =
|
||||
Client("http://localhost:8080")
|
||||
|
||||
async with client:
|
||||
# List available resources resources = await client.list_resources()
|
||||
|
||||
# Call a tool result = await client.call_tool("my_tool", {"param":
|
||||
"value"})
|
||||
```
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `session`
|
||||
|
||||
```python
|
||||
session(self) -> ClientSession
|
||||
```
|
||||
|
||||
Get the current active session. Raises RuntimeError if not connected.
|
||||
|
||||
|
||||
#### `initialize_result`
|
||||
|
||||
```python
|
||||
initialize_result(self) -> mcp.types.InitializeResult
|
||||
```
|
||||
|
||||
Get the result of the initialization request.
|
||||
|
||||
|
||||
#### `set_roots`
|
||||
|
||||
```python
|
||||
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`
|
||||
|
||||
```python
|
||||
set_sampling_callback(self, sampling_callback: SamplingHandler) -> None
|
||||
```
|
||||
|
||||
Set the sampling callback for the client.
|
||||
|
||||
|
||||
#### `is_connected`
|
||||
|
||||
```python
|
||||
is_connected(self) -> bool
|
||||
```
|
||||
|
||||
Check if the client is currently connected.
|
||||
|
||||
14
docs/python-sdk/fastmcp-client-logging.mdx
Normal file
14
docs/python-sdk/fastmcp-client-logging.mdx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: logging
|
||||
sidebarTitle: logging
|
||||
---
|
||||
|
||||
# `fastmcp.client.logging`
|
||||
|
||||
## Functions
|
||||
|
||||
### `create_log_callback`
|
||||
|
||||
```python
|
||||
create_log_callback(handler: LogHandler | None = None) -> LoggingFnT
|
||||
```
|
||||
63
docs/python-sdk/fastmcp-client-oauth_callback.mdx
Normal file
63
docs/python-sdk/fastmcp-client-oauth_callback.mdx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
title: oauth_callback
|
||||
sidebarTitle: oauth_callback
|
||||
---
|
||||
|
||||
# `fastmcp.client.oauth_callback`
|
||||
|
||||
|
||||
|
||||
OAuth callback server for handling authorization code flows.
|
||||
|
||||
This module provides a reusable callback server that can handle OAuth redirects
|
||||
and display styled responses to users.
|
||||
|
||||
|
||||
## Functions
|
||||
|
||||
### `create_callback_html`
|
||||
|
||||
```python
|
||||
create_callback_html(message: str, is_success: bool = True, title: str = 'FastMCP OAuth', server_url: str | None = None) -> str
|
||||
```
|
||||
|
||||
|
||||
Create a styled HTML response for OAuth callbacks.
|
||||
|
||||
|
||||
### `create_oauth_callback_server`
|
||||
|
||||
```python
|
||||
create_oauth_callback_server(port: int, callback_path: str = '/callback', server_url: str | None = None, response_future: asyncio.Future | None = None) -> Server
|
||||
```
|
||||
|
||||
|
||||
Create an OAuth callback server.
|
||||
|
||||
**Args:**
|
||||
- `port`: The port to run the server on
|
||||
- `callback_path`: The path to listen for OAuth redirects on
|
||||
- `server_url`: Optional server URL to display in success messages
|
||||
- `response_future`: Optional future to resolve when OAuth callback is received
|
||||
|
||||
**Returns:**
|
||||
- Configured uvicorn Server instance (not yet running)
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `CallbackResponse`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_dict`
|
||||
|
||||
```python
|
||||
from_dict(cls, data: dict[str, str]) -> CallbackResponse
|
||||
```
|
||||
|
||||
#### `to_dict`
|
||||
|
||||
```python
|
||||
to_dict(self) -> dict[str, str]
|
||||
```
|
||||
8
docs/python-sdk/fastmcp-client-progress.mdx
Normal file
8
docs/python-sdk/fastmcp-client-progress.mdx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: progress
|
||||
sidebarTitle: progress
|
||||
---
|
||||
|
||||
# `fastmcp.client.progress`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
20
docs/python-sdk/fastmcp-client-roots.mdx
Normal file
20
docs/python-sdk/fastmcp-client-roots.mdx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: roots
|
||||
sidebarTitle: roots
|
||||
---
|
||||
|
||||
# `fastmcp.client.roots`
|
||||
|
||||
## Functions
|
||||
|
||||
### `convert_roots_list`
|
||||
|
||||
```python
|
||||
convert_roots_list(roots: RootsList) -> list[mcp.types.Root]
|
||||
```
|
||||
|
||||
### `create_roots_callback`
|
||||
|
||||
```python
|
||||
create_roots_callback(handler: RootsList | RootsHandler) -> ListRootsFnT
|
||||
```
|
||||
14
docs/python-sdk/fastmcp-client-sampling.mdx
Normal file
14
docs/python-sdk/fastmcp-client-sampling.mdx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: sampling
|
||||
sidebarTitle: sampling
|
||||
---
|
||||
|
||||
# `fastmcp.client.sampling`
|
||||
|
||||
## Functions
|
||||
|
||||
### `create_sampling_callback`
|
||||
|
||||
```python
|
||||
create_sampling_callback(sampling_handler: SamplingHandler) -> SamplingFnT
|
||||
```
|
||||
191
docs/python-sdk/fastmcp-client-transports.mdx
Normal file
191
docs/python-sdk/fastmcp-client-transports.mdx
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
---
|
||||
title: transports
|
||||
sidebarTitle: transports
|
||||
---
|
||||
|
||||
# `fastmcp.client.transports`
|
||||
|
||||
## Functions
|
||||
|
||||
### `infer_transport`
|
||||
|
||||
```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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
Examples:
|
||||
```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 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`
|
||||
|
||||
|
||||
Keyword arguments for the MCP ClientSession constructor.
|
||||
|
||||
|
||||
### `ClientTransport`
|
||||
|
||||
|
||||
Abstract base class for different MCP client transport mechanisms.
|
||||
|
||||
A Transport is responsible for establishing and managing connections
|
||||
to an MCP server, and providing a ClientSession within an async context.
|
||||
|
||||
|
||||
### `WSTransport`
|
||||
|
||||
|
||||
Transport implementation that connects to an MCP server via WebSockets.
|
||||
|
||||
|
||||
### `SSETransport`
|
||||
|
||||
|
||||
Transport implementation that connects to an MCP server via Server-Sent Events.
|
||||
|
||||
|
||||
### `StreamableHttpTransport`
|
||||
|
||||
|
||||
Transport implementation that connects to an MCP server via Streamable HTTP Requests.
|
||||
|
||||
|
||||
### `StdioTransport`
|
||||
|
||||
|
||||
Base transport for connecting to an MCP server via subprocess with stdio.
|
||||
|
||||
This is a base class that can be subclassed for specific command-based
|
||||
transports like Python, Node, Uvx, etc.
|
||||
|
||||
|
||||
### `PythonStdioTransport`
|
||||
|
||||
|
||||
Transport for running Python scripts.
|
||||
|
||||
|
||||
### `FastMCPStdioTransport`
|
||||
|
||||
|
||||
Transport for running FastMCP servers using the FastMCP CLI.
|
||||
|
||||
|
||||
### `NodeStdioTransport`
|
||||
|
||||
|
||||
Transport for running Node.js scripts.
|
||||
|
||||
|
||||
### `UvxStdioTransport`
|
||||
|
||||
|
||||
Transport for running commands via the uvx tool.
|
||||
|
||||
|
||||
### `NpxStdioTransport`
|
||||
|
||||
|
||||
Transport for running commands via the npx tool.
|
||||
|
||||
|
||||
### `FastMCPTransport`
|
||||
|
||||
|
||||
In-memory transport for FastMCP servers.
|
||||
|
||||
This transport connects directly to a FastMCP server instance in the same
|
||||
Python process. It works with both FastMCP 2.x servers and FastMCP 1.0
|
||||
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`
|
||||
|
||||
|
||||
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:
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
|
||||
# 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 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"})
|
||||
|
||||
# Access resources with prefixed URIs
|
||||
icons = await client.read_resource("weather://weather/icons/sunny")
|
||||
```
|
||||
|
||||
65
docs/python-sdk/fastmcp-exceptions.mdx
Normal file
65
docs/python-sdk/fastmcp-exceptions.mdx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
title: exceptions
|
||||
sidebarTitle: exceptions
|
||||
---
|
||||
|
||||
# `fastmcp.exceptions`
|
||||
|
||||
|
||||
Custom exceptions for FastMCP.
|
||||
|
||||
## Classes
|
||||
|
||||
### `FastMCPError`
|
||||
|
||||
|
||||
Base error for FastMCP.
|
||||
|
||||
|
||||
### `ValidationError`
|
||||
|
||||
|
||||
Error in validating parameters or return values.
|
||||
|
||||
|
||||
### `ResourceError`
|
||||
|
||||
|
||||
Error in resource operations.
|
||||
|
||||
|
||||
### `ToolError`
|
||||
|
||||
|
||||
Error in tool operations.
|
||||
|
||||
|
||||
### `PromptError`
|
||||
|
||||
|
||||
Error in prompt operations.
|
||||
|
||||
|
||||
### `InvalidSignature`
|
||||
|
||||
|
||||
Invalid signature for use with FastMCP.
|
||||
|
||||
|
||||
### `ClientError`
|
||||
|
||||
|
||||
Error in client operations.
|
||||
|
||||
|
||||
### `NotFoundError`
|
||||
|
||||
|
||||
Object not found.
|
||||
|
||||
|
||||
### `DisabledError`
|
||||
|
||||
|
||||
Object is disabled.
|
||||
|
||||
8
docs/python-sdk/fastmcp-prompts-__init__.mdx
Normal file
8
docs/python-sdk/fastmcp-prompts-__init__.mdx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.prompts`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
84
docs/python-sdk/fastmcp-prompts-prompt.mdx
Normal file
84
docs/python-sdk/fastmcp-prompts-prompt.mdx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
title: prompt
|
||||
sidebarTitle: prompt
|
||||
---
|
||||
|
||||
# `fastmcp.prompts.prompt`
|
||||
|
||||
|
||||
Base classes for FastMCP prompts.
|
||||
|
||||
## Functions
|
||||
|
||||
### `Message`
|
||||
|
||||
```python
|
||||
Message(content: str | MCPContent, role: Role | None = None, **kwargs: Any) -> PromptMessage
|
||||
```
|
||||
|
||||
|
||||
A user-friendly constructor for PromptMessage.
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `PromptArgument`
|
||||
|
||||
|
||||
An argument that can be passed to a prompt.
|
||||
|
||||
|
||||
### `Prompt`
|
||||
|
||||
|
||||
A prompt template that can be rendered with parameters.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `to_mcp_prompt`
|
||||
|
||||
```python
|
||||
to_mcp_prompt(self, **overrides: Any) -> MCPPrompt
|
||||
```
|
||||
|
||||
Convert the prompt to an MCP prompt.
|
||||
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a Prompt from a function.
|
||||
|
||||
The function can return:
|
||||
- A string (converted to a message)
|
||||
- A Message object
|
||||
- A dict (converted to a message)
|
||||
- A sequence of any of the above
|
||||
|
||||
|
||||
### `FunctionPrompt`
|
||||
|
||||
|
||||
A prompt that is a function.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a Prompt from a function.
|
||||
|
||||
The function can return:
|
||||
- A string (converted to a message)
|
||||
- A Message object
|
||||
- A dict (converted to a message)
|
||||
- A sequence of any of the above
|
||||
|
||||
43
docs/python-sdk/fastmcp-prompts-prompt_manager.mdx
Normal file
43
docs/python-sdk/fastmcp-prompts-prompt_manager.mdx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
title: prompt_manager
|
||||
sidebarTitle: prompt_manager
|
||||
---
|
||||
|
||||
# `fastmcp.prompts.prompt_manager`
|
||||
|
||||
## Classes
|
||||
|
||||
### `PromptManager`
|
||||
|
||||
|
||||
Manages FastMCP prompts.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `mount`
|
||||
|
||||
```python
|
||||
mount(self, server: MountedServer) -> None
|
||||
```
|
||||
|
||||
Adds a mounted server as a source for prompts.
|
||||
|
||||
|
||||
#### `add_prompt_from_fn`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a prompt from a function.
|
||||
|
||||
|
||||
#### `add_prompt`
|
||||
|
||||
```python
|
||||
add_prompt(self, prompt: Prompt) -> Prompt
|
||||
```
|
||||
|
||||
Add a prompt to the manager.
|
||||
|
||||
8
docs/python-sdk/fastmcp-resources-__init__.mdx
Normal file
8
docs/python-sdk/fastmcp-resources-__init__.mdx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.resources`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
90
docs/python-sdk/fastmcp-resources-resource.mdx
Normal file
90
docs/python-sdk/fastmcp-resources-resource.mdx
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
---
|
||||
title: resource
|
||||
sidebarTitle: resource
|
||||
---
|
||||
|
||||
# `fastmcp.resources.resource`
|
||||
|
||||
|
||||
Base classes and interfaces for FastMCP resources.
|
||||
|
||||
## Classes
|
||||
|
||||
### `Resource`
|
||||
|
||||
|
||||
Base class for all resources.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```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`
|
||||
|
||||
```python
|
||||
set_default_mime_type(cls, mime_type: str | None) -> str
|
||||
```
|
||||
|
||||
Set default MIME type if not provided.
|
||||
|
||||
|
||||
#### `set_default_name`
|
||||
|
||||
```python
|
||||
set_default_name(self) -> Self
|
||||
```
|
||||
|
||||
Set default name from URI if not provided.
|
||||
|
||||
|
||||
#### `to_mcp_resource`
|
||||
|
||||
```python
|
||||
to_mcp_resource(self, **overrides: Any) -> MCPResource
|
||||
```
|
||||
|
||||
Convert the resource to an MCPResource.
|
||||
|
||||
|
||||
#### `key`
|
||||
|
||||
```python
|
||||
key(self) -> str
|
||||
```
|
||||
|
||||
The key of the component. This is used for internal bookkeeping
|
||||
and may reflect e.g. prefixes or other identifiers. You should not depend on
|
||||
keys having a certain value, as the same tool loaded from different
|
||||
hierarchies of servers may have different keys.
|
||||
|
||||
|
||||
### `FunctionResource`
|
||||
|
||||
|
||||
A resource that defers data loading by wrapping a function.
|
||||
|
||||
The function is only called when the resource is read, allowing for lazy loading
|
||||
of potentially expensive data. This is particularly useful when listing resources,
|
||||
as the function won't be called until the resource is actually accessed.
|
||||
|
||||
The function can return:
|
||||
- str for text content (default)
|
||||
- bytes for binary content
|
||||
- other types will be converted to JSON
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a FunctionResource from a function.
|
||||
|
||||
111
docs/python-sdk/fastmcp-resources-resource_manager.mdx
Normal file
111
docs/python-sdk/fastmcp-resources-resource_manager.mdx
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
---
|
||||
title: resource_manager
|
||||
sidebarTitle: resource_manager
|
||||
---
|
||||
|
||||
# `fastmcp.resources.resource_manager`
|
||||
|
||||
|
||||
Resource manager functionality.
|
||||
|
||||
## Classes
|
||||
|
||||
### `ResourceManager`
|
||||
|
||||
|
||||
Manages FastMCP resources.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `mount`
|
||||
|
||||
```python
|
||||
mount(self, server: MountedServer) -> None
|
||||
```
|
||||
|
||||
Adds a mounted server as a source for resources and templates.
|
||||
|
||||
|
||||
#### `add_resource_or_template_from_fn`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Add a resource or template to the manager from a function.
|
||||
|
||||
**Args:**
|
||||
- `fn`: The function to register as a resource or template
|
||||
- `uri`: The URI for the resource or template
|
||||
- `name`: Optional name for the resource or template
|
||||
- `description`: Optional description of the resource or template
|
||||
- `mime_type`: Optional MIME type for the resource or template
|
||||
- `tags`: Optional set of tags for categorizing the resource or template
|
||||
|
||||
**Returns:**
|
||||
- The added resource or template. If a resource or template with the same URI already exists,
|
||||
- returns the existing resource or template.
|
||||
|
||||
|
||||
#### `add_resource_from_fn`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Add a resource to the manager from a function.
|
||||
|
||||
**Args:**
|
||||
- `fn`: The function to register as a resource
|
||||
- `uri`: The URI for the resource
|
||||
- `name`: Optional name for the resource
|
||||
- `description`: Optional description of the resource
|
||||
- `mime_type`: Optional MIME type for the resource
|
||||
- `tags`: Optional set of tags for categorizing the resource
|
||||
|
||||
**Returns:**
|
||||
- The added resource. If a resource with the same URI already exists,
|
||||
- returns the existing resource.
|
||||
|
||||
|
||||
#### `add_resource`
|
||||
|
||||
```python
|
||||
add_resource(self, resource: Resource) -> Resource
|
||||
```
|
||||
|
||||
Add a resource to the manager.
|
||||
|
||||
**Args:**
|
||||
- `resource`: A Resource instance to add. The resource's .key attribute
|
||||
will be used as the storage key. To overwrite it, call
|
||||
Resource.with_key() before calling this method.
|
||||
|
||||
|
||||
#### `add_template_from_fn`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a template from a function.
|
||||
|
||||
|
||||
#### `add_template`
|
||||
|
||||
```python
|
||||
add_template(self, template: ResourceTemplate) -> ResourceTemplate
|
||||
```
|
||||
|
||||
Add a template to the manager.
|
||||
|
||||
**Args:**
|
||||
- `template`: A ResourceTemplate instance to add. The template's .key attribute
|
||||
will be used as the storage key. To overwrite it, call
|
||||
ResourceTemplate.with_key() before calling this method.
|
||||
|
||||
**Returns:**
|
||||
- The added template. If a template with the same URI already exists,
|
||||
- returns the existing template.
|
||||
|
||||
104
docs/python-sdk/fastmcp-resources-template.mdx
Normal file
104
docs/python-sdk/fastmcp-resources-template.mdx
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
---
|
||||
title: template
|
||||
sidebarTitle: template
|
||||
---
|
||||
|
||||
# `fastmcp.resources.template`
|
||||
|
||||
|
||||
Resource template functionality.
|
||||
|
||||
## Functions
|
||||
|
||||
### `build_regex`
|
||||
|
||||
```python
|
||||
build_regex(template: str) -> re.Pattern
|
||||
```
|
||||
|
||||
### `match_uri_template`
|
||||
|
||||
```python
|
||||
match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None
|
||||
```
|
||||
|
||||
## Classes
|
||||
|
||||
### `ResourceTemplate`
|
||||
|
||||
|
||||
A template for dynamically creating resources.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```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`
|
||||
|
||||
```python
|
||||
set_default_mime_type(cls, mime_type: str | None) -> str
|
||||
```
|
||||
|
||||
Set default MIME type if not provided.
|
||||
|
||||
|
||||
#### `matches`
|
||||
|
||||
```python
|
||||
matches(self, uri: str) -> dict[str, Any] | None
|
||||
```
|
||||
|
||||
Check if URI matches template and extract parameters.
|
||||
|
||||
|
||||
#### `to_mcp_template`
|
||||
|
||||
```python
|
||||
to_mcp_template(self, **overrides: Any) -> MCPResourceTemplate
|
||||
```
|
||||
|
||||
Convert the resource template to an MCPResourceTemplate.
|
||||
|
||||
|
||||
#### `from_mcp_template`
|
||||
|
||||
```python
|
||||
from_mcp_template(cls, mcp_template: MCPResourceTemplate) -> ResourceTemplate
|
||||
```
|
||||
|
||||
Creates a FastMCP ResourceTemplate from a raw MCP ResourceTemplate object.
|
||||
|
||||
|
||||
#### `key`
|
||||
|
||||
```python
|
||||
key(self) -> str
|
||||
```
|
||||
|
||||
The key of the component. This is used for internal bookkeeping
|
||||
and may reflect e.g. prefixes or other identifiers. You should not depend on
|
||||
keys having a certain value, as the same tool loaded from different
|
||||
hierarchies of servers may have different keys.
|
||||
|
||||
|
||||
### `FunctionResourceTemplate`
|
||||
|
||||
|
||||
A template for dynamically creating resources.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a template from a function.
|
||||
|
||||
83
docs/python-sdk/fastmcp-resources-types.mdx
Normal file
83
docs/python-sdk/fastmcp-resources-types.mdx
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
title: types
|
||||
sidebarTitle: types
|
||||
---
|
||||
|
||||
# `fastmcp.resources.types`
|
||||
|
||||
|
||||
Concrete resource implementations.
|
||||
|
||||
## Classes
|
||||
|
||||
### `TextResource`
|
||||
|
||||
|
||||
A resource that reads from a string.
|
||||
|
||||
|
||||
### `BinaryResource`
|
||||
|
||||
|
||||
A resource that reads from bytes.
|
||||
|
||||
|
||||
### `FileResource`
|
||||
|
||||
|
||||
A resource that reads from a file.
|
||||
|
||||
Set is_binary=True to read file as binary data instead of text.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `validate_absolute_path`
|
||||
|
||||
```python
|
||||
validate_absolute_path(cls, path: Path) -> Path
|
||||
```
|
||||
|
||||
Ensure path is absolute.
|
||||
|
||||
|
||||
#### `set_binary_from_mime_type`
|
||||
|
||||
```python
|
||||
set_binary_from_mime_type(cls, is_binary: bool, info: ValidationInfo) -> bool
|
||||
```
|
||||
|
||||
Set is_binary based on mime_type if not explicitly set.
|
||||
|
||||
|
||||
### `HttpResource`
|
||||
|
||||
|
||||
A resource that reads from an HTTP endpoint.
|
||||
|
||||
|
||||
### `DirectoryResource`
|
||||
|
||||
|
||||
A resource that lists files in a directory.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `validate_absolute_path`
|
||||
|
||||
```python
|
||||
validate_absolute_path(cls, path: Path) -> Path
|
||||
```
|
||||
|
||||
Ensure path is absolute.
|
||||
|
||||
|
||||
#### `list_files`
|
||||
|
||||
```python
|
||||
list_files(self) -> list[Path]
|
||||
```
|
||||
|
||||
List files in the directory.
|
||||
|
||||
8
docs/python-sdk/fastmcp-server-__init__.mdx
Normal file
8
docs/python-sdk/fastmcp-server-__init__.mdx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.server`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
8
docs/python-sdk/fastmcp-server-auth-__init__.mdx
Normal file
8
docs/python-sdk/fastmcp-server-auth-__init__.mdx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.server.auth`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
10
docs/python-sdk/fastmcp-server-auth-auth.mdx
Normal file
10
docs/python-sdk/fastmcp-server-auth-auth.mdx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: auth
|
||||
sidebarTitle: auth
|
||||
---
|
||||
|
||||
# `fastmcp.server.auth.auth`
|
||||
|
||||
## Classes
|
||||
|
||||
### `OAuthProvider`
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.server.auth.providers`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
69
docs/python-sdk/fastmcp-server-auth-providers-bearer.mdx
Normal file
69
docs/python-sdk/fastmcp-server-auth-providers-bearer.mdx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
title: bearer
|
||||
sidebarTitle: bearer
|
||||
---
|
||||
|
||||
# `fastmcp.server.auth.providers.bearer`
|
||||
|
||||
## Classes
|
||||
|
||||
### `JWKData`
|
||||
|
||||
|
||||
JSON Web Key data structure.
|
||||
|
||||
|
||||
### `JWKSData`
|
||||
|
||||
|
||||
JSON Web Key Set data structure.
|
||||
|
||||
|
||||
### `RSAKeyPair`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `generate`
|
||||
|
||||
```python
|
||||
generate(cls) -> 'RSAKeyPair'
|
||||
```
|
||||
|
||||
Generate an RSA key pair for testing.
|
||||
|
||||
**Returns:**
|
||||
- (private_key_pem, public_key_pem)
|
||||
|
||||
|
||||
#### `create_token`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Generate a test JWT token for testing purposes.
|
||||
|
||||
**Args:**
|
||||
- `private_key_pem`: RSA private key in PEM format
|
||||
- `subject`: Subject claim (usually user ID)
|
||||
- `issuer`: Issuer claim
|
||||
- `audience`: Audience claim - can be a string or list of strings (optional)
|
||||
- `scopes`: List of scopes to include
|
||||
- `expires_in_seconds`: Token expiration time in seconds
|
||||
- `additional_claims`: Any additional claims to include
|
||||
- `kid`: Key ID for JWKS lookup (optional)
|
||||
|
||||
**Returns:**
|
||||
- Signed JWT token string
|
||||
|
||||
|
||||
### `BearerAuthProvider`
|
||||
|
||||
|
||||
Simple JWT Bearer Token validator for hosted MCP servers.
|
||||
Uses RS256 asymmetric encryption. Supports either static public key
|
||||
or JWKS URI for key rotation.
|
||||
|
||||
Note that this provider DOES NOT permit client registration or revocation, or any OAuth flows.
|
||||
It is intended to be used with a control plane that manages clients and tokens.
|
||||
|
||||
22
docs/python-sdk/fastmcp-server-auth-providers-bearer_env.mdx
Normal file
22
docs/python-sdk/fastmcp-server-auth-providers-bearer_env.mdx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: bearer_env
|
||||
sidebarTitle: bearer_env
|
||||
---
|
||||
|
||||
# `fastmcp.server.auth.providers.bearer_env`
|
||||
|
||||
## Classes
|
||||
|
||||
### `EnvBearerAuthProviderSettings`
|
||||
|
||||
|
||||
Settings for the BearerAuthProvider.
|
||||
|
||||
|
||||
### `EnvBearerAuthProvider`
|
||||
|
||||
|
||||
A BearerAuthProvider that loads settings from environment variables. Any
|
||||
providing setting will always take precedence over the environment
|
||||
variables.
|
||||
|
||||
15
docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx
Normal file
15
docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
title: in_memory
|
||||
sidebarTitle: in_memory
|
||||
---
|
||||
|
||||
# `fastmcp.server.auth.providers.in_memory`
|
||||
|
||||
## Classes
|
||||
|
||||
### `InMemoryOAuthProvider`
|
||||
|
||||
|
||||
An in-memory OAuth provider for testing purposes.
|
||||
It simulates the OAuth 2.1 flow locally without external calls.
|
||||
|
||||
118
docs/python-sdk/fastmcp-server-context.mdx
Normal file
118
docs/python-sdk/fastmcp-server-context.mdx
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
title: context
|
||||
sidebarTitle: context
|
||||
---
|
||||
|
||||
# `fastmcp.server.context`
|
||||
|
||||
## Functions
|
||||
|
||||
### `set_context`
|
||||
|
||||
```python
|
||||
set_context(context: Context) -> Generator[Context, None, None]
|
||||
```
|
||||
|
||||
## Classes
|
||||
|
||||
### `Context`
|
||||
|
||||
|
||||
Context object providing access to MCP capabilities.
|
||||
|
||||
This provides a cleaner interface to MCP's RequestContext functionality.
|
||||
It gets injected into tool and resource functions that request it via type hints.
|
||||
|
||||
To use context in a tool function, add a parameter with the Context type annotation:
|
||||
|
||||
```python
|
||||
@server.tool
|
||||
def my_tool(x: int, ctx: Context) -> str:
|
||||
# Log messages to the client
|
||||
ctx.info(f"Processing {x}")
|
||||
ctx.debug("Debug info")
|
||||
ctx.warning("Warning message")
|
||||
ctx.error("Error message")
|
||||
|
||||
# Report progress
|
||||
ctx.report_progress(50, 100, "Processing")
|
||||
|
||||
# Access resources
|
||||
data = ctx.read_resource("resource://data")
|
||||
|
||||
# Get request info
|
||||
request_id = ctx.request_id
|
||||
client_id = ctx.client_id
|
||||
|
||||
return str(x)
|
||||
```
|
||||
|
||||
The context parameter name can be anything as long as it's annotated with Context.
|
||||
The context is optional - tools that don't need it can omit the parameter.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `request_context`
|
||||
|
||||
```python
|
||||
request_context(self) -> RequestContext
|
||||
```
|
||||
|
||||
Access to the underlying request context.
|
||||
|
||||
If called outside of a request context, this will raise a ValueError.
|
||||
|
||||
|
||||
#### `client_id`
|
||||
|
||||
```python
|
||||
client_id(self) -> str | None
|
||||
```
|
||||
|
||||
Get the client ID if available.
|
||||
|
||||
|
||||
#### `request_id`
|
||||
|
||||
```python
|
||||
request_id(self) -> str
|
||||
```
|
||||
|
||||
Get the unique ID for this request.
|
||||
|
||||
|
||||
#### `session_id`
|
||||
|
||||
```python
|
||||
session_id(self) -> str | None
|
||||
```
|
||||
|
||||
Get the MCP session ID for HTTP transports.
|
||||
|
||||
Returns the session ID that can be used as a key for session-based
|
||||
data storage (e.g., Redis) to share data between tool calls within
|
||||
the same client session.
|
||||
|
||||
**Returns:**
|
||||
- The session ID for HTTP transports (SSE, StreamableHTTP), or None
|
||||
- for stdio and in-memory transports which don't use session IDs.
|
||||
|
||||
|
||||
#### `session`
|
||||
|
||||
```python
|
||||
session(self)
|
||||
```
|
||||
|
||||
Access to the underlying session for advanced usage.
|
||||
|
||||
|
||||
#### `get_http_request`
|
||||
|
||||
```python
|
||||
get_http_request(self) -> Request
|
||||
```
|
||||
|
||||
Get the active starlette request.
|
||||
|
||||
36
docs/python-sdk/fastmcp-server-dependencies.mdx
Normal file
36
docs/python-sdk/fastmcp-server-dependencies.mdx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
title: dependencies
|
||||
sidebarTitle: dependencies
|
||||
---
|
||||
|
||||
# `fastmcp.server.dependencies`
|
||||
|
||||
## Functions
|
||||
|
||||
### `get_context`
|
||||
|
||||
```python
|
||||
get_context() -> Context
|
||||
```
|
||||
|
||||
### `get_http_request`
|
||||
|
||||
```python
|
||||
get_http_request() -> Request
|
||||
```
|
||||
|
||||
### `get_http_headers`
|
||||
|
||||
```python
|
||||
get_http_headers(include_all: bool = False) -> dict[str, str]
|
||||
```
|
||||
|
||||
|
||||
Extract headers from the current HTTP request if available.
|
||||
|
||||
Never raises an exception, even if there is no active HTTP request (in which case
|
||||
an empty dict is returned).
|
||||
|
||||
By default, strips problematic headers like `content-length` that cause issues if forwarded to downstream clients.
|
||||
If `include_all` is True, all headers are returned.
|
||||
|
||||
113
docs/python-sdk/fastmcp-server-http.mdx
Normal file
113
docs/python-sdk/fastmcp-server-http.mdx
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
title: http
|
||||
sidebarTitle: http
|
||||
---
|
||||
|
||||
# `fastmcp.server.http`
|
||||
|
||||
## Functions
|
||||
|
||||
### `set_http_request`
|
||||
|
||||
```python
|
||||
set_http_request(request: Request) -> Generator[Request, None, None]
|
||||
```
|
||||
|
||||
### `setup_auth_middleware_and_routes`
|
||||
|
||||
```python
|
||||
setup_auth_middleware_and_routes(auth: OAuthProvider) -> tuple[list[Middleware], list[BaseRoute], list[str]]
|
||||
```
|
||||
|
||||
|
||||
Set up authentication middleware and routes if auth is enabled.
|
||||
|
||||
**Args:**
|
||||
- `auth`: The OAuthProvider authorization server provider
|
||||
|
||||
**Returns:**
|
||||
- Tuple of (middleware, auth_routes, required_scopes)
|
||||
|
||||
|
||||
### `create_base_app`
|
||||
|
||||
```python
|
||||
create_base_app(routes: list[BaseRoute], middleware: list[Middleware], debug: bool = False, lifespan: Callable | None = None) -> StarletteWithLifespan
|
||||
```
|
||||
|
||||
|
||||
Create a base Starlette app with common middleware and routes.
|
||||
|
||||
**Args:**
|
||||
- `routes`: List of routes to include in the app
|
||||
- `middleware`: List of middleware to include in the app
|
||||
- `debug`: Whether to enable debug mode
|
||||
- `lifespan`: Optional lifespan manager for the app
|
||||
|
||||
**Returns:**
|
||||
- A Starlette application
|
||||
|
||||
|
||||
### `create_sse_app`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
|
||||
Return an instance of the SSE server app.
|
||||
|
||||
**Args:**
|
||||
- `server`: The FastMCP server instance
|
||||
- `message_path`: Path for SSE messages
|
||||
- `sse_path`: Path for SSE connections
|
||||
- `auth`: Optional auth provider
|
||||
- `debug`: Whether to enable debug mode
|
||||
- `routes`: Optional list of custom routes
|
||||
- `middleware`: Optional list of middleware
|
||||
|
||||
Returns:
|
||||
A Starlette application with RequestContextMiddleware
|
||||
|
||||
|
||||
### `create_streamable_http_app`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
|
||||
Return an instance of the StreamableHTTP server app.
|
||||
|
||||
**Args:**
|
||||
- `server`: The FastMCP server instance
|
||||
- `streamable_http_path`: Path for StreamableHTTP connections
|
||||
- `event_store`: Optional event store for session management
|
||||
- `auth`: Optional auth provider
|
||||
- `json_response`: Whether to use JSON response format
|
||||
- `stateless_http`: Whether to use stateless mode (new transport per request)
|
||||
- `debug`: Whether to enable debug mode
|
||||
- `routes`: Optional list of custom routes
|
||||
- `middleware`: Optional list of middleware
|
||||
|
||||
**Returns:**
|
||||
- A Starlette application with StreamableHTTP support
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `StarletteWithLifespan`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `lifespan`
|
||||
|
||||
```python
|
||||
lifespan(self) -> Lifespan
|
||||
```
|
||||
|
||||
### `RequestContextMiddleware`
|
||||
|
||||
|
||||
Middleware that stores each request in a ContextVar
|
||||
|
||||
56
docs/python-sdk/fastmcp-server-middleware.mdx
Normal file
56
docs/python-sdk/fastmcp-server-middleware.mdx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
title: middleware
|
||||
sidebarTitle: middleware
|
||||
---
|
||||
|
||||
# `fastmcp.server.middleware`
|
||||
|
||||
## Functions
|
||||
|
||||
### `make_middleware_wrapper`
|
||||
|
||||
```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`
|
||||
|
||||
### `CallToolResult`
|
||||
|
||||
### `ListToolsResult`
|
||||
|
||||
### `ListResourcesResult`
|
||||
|
||||
### `ListResourceTemplatesResult`
|
||||
|
||||
### `ListPromptsResult`
|
||||
|
||||
### `ServerResultProtocol`
|
||||
|
||||
### `MiddlewareContext`
|
||||
|
||||
|
||||
Unified context for all middleware operations.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `copy`
|
||||
|
||||
```python
|
||||
copy(self, **kwargs: Any) -> MiddlewareContext[T]
|
||||
```
|
||||
|
||||
### `Middleware`
|
||||
|
||||
|
||||
Base class for FastMCP middleware with dispatching hooks.
|
||||
|
||||
58
docs/python-sdk/fastmcp-server-openapi.mdx
Normal file
58
docs/python-sdk/fastmcp-server-openapi.mdx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
title: openapi
|
||||
sidebarTitle: openapi
|
||||
---
|
||||
|
||||
# `fastmcp.server.openapi`
|
||||
|
||||
|
||||
FastMCP server implementation for OpenAPI integration.
|
||||
|
||||
## Classes
|
||||
|
||||
### `MCPType`
|
||||
|
||||
|
||||
Type of FastMCP component to create from a route.
|
||||
|
||||
|
||||
### `RouteType`
|
||||
|
||||
|
||||
Deprecated: Use MCPType instead.
|
||||
|
||||
This enum is kept for backward compatibility and will be removed in a future version.
|
||||
|
||||
|
||||
### `RouteMap`
|
||||
|
||||
|
||||
Mapping configuration for HTTP routes to FastMCP component types.
|
||||
|
||||
|
||||
### `OpenAPITool`
|
||||
|
||||
|
||||
Tool implementation for OpenAPI endpoints.
|
||||
|
||||
|
||||
### `OpenAPIResource`
|
||||
|
||||
|
||||
Resource implementation for OpenAPI endpoints.
|
||||
|
||||
|
||||
### `OpenAPIResourceTemplate`
|
||||
|
||||
|
||||
Resource template implementation for OpenAPI endpoints.
|
||||
|
||||
|
||||
### `FastMCPOpenAPI`
|
||||
|
||||
|
||||
FastMCP server implementation that creates components from an OpenAPI schema.
|
||||
|
||||
This class parses an OpenAPI specification and creates appropriate FastMCP components
|
||||
(Tools, Resources, ResourceTemplates) based on route mappings.
|
||||
|
||||
101
docs/python-sdk/fastmcp-server-proxy.mdx
Normal file
101
docs/python-sdk/fastmcp-server-proxy.mdx
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
---
|
||||
title: proxy
|
||||
sidebarTitle: proxy
|
||||
---
|
||||
|
||||
# `fastmcp.server.proxy`
|
||||
|
||||
## Classes
|
||||
|
||||
### `ProxyToolManager`
|
||||
|
||||
|
||||
A ToolManager that sources its tools from a remote client in addition to local and mounted tools.
|
||||
|
||||
|
||||
### `ProxyResourceManager`
|
||||
|
||||
|
||||
A ResourceManager that sources its resources from a remote client in addition to local and mounted resources.
|
||||
|
||||
|
||||
### `ProxyPromptManager`
|
||||
|
||||
|
||||
A PromptManager that sources its prompts from a remote client in addition to local and mounted prompts.
|
||||
|
||||
|
||||
### `ProxyTool`
|
||||
|
||||
|
||||
A Tool that represents and executes a tool on a remote server.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_mcp_tool`
|
||||
|
||||
```python
|
||||
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`
|
||||
|
||||
|
||||
A Resource that represents and reads a resource from a remote server.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_mcp_resource`
|
||||
|
||||
```python
|
||||
from_mcp_resource(cls, client: Client, mcp_resource: mcp.types.Resource) -> ProxyResource
|
||||
```
|
||||
|
||||
Factory method to create a ProxyResource from a raw MCP resource schema.
|
||||
|
||||
|
||||
### `ProxyTemplate`
|
||||
|
||||
|
||||
A ResourceTemplate that represents and creates resources from a remote server template.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_mcp_template`
|
||||
|
||||
```python
|
||||
from_mcp_template(cls, client: Client, mcp_template: mcp.types.ResourceTemplate) -> ProxyTemplate
|
||||
```
|
||||
|
||||
Factory method to create a ProxyTemplate from a raw MCP template schema.
|
||||
|
||||
|
||||
### `ProxyPrompt`
|
||||
|
||||
|
||||
A Prompt that represents and renders a prompt from a remote server.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_mcp_prompt`
|
||||
|
||||
```python
|
||||
from_mcp_prompt(cls, client: Client, mcp_prompt: mcp.types.Prompt) -> ProxyPrompt
|
||||
```
|
||||
|
||||
Factory method to create a ProxyPrompt from a raw MCP prompt schema.
|
||||
|
||||
|
||||
### `FastMCPProxy`
|
||||
|
||||
|
||||
A FastMCP server that acts as a proxy to a remote MCP-compliant server.
|
||||
It uses specialized managers that fulfill requests via an HTTP client.
|
||||
|
||||
542
docs/python-sdk/fastmcp-server-server.mdx
Normal file
542
docs/python-sdk/fastmcp-server-server.mdx
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
---
|
||||
title: server
|
||||
sidebarTitle: server
|
||||
---
|
||||
|
||||
# `fastmcp.server.server`
|
||||
|
||||
|
||||
FastMCP - A more ergonomic interface for MCP servers.
|
||||
|
||||
## Functions
|
||||
|
||||
### `add_resource_prefix`
|
||||
|
||||
```python
|
||||
add_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str
|
||||
```
|
||||
|
||||
|
||||
Add a prefix to a resource URI.
|
||||
|
||||
Args:
|
||||
uri: The original resource URI
|
||||
prefix: The prefix to add
|
||||
|
||||
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
|
||||
|
||||
Raises:
|
||||
ValueError: If the URI doesn't match the expected protocol://path format
|
||||
|
||||
|
||||
### `remove_resource_prefix`
|
||||
|
||||
```python
|
||||
remove_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
Raises:
|
||||
ValueError: If the URI doesn't match the expected protocol://path format
|
||||
|
||||
|
||||
### `has_resource_prefix`
|
||||
|
||||
```python
|
||||
has_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> bool
|
||||
```
|
||||
|
||||
|
||||
Check if a resource URI has a specific prefix.
|
||||
|
||||
Args:
|
||||
uri: The resource URI to check
|
||||
prefix: The prefix to look for
|
||||
|
||||
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
|
||||
|
||||
Raises:
|
||||
ValueError: If the URI doesn't match the expected protocol://path format
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `FastMCP`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `settings`
|
||||
|
||||
```python
|
||||
settings(self) -> Settings
|
||||
```
|
||||
|
||||
#### `name`
|
||||
|
||||
```python
|
||||
name(self) -> str
|
||||
```
|
||||
|
||||
#### `instructions`
|
||||
|
||||
```python
|
||||
instructions(self) -> str | None
|
||||
```
|
||||
|
||||
#### `run`
|
||||
|
||||
```python
|
||||
run(self, transport: Literal['stdio', 'streamable-http', 'sse'] | None = None, **transport_kwargs: Any) -> None
|
||||
```
|
||||
|
||||
Run the FastMCP server. Note this is a synchronous function.
|
||||
|
||||
**Args:**
|
||||
- `transport`: Transport protocol to use ("stdio", "sse", or "streamable-http")
|
||||
|
||||
|
||||
#### `add_middleware`
|
||||
|
||||
```python
|
||||
add_middleware(self, middleware: Middleware) -> None
|
||||
```
|
||||
|
||||
#### `custom_route`
|
||||
|
||||
```python
|
||||
custom_route(self, path: str, methods: list[str], name: str | None = None, include_in_schema: bool = True)
|
||||
```
|
||||
|
||||
Decorator to register a custom HTTP route on the FastMCP server.
|
||||
|
||||
Allows adding arbitrary HTTP endpoints outside the standard MCP protocol,
|
||||
which can be useful for OAuth callbacks, health checks, or admin APIs.
|
||||
The handler function must be an async function that accepts a Starlette
|
||||
Request and returns a Response.
|
||||
|
||||
**Args:**
|
||||
- `path`: URL path for the route (e.g., "/oauth/callback")
|
||||
- `methods`: List of HTTP methods to support (e.g., ["GET", "POST"])
|
||||
- `name`: Optional name for the route (to reference this route with
|
||||
Starlette's reverse URL lookup feature)
|
||||
- `include_in_schema`: Whether to include in OpenAPI schema, defaults to True
|
||||
|
||||
|
||||
#### `add_tool`
|
||||
|
||||
```python
|
||||
add_tool(self, tool: Tool) -> None
|
||||
```
|
||||
|
||||
Add a tool to the server.
|
||||
|
||||
The tool function can optionally request a Context object by adding a parameter
|
||||
with the Context type annotation. See the @tool decorator for examples.
|
||||
|
||||
**Args:**
|
||||
- `tool`: The Tool instance to register
|
||||
|
||||
|
||||
#### `remove_tool`
|
||||
|
||||
```python
|
||||
remove_tool(self, name: str) -> None
|
||||
```
|
||||
|
||||
Remove a tool from the server.
|
||||
|
||||
**Args:**
|
||||
- `name`: The name of the tool to remove
|
||||
|
||||
**Raises:**
|
||||
- `NotFoundError`: If the tool is not found
|
||||
|
||||
|
||||
#### `tool`
|
||||
|
||||
```python
|
||||
tool(self, name_or_fn: AnyFunction) -> FunctionTool
|
||||
```
|
||||
|
||||
#### `tool`
|
||||
|
||||
```python
|
||||
tool(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionTool]
|
||||
```
|
||||
|
||||
#### `tool`
|
||||
|
||||
```python
|
||||
tool(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool
|
||||
```
|
||||
|
||||
Decorator to register a tool.
|
||||
|
||||
Tools can optionally request a Context object by adding a parameter with the
|
||||
Context type annotation. The context provides access to MCP capabilities like
|
||||
logging, progress reporting, and resource access.
|
||||
|
||||
This decorator supports multiple calling patterns:
|
||||
- @server.tool (without parentheses)
|
||||
- @server.tool (with empty parentheses)
|
||||
- @server.tool("custom_name") (with name as first argument)
|
||||
- @server.tool(name="custom_name") (with name as keyword argument)
|
||||
- server.tool(function, name="custom_name") (direct function call)
|
||||
|
||||
**Args:**
|
||||
- `name_or_fn`: Either a function (when used as @tool), a string name, or None
|
||||
- `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})
|
||||
- `exclude_args`: Optional list of argument names to exclude from the tool schema
|
||||
- `enabled`: Optional boolean to enable or disable the tool
|
||||
|
||||
|
||||
#### `add_resource`
|
||||
|
||||
```python
|
||||
add_resource(self, resource: Resource) -> None
|
||||
```
|
||||
|
||||
Add a resource to the server.
|
||||
|
||||
**Args:**
|
||||
- `resource`: A Resource instance to add
|
||||
|
||||
|
||||
#### `add_template`
|
||||
|
||||
```python
|
||||
add_template(self, template: ResourceTemplate) -> None
|
||||
```
|
||||
|
||||
Add a resource template to the server.
|
||||
|
||||
**Args:**
|
||||
- `template`: A ResourceTemplate instance to add
|
||||
|
||||
|
||||
#### `add_resource_fn`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Add a resource or template to the server from a function.
|
||||
|
||||
If the URI contains parameters (e.g. "resource://{param}") or the function
|
||||
has parameters, it will be registered as a template resource.
|
||||
|
||||
**Args:**
|
||||
- `fn`: The function to register as a resource
|
||||
- `uri`: The URI for the resource
|
||||
- `name`: Optional name for the resource
|
||||
- `description`: Optional description of the resource
|
||||
- `mime_type`: Optional MIME type for the resource
|
||||
- `tags`: Optional set of tags for categorizing the resource
|
||||
|
||||
|
||||
#### `resource`
|
||||
|
||||
```python
|
||||
resource(self, uri: str) -> Callable[[AnyFunction], Resource | ResourceTemplate]
|
||||
```
|
||||
|
||||
Decorator to register a function as a resource.
|
||||
|
||||
The function will be called when the resource is read to generate its content.
|
||||
The function can return:
|
||||
- str for text content
|
||||
- bytes for binary content
|
||||
- other types will be converted to JSON
|
||||
|
||||
Resources can optionally request a Context object by adding a parameter with the
|
||||
Context type annotation. The context provides access to MCP capabilities like
|
||||
logging, progress reporting, and session information.
|
||||
|
||||
If the URI contains parameters (e.g. "resource://{param}") or the function
|
||||
has parameters, it will be registered as a template resource.
|
||||
|
||||
**Args:**
|
||||
- `uri`: URI for the resource (e.g. "resource\://my-resource" or "resource\://{param}")
|
||||
- `name`: Optional name for the resource
|
||||
- `description`: Optional description of the resource
|
||||
- `mime_type`: Optional MIME type for the resource
|
||||
- `tags`: Optional set of tags for categorizing the resource
|
||||
- `enabled`: Optional boolean to enable or disable the resource
|
||||
|
||||
|
||||
#### `add_prompt`
|
||||
|
||||
```python
|
||||
add_prompt(self, prompt: Prompt) -> None
|
||||
```
|
||||
|
||||
Add a prompt to the server.
|
||||
|
||||
**Args:**
|
||||
- `prompt`: A Prompt instance to add
|
||||
|
||||
|
||||
#### `prompt`
|
||||
|
||||
```python
|
||||
prompt(self, name_or_fn: AnyFunction) -> FunctionPrompt
|
||||
```
|
||||
|
||||
#### `prompt`
|
||||
|
||||
```python
|
||||
prompt(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionPrompt]
|
||||
```
|
||||
|
||||
#### `prompt`
|
||||
|
||||
```python
|
||||
prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt
|
||||
```
|
||||
|
||||
Decorator to register a prompt.
|
||||
|
||||
Prompts can optionally request a Context object by adding a parameter with the
|
||||
Context type annotation. The context provides access to MCP capabilities like
|
||||
logging, progress reporting, and session information.
|
||||
|
||||
This decorator supports multiple calling patterns:
|
||||
- @server.prompt (without parentheses)
|
||||
- @server.prompt() (with empty parentheses)
|
||||
- @server.prompt("custom_name") (with name as first argument)
|
||||
- @server.prompt(name="custom_name") (with name as keyword argument)
|
||||
- server.prompt(function, name="custom_name") (direct function call)
|
||||
|
||||
Args:
|
||||
name_or_fn: Either a function (when used as @prompt), a string name, or None
|
||||
name: Optional name for the prompt (keyword-only, alternative to name_or_fn)
|
||||
description: Optional description of what the prompt does
|
||||
tags: Optional set of tags for categorizing the prompt
|
||||
enabled: Optional boolean to enable or disable the prompt
|
||||
|
||||
Example:
|
||||
@server.prompt
|
||||
def analyze_table(table_name: str) -> list\[Message]:
|
||||
schema = read_table_schema(table_name)
|
||||
return [
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"Analyze this schema:
|
||||
{schema}"
|
||||
}
|
||||
]
|
||||
|
||||
@server.prompt()
|
||||
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 [
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"Analyze this schema:
|
||||
{schema}"
|
||||
}
|
||||
]
|
||||
|
||||
@server.prompt("custom_name")
|
||||
def analyze_file(path: str) -> list\[Message]:
|
||||
content = await read_file(path)
|
||||
return [
|
||||
{
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "resource",
|
||||
"resource": {
|
||||
"uri": f"file://{path}",
|
||||
"text": content
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@server.prompt(name="custom_name")
|
||||
def another_prompt(data: str) -> list\[Message]:
|
||||
return [{"role": "user", "content": data}]
|
||||
|
||||
# Direct function call
|
||||
server.prompt(my_function, name="custom_name")
|
||||
|
||||
|
||||
#### `sse_app`
|
||||
|
||||
```python
|
||||
sse_app(self, path: str | None = None, message_path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan
|
||||
```
|
||||
|
||||
Create a Starlette app for the SSE server.
|
||||
|
||||
**Args:**
|
||||
- `path`: The path to the SSE endpoint
|
||||
- `message_path`: The path to the message endpoint
|
||||
- `middleware`: A list of middleware to apply to the app
|
||||
|
||||
|
||||
#### `streamable_http_app`
|
||||
|
||||
```python
|
||||
streamable_http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan
|
||||
```
|
||||
|
||||
Create a Starlette app for the StreamableHTTP server.
|
||||
|
||||
**Args:**
|
||||
- `path`: The path to the StreamableHTTP endpoint
|
||||
- `middleware`: A list of middleware to apply to the app
|
||||
|
||||
|
||||
#### `http_app`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a Starlette app using the specified HTTP transport.
|
||||
|
||||
**Args:**
|
||||
- `path`: The path for the HTTP endpoint
|
||||
- `middleware`: A list of middleware to apply to the app
|
||||
- `transport`: Transport protocol to use - either "streamable-http" (default) or "sse"
|
||||
|
||||
**Returns:**
|
||||
- A Starlette application configured with the specified transport
|
||||
|
||||
|
||||
#### `mount`
|
||||
|
||||
```python
|
||||
mount(self, server: FastMCP[LifespanResultT], prefix: str | None = None, as_proxy: bool | None = None) -> None
|
||||
```
|
||||
|
||||
Mount another FastMCP server on this server with an optional prefix.
|
||||
|
||||
Unlike importing (with import_server), mounting establishes a dynamic connection
|
||||
between servers. When a client interacts with a mounted server's objects through
|
||||
the parent server, requests are forwarded to the mounted server in real-time.
|
||||
This means changes to the mounted server are immediately reflected when accessed
|
||||
through the parent.
|
||||
|
||||
When a server is mounted with a prefix:
|
||||
- Tools from the mounted server are accessible with prefixed names.
|
||||
Example: If server has a tool named "get_weather", it will be available as "prefix_get_weather".
|
||||
- Resources are accessible with prefixed URIs.
|
||||
Example: If server has a resource with URI "weather://forecast", it will be available as
|
||||
"weather://prefix/forecast".
|
||||
- Templates are accessible with prefixed URI templates.
|
||||
Example: If server has a template with URI "weather://location/{id}", it will be available
|
||||
as "weather://prefix/location/{id}".
|
||||
- Prompts are accessible with prefixed names.
|
||||
Example: If server has a prompt named "weather_prompt", it will be available as
|
||||
"prefix_weather_prompt".
|
||||
|
||||
When a server is mounted without a prefix (prefix=None), its tools, resources, templates,
|
||||
and prompts are accessible with their original names. Multiple servers can be mounted
|
||||
without prefixes, and they will be tried in order until a match is found.
|
||||
|
||||
There are two modes for mounting servers:
|
||||
1. Direct mounting (default when server has no custom lifespan): The parent server
|
||||
directly accesses the mounted server's objects in-memory for better performance.
|
||||
In this mode, no client lifecycle events occur on the mounted server, including
|
||||
lifespan execution.
|
||||
|
||||
2. Proxy mounting (default when server has a custom lifespan): The parent server
|
||||
treats the mounted server as a separate entity and communicates with it via a
|
||||
Client transport. This preserves all client-facing behaviors, including lifespan
|
||||
execution, but with slightly higher overhead.
|
||||
|
||||
**Args:**
|
||||
- `server`: The FastMCP server to mount.
|
||||
- `prefix`: Optional prefix to use for the mounted server's objects. If None,
|
||||
the server's objects are accessible with their original names.
|
||||
- `as_proxy`: Whether to treat the mounted server as a proxy. If None (default),
|
||||
automatically determined based on whether the server has a custom lifespan
|
||||
(True if it has a custom lifespan, False otherwise).
|
||||
- `tool_separator`: Deprecated. Separator character for tool names.
|
||||
- `resource_separator`: Deprecated. Separator character for resource URIs.
|
||||
- `prompt_separator`: Deprecated. Separator character for prompt names.
|
||||
|
||||
|
||||
#### `from_openapi`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a FastMCP server from an OpenAPI specification.
|
||||
|
||||
|
||||
#### `from_fastapi`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a FastMCP server from a FastAPI application.
|
||||
|
||||
|
||||
#### `as_proxy`
|
||||
|
||||
```python
|
||||
as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
#### `from_client`
|
||||
|
||||
```python
|
||||
from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPProxy
|
||||
```
|
||||
|
||||
Create a FastMCP proxy server from a FastMCP client.
|
||||
|
||||
|
||||
### `MountedServer`
|
||||
59
docs/python-sdk/fastmcp-settings.mdx
Normal file
59
docs/python-sdk/fastmcp-settings.mdx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
title: settings
|
||||
sidebarTitle: settings
|
||||
---
|
||||
|
||||
# `fastmcp.settings`
|
||||
|
||||
## Classes
|
||||
|
||||
### `ExtendedEnvSettingsSource`
|
||||
|
||||
|
||||
A special EnvSettingsSource that allows for multiple env var prefixes to be used.
|
||||
|
||||
Raises a deprecation warning if the old `FASTMCP_SERVER_` prefix is used.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `get_field_value`
|
||||
|
||||
```python
|
||||
get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]
|
||||
```
|
||||
|
||||
### `ExtendedSettingsConfigDict`
|
||||
|
||||
### `Settings`
|
||||
|
||||
|
||||
FastMCP settings.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `settings_customise_sources`
|
||||
|
||||
```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`
|
||||
|
||||
```python
|
||||
settings(self) -> Self
|
||||
```
|
||||
|
||||
This property is for backwards compatibility with FastMCP < 2.8.0,
|
||||
which accessed fastmcp.settings.settings
|
||||
|
||||
|
||||
#### `setup_logging`
|
||||
|
||||
```python
|
||||
setup_logging(self) -> Self
|
||||
```
|
||||
|
||||
Finalize the settings.
|
||||
|
||||
8
docs/python-sdk/fastmcp-tools-__init__.mdx
Normal file
8
docs/python-sdk/fastmcp-tools-__init__.mdx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.tools`
|
||||
|
||||
*This module is empty or contains only private/internal implementations.*
|
||||
68
docs/python-sdk/fastmcp-tools-tool.mdx
Normal file
68
docs/python-sdk/fastmcp-tools-tool.mdx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
title: tool
|
||||
sidebarTitle: tool
|
||||
---
|
||||
|
||||
# `fastmcp.tools.tool`
|
||||
|
||||
## Functions
|
||||
|
||||
### `default_serializer`
|
||||
|
||||
```python
|
||||
default_serializer(data: Any) -> str
|
||||
```
|
||||
|
||||
## Classes
|
||||
|
||||
### `Tool`
|
||||
|
||||
|
||||
Internal tool registration info.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `to_mcp_tool`
|
||||
|
||||
```python
|
||||
to_mcp_tool(self, **overrides: Any) -> MCPTool
|
||||
```
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a Tool from a function.
|
||||
|
||||
|
||||
#### `from_tool`
|
||||
|
||||
```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`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a Tool from a function.
|
||||
|
||||
|
||||
### `ParsedFunction`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_function`
|
||||
|
||||
```python
|
||||
from_function(cls, fn: Callable[..., Any], exclude_args: list[str] | None = None, validate: bool = True) -> ParsedFunction
|
||||
```
|
||||
58
docs/python-sdk/fastmcp-tools-tool_manager.mdx
Normal file
58
docs/python-sdk/fastmcp-tools-tool_manager.mdx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
title: tool_manager
|
||||
sidebarTitle: tool_manager
|
||||
---
|
||||
|
||||
# `fastmcp.tools.tool_manager`
|
||||
|
||||
## Classes
|
||||
|
||||
### `ToolManager`
|
||||
|
||||
|
||||
Manages FastMCP tools.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `mount`
|
||||
|
||||
```python
|
||||
mount(self, server: MountedServer) -> None
|
||||
```
|
||||
|
||||
Adds a mounted server as a source for tools.
|
||||
|
||||
|
||||
#### `add_tool_from_fn`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Add a tool to the server.
|
||||
|
||||
|
||||
#### `add_tool`
|
||||
|
||||
```python
|
||||
add_tool(self, tool: Tool) -> Tool
|
||||
```
|
||||
|
||||
Register a tool with the server.
|
||||
|
||||
|
||||
#### `remove_tool`
|
||||
|
||||
```python
|
||||
remove_tool(self, key: str) -> None
|
||||
```
|
||||
|
||||
Remove a tool from the server.
|
||||
|
||||
**Args:**
|
||||
- `key`: The key of the tool to remove
|
||||
|
||||
**Raises:**
|
||||
- `NotFoundError`: If the tool is not found
|
||||
|
||||
117
docs/python-sdk/fastmcp-tools-tool_transform.mdx
Normal file
117
docs/python-sdk/fastmcp-tools-tool_transform.mdx
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
title: tool_transform
|
||||
sidebarTitle: tool_transform
|
||||
---
|
||||
|
||||
# `fastmcp.tools.tool_transform`
|
||||
|
||||
## Classes
|
||||
|
||||
### `ArgTransform`
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
# Rename argument 'old_name' to 'new_name'
|
||||
ArgTransform(name="new_name")
|
||||
|
||||
# Change description only
|
||||
ArgTransform(description="Updated description")
|
||||
|
||||
# Add a default value (makes argument optional)
|
||||
ArgTransform(default=42)
|
||||
|
||||
# Add a default factory (makes argument optional)
|
||||
ArgTransform(default_factory=lambda: time.time())
|
||||
|
||||
# Change the type
|
||||
ArgTransform(type=str)
|
||||
|
||||
# Hide the argument entirely from clients
|
||||
ArgTransform(hide=True)
|
||||
|
||||
# Hide argument but pass a constant value to parent
|
||||
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)
|
||||
|
||||
# Make an optional parameter required (removes any default)
|
||||
ArgTransform(required=True)
|
||||
|
||||
# Combine multiple transformations
|
||||
ArgTransform(name="new_name", description="New desc", default=None, type=int)
|
||||
|
||||
|
||||
### `TransformedTool`
|
||||
|
||||
|
||||
A tool that is transformed from another tool.
|
||||
|
||||
This class represents a tool that has been created by transforming another tool.
|
||||
It supports argument renaming, schema modification, custom function injection,
|
||||
and provides context for the forward() and forward_raw() functions.
|
||||
|
||||
The transformation can be purely schema-based (argument renaming, dropping, etc.)
|
||||
or can include a custom function that uses forward() to call the parent tool
|
||||
with transformed arguments.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_tool`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Create a transformed tool from a parent tool.
|
||||
|
||||
**Args:**
|
||||
- `tool`: The parent tool to transform.
|
||||
- `transform_fn`: Optional custom function. Can use forward() and forward_raw()
|
||||
to call the parent tool. Functions with **kwargs receive transformed
|
||||
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
|
||||
- `description`: New description. Defaults to parent's description.
|
||||
- `tags`: New tags. Defaults to parent's tags.
|
||||
- `annotations`: New annotations. Defaults to parent's annotations.
|
||||
- `serializer`: New serializer. Defaults to parent's serializer.
|
||||
|
||||
**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"})
|
||||
|
||||
9
docs/python-sdk/fastmcp-utilities-__init__.mdx
Normal file
9
docs/python-sdk/fastmcp-utilities-__init__.mdx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: __init__
|
||||
sidebarTitle: __init__
|
||||
---
|
||||
|
||||
# `fastmcp.utilities`
|
||||
|
||||
|
||||
FastMCP utility modules.
|
||||
30
docs/python-sdk/fastmcp-utilities-cache.mdx
Normal file
30
docs/python-sdk/fastmcp-utilities-cache.mdx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
title: cache
|
||||
sidebarTitle: cache
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.cache`
|
||||
|
||||
## Classes
|
||||
|
||||
### `TimedCache`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `set`
|
||||
|
||||
```python
|
||||
set(self, key: Any, value: Any) -> None
|
||||
```
|
||||
|
||||
#### `get`
|
||||
|
||||
```python
|
||||
get(self, key: Any) -> Any
|
||||
```
|
||||
|
||||
#### `clear`
|
||||
|
||||
```python
|
||||
clear(self) -> None
|
||||
```
|
||||
52
docs/python-sdk/fastmcp-utilities-components.mdx
Normal file
52
docs/python-sdk/fastmcp-utilities-components.mdx
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: components
|
||||
sidebarTitle: components
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.components`
|
||||
|
||||
## Classes
|
||||
|
||||
### `FastMCPComponent`
|
||||
|
||||
|
||||
Base class for FastMCP tools, prompts, resources, and resource templates.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `key`
|
||||
|
||||
```python
|
||||
key(self) -> str
|
||||
```
|
||||
|
||||
The key of the component. This is used for internal bookkeeping
|
||||
and may reflect e.g. prefixes or other identifiers. You should not depend on
|
||||
keys having a certain value, as the same tool loaded from different
|
||||
hierarchies of servers may have different keys.
|
||||
|
||||
|
||||
#### `with_key`
|
||||
|
||||
```python
|
||||
with_key(self, key: str) -> Self
|
||||
```
|
||||
|
||||
#### `enable`
|
||||
|
||||
```python
|
||||
enable(self) -> None
|
||||
```
|
||||
|
||||
Enable the component.
|
||||
|
||||
|
||||
#### `disable`
|
||||
|
||||
```python
|
||||
disable(self) -> None
|
||||
```
|
||||
|
||||
Disable the component.
|
||||
|
||||
20
docs/python-sdk/fastmcp-utilities-exceptions.mdx
Normal file
20
docs/python-sdk/fastmcp-utilities-exceptions.mdx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: exceptions
|
||||
sidebarTitle: exceptions
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.exceptions`
|
||||
|
||||
## Functions
|
||||
|
||||
### `iter_exc`
|
||||
|
||||
```python
|
||||
iter_exc(group: BaseExceptionGroup)
|
||||
```
|
||||
|
||||
### `get_catch_handlers`
|
||||
|
||||
```python
|
||||
get_catch_handlers() -> Mapping[type[BaseException] | Iterable[type[BaseException]], Callable[[BaseExceptionGroup[Any]], Any]]
|
||||
```
|
||||
18
docs/python-sdk/fastmcp-utilities-http.mdx
Normal file
18
docs/python-sdk/fastmcp-utilities-http.mdx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: http
|
||||
sidebarTitle: http
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.http`
|
||||
|
||||
## Functions
|
||||
|
||||
### `find_available_port`
|
||||
|
||||
```python
|
||||
find_available_port() -> int
|
||||
```
|
||||
|
||||
|
||||
Find an available port by letting the OS assign one.
|
||||
|
||||
25
docs/python-sdk/fastmcp-utilities-json_schema.mdx
Normal file
25
docs/python-sdk/fastmcp-utilities-json_schema.mdx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
title: json_schema
|
||||
sidebarTitle: json_schema
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.json_schema`
|
||||
|
||||
## Functions
|
||||
|
||||
### `compress_schema`
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
|
||||
Remove the given parameters from the schema.
|
||||
|
||||
**Args:**
|
||||
- `schema`: The schema to compress
|
||||
- `prune_params`: List of parameter names to remove from properties
|
||||
- `prune_defs`: Whether to remove unused definitions
|
||||
- `prune_additional_properties`: Whether to remove additionalProperties\: false
|
||||
- `prune_titles`: Whether to remove title fields from the schema
|
||||
|
||||
41
docs/python-sdk/fastmcp-utilities-logging.mdx
Normal file
41
docs/python-sdk/fastmcp-utilities-logging.mdx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
title: logging
|
||||
sidebarTitle: logging
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.logging`
|
||||
|
||||
|
||||
Logging utilities for FastMCP.
|
||||
|
||||
## Functions
|
||||
|
||||
### `get_logger`
|
||||
|
||||
```python
|
||||
get_logger(name: str) -> logging.Logger
|
||||
```
|
||||
|
||||
|
||||
Get a logger nested under FastMCP namespace.
|
||||
|
||||
**Args:**
|
||||
- `name`: the name of the logger, which will be prefixed with 'FastMCP.'
|
||||
|
||||
**Returns:**
|
||||
- a configured logger instance
|
||||
|
||||
|
||||
### `configure_logging`
|
||||
|
||||
```python
|
||||
configure_logging(level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] | int = 'INFO', logger: logging.Logger | None = None, enable_rich_tracebacks: bool = True) -> None
|
||||
```
|
||||
|
||||
|
||||
Configure logging for FastMCP.
|
||||
|
||||
**Args:**
|
||||
- `logger`: the logger to configure
|
||||
- `level`: the log level to use
|
||||
|
||||
50
docs/python-sdk/fastmcp-utilities-mcp_config.mdx
Normal file
50
docs/python-sdk/fastmcp-utilities-mcp_config.mdx
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: mcp_config
|
||||
sidebarTitle: mcp_config
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.mcp_config`
|
||||
|
||||
## Functions
|
||||
|
||||
### `infer_transport_type_from_url`
|
||||
|
||||
```python
|
||||
infer_transport_type_from_url(url: str | AnyUrl) -> Literal['streamable-http', 'sse']
|
||||
```
|
||||
|
||||
|
||||
Infer the appropriate transport type from the given URL.
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `StdioMCPServer`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `to_transport`
|
||||
|
||||
```python
|
||||
to_transport(self) -> StdioTransport
|
||||
```
|
||||
|
||||
### `RemoteMCPServer`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `to_transport`
|
||||
|
||||
```python
|
||||
to_transport(self) -> StreamableHttpTransport | SSETransport
|
||||
```
|
||||
|
||||
### `MCPConfig`
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `from_dict`
|
||||
|
||||
```python
|
||||
from_dict(cls, config: dict[str, Any]) -> MCPConfig
|
||||
```
|
||||
118
docs/python-sdk/fastmcp-utilities-openapi.mdx
Normal file
118
docs/python-sdk/fastmcp-utilities-openapi.mdx
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
title: openapi
|
||||
sidebarTitle: openapi
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.openapi`
|
||||
|
||||
## Functions
|
||||
|
||||
### `parse_openapi_to_http_routes`
|
||||
|
||||
```python
|
||||
parse_openapi_to_http_routes(openapi_dict: dict[str, Any]) -> list[HTTPRoute]
|
||||
```
|
||||
|
||||
|
||||
Parses an OpenAPI schema dictionary into a list of HTTPRoute objects
|
||||
using the openapi-pydantic library.
|
||||
|
||||
Supports both OpenAPI 3.0.x and 3.1.x versions.
|
||||
|
||||
|
||||
### `clean_schema_for_display`
|
||||
|
||||
```python
|
||||
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`
|
||||
|
||||
```python
|
||||
generate_example_from_schema(schema: JsonSchema | None) -> Any
|
||||
```
|
||||
|
||||
|
||||
Generate a simple example value from a JSON schema dictionary.
|
||||
Very basic implementation focusing on types.
|
||||
|
||||
|
||||
### `format_json_for_description`
|
||||
|
||||
```python
|
||||
format_json_for_description(data: Any, indent: int = 2) -> str
|
||||
```
|
||||
|
||||
|
||||
Formats Python data as a JSON string block for markdown.
|
||||
|
||||
|
||||
### `format_description_with_responses`
|
||||
|
||||
```python
|
||||
format_description_with_responses(base_description: str, responses: dict[str, Any], parameters: list[ParameterInfo] | None = None, request_body: RequestBodyInfo | None = None) -> str
|
||||
```
|
||||
|
||||
|
||||
Formats the base description string with response, parameter, and request body information.
|
||||
|
||||
**Args:**
|
||||
- `base_description`: The initial description to be formatted.
|
||||
- `responses`: A dictionary of response information, keyed by status code.
|
||||
- `parameters`: A list of parameter information,
|
||||
including path and query parameters. Each parameter includes details such as name,
|
||||
location, whether it is required, and a description.
|
||||
- `request_body`: Information about the request body,
|
||||
including its description, whether it is required, and its content schema.
|
||||
|
||||
**Returns:**
|
||||
- The formatted description string with additional details about responses, parameters,
|
||||
- and the request body.
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `ParameterInfo`
|
||||
|
||||
|
||||
Represents a single parameter for an HTTP operation in our IR.
|
||||
|
||||
|
||||
### `RequestBodyInfo`
|
||||
|
||||
|
||||
Represents the request body for an HTTP operation in our IR.
|
||||
|
||||
|
||||
### `ResponseInfo`
|
||||
|
||||
|
||||
Represents response information in our IR.
|
||||
|
||||
|
||||
### `HTTPRoute`
|
||||
|
||||
|
||||
Intermediate Representation for a single OpenAPI operation.
|
||||
|
||||
|
||||
### `OpenAPIParser`
|
||||
|
||||
|
||||
Unified parser for OpenAPI schemas with generic type parameters to handle both 3.0 and 3.1.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `parse`
|
||||
|
||||
```python
|
||||
parse(self) -> list[HTTPRoute]
|
||||
```
|
||||
|
||||
Parse the OpenAPI schema into HTTP routes.
|
||||
|
||||
112
docs/python-sdk/fastmcp-utilities-types.mdx
Normal file
112
docs/python-sdk/fastmcp-utilities-types.mdx
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
---
|
||||
title: types
|
||||
sidebarTitle: types
|
||||
---
|
||||
|
||||
# `fastmcp.utilities.types`
|
||||
|
||||
|
||||
Common types used across FastMCP.
|
||||
|
||||
## Functions
|
||||
|
||||
### `get_cached_typeadapter`
|
||||
|
||||
```python
|
||||
get_cached_typeadapter(cls: T) -> TypeAdapter[T]
|
||||
```
|
||||
|
||||
|
||||
TypeAdapters are heavy objects, and in an application context we'd typically
|
||||
create them once in a global scope and reuse them as often as possible.
|
||||
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`
|
||||
|
||||
```python
|
||||
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`
|
||||
|
||||
```python
|
||||
is_class_member_of_type(cls: type, base: type) -> bool
|
||||
```
|
||||
|
||||
|
||||
Check if cls is a member of base, even if cls is a type variable.
|
||||
|
||||
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`
|
||||
|
||||
```python
|
||||
find_kwarg_by_type(fn: Callable, kwarg_type: type) -> str | None
|
||||
```
|
||||
|
||||
|
||||
Find the name of the kwarg that is of type kwarg_type.
|
||||
|
||||
Includes union types that contain the kwarg_type, as well as Annotated types.
|
||||
|
||||
|
||||
## Classes
|
||||
|
||||
### `FastMCPBaseModel`
|
||||
|
||||
|
||||
Base model for FastMCP models.
|
||||
|
||||
|
||||
### `Image`
|
||||
|
||||
|
||||
Helper class for returning images from tools.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `to_image_content`
|
||||
|
||||
```python
|
||||
to_image_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> ImageContent
|
||||
```
|
||||
|
||||
Convert to MCP ImageContent.
|
||||
|
||||
|
||||
### `Audio`
|
||||
|
||||
|
||||
Helper class for returning audio from tools.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `to_audio_content`
|
||||
|
||||
```python
|
||||
to_audio_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> AudioContent
|
||||
```
|
||||
|
||||
### `File`
|
||||
|
||||
|
||||
Helper class for returning audio from tools.
|
||||
|
||||
|
||||
**Methods:**
|
||||
|
||||
#### `to_resource_content`
|
||||
|
||||
```python
|
||||
to_resource_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> EmbeddedResource
|
||||
```
|
||||
|
|
@ -3,7 +3,7 @@ title: Bearer Token Authentication
|
|||
sidebarTitle: Bearer Auth
|
||||
description: Secure your FastMCP server's HTTP endpoints by validating JWT Bearer tokens.
|
||||
icon: key
|
||||
tag: "New!"
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
|
|
|||
|
|
@ -57,6 +57,84 @@ 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>
|
||||
|
||||
### Argument Types
|
||||
|
||||
<VersionBadge version="2.9.0" />
|
||||
|
||||
The MCP specification requires that all prompt arguments be passed as strings, but FastMCP allows you to use typed annotations for better developer experience. When you use complex types like `list[int]` or `dict[str, str]`, FastMCP:
|
||||
|
||||
1. **Automatically converts** string arguments from MCP clients to the expected types
|
||||
2. **Generates helpful descriptions** showing the exact JSON string format needed
|
||||
3. **Preserves direct usage** - you can still call prompts with properly typed arguments
|
||||
|
||||
Since the MCP specification only allows string arguments, clients need to know what string format to use for complex types. FastMCP solves this by automatically enhancing the argument descriptions with JSON schema information, making it clear to both humans and LLMs how to format their arguments.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Python Code
|
||||
@mcp.prompt
|
||||
def analyze_data(
|
||||
numbers: list[int],
|
||||
metadata: dict[str, str],
|
||||
threshold: float
|
||||
) -> str:
|
||||
"""Analyze numerical data."""
|
||||
avg = sum(numbers) / len(numbers)
|
||||
return f"Average: {avg}, above threshold: {avg > threshold}"
|
||||
```
|
||||
|
||||
```json Resulting MCP Prompt
|
||||
{
|
||||
"name": "analyze_data",
|
||||
"description": "Analyze numerical data.",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numbers",
|
||||
"description": "Provide as a JSON string matching the following schema: {\"items\":{\"type\":\"integer\"},\"type\":\"array\"}",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"description": "Provide as a JSON string matching the following schema: {\"additionalProperties\":{\"type\":\"string\"},\"type\":\"object\"}",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "threshold",
|
||||
"description": "Provide as a JSON string matching the following schema: {\"type\":\"number\"}",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
**MCP clients will call this prompt with string arguments:**
|
||||
```json
|
||||
{
|
||||
"numbers": "[1, 2, 3, 4, 5]",
|
||||
"metadata": "{\"source\": \"api\", \"version\": \"1.0\"}",
|
||||
"threshold": "2.5"
|
||||
}
|
||||
```
|
||||
|
||||
**But you can still call it directly with proper types:**
|
||||
```python
|
||||
# This also works for direct calls
|
||||
result = await prompt.render({
|
||||
"numbers": [1, 2, 3, 4, 5],
|
||||
"metadata": {"source": "api", "version": "1.0"},
|
||||
"threshold": 2.5
|
||||
})
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Keep your type annotations simple when using this feature. Complex nested types or custom classes may not convert reliably from JSON strings. The automatically generated schema descriptions are the only guidance users receive about the expected format.
|
||||
|
||||
Good choices: `list[int]`, `dict[str, str]`, `float`, `bool`
|
||||
Avoid: Complex Pydantic models, deeply nested structures, custom classes
|
||||
</Warning>
|
||||
|
||||
### Return Values
|
||||
|
||||
FastMCP intelligently handles different return types from your prompt function:
|
||||
|
|
@ -78,33 +156,6 @@ def roleplay_scenario(character: str, situation: str) -> list[Message]:
|
|||
]
|
||||
```
|
||||
|
||||
### Type Annotations
|
||||
|
||||
Type annotations are important for prompts. They:
|
||||
1. Inform FastMCP about the expected types for each parameter.
|
||||
2. Allow validation of parameters received from clients.
|
||||
3. Are used to generate the prompt's schema for the MCP protocol.
|
||||
|
||||
```python
|
||||
from pydantic import Field
|
||||
from typing import Literal, Optional
|
||||
|
||||
@mcp.prompt
|
||||
def generate_content_request(
|
||||
topic: str = Field(description="The main subject to cover"),
|
||||
format: Literal["blog", "email", "social"] = "blog",
|
||||
tone: str = "professional",
|
||||
word_count: Optional[int] = None
|
||||
) -> str:
|
||||
"""Create a request for generating content in a specific format."""
|
||||
prompt = f"Please write a {format} post about {topic} in a {tone} tone."
|
||||
|
||||
if word_count:
|
||||
prompt += f" It should be approximately {word_count} words long."
|
||||
|
||||
return prompt
|
||||
```
|
||||
|
||||
|
||||
### Required vs. Optional Parameters
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
title: Resources & Templates
|
||||
sidebarTitle: Resources
|
||||
description: Expose data sources and dynamic content generators to your MCP client.
|
||||
icon: database
|
||||
icon: folder-open
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: The FastMCP Server
|
||||
sidebarTitle: FastMCP Servers
|
||||
description: Learn about the core FastMCP server class and how to run it.
|
||||
sidebarTitle: Overview
|
||||
description: The core FastMCP server class for building MCP applications with tools, resources, and prompts.
|
||||
icon: server
|
||||
---
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ from fastmcp import Client
|
|||
|
||||
async def main():
|
||||
# Connect to the MCP server we just created
|
||||
async with Client("http://127.0.0.1:8000/mcp") as client:
|
||||
async with Client("http://127.0.0.1:8000/mcp/") as client:
|
||||
|
||||
# List the tools that were automatically generated
|
||||
tools = await client.list_tools()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
title: "FastMCP Updates"
|
||||
sidebarTitle: "Updates"
|
||||
icon: "sparkles"
|
||||
tag: "New!"
|
||||
tag: NEW
|
||||
---
|
||||
|
||||
<Update label="FastMCP 2.8" description="June 11, 2025" tags={["Releases", "Blog Posts"]}>
|
||||
|
|
|
|||
20
justfile
20
justfile
|
|
@ -1,6 +1,8 @@
|
|||
# Build the project
|
||||
build:
|
||||
uv sync
|
||||
|
||||
# Run tests
|
||||
test: build
|
||||
uv run --frozen pytest -xvs tests
|
||||
|
||||
|
|
@ -8,5 +10,21 @@ test: build
|
|||
typecheck:
|
||||
uv run --frozen pyright
|
||||
|
||||
# Serve documentation locally
|
||||
docs:
|
||||
cd docs && npx mintlify dev
|
||||
cd docs && npx mint@latest dev
|
||||
|
||||
# Generate API reference documentation for all modules
|
||||
api-ref-all:
|
||||
uvx --with-editable . --refresh-package mdxify mdxify@latest --all --root-module fastmcp --anchor-name "SDK Reference"
|
||||
|
||||
# Generate API reference for specific modules (e.g., just api-ref prefect.flows prefect.tasks)
|
||||
api-ref *MODULES:
|
||||
uvx --with-editable . --refresh-package mdxify mdxify@latest {{MODULES}} --root-module fastmcp --anchor-name "SDK Reference"
|
||||
|
||||
# Clean up API reference documentation
|
||||
api-ref-clean:
|
||||
rm -rf docs/python-sdk
|
||||
|
||||
copy-context:
|
||||
uvx --with-editable . --refresh-package copychat copychat@latest src/ docs/ -x changelog.mdx -x python-sdk/ -v
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"""FastMCP CLI tools."""
|
||||
|
||||
import asyncio
|
||||
import importlib.metadata
|
||||
import importlib.util
|
||||
import os
|
||||
|
|
@ -11,6 +12,7 @@ from typing import Annotated
|
|||
|
||||
import dotenv
|
||||
import typer
|
||||
from pydantic import TypeAdapter
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from typer import Context, Exit
|
||||
|
|
@ -19,6 +21,7 @@ import fastmcp
|
|||
from fastmcp.cli import claude
|
||||
from fastmcp.cli import run as run_module
|
||||
from fastmcp.server.server import FastMCP
|
||||
from fastmcp.utilities.inspect import FastMCPInfo, inspect_fastmcp
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
logger = get_logger("cli")
|
||||
|
|
@ -435,3 +438,98 @@ def install(
|
|||
else:
|
||||
logger.error(f"Failed to install {name} in Claude app")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@app.command()
|
||||
def 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
|
||||
"""
|
||||
|
||||
# Parse the server specification
|
||||
file, server_object = run_module.parse_file_path(server_spec)
|
||||
|
||||
logger.debug(
|
||||
"Inspecting server",
|
||||
extra={
|
||||
"file": str(file),
|
||||
"server_object": server_object,
|
||||
"output": str(output),
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
# Import the server
|
||||
server = run_module.import_server(file, server_object)
|
||||
|
||||
# Get server information
|
||||
async def get_info():
|
||||
return await inspect_fastmcp(server)
|
||||
|
||||
try:
|
||||
# Try to use existing event loop if available
|
||||
asyncio.get_running_loop()
|
||||
# If there's already a loop running, we need to run in a thread
|
||||
import concurrent.futures
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
future = executor.submit(asyncio.run, get_info())
|
||||
info = future.result()
|
||||
except RuntimeError:
|
||||
# No running loop, safe to use asyncio.run
|
||||
info = asyncio.run(get_info())
|
||||
|
||||
info_json = TypeAdapter(FastMCPInfo).dump_json(info, indent=2)
|
||||
|
||||
# Ensure output directory exists
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write JSON report (always pretty-printed)
|
||||
with output.open("w", encoding="utf-8") as f:
|
||||
f.write(info_json.decode("utf-8"))
|
||||
|
||||
logger.info(f"Server inspection complete. Report saved to {output}")
|
||||
|
||||
# Print summary to console
|
||||
console.print(
|
||||
f"[bold green]✓[/bold green] Inspected server: [bold]{info.name}[/bold]"
|
||||
)
|
||||
console.print(f" Tools: {len(info.tools)}")
|
||||
console.print(f" Prompts: {len(info.prompts)}")
|
||||
console.print(f" Resources: {len(info.resources)}")
|
||||
console.print(f" Templates: {len(info.templates)}")
|
||||
console.print(f" Report saved to: [cyan]{output}[/cyan]")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to inspect server: {e}",
|
||||
extra={
|
||||
"server_spec": server_spec,
|
||||
"error": str(e),
|
||||
},
|
||||
)
|
||||
console.print(f"[bold red]✗[/bold red] Failed to inspect server: {e}")
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -306,8 +306,7 @@ def OAuth(
|
|||
httpx.AsyncClient (or appropriate FastMCP client/transport instance)
|
||||
|
||||
Args:
|
||||
mcp_url: Full URL to the MCP endpoint (e.g.,
|
||||
"http://host/mcp/sse")
|
||||
mcp_url: Full URL to the MCP endpoint (e.g. "http://host/mcp/sse/")
|
||||
scopes: OAuth scopes to request. Can be a
|
||||
space-separated string or a list of strings.
|
||||
client_name: Name for this client during registration
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from typing import Any, Generic, Literal, cast, overload
|
|||
import anyio
|
||||
import httpx
|
||||
import mcp.types
|
||||
import pydantic_core
|
||||
from exceptiongroup import catch
|
||||
from mcp import ClientSession
|
||||
from mcp.types import ContentBlock
|
||||
|
|
@ -508,13 +509,13 @@ class Client(Generic[ClientTransportT]):
|
|||
|
||||
# --- Prompt ---
|
||||
async def get_prompt_mcp(
|
||||
self, name: str, arguments: dict[str, str] | None = None
|
||||
self, name: str, arguments: dict[str, Any] | None = None
|
||||
) -> mcp.types.GetPromptResult:
|
||||
"""Send a prompts/get request and return the complete MCP protocol result.
|
||||
|
||||
Args:
|
||||
name (str): The name of the prompt to retrieve.
|
||||
arguments (dict[str, str] | None, optional): Arguments to pass to the prompt. Defaults to None.
|
||||
arguments (dict[str, Any] | None, optional): Arguments to pass to the prompt. Defaults to None.
|
||||
|
||||
Returns:
|
||||
mcp.types.GetPromptResult: The complete response object from the protocol,
|
||||
|
|
@ -523,17 +524,32 @@ class Client(Generic[ClientTransportT]):
|
|||
Raises:
|
||||
RuntimeError: If called while the client is not connected.
|
||||
"""
|
||||
result = await self.session.get_prompt(name=name, arguments=arguments)
|
||||
# Serialize arguments for MCP protocol - convert non-string values to JSON
|
||||
serialized_arguments: dict[str, str] | None = None
|
||||
if arguments:
|
||||
serialized_arguments = {}
|
||||
for key, value in arguments.items():
|
||||
if isinstance(value, str):
|
||||
serialized_arguments[key] = value
|
||||
else:
|
||||
# Use pydantic_core.to_json for consistent serialization
|
||||
serialized_arguments[key] = pydantic_core.to_json(value).decode(
|
||||
"utf-8"
|
||||
)
|
||||
|
||||
result = await self.session.get_prompt(
|
||||
name=name, arguments=serialized_arguments
|
||||
)
|
||||
return result
|
||||
|
||||
async def get_prompt(
|
||||
self, name: str, arguments: dict[str, str] | None = None
|
||||
self, name: str, arguments: dict[str, Any] | None = None
|
||||
) -> mcp.types.GetPromptResult:
|
||||
"""Retrieve a rendered prompt message list from the server.
|
||||
|
||||
Args:
|
||||
name (str): The name of the prompt to retrieve.
|
||||
arguments (dict[str, str] | None, optional): Arguments to pass to the prompt. Defaults to None.
|
||||
arguments (dict[str, Any] | None, optional): Arguments to pass to the prompt. Defaults to None.
|
||||
|
||||
Returns:
|
||||
mcp.types.GetPromptResult: The complete response object from the protocol,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import warnings
|
|||
from collections.abc import AsyncIterator, Callable
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal, TypedDict, TypeVar, cast, overload
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
import anyio
|
||||
import httpx
|
||||
|
|
@ -159,6 +160,13 @@ class SSETransport(ClientTransport):
|
|||
url = str(url)
|
||||
if not isinstance(url, str) or not url.startswith("http"):
|
||||
raise ValueError("Invalid HTTP/S URL provided for SSE.")
|
||||
|
||||
# Ensure the URL path ends with a trailing slash to avoid automatic redirects
|
||||
parsed = urlparse(url)
|
||||
if not parsed.path.endswith("/"):
|
||||
parsed = parsed._replace(path=parsed.path + "/")
|
||||
url = urlunparse(parsed)
|
||||
|
||||
self.url = url
|
||||
self.headers = headers or {}
|
||||
self._set_auth(auth)
|
||||
|
|
@ -227,6 +235,13 @@ class StreamableHttpTransport(ClientTransport):
|
|||
url = str(url)
|
||||
if not isinstance(url, str) or not url.startswith("http"):
|
||||
raise ValueError("Invalid HTTP/S URL provided for Streamable HTTP.")
|
||||
|
||||
# Ensure the URL path ends with a trailing slash to avoid automatic redirects
|
||||
parsed = urlparse(url)
|
||||
if not parsed.path.endswith("/"):
|
||||
parsed = parsed._replace(path=parsed.path + "/")
|
||||
url = urlunparse(parsed)
|
||||
|
||||
self.url = url
|
||||
self.headers = headers or {}
|
||||
self._set_auth(auth)
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@
|
|||
from __future__ import annotations as _annotations
|
||||
|
||||
import inspect
|
||||
import json
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Awaitable, Callable, Sequence
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import Any
|
||||
|
||||
import pydantic_core
|
||||
from mcp.types import ContentBlock, PromptMessage, Role, TextContent
|
||||
from mcp.types import Prompt as MCPPrompt
|
||||
from mcp.types import PromptArgument as MCPPromptArgument
|
||||
from pydantic import Field, TypeAdapter, validate_call
|
||||
from pydantic import Field, TypeAdapter
|
||||
|
||||
from fastmcp.exceptions import PromptError
|
||||
from fastmcp.server.dependencies import get_context
|
||||
|
|
@ -24,10 +25,6 @@ from fastmcp.utilities.types import (
|
|||
get_cached_typeadapter,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
|
|
@ -180,17 +177,43 @@ class FunctionPrompt(Prompt):
|
|||
arguments: list[PromptArgument] = []
|
||||
if "properties" in parameters:
|
||||
for param_name, param in parameters["properties"].items():
|
||||
arg_description = param.get("description")
|
||||
|
||||
# For non-string parameters, append JSON schema info to help users
|
||||
# understand the expected format when passing as strings (MCP requirement)
|
||||
if param_name in sig.parameters:
|
||||
sig_param = sig.parameters[param_name]
|
||||
if (
|
||||
sig_param.annotation != inspect.Parameter.empty
|
||||
and sig_param.annotation is not str
|
||||
and param_name != context_kwarg
|
||||
):
|
||||
# Get the JSON schema for this specific parameter type
|
||||
try:
|
||||
param_adapter = get_cached_typeadapter(sig_param.annotation)
|
||||
param_schema = param_adapter.json_schema()
|
||||
|
||||
# Create compact schema representation
|
||||
schema_str = json.dumps(param_schema, separators=(",", ":"))
|
||||
|
||||
# Append schema info to description
|
||||
schema_note = f"Provide as a JSON string matching the following schema: {schema_str}"
|
||||
if arg_description:
|
||||
arg_description = f"{arg_description}\n\n{schema_note}"
|
||||
else:
|
||||
arg_description = schema_note
|
||||
except Exception:
|
||||
# If schema generation fails, skip enhancement
|
||||
pass
|
||||
|
||||
arguments.append(
|
||||
PromptArgument(
|
||||
name=param_name,
|
||||
description=param.get("description"),
|
||||
description=arg_description,
|
||||
required=param_name in parameters.get("required", []),
|
||||
)
|
||||
)
|
||||
|
||||
# ensure the arguments are properly cast
|
||||
fn = validate_call(fn)
|
||||
|
||||
return cls(
|
||||
name=func_name,
|
||||
description=description,
|
||||
|
|
@ -200,6 +223,60 @@ class FunctionPrompt(Prompt):
|
|||
fn=fn,
|
||||
)
|
||||
|
||||
def _convert_string_arguments(self, kwargs: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Convert string arguments to expected types based on function signature."""
|
||||
from fastmcp.server.context import Context
|
||||
|
||||
sig = inspect.signature(self.fn)
|
||||
converted_kwargs = {}
|
||||
|
||||
# Find context parameter name if any
|
||||
context_param_name = find_kwarg_by_type(self.fn, kwarg_type=Context)
|
||||
|
||||
for param_name, param_value in kwargs.items():
|
||||
if param_name in sig.parameters:
|
||||
param = sig.parameters[param_name]
|
||||
|
||||
# Skip Context parameters - they're handled separately
|
||||
if param_name == context_param_name:
|
||||
converted_kwargs[param_name] = param_value
|
||||
continue
|
||||
|
||||
# If parameter has no annotation or annotation is str, pass as-is
|
||||
if (
|
||||
param.annotation == inspect.Parameter.empty
|
||||
or param.annotation is str
|
||||
):
|
||||
converted_kwargs[param_name] = param_value
|
||||
# If argument is not a string, pass as-is (already properly typed)
|
||||
elif not isinstance(param_value, str):
|
||||
converted_kwargs[param_name] = param_value
|
||||
else:
|
||||
# Try to convert string argument using type adapter
|
||||
try:
|
||||
adapter = get_cached_typeadapter(param.annotation)
|
||||
# Try JSON parsing first for complex types
|
||||
try:
|
||||
converted_kwargs[param_name] = adapter.validate_json(
|
||||
param_value
|
||||
)
|
||||
except (ValueError, TypeError, pydantic_core.ValidationError):
|
||||
# Fallback to direct validation
|
||||
converted_kwargs[param_name] = adapter.validate_python(
|
||||
param_value
|
||||
)
|
||||
except (ValueError, TypeError, pydantic_core.ValidationError) as e:
|
||||
# If conversion fails, provide informative error
|
||||
raise PromptError(
|
||||
f"Could not convert argument '{param_name}' with value '{param_value}' "
|
||||
f"to expected type {param.annotation}. Error: {e}"
|
||||
)
|
||||
else:
|
||||
# Parameter not in function signature, pass as-is
|
||||
converted_kwargs[param_name] = param_value
|
||||
|
||||
return converted_kwargs
|
||||
|
||||
async def render(
|
||||
self,
|
||||
arguments: dict[str, Any] | None = None,
|
||||
|
|
@ -222,6 +299,9 @@ class FunctionPrompt(Prompt):
|
|||
if context_kwarg and context_kwarg not in kwargs:
|
||||
kwargs[context_kwarg] = get_context()
|
||||
|
||||
# Convert string arguments to expected types when needed
|
||||
kwargs = self._convert_string_arguments(kwargs)
|
||||
|
||||
# Call function and check if result is a coroutine
|
||||
result = self.fn(**kwargs)
|
||||
if inspect.iscoroutine(result):
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from mcp.shared.auth import (
|
|||
OAuthClientInformationFull,
|
||||
OAuthToken,
|
||||
)
|
||||
from pydantic import SecretStr
|
||||
from pydantic import AnyHttpUrl, SecretStr, ValidationError
|
||||
|
||||
from fastmcp.server.auth.auth import (
|
||||
ClientRegistrationOptions,
|
||||
|
|
@ -89,7 +89,7 @@ class RSAKeyPair:
|
|||
self,
|
||||
subject: str = "fastmcp-user",
|
||||
issuer: str = "https://fastmcp.example.com",
|
||||
audience: str | None = None,
|
||||
audience: str | list[str] | None = None,
|
||||
scopes: list[str] | None = None,
|
||||
expires_in_seconds: int = 3600,
|
||||
additional_claims: dict[str, Any] | None = None,
|
||||
|
|
@ -102,7 +102,7 @@ class RSAKeyPair:
|
|||
private_key_pem: RSA private key in PEM format
|
||||
subject: Subject claim (usually user ID)
|
||||
issuer: Issuer claim
|
||||
audience: Audience claim (optional)
|
||||
audience: Audience claim - can be a string or list of strings (optional)
|
||||
scopes: List of scopes to include
|
||||
expires_in_seconds: Token expiration time in seconds
|
||||
additional_claims: Any additional claims to include
|
||||
|
|
@ -161,7 +161,7 @@ class BearerAuthProvider(OAuthProvider):
|
|||
public_key: str | None = None,
|
||||
jwks_uri: str | None = None,
|
||||
issuer: str | None = None,
|
||||
audience: str | None = None,
|
||||
audience: str | list[str] | None = None,
|
||||
required_scopes: list[str] | None = None,
|
||||
):
|
||||
"""
|
||||
|
|
@ -171,7 +171,7 @@ class BearerAuthProvider(OAuthProvider):
|
|||
public_key: RSA public key in PEM format (for static key)
|
||||
jwks_uri: URI to fetch keys from (for key rotation)
|
||||
issuer: Expected issuer claim (optional)
|
||||
audience: Expected audience claim (optional)
|
||||
audience: Expected audience claim - can be a string or list of strings (optional)
|
||||
required_scopes: List of required scopes for access (optional)
|
||||
"""
|
||||
if not (public_key or jwks_uri):
|
||||
|
|
@ -179,8 +179,16 @@ class BearerAuthProvider(OAuthProvider):
|
|||
if public_key and jwks_uri:
|
||||
raise ValueError("Provide either public_key or jwks_uri, not both")
|
||||
|
||||
# Only pass issuer to parent if it's a valid URL, otherwise use default
|
||||
# This allows the issuer claim validation to work with string issuers per RFC 7519
|
||||
try:
|
||||
issuer_url = AnyHttpUrl(issuer) if issuer else "https://fastmcp.example.com"
|
||||
except ValidationError:
|
||||
# Issuer is not a valid URL, use default for parent class
|
||||
issuer_url = "https://fastmcp.example.com"
|
||||
|
||||
super().__init__(
|
||||
issuer_url=issuer or "https://fastmcp.example.com",
|
||||
issuer_url=issuer_url,
|
||||
client_registration_options=ClientRegistrationOptions(enabled=False),
|
||||
revocation_options=RevocationOptions(enabled=False),
|
||||
required_scopes=required_scopes,
|
||||
|
|
@ -304,11 +312,25 @@ class BearerAuthProvider(OAuthProvider):
|
|||
# Validate audience if configured
|
||||
if self.audience:
|
||||
aud = claims.get("aud")
|
||||
if isinstance(aud, list):
|
||||
if self.audience not in aud:
|
||||
|
||||
# Handle different combinations of audience types
|
||||
if isinstance(self.audience, list):
|
||||
# self.audience is a list - check if any expected audience is present
|
||||
if isinstance(aud, list):
|
||||
# Both are lists - check for intersection
|
||||
if not any(expected in aud for expected in self.audience):
|
||||
return None
|
||||
else:
|
||||
# aud is a string - check if it's in our expected list
|
||||
if aud not in self.audience:
|
||||
return None
|
||||
else:
|
||||
# self.audience is a string - use original logic
|
||||
if isinstance(aud, list):
|
||||
if self.audience not in aud:
|
||||
return None
|
||||
elif aud != self.audience:
|
||||
return None
|
||||
elif aud != self.audience:
|
||||
return None
|
||||
|
||||
# Extract claims - prefer client_id over sub for OAuth application identification
|
||||
client_id = claims.get("client_id") or claims.get("sub") or "unknown"
|
||||
|
|
|
|||
|
|
@ -158,6 +158,10 @@ def create_sse_app(
|
|||
A Starlette application with RequestContextMiddleware
|
||||
"""
|
||||
|
||||
# Ensure the message_path ends with a trailing slash to avoid automatic redirects
|
||||
if not message_path.endswith("/"):
|
||||
message_path = message_path + "/"
|
||||
|
||||
server_routes: list[BaseRoute] = []
|
||||
server_middleware: list[Middleware] = []
|
||||
|
||||
|
|
@ -305,6 +309,10 @@ def create_streamable_http_app(
|
|||
# Re-raise other RuntimeErrors if they don't match the specific message
|
||||
raise
|
||||
|
||||
# Ensure the streamable_http_path ends with a trailing slash to avoid automatic redirects
|
||||
if not streamable_http_path.endswith("/"):
|
||||
streamable_http_path = streamable_http_path + "/"
|
||||
|
||||
# Add StreamableHTTP routes with or without auth
|
||||
if auth:
|
||||
auth_middleware, auth_routes, required_scopes = (
|
||||
|
|
|
|||
|
|
@ -192,9 +192,9 @@ class Settings(BaseSettings):
|
|||
# HTTP settings
|
||||
host: str = "127.0.0.1"
|
||||
port: int = 8000
|
||||
sse_path: str = "/sse"
|
||||
sse_path: str = "/sse/"
|
||||
message_path: str = "/messages/"
|
||||
streamable_http_path: str = "/mcp"
|
||||
streamable_http_path: str = "/mcp/"
|
||||
debug: bool = False
|
||||
|
||||
# error handling
|
||||
|
|
|
|||
326
src/fastmcp/utilities/inspect.py
Normal file
326
src/fastmcp/utilities/inspect.py
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
"""Utilities for inspecting FastMCP instances."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.metadata
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from mcp.server.fastmcp import FastMCP as FastMCP1x
|
||||
|
||||
import fastmcp
|
||||
from fastmcp.server.server import FastMCP
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolInfo:
|
||||
"""Information about a tool."""
|
||||
|
||||
key: str
|
||||
name: str
|
||||
description: str | None
|
||||
input_schema: dict[str, Any]
|
||||
annotations: dict[str, Any] | None = None
|
||||
tags: list[str] | None = None
|
||||
enabled: bool | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class PromptInfo:
|
||||
"""Information about a prompt."""
|
||||
|
||||
key: str
|
||||
name: str
|
||||
description: str | None
|
||||
arguments: list[dict[str, Any]] | None = None
|
||||
tags: list[str] | None = None
|
||||
enabled: bool | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class ResourceInfo:
|
||||
"""Information about a resource."""
|
||||
|
||||
key: str
|
||||
uri: str
|
||||
name: str | None
|
||||
description: str | None
|
||||
mime_type: str | None = None
|
||||
tags: list[str] | None = None
|
||||
enabled: bool | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class TemplateInfo:
|
||||
"""Information about a resource template."""
|
||||
|
||||
key: str
|
||||
uri_template: str
|
||||
name: str | None
|
||||
description: str | None
|
||||
mime_type: str | None = None
|
||||
tags: list[str] | None = None
|
||||
enabled: bool | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class FastMCPInfo:
|
||||
"""Information extracted from a FastMCP instance."""
|
||||
|
||||
name: str
|
||||
instructions: str | None
|
||||
fastmcp_version: str
|
||||
mcp_version: str
|
||||
server_version: str
|
||||
tools: list[ToolInfo]
|
||||
prompts: list[PromptInfo]
|
||||
resources: list[ResourceInfo]
|
||||
templates: list[TemplateInfo]
|
||||
capabilities: dict[str, Any]
|
||||
|
||||
|
||||
async def inspect_fastmcp_v2(mcp: FastMCP[Any]) -> FastMCPInfo:
|
||||
"""Extract information from a FastMCP v2.x instance.
|
||||
|
||||
Args:
|
||||
mcp: The FastMCP v2.x instance to inspect
|
||||
|
||||
Returns:
|
||||
FastMCPInfo dataclass containing the extracted information
|
||||
"""
|
||||
# Get all the components using FastMCP2's direct methods
|
||||
tools_dict = await mcp.get_tools()
|
||||
prompts_dict = await mcp.get_prompts()
|
||||
resources_dict = await mcp.get_resources()
|
||||
templates_dict = await mcp.get_resource_templates()
|
||||
|
||||
# Extract detailed tool information
|
||||
tool_infos = []
|
||||
for key, tool in tools_dict.items():
|
||||
# Convert to MCP tool to get input schema
|
||||
mcp_tool = tool.to_mcp_tool(name=key)
|
||||
tool_infos.append(
|
||||
ToolInfo(
|
||||
key=key,
|
||||
name=tool.name or key,
|
||||
description=tool.description,
|
||||
input_schema=mcp_tool.inputSchema if mcp_tool.inputSchema else {},
|
||||
annotations=tool.annotations.model_dump() if tool.annotations else None,
|
||||
tags=list(tool.tags) if tool.tags else None,
|
||||
enabled=tool.enabled,
|
||||
)
|
||||
)
|
||||
|
||||
# Extract detailed prompt information
|
||||
prompt_infos = []
|
||||
for key, prompt in prompts_dict.items():
|
||||
prompt_infos.append(
|
||||
PromptInfo(
|
||||
key=key,
|
||||
name=prompt.name or key,
|
||||
description=prompt.description,
|
||||
arguments=[arg.model_dump() for arg in prompt.arguments]
|
||||
if prompt.arguments
|
||||
else None,
|
||||
tags=list(prompt.tags) if prompt.tags else None,
|
||||
enabled=prompt.enabled,
|
||||
)
|
||||
)
|
||||
|
||||
# Extract detailed resource information
|
||||
resource_infos = []
|
||||
for key, resource in resources_dict.items():
|
||||
resource_infos.append(
|
||||
ResourceInfo(
|
||||
key=key,
|
||||
uri=key, # For v2, key is the URI
|
||||
name=resource.name,
|
||||
description=resource.description,
|
||||
mime_type=resource.mime_type,
|
||||
tags=list(resource.tags) if resource.tags else None,
|
||||
enabled=resource.enabled,
|
||||
)
|
||||
)
|
||||
|
||||
# Extract detailed template information
|
||||
template_infos = []
|
||||
for key, template in templates_dict.items():
|
||||
template_infos.append(
|
||||
TemplateInfo(
|
||||
key=key,
|
||||
uri_template=key, # For v2, key is the URI template
|
||||
name=template.name,
|
||||
description=template.description,
|
||||
mime_type=template.mime_type,
|
||||
tags=list(template.tags) if template.tags else None,
|
||||
enabled=template.enabled,
|
||||
)
|
||||
)
|
||||
|
||||
# Basic MCP capabilities that FastMCP supports
|
||||
capabilities = {
|
||||
"tools": {"listChanged": True},
|
||||
"resources": {"subscribe": False, "listChanged": False},
|
||||
"prompts": {"listChanged": False},
|
||||
"logging": {},
|
||||
}
|
||||
|
||||
return FastMCPInfo(
|
||||
name=mcp.name,
|
||||
instructions=mcp.instructions,
|
||||
fastmcp_version=fastmcp.__version__,
|
||||
mcp_version=importlib.metadata.version("mcp"),
|
||||
server_version=fastmcp.__version__, # v2.x uses FastMCP version
|
||||
tools=tool_infos,
|
||||
prompts=prompt_infos,
|
||||
resources=resource_infos,
|
||||
templates=template_infos,
|
||||
capabilities=capabilities,
|
||||
)
|
||||
|
||||
|
||||
async def inspect_fastmcp_v1(mcp: Any) -> FastMCPInfo:
|
||||
"""Extract information from a FastMCP v1.x instance using a Client.
|
||||
|
||||
Args:
|
||||
mcp: The FastMCP v1.x instance to inspect
|
||||
|
||||
Returns:
|
||||
FastMCPInfo dataclass containing the extracted information
|
||||
"""
|
||||
from fastmcp import Client
|
||||
|
||||
# Use a client to interact with the FastMCP1x server
|
||||
async with Client(mcp) as client:
|
||||
# Get components via client calls (these return MCP objects)
|
||||
mcp_tools = await client.list_tools()
|
||||
mcp_prompts = await client.list_prompts()
|
||||
mcp_resources = await client.list_resources()
|
||||
|
||||
# Try to get resource templates (FastMCP 1.x does have templates)
|
||||
try:
|
||||
mcp_templates = await client.list_resource_templates()
|
||||
except Exception:
|
||||
mcp_templates = []
|
||||
|
||||
# Extract detailed tool information from MCP Tool objects
|
||||
tool_infos = []
|
||||
for mcp_tool in mcp_tools:
|
||||
# Extract annotations if they exist
|
||||
annotations = None
|
||||
if hasattr(mcp_tool, "annotations") and mcp_tool.annotations:
|
||||
if hasattr(mcp_tool.annotations, "model_dump"):
|
||||
annotations = mcp_tool.annotations.model_dump()
|
||||
elif isinstance(mcp_tool.annotations, dict):
|
||||
annotations = mcp_tool.annotations
|
||||
else:
|
||||
annotations = None
|
||||
|
||||
tool_infos.append(
|
||||
ToolInfo(
|
||||
key=mcp_tool.name, # For 1.x, key and name are the same
|
||||
name=mcp_tool.name,
|
||||
description=mcp_tool.description,
|
||||
input_schema=mcp_tool.inputSchema if mcp_tool.inputSchema else {},
|
||||
annotations=annotations,
|
||||
tags=None, # 1.x doesn't have tags
|
||||
enabled=None, # 1.x doesn't have enabled field
|
||||
)
|
||||
)
|
||||
|
||||
# Extract detailed prompt information from MCP Prompt objects
|
||||
prompt_infos = []
|
||||
for mcp_prompt in mcp_prompts:
|
||||
# Convert arguments if they exist
|
||||
arguments = None
|
||||
if hasattr(mcp_prompt, "arguments") and mcp_prompt.arguments:
|
||||
arguments = [arg.model_dump() for arg in mcp_prompt.arguments]
|
||||
|
||||
prompt_infos.append(
|
||||
PromptInfo(
|
||||
key=mcp_prompt.name, # For 1.x, key and name are the same
|
||||
name=mcp_prompt.name,
|
||||
description=mcp_prompt.description,
|
||||
arguments=arguments,
|
||||
tags=None, # 1.x doesn't have tags
|
||||
enabled=None, # 1.x doesn't have enabled field
|
||||
)
|
||||
)
|
||||
|
||||
# Extract detailed resource information from MCP Resource objects
|
||||
resource_infos = []
|
||||
for mcp_resource in mcp_resources:
|
||||
resource_infos.append(
|
||||
ResourceInfo(
|
||||
key=str(mcp_resource.uri), # For 1.x, key and uri are the same
|
||||
uri=str(mcp_resource.uri),
|
||||
name=mcp_resource.name,
|
||||
description=mcp_resource.description,
|
||||
mime_type=mcp_resource.mimeType,
|
||||
tags=None, # 1.x doesn't have tags
|
||||
enabled=None, # 1.x doesn't have enabled field
|
||||
)
|
||||
)
|
||||
|
||||
# Extract detailed template information from MCP ResourceTemplate objects
|
||||
template_infos = []
|
||||
for mcp_template in mcp_templates:
|
||||
template_infos.append(
|
||||
TemplateInfo(
|
||||
key=str(
|
||||
mcp_template.uriTemplate
|
||||
), # For 1.x, key and uriTemplate are the same
|
||||
uri_template=str(mcp_template.uriTemplate),
|
||||
name=mcp_template.name,
|
||||
description=mcp_template.description,
|
||||
mime_type=mcp_template.mimeType,
|
||||
tags=None, # 1.x doesn't have tags
|
||||
enabled=None, # 1.x doesn't have enabled field
|
||||
)
|
||||
)
|
||||
|
||||
# Basic MCP capabilities
|
||||
capabilities = {
|
||||
"tools": {"listChanged": True},
|
||||
"resources": {"subscribe": False, "listChanged": False},
|
||||
"prompts": {"listChanged": False},
|
||||
"logging": {},
|
||||
}
|
||||
|
||||
return FastMCPInfo(
|
||||
name=mcp.name,
|
||||
instructions=getattr(mcp, "instructions", None),
|
||||
fastmcp_version=fastmcp.__version__, # Report current fastmcp version
|
||||
mcp_version=importlib.metadata.version("mcp"),
|
||||
server_version="1.0", # FastMCP 1.x version
|
||||
tools=tool_infos,
|
||||
prompts=prompt_infos,
|
||||
resources=resource_infos,
|
||||
templates=template_infos, # FastMCP1x does have templates
|
||||
capabilities=capabilities,
|
||||
)
|
||||
|
||||
|
||||
def _is_fastmcp_v1(mcp: Any) -> bool:
|
||||
"""Check if the given instance is a FastMCP v1.x instance."""
|
||||
|
||||
# Check if it's an instance of FastMCP1x and not FastMCP2
|
||||
return isinstance(mcp, FastMCP1x) and not isinstance(mcp, FastMCP)
|
||||
|
||||
|
||||
async def inspect_fastmcp(mcp: FastMCP[Any] | Any) -> FastMCPInfo:
|
||||
"""Extract information from a FastMCP instance into a dataclass.
|
||||
|
||||
This function automatically detects whether the instance is FastMCP v1.x or v2.x
|
||||
and uses the appropriate extraction method.
|
||||
|
||||
Args:
|
||||
mcp: The FastMCP instance to inspect (v1.x or v2.x)
|
||||
|
||||
Returns:
|
||||
FastMCPInfo dataclass containing the extracted information
|
||||
"""
|
||||
if _is_fastmcp_v1(mcp):
|
||||
return await inspect_fastmcp_v1(mcp)
|
||||
else:
|
||||
return await inspect_fastmcp_v2(mcp)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def _prune_param(schema: dict, param: str) -> dict:
|
||||
|
|
@ -24,25 +25,77 @@ def _prune_param(schema: dict, param: str) -> dict:
|
|||
return schema
|
||||
|
||||
|
||||
def _prune_unused_defs(schema: dict) -> dict:
|
||||
"""Walk the schema and prune unused defs."""
|
||||
|
||||
root_defs: set[str] = set()
|
||||
referenced_by: defaultdict[str, list] = defaultdict(list)
|
||||
|
||||
defs = schema.get("$defs")
|
||||
if defs is None:
|
||||
return schema
|
||||
|
||||
def walk(
|
||||
node: object, current_def: str | None = None, skip_defs: bool = False
|
||||
) -> None:
|
||||
if isinstance(node, dict):
|
||||
# Process $ref for definition tracking
|
||||
ref = node.get("$ref")
|
||||
if isinstance(ref, str) and ref.startswith("#/$defs/"):
|
||||
def_name = ref.split("/")[-1]
|
||||
if current_def:
|
||||
referenced_by[def_name].append(current_def)
|
||||
else:
|
||||
root_defs.add(def_name)
|
||||
|
||||
# Walk children
|
||||
for k, v in node.items():
|
||||
if skip_defs and k == "$defs":
|
||||
continue
|
||||
|
||||
walk(v, current_def=current_def)
|
||||
|
||||
elif isinstance(node, list):
|
||||
for v in node:
|
||||
walk(v)
|
||||
|
||||
# Traverse the schema once, skipping the $defs
|
||||
walk(schema, skip_defs=True)
|
||||
|
||||
# Now figure out what defs reference other defs
|
||||
for def_name, value in defs.items():
|
||||
walk(value, current_def=def_name)
|
||||
|
||||
# Figure out what defs were referenced directly or recursively
|
||||
def def_is_referenced(def_name):
|
||||
if def_name in root_defs:
|
||||
return True
|
||||
references = referenced_by.get(def_name)
|
||||
if references:
|
||||
for reference in references:
|
||||
if def_is_referenced(reference):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Remove orphaned definitions if requested
|
||||
for def_name in list(defs):
|
||||
if not def_is_referenced(def_name):
|
||||
defs.pop(def_name)
|
||||
if not defs:
|
||||
schema.pop("$defs", None)
|
||||
|
||||
return schema
|
||||
|
||||
|
||||
def _walk_and_prune(
|
||||
schema: dict,
|
||||
prune_defs: bool = False,
|
||||
prune_titles: bool = False,
|
||||
prune_additional_properties: bool = False,
|
||||
) -> dict:
|
||||
"""Walk the schema and optionally prune titles, unused definitions, and additionalProperties: false."""
|
||||
|
||||
# Will only be used if prune_defs is True
|
||||
used_defs: set[str] = set()
|
||||
"""Walk the schema and optionally prune titles and additionalProperties: false."""
|
||||
|
||||
def walk(node: object) -> None:
|
||||
if isinstance(node, dict):
|
||||
# Process $ref for definition tracking
|
||||
if prune_defs:
|
||||
ref = node.get("$ref")
|
||||
if isinstance(ref, str) and ref.startswith("#/$defs/"):
|
||||
used_defs.add(ref.split("/")[-1])
|
||||
|
||||
# Remove title if requested
|
||||
if prune_titles and "title" in node:
|
||||
node.pop("title")
|
||||
|
|
@ -62,18 +115,8 @@ def _walk_and_prune(
|
|||
for v in node:
|
||||
walk(v)
|
||||
|
||||
# Traverse the schema once
|
||||
walk(schema)
|
||||
|
||||
# Remove orphaned definitions if requested
|
||||
if prune_defs:
|
||||
defs = schema.get("$defs", {})
|
||||
for def_name in list(defs):
|
||||
if def_name not in used_defs:
|
||||
defs.pop(def_name)
|
||||
if not defs:
|
||||
schema.pop("$defs", None)
|
||||
|
||||
return schema
|
||||
|
||||
|
||||
|
|
@ -109,12 +152,13 @@ def compress_schema(
|
|||
schema = _prune_param(schema, param=param)
|
||||
|
||||
# Do a single walk to handle pruning operations
|
||||
if prune_defs or prune_titles or prune_additional_properties:
|
||||
if prune_titles or prune_additional_properties:
|
||||
schema = _walk_and_prune(
|
||||
schema,
|
||||
prune_defs=prune_defs,
|
||||
prune_titles=prune_titles,
|
||||
prune_additional_properties=prune_additional_properties,
|
||||
)
|
||||
if prune_defs:
|
||||
schema = _prune_unused_defs(schema)
|
||||
|
||||
return schema
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from pydantic import AnyUrl, Field
|
||||
import httpx
|
||||
from pydantic import AnyUrl, ConfigDict, Field
|
||||
|
||||
from fastmcp.utilities.types import FastMCPBaseModel
|
||||
|
||||
|
|
@ -28,7 +30,8 @@ def infer_transport_type_from_url(
|
|||
parsed_url = urlparse(url)
|
||||
path = parsed_url.path
|
||||
|
||||
if "/sse/" in path or path.rstrip("/").endswith("/sse"):
|
||||
# Match /sse followed by /, ?, &, or end of string
|
||||
if re.search(r"/sse(/|\?|&|$)", path):
|
||||
return "sse"
|
||||
else:
|
||||
return "streamable-http"
|
||||
|
|
@ -57,12 +60,14 @@ class RemoteMCPServer(FastMCPBaseModel):
|
|||
headers: dict[str, str] = Field(default_factory=dict)
|
||||
transport: Literal["streamable-http", "sse"] | None = None
|
||||
auth: Annotated[
|
||||
str | Literal["oauth"] | None,
|
||||
str | Literal["oauth"] | httpx.Auth | None,
|
||||
Field(
|
||||
description='Either a string representing a Bearer token or the literal "oauth" to use OAuth authentication.'
|
||||
description='Either a string representing a Bearer token, the literal "oauth" to use OAuth authentication, or an httpx.Auth instance for custom authentication.',
|
||||
),
|
||||
] = None
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
def to_transport(self) -> StreamableHttpTransport | SSETransport:
|
||||
from fastmcp.client.transports import SSETransport, StreamableHttpTransport
|
||||
|
||||
|
|
|
|||
|
|
@ -262,16 +262,18 @@ class OpenAPIParser(
|
|||
|
||||
if isinstance(resolved_schema, (self.schema_cls)):
|
||||
# Convert schema to dictionary
|
||||
return resolved_schema.model_dump(
|
||||
result = resolved_schema.model_dump(
|
||||
mode="json", by_alias=True, exclude_none=True
|
||||
)
|
||||
elif isinstance(resolved_schema, dict):
|
||||
return resolved_schema
|
||||
result = resolved_schema
|
||||
else:
|
||||
logger.warning(
|
||||
f"Expected Schema after resolving, got {type(resolved_schema)}. Returning empty dict."
|
||||
)
|
||||
return {}
|
||||
result = {}
|
||||
|
||||
return _replace_ref_with_defs(result)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to extract schema as dict: {e}", exc_info=False)
|
||||
return {}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ def mcp_server_url(rsa_key_pair: RSAKeyPair) -> Generator[str]:
|
|||
public_key=rsa_key_pair.public_key,
|
||||
run_kwargs=dict(transport="streamable-http"),
|
||||
) as url:
|
||||
yield f"{url}/mcp"
|
||||
yield f"{url}/mcp/"
|
||||
|
||||
|
||||
class TestRSAKeyPair:
|
||||
|
|
@ -446,6 +446,45 @@ class TestBearerToken:
|
|||
access_token = await provider.load_access_token(token)
|
||||
assert access_token is not None
|
||||
|
||||
async def test_provider_with_multiple_expected_audiences(
|
||||
self, rsa_key_pair: RSAKeyPair
|
||||
):
|
||||
"""Test provider configured with multiple expected audiences."""
|
||||
provider = BearerAuthProvider(
|
||||
public_key=rsa_key_pair.public_key,
|
||||
issuer="https://test.example.com",
|
||||
audience=["https://api.example.com", "https://other-api.example.com"],
|
||||
)
|
||||
|
||||
# Token with single audience that matches one of the expected
|
||||
token1 = rsa_key_pair.create_token(
|
||||
subject="test-user",
|
||||
issuer="https://test.example.com",
|
||||
audience="https://api.example.com",
|
||||
)
|
||||
access_token1 = await provider.load_access_token(token1)
|
||||
assert access_token1 is not None
|
||||
|
||||
# Token with multiple audiences, one of which matches
|
||||
token2 = rsa_key_pair.create_token(
|
||||
subject="test-user",
|
||||
issuer="https://test.example.com",
|
||||
additional_claims={
|
||||
"aud": ["https://api.example.com", "https://third-party.example.com"]
|
||||
},
|
||||
)
|
||||
access_token2 = await provider.load_access_token(token2)
|
||||
assert access_token2 is not None
|
||||
|
||||
# Token with audience that doesn't match any expected
|
||||
token3 = rsa_key_pair.create_token(
|
||||
subject="test-user",
|
||||
issuer="https://test.example.com",
|
||||
audience="https://wrong-api.example.com",
|
||||
)
|
||||
access_token3 = await provider.load_access_token(token3)
|
||||
assert access_token3 is None
|
||||
|
||||
async def test_scope_extraction_string(
|
||||
self, rsa_key_pair: RSAKeyPair, bearer_provider: BearerAuthProvider
|
||||
):
|
||||
|
|
@ -539,6 +578,59 @@ class TestBearerToken:
|
|||
assert access_token is not None
|
||||
assert access_token.client_id == "app456" # Should prefer client_id over sub
|
||||
|
||||
async def test_string_issuer_validation(self, rsa_key_pair: RSAKeyPair):
|
||||
"""Test that string (non-URL) issuers are supported per RFC 7519."""
|
||||
# Create provider with string issuer
|
||||
provider = BearerAuthProvider(
|
||||
public_key=rsa_key_pair.public_key,
|
||||
issuer="my-service", # String issuer, not a URL
|
||||
)
|
||||
|
||||
# Create token with matching string issuer
|
||||
token = rsa_key_pair.create_token(
|
||||
subject="test-user",
|
||||
issuer="my-service", # Same string issuer
|
||||
)
|
||||
|
||||
access_token = await provider.load_access_token(token)
|
||||
assert access_token is not None
|
||||
assert access_token.client_id == "test-user"
|
||||
|
||||
async def test_string_issuer_mismatch_rejection(self, rsa_key_pair: RSAKeyPair):
|
||||
"""Test that mismatched string issuers are rejected."""
|
||||
# Create provider with one string issuer
|
||||
provider = BearerAuthProvider(
|
||||
public_key=rsa_key_pair.public_key,
|
||||
issuer="my-service",
|
||||
)
|
||||
|
||||
# Create token with different string issuer
|
||||
token = rsa_key_pair.create_token(
|
||||
subject="test-user",
|
||||
issuer="other-service", # Different string issuer
|
||||
)
|
||||
|
||||
access_token = await provider.load_access_token(token)
|
||||
assert access_token is None
|
||||
|
||||
async def test_url_issuer_still_works(self, rsa_key_pair: RSAKeyPair):
|
||||
"""Test that URL issuers still work after the fix."""
|
||||
# Create provider with URL issuer
|
||||
provider = BearerAuthProvider(
|
||||
public_key=rsa_key_pair.public_key,
|
||||
issuer="https://my-auth-server.com", # URL issuer
|
||||
)
|
||||
|
||||
# Create token with matching URL issuer
|
||||
token = rsa_key_pair.create_token(
|
||||
subject="test-user",
|
||||
issuer="https://my-auth-server.com", # Same URL issuer
|
||||
)
|
||||
|
||||
access_token = await provider.load_access_token(token)
|
||||
assert access_token is not None
|
||||
assert access_token.client_id == "test-user"
|
||||
|
||||
|
||||
class TestFastMCPBearerAuth:
|
||||
def test_bearer_auth(self):
|
||||
|
|
@ -606,7 +698,7 @@ class TestFastMCPBearerAuth:
|
|||
auth_kwargs=dict(required_scopes=["read", "write"]),
|
||||
run_kwargs=dict(transport="streamable-http"),
|
||||
) as url:
|
||||
mcp_server_url = f"{url}/mcp"
|
||||
mcp_server_url = f"{url}/mcp/"
|
||||
with pytest.raises(httpx.HTTPStatusError) as exc_info:
|
||||
async with Client(mcp_server_url, auth=BearerAuth(token)) as client:
|
||||
tools = await client.list_tools() # noqa: F841
|
||||
|
|
@ -629,7 +721,7 @@ class TestFastMCPBearerAuth:
|
|||
auth_kwargs=dict(required_scopes=["read", "write"]),
|
||||
run_kwargs=dict(transport="streamable-http"),
|
||||
) as url:
|
||||
mcp_server_url = f"{url}/mcp"
|
||||
mcp_server_url = f"{url}/mcp/"
|
||||
async with Client(mcp_server_url, auth=BearerAuth(token)) as client:
|
||||
tools = await client.list_tools()
|
||||
assert tools
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ def run_server(host: str, port: int, **kwargs) -> None:
|
|||
@pytest.fixture(scope="module")
|
||||
def streamable_http_server() -> Generator[str, None, None]:
|
||||
with run_server_in_process(run_server, transport="streamable-http") as url:
|
||||
yield f"{url}/mcp"
|
||||
yield f"{url}/mcp/"
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
|
|||
|
|
@ -220,6 +220,101 @@ async def test_get_prompt_mcp(fastmcp_server):
|
|||
assert result.description == "Example greeting prompt."
|
||||
|
||||
|
||||
async def test_client_serializes_all_non_string_arguments():
|
||||
"""Test that client always serializes non-string arguments to JSON, regardless of server types."""
|
||||
server = FastMCP("TestServer")
|
||||
|
||||
@server.prompt
|
||||
def echo_args(arg1: str, arg2: str, arg3: str) -> str:
|
||||
"""Server accepts all string args but client sends mixed types."""
|
||||
return f"arg1: {arg1}, arg2: {arg2}, arg3: {arg3}"
|
||||
|
||||
client = Client(transport=FastMCPTransport(server))
|
||||
|
||||
async with client:
|
||||
result = await client.get_prompt(
|
||||
"echo_args",
|
||||
{
|
||||
"arg1": "hello", # string - should pass through
|
||||
"arg2": [1, 2, 3], # list - should be JSON serialized
|
||||
"arg3": {"key": "value"}, # dict - should be JSON serialized
|
||||
},
|
||||
)
|
||||
|
||||
content = result.messages[0].content.text # type: ignore[attr-defined]
|
||||
assert "arg1: hello" in content
|
||||
assert "arg2: [1,2,3]" in content # JSON serialized list
|
||||
assert 'arg3: {"key":"value"}' in content # JSON serialized dict
|
||||
|
||||
|
||||
async def test_client_server_type_conversion_integration():
|
||||
"""Test that client serialization works with server-side type conversion."""
|
||||
server = FastMCP("TestServer")
|
||||
|
||||
@server.prompt
|
||||
def typed_prompt(numbers: list[int], config: dict[str, str]) -> str:
|
||||
"""Server expects typed args - will convert from JSON strings."""
|
||||
return f"Got {len(numbers)} numbers and {len(config)} config items"
|
||||
|
||||
client = Client(transport=FastMCPTransport(server))
|
||||
|
||||
async with client:
|
||||
result = await client.get_prompt(
|
||||
"typed_prompt",
|
||||
{"numbers": [1, 2, 3, 4], "config": {"theme": "dark", "lang": "en"}},
|
||||
)
|
||||
|
||||
content = result.messages[0].content.text # type: ignore[attr-defined]
|
||||
assert "Got 4 numbers and 2 config items" in content
|
||||
|
||||
|
||||
async def test_client_serialization_error():
|
||||
"""Test client error when object cannot be serialized."""
|
||||
import pydantic_core
|
||||
|
||||
server = FastMCP("TestServer")
|
||||
|
||||
@server.prompt
|
||||
def any_prompt(data: str) -> str:
|
||||
return f"Got: {data}"
|
||||
|
||||
# Create an unserializable object
|
||||
class UnserializableClass:
|
||||
def __init__(self):
|
||||
self.func = lambda x: x # functions can't be JSON serialized
|
||||
|
||||
client = Client(transport=FastMCPTransport(server))
|
||||
|
||||
async with client:
|
||||
with pytest.raises(
|
||||
pydantic_core.PydanticSerializationError, match="Unable to serialize"
|
||||
):
|
||||
await client.get_prompt("any_prompt", {"data": UnserializableClass()})
|
||||
|
||||
|
||||
async def test_server_deserialization_error():
|
||||
"""Test server error when JSON string cannot be converted to expected type."""
|
||||
from mcp import McpError
|
||||
|
||||
server = FastMCP("TestServer")
|
||||
|
||||
@server.prompt
|
||||
def strict_typed_prompt(numbers: list[int]) -> str:
|
||||
"""Expects list of integers but will receive invalid JSON."""
|
||||
return f"Got {len(numbers)} numbers"
|
||||
|
||||
client = Client(transport=FastMCPTransport(server))
|
||||
|
||||
async with client:
|
||||
with pytest.raises(McpError, match="Error rendering prompt"):
|
||||
await client.get_prompt(
|
||||
"strict_typed_prompt",
|
||||
{
|
||||
"numbers": "not valid json" # This will fail server-side conversion
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def test_read_resource_invalid_uri(fastmcp_server):
|
||||
"""Test reading a resource with an invalid URI."""
|
||||
client = Client(transport=FastMCPTransport(fastmcp_server))
|
||||
|
|
@ -735,7 +830,8 @@ class TestInferTransport:
|
|||
"http://example.com/api/sse/stream",
|
||||
"https://localhost:8080/mcp/sse/endpoint",
|
||||
"http://example.com/api/sse",
|
||||
"https://localhost:8080/mcp/sse",
|
||||
"http://example.com/api/sse/",
|
||||
"https://localhost:8080/mcp/sse/",
|
||||
"http://example.com/api/sse?param=value",
|
||||
"https://localhost:8080/mcp/sse/?param=value",
|
||||
"https://localhost:8000/mcp/sse?x=1&y=2",
|
||||
|
|
@ -744,6 +840,7 @@ class TestInferTransport:
|
|||
"path_with_sse_directory",
|
||||
"path_with_sse_subdirectory",
|
||||
"path_ending_with_sse",
|
||||
"path_ending_with_sse_slash",
|
||||
"path_ending_with_sse_https",
|
||||
"path_with_sse_and_query_params",
|
||||
"path_with_sse_slash_and_query_params",
|
||||
|
|
@ -758,7 +855,7 @@ class TestInferTransport:
|
|||
"url",
|
||||
[
|
||||
"http://example.com/api",
|
||||
"https://localhost:8080/mcp",
|
||||
"https://localhost:8080/mcp/",
|
||||
"http://example.com/asset/image.jpg",
|
||||
"https://localhost:8080/sservice/endpoint",
|
||||
"https://example.com/assets/file",
|
||||
|
|
@ -779,7 +876,7 @@ class TestInferTransport:
|
|||
config = {
|
||||
"mcpServers": {
|
||||
"test_server": {
|
||||
"url": "http://localhost:8000/sse",
|
||||
"url": "http://localhost:8000/sse/",
|
||||
"headers": {"Authorization": "Bearer 123"},
|
||||
},
|
||||
}
|
||||
|
|
@ -787,7 +884,7 @@ class TestInferTransport:
|
|||
transport = infer_transport(config)
|
||||
assert isinstance(transport, MCPConfigTransport)
|
||||
assert isinstance(transport.transport, SSETransport)
|
||||
assert transport.transport.url == "http://localhost:8000/sse"
|
||||
assert transport.transport.url == "http://localhost:8000/sse/"
|
||||
assert transport.transport.headers == {"Authorization": "Bearer 123"}
|
||||
|
||||
def test_infer_local_transport_from_config(self):
|
||||
|
|
@ -825,7 +922,7 @@ class TestInferTransport:
|
|||
"args": ["hello"],
|
||||
},
|
||||
"remote": {
|
||||
"url": "http://localhost:8000/sse",
|
||||
"url": "http://localhost:8000/sse/",
|
||||
"headers": {"Authorization": "Bearer 123"},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,12 +57,12 @@ class TestClientHeaders:
|
|||
@pytest.fixture(scope="class")
|
||||
def shttp_server(self) -> Generator[str, None, None]:
|
||||
with run_server_in_process(run_server, transport="streamable-http") as url:
|
||||
yield f"{url}/mcp"
|
||||
yield f"{url}/mcp/"
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def sse_server(self) -> Generator[str, None, None]:
|
||||
with run_server_in_process(run_server, transport="sse") as url:
|
||||
yield f"{url}/sse"
|
||||
yield f"{url}/sse/"
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def proxy_server(self, shttp_server: str) -> Generator[str, None, None]:
|
||||
|
|
@ -71,7 +71,7 @@ class TestClientHeaders:
|
|||
shttp_url=shttp_server,
|
||||
transport="streamable-http",
|
||||
) as url:
|
||||
yield f"{url}/mcp"
|
||||
yield f"{url}/mcp/"
|
||||
|
||||
async def test_client_headers_sse_resource(self, sse_server: str):
|
||||
async with Client(
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ def run_server(host: str, port: int, **kwargs) -> None:
|
|||
@pytest.fixture(autouse=True, scope="module")
|
||||
def sse_server() -> Generator[str, None, None]:
|
||||
with run_server_in_process(run_server, transport="sse") as url:
|
||||
yield f"{url}/sse"
|
||||
yield f"{url}/sse/"
|
||||
|
||||
|
||||
async def test_ping(sse_server: str):
|
||||
|
|
@ -92,7 +92,7 @@ async def test_http_headers(sse_server: str):
|
|||
|
||||
|
||||
def run_nested_server(host: str, port: int) -> None:
|
||||
app = fastmcp_server().sse_app(path="/mcp/sse", message_path="/mcp/messages")
|
||||
app = fastmcp_server().sse_app(path="/mcp/sse/", message_path="/mcp/messages")
|
||||
mount = Starlette(routes=[Mount("/nest-inner", app=app)])
|
||||
mount2 = Starlette(routes=[Mount("/nest-outer", app=mount)])
|
||||
server = uvicorn.Server(
|
||||
|
|
@ -114,7 +114,7 @@ async def test_nested_sse_server_resolves_correctly():
|
|||
|
||||
with run_server_in_process(run_nested_server) as url:
|
||||
async with Client(
|
||||
transport=SSETransport(f"{url}/nest-outer/nest-inner/mcp/sse")
|
||||
transport=SSETransport(f"{url}/nest-outer/nest-inner/mcp/sse/")
|
||||
) as client:
|
||||
result = await client.ping()
|
||||
assert result is True
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ def run_server(host: str, port: int, stateless_http: bool = False, **kwargs) ->
|
|||
|
||||
|
||||
def run_nested_server(host: str, port: int) -> None:
|
||||
mcp_app = fastmcp_server().http_app(path="/final/mcp")
|
||||
mcp_app = fastmcp_server().http_app(path="/final/mcp/")
|
||||
|
||||
mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
|
||||
mount2 = Starlette(
|
||||
|
|
@ -105,9 +105,9 @@ async def streamable_http_server(
|
|||
with run_server_in_process(
|
||||
run_server, stateless_http=stateless_http, transport="streamable-http"
|
||||
) as url:
|
||||
async with Client(transport=StreamableHttpTransport(f"{url}/mcp")) as client:
|
||||
async with Client(transport=StreamableHttpTransport(f"{url}/mcp/")) as client:
|
||||
assert await client.ping()
|
||||
yield f"{url}/mcp"
|
||||
yield f"{url}/mcp/"
|
||||
|
||||
|
||||
async def test_ping(streamable_http_server: str):
|
||||
|
|
@ -156,7 +156,7 @@ async def test_nested_streamable_http_server_resolves_correctly():
|
|||
|
||||
with run_server_in_process(run_nested_server) as url:
|
||||
async with Client(
|
||||
transport=StreamableHttpTransport(f"{url}/nest-outer/nest-inner/final/mcp")
|
||||
transport=StreamableHttpTransport(f"{url}/nest-outer/nest-inner/final/mcp/")
|
||||
) as client:
|
||||
result = await client.ping()
|
||||
assert result is True
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class TestDeprecatedServerInitKwargs:
|
|||
debug=False,
|
||||
host="127.0.0.1",
|
||||
port=9999,
|
||||
sse_path="/sse",
|
||||
sse_path="/sse/",
|
||||
message_path="/msg",
|
||||
streamable_http_path="/http",
|
||||
json_response=False,
|
||||
|
|
@ -162,7 +162,7 @@ class TestDeprecatedServerInitKwargs:
|
|||
assert server._deprecated_settings.debug is False
|
||||
assert server._deprecated_settings.host == "127.0.0.1"
|
||||
assert server._deprecated_settings.port == 9999
|
||||
assert server._deprecated_settings.sse_path == "/sse"
|
||||
assert server._deprecated_settings.sse_path == "/sse/"
|
||||
assert server._deprecated_settings.message_path == "/msg"
|
||||
assert server._deprecated_settings.streamable_http_path == "/http"
|
||||
assert server._deprecated_settings.json_response is False
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue