Spaces:
Running
Running
Jeremiah Lowin Claude commited on
Commit ·
ae32ca4
1
Parent(s): 3892c4b
Update client docs
Browse filesCo-Authored-By: Claude <claude@users.noreply.github.com>
- docs/clients/advanced-features.mdx +0 -152
- docs/clients/client.mdx +126 -316
- docs/clients/logging.mdx +63 -0
- docs/clients/progress.mdx +59 -0
- docs/clients/prompts.mdx +187 -0
- docs/clients/resources.mdx +171 -0
- docs/clients/roots.mdx +42 -0
- docs/clients/sampling.mdx +94 -0
- docs/clients/tools.mdx +143 -0
- docs/docs.json +20 -2
- justfile +4 -1
docs/clients/advanced-features.mdx
DELETED
|
@@ -1,152 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Advanced Features
|
| 3 |
-
sidebarTitle: Advanced Features
|
| 4 |
-
description: Learn about the advanced features of the FastMCP Client.
|
| 5 |
-
icon: stars
|
| 6 |
-
---
|
| 7 |
-
|
| 8 |
-
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 9 |
-
|
| 10 |
-
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.
|
| 11 |
-
|
| 12 |
-
<Tip>
|
| 13 |
-
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.
|
| 14 |
-
</Tip>
|
| 15 |
-
|
| 16 |
-
## Logging and Notifications
|
| 17 |
-
|
| 18 |
-
<VersionBadge version="2.0.0" />
|
| 19 |
-
MCP servers can emit logs to clients. To process these logs, you can provide a `log_handler` to the client.
|
| 20 |
-
|
| 21 |
-
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`.
|
| 22 |
-
|
| 23 |
-
```python {2, 12}
|
| 24 |
-
from fastmcp import Client
|
| 25 |
-
from fastmcp.client.logging import LogMessage
|
| 26 |
-
|
| 27 |
-
async def log_handler(message: LogMessage):
|
| 28 |
-
level = message.level.upper()
|
| 29 |
-
logger = message.logger or 'default'
|
| 30 |
-
data = message.data
|
| 31 |
-
print(f"[Server Log - {level}] {logger}: {data}")
|
| 32 |
-
|
| 33 |
-
client_with_logging = Client(
|
| 34 |
-
...,
|
| 35 |
-
log_handler=log_handler,
|
| 36 |
-
)
|
| 37 |
-
```
|
| 38 |
-
## Progress Monitoring
|
| 39 |
-
|
| 40 |
-
<VersionBadge version="2.3.5" />
|
| 41 |
-
|
| 42 |
-
MCP servers can report progress during long-running operations. The client can set a progress handler to receive and process these updates.
|
| 43 |
-
|
| 44 |
-
```python {2, 13}
|
| 45 |
-
from fastmcp import Client
|
| 46 |
-
from fastmcp.client.progress import ProgressHandler
|
| 47 |
-
|
| 48 |
-
async def my_progress_handler(
|
| 49 |
-
progress: float,
|
| 50 |
-
total: float | None,
|
| 51 |
-
message: str | None
|
| 52 |
-
) -> None:
|
| 53 |
-
print(f"Progress: {progress} / {total} ({message})")
|
| 54 |
-
|
| 55 |
-
client = Client(
|
| 56 |
-
...,
|
| 57 |
-
progress_handler=my_progress_handler
|
| 58 |
-
)
|
| 59 |
-
```
|
| 60 |
-
|
| 61 |
-
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.
|
| 62 |
-
|
| 63 |
-
You can override the progress handler for specific tool calls:
|
| 64 |
-
|
| 65 |
-
```python
|
| 66 |
-
# Client uses the default debug logger for progress
|
| 67 |
-
client = Client(...)
|
| 68 |
-
|
| 69 |
-
async with client:
|
| 70 |
-
# Use default progress handler (debug logging)
|
| 71 |
-
result1 = await client.call_tool("long_task", {"param": "value"})
|
| 72 |
-
|
| 73 |
-
# Override with custom progress handler just for this call
|
| 74 |
-
result2 = await client.call_tool(
|
| 75 |
-
"another_task",
|
| 76 |
-
{"param": "value"},
|
| 77 |
-
progress_handler=my_progress_handler
|
| 78 |
-
)
|
| 79 |
-
```
|
| 80 |
-
|
| 81 |
-
A typical progress update includes:
|
| 82 |
-
- Current progress value (e.g., 2 of 5 steps completed)
|
| 83 |
-
- Total expected value (may be None)
|
| 84 |
-
- Status message (may be None)
|
| 85 |
-
|
| 86 |
-
## LLM Sampling
|
| 87 |
-
|
| 88 |
-
<VersionBadge version="2.0.0" />
|
| 89 |
-
|
| 90 |
-
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.
|
| 91 |
-
|
| 92 |
-
The following example uses the `marvin` library to generate a completion:
|
| 93 |
-
|
| 94 |
-
```python {8-17, 21}
|
| 95 |
-
import marvin
|
| 96 |
-
from fastmcp import Client
|
| 97 |
-
from fastmcp.client.sampling import (
|
| 98 |
-
SamplingMessage,
|
| 99 |
-
SamplingParams,
|
| 100 |
-
RequestContext,
|
| 101 |
-
)
|
| 102 |
-
|
| 103 |
-
async def sampling_handler(
|
| 104 |
-
messages: list[SamplingMessage],
|
| 105 |
-
params: SamplingParams,
|
| 106 |
-
context: RequestContext
|
| 107 |
-
) -> str:
|
| 108 |
-
return await marvin.say_async(
|
| 109 |
-
message=[m.content.text for m in messages],
|
| 110 |
-
instructions=params.systemPrompt,
|
| 111 |
-
)
|
| 112 |
-
|
| 113 |
-
client = Client(
|
| 114 |
-
...,
|
| 115 |
-
sampling_handler=sampling_handler,
|
| 116 |
-
)
|
| 117 |
-
```
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
## Roots
|
| 121 |
-
|
| 122 |
-
<VersionBadge version="2.0.0" />
|
| 123 |
-
|
| 124 |
-
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.
|
| 125 |
-
|
| 126 |
-
Servers can request roots from clients, and clients can notify servers when their roots change.
|
| 127 |
-
|
| 128 |
-
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.
|
| 129 |
-
|
| 130 |
-
<CodeGroup>
|
| 131 |
-
```python Static Roots {5}
|
| 132 |
-
from fastmcp import Client
|
| 133 |
-
|
| 134 |
-
client = Client(
|
| 135 |
-
...,
|
| 136 |
-
roots=["/path/to/root1", "/path/to/root2"],
|
| 137 |
-
)
|
| 138 |
-
```
|
| 139 |
-
```python Dynamic Roots Callback {4-6, 10}
|
| 140 |
-
from fastmcp import Client
|
| 141 |
-
from fastmcp.client.roots import RequestContext
|
| 142 |
-
|
| 143 |
-
async def roots_callback(context: RequestContext) -> list[str]:
|
| 144 |
-
print(f"Server requested roots (Request ID: {context.request_id})")
|
| 145 |
-
return ["/path/to/root1", "/path/to/root2"]
|
| 146 |
-
|
| 147 |
-
client = Client(
|
| 148 |
-
...,
|
| 149 |
-
roots=roots_callback,
|
| 150 |
-
)
|
| 151 |
-
```
|
| 152 |
-
</CodeGroup>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/clients/client.mdx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
---
|
| 2 |
title: Client Overview
|
| 3 |
sidebarTitle: Overview
|
| 4 |
-
description: Learn how to use the FastMCP Client to interact with MCP servers.
|
| 5 |
icon: user-robot
|
| 6 |
---
|
| 7 |
|
|
@@ -9,388 +9,198 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
|
|
| 9 |
|
| 10 |
<VersionBadge version="2.0.0" />
|
| 11 |
|
| 12 |
-
The `fastmcp.Client`
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
|
| 18 |
-
|
| 19 |
-
-
|
| 20 |
-
|
| 21 |
-
### Transports
|
| 22 |
-
|
| 23 |
-
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.
|
| 24 |
|
| 25 |
-
|
| 26 |
|
| 27 |
-
|
| 28 |
-
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`.
|
| 29 |
-
3. **`Path` or `str` pointing to an existing file**:
|
| 30 |
-
* If it ends with `.py`: Creates a `PythonStdioTransport` to run the script using `python`.
|
| 31 |
-
* If it ends with `.js`: Creates a `NodeStdioTransport` to run the script using `node`.
|
| 32 |
-
4. **`AnyUrl` or `str` pointing to a URL that begins with `http://` or `https://`**:
|
| 33 |
-
* Creates a `StreamableHttpTransport`
|
| 34 |
-
5. **`MCPConfig` or dictionary matching MCPConfig schema**: Creates a client that connects to one or more MCP servers specified in the config.
|
| 35 |
-
6. **Other**: Raises a `ValueError` if the type cannot be inferred.
|
| 36 |
|
| 37 |
```python
|
| 38 |
import asyncio
|
| 39 |
from fastmcp import Client, FastMCP
|
| 40 |
|
| 41 |
-
#
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
server_script = "my_mcp_server.py" # Path to a Python server file
|
| 45 |
|
| 46 |
-
#
|
| 47 |
-
|
| 48 |
-
client_http = Client(http_url)
|
| 49 |
|
| 50 |
-
|
|
|
|
| 51 |
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
-
|
| 57 |
-
# <FastMCP(server='TestServer')>
|
| 58 |
-
# <StreamableHttp(url='https://example.com/mcp')>
|
| 59 |
-
# <PythonStdioTransport(command='python', args=['/path/to/your/my_mcp_server.py'])>
|
| 60 |
```
|
| 61 |
|
| 62 |
-
|
| 63 |
|
| 64 |
-
|
| 65 |
-
from fastmcp import Client
|
| 66 |
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
```
|
|
|
|
| 76 |
<Tip>
|
| 77 |
-
For
|
| 78 |
</Tip>
|
| 79 |
|
| 80 |
-
##
|
| 81 |
|
| 82 |
<VersionBadge version="2.4.0" />
|
| 83 |
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
<Note>
|
| 87 |
-
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.
|
| 88 |
-
</Note>
|
| 89 |
-
|
| 90 |
-
When you create a client with an `MCPConfig` containing multiple servers:
|
| 91 |
-
|
| 92 |
-
1. FastMCP creates a composite client that internally mounts all servers using their config names as prefixes
|
| 93 |
-
2. Tools and resources from each server are accessible with appropriate prefixes in the format `servername_toolname` and `protocol://servername/resource/path`
|
| 94 |
-
3. You interact with this as a single unified client, with requests automatically routed to the appropriate server
|
| 95 |
|
| 96 |
```python
|
| 97 |
-
from fastmcp import Client
|
| 98 |
-
|
| 99 |
-
# Create a standard MCP configuration with multiple servers
|
| 100 |
config = {
|
| 101 |
"mcpServers": {
|
| 102 |
-
|
| 103 |
-
"
|
| 104 |
-
"url": "https://weather-api.example.com/mcp",
|
| 105 |
-
"transport": "streamable-http"
|
| 106 |
-
},
|
| 107 |
-
# A local server running via stdio
|
| 108 |
-
"assistant": {
|
| 109 |
-
"command": "python",
|
| 110 |
-
"args": ["./my_assistant_server.py"],
|
| 111 |
-
"env": {"DEBUG": "true"}
|
| 112 |
-
}
|
| 113 |
}
|
| 114 |
}
|
| 115 |
|
| 116 |
-
# Create a client that connects to both servers
|
| 117 |
client = Client(config)
|
| 118 |
|
| 119 |
-
async
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
templates = await client.read_resource("resource://assistant/templates/list")
|
| 128 |
-
|
| 129 |
-
print(f"Weather: {weather_data}")
|
| 130 |
-
print(f"Assistant: {response}")
|
| 131 |
-
|
| 132 |
-
if __name__ == "__main__":
|
| 133 |
-
asyncio.run(main())
|
| 134 |
```
|
| 135 |
|
| 136 |
-
|
| 137 |
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
### Connection Lifecycle
|
| 141 |
-
|
| 142 |
-
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.
|
| 143 |
|
| 144 |
```python
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
async def main():
|
| 151 |
-
# Connection is established here
|
| 152 |
async with client:
|
| 153 |
-
print(f"
|
| 154 |
-
|
| 155 |
-
# Make
|
| 156 |
tools = await client.list_tools()
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
print(f"Greet result: {result}")
|
| 162 |
-
|
| 163 |
-
# Connection is closed automatically here
|
| 164 |
-
print(f"Client connected: {client.is_connected()}")
|
| 165 |
-
|
| 166 |
-
if __name__ == "__main__":
|
| 167 |
-
asyncio.run(main())
|
| 168 |
```
|
| 169 |
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
### Client Methods
|
| 173 |
|
| 174 |
-
The
|
| 175 |
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
-
###
|
| 181 |
|
| 182 |
-
|
| 183 |
-
|
|
|
|
| 184 |
tools = await client.list_tools()
|
| 185 |
-
|
| 186 |
-
```
|
| 187 |
-
* **`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.
|
| 188 |
-
```python
|
| 189 |
-
result = await client.call_tool("add", {"a": 5, "b": 3})
|
| 190 |
-
# result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
|
| 191 |
-
print(result[0].text) # Assuming TextContent, e.g., '8'
|
| 192 |
|
| 193 |
-
#
|
| 194 |
-
result = await client.call_tool("long_running_task", {"param": "value"}, timeout=2.0)
|
| 195 |
-
|
| 196 |
-
# With progress handler (to track execution progress)
|
| 197 |
-
result = await client.call_tool(
|
| 198 |
-
"long_running_task",
|
| 199 |
-
{"param": "value"},
|
| 200 |
-
progress_handler=my_progress_handler
|
| 201 |
-
)
|
| 202 |
-
```
|
| 203 |
-
* Arguments are passed as a dictionary. FastMCP servers automatically handle JSON string parsing for complex types if needed.
|
| 204 |
-
* Returns a list of content objects (usually `TextContent` or `ImageContent`).
|
| 205 |
-
* The optional `timeout` parameter limits the maximum execution time (in seconds) for this specific call, overriding any client-level timeout.
|
| 206 |
-
* The optional `progress_handler` parameter receives progress updates during execution, overriding any client-level progress handler.
|
| 207 |
-
|
| 208 |
-
#### Resource Operations
|
| 209 |
-
|
| 210 |
-
* **`list_resources()`**: Retrieves a list of static resources.
|
| 211 |
-
```python
|
| 212 |
resources = await client.list_resources()
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
# templates -> list[mcp.types.ResourceTemplate]
|
| 219 |
-
```
|
| 220 |
-
* **`read_resource(uri: str | AnyUrl)`**: Reads the content of a resource or a resolved template.
|
| 221 |
-
```python
|
| 222 |
-
# Read a static resource
|
| 223 |
-
readme_content = await client.read_resource("file:///path/to/README.md")
|
| 224 |
-
# readme_content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
|
| 225 |
-
print(readme_content[0].text) # Assuming text
|
| 226 |
-
|
| 227 |
-
# Read a resource generated from a template
|
| 228 |
-
weather_content = await client.read_resource("data://weather/london")
|
| 229 |
-
print(weather_content[0].text) # Assuming text JSON
|
| 230 |
-
```
|
| 231 |
-
|
| 232 |
-
#### Prompt Operations
|
| 233 |
-
|
| 234 |
-
* **`list_prompts()`**: Retrieves available prompt templates.
|
| 235 |
-
* **`get_prompt(name: str, arguments: dict[str, Any] | None = None)`**: Retrieves a rendered prompt message list.
|
| 236 |
-
|
| 237 |
-
<VersionBadge version="2.9.0" />
|
| 238 |
-
|
| 239 |
-
**Automatic Argument Serialization**: When calling prompts with complex arguments, the FastMCP client automatically serializes non-string values to JSON strings as required by the MCP specification. This allows you to pass typed objects directly while maintaining protocol compliance.
|
| 240 |
-
|
| 241 |
-
```python
|
| 242 |
-
from dataclasses import dataclass
|
| 243 |
-
|
| 244 |
-
@dataclass
|
| 245 |
-
class UserData:
|
| 246 |
-
name: str
|
| 247 |
-
age: int
|
| 248 |
-
|
| 249 |
-
async with client:
|
| 250 |
-
# You can pass complex objects directly
|
| 251 |
-
result = await client.get_prompt("analyze_user", {
|
| 252 |
-
"user": UserData(name="Alice", age=30), # Automatically serialized to JSON
|
| 253 |
-
"preferences": {"theme": "dark"}, # Dict serialized to JSON string
|
| 254 |
-
"scores": [85, 92, 78], # List serialized to JSON string
|
| 255 |
-
"simple_name": "Bob" # Strings passed through unchanged
|
| 256 |
-
})
|
| 257 |
-
```
|
| 258 |
-
|
| 259 |
-
The client handles the serialization automatically using `pydantic_core.to_json()` for consistent formatting, while the server can deserialize these JSON strings back to the expected types if using FastMCP's server-side type conversion.
|
| 260 |
-
|
| 261 |
-
### Raw MCP Protocol Objects
|
| 262 |
-
|
| 263 |
-
<VersionBadge version="2.2.7" />
|
| 264 |
-
|
| 265 |
-
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.
|
| 266 |
-
|
| 267 |
-
<Warning>
|
| 268 |
-
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.
|
| 269 |
-
</Warning>
|
| 270 |
-
|
| 271 |
-
```python
|
| 272 |
-
# Standard method - returns just the list of tools
|
| 273 |
-
tools = await client.list_tools()
|
| 274 |
-
# tools -> list[mcp.types.Tool]
|
| 275 |
-
|
| 276 |
-
# Raw MCP method - returns the full protocol object
|
| 277 |
-
result = await client.list_tools_mcp()
|
| 278 |
-
# result -> mcp.types.ListToolsResult
|
| 279 |
-
tools = result.tools
|
| 280 |
```
|
| 281 |
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
* **`list_tools_mcp()`**: Returns `mcp.types.ListToolsResult`
|
| 285 |
-
* **`call_tool_mcp(name, arguments)`**: Returns `mcp.types.CallToolResult`
|
| 286 |
-
* **`list_resources_mcp()`**: Returns `mcp.types.ListResourcesResult`
|
| 287 |
-
* **`list_resource_templates_mcp()`**: Returns `mcp.types.ListResourceTemplatesResult`
|
| 288 |
-
* **`read_resource_mcp(uri)`**: Returns `mcp.types.ReadResourceResult`
|
| 289 |
-
* **`list_prompts_mcp()`**: Returns `mcp.types.ListPromptsResult`
|
| 290 |
-
* **`get_prompt_mcp(name, arguments)`**: Returns `mcp.types.GetPromptResult`
|
| 291 |
-
* **`complete_mcp(ref, argument)`**: Returns `mcp.types.CompleteResult`
|
| 292 |
-
|
| 293 |
-
These methods are especially useful for debugging or when you need to access metadata or fields that aren't exposed by the simplified methods.
|
| 294 |
-
|
| 295 |
-
### Additional Features
|
| 296 |
-
|
| 297 |
-
#### Pinging the Server
|
| 298 |
|
| 299 |
-
The client
|
| 300 |
-
|
| 301 |
-
```python
|
| 302 |
-
async with client:
|
| 303 |
-
await client.ping()
|
| 304 |
-
print("Server is reachable")
|
| 305 |
-
```
|
| 306 |
-
|
| 307 |
-
#### Session Management
|
| 308 |
-
|
| 309 |
-
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.
|
| 310 |
-
|
| 311 |
-
When `keep_alive=False`, the client will automatically close the session when the context manager exits.
|
| 312 |
|
| 313 |
```python
|
| 314 |
from fastmcp import Client
|
|
|
|
| 315 |
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
async def example():
|
| 319 |
-
async with client:
|
| 320 |
-
await client.ping()
|
| 321 |
-
|
| 322 |
-
async with client:
|
| 323 |
-
await client.ping() # Same subprocess as above
|
| 324 |
-
```
|
| 325 |
-
|
| 326 |
-
<Note>
|
| 327 |
-
For detailed examples and configuration options, see [Session Management in Transports](/clients/transports#session-management).
|
| 328 |
-
</Note>
|
| 329 |
-
|
| 330 |
-
#### Timeouts
|
| 331 |
-
|
| 332 |
-
<VersionBadge version="2.3.4" />
|
| 333 |
|
| 334 |
-
|
|
|
|
| 335 |
|
| 336 |
-
```python
|
| 337 |
-
from fastmcp import Client
|
| 338 |
-
from fastmcp.exceptions import McpError
|
| 339 |
-
|
| 340 |
-
# Client with a global 5-second timeout for all requests
|
| 341 |
client = Client(
|
| 342 |
-
my_mcp_server,
|
| 343 |
-
|
|
|
|
|
|
|
| 344 |
)
|
| 345 |
-
|
| 346 |
-
async with client:
|
| 347 |
-
# This uses the global 5-second timeout
|
| 348 |
-
result1 = await client.call_tool("quick_task", {"param": "value"})
|
| 349 |
-
|
| 350 |
-
# This specifies a 10-second timeout for this specific call
|
| 351 |
-
result2 = await client.call_tool("slow_task", {"param": "value"}, timeout=10.0)
|
| 352 |
-
|
| 353 |
-
try:
|
| 354 |
-
# This will likely timeout
|
| 355 |
-
result3 = await client.call_tool("medium_task", {"param": "value"}, timeout=0.01)
|
| 356 |
-
except McpError as e:
|
| 357 |
-
# Handle timeout error
|
| 358 |
-
print(f"The task timed out: {e}")
|
| 359 |
```
|
| 360 |
|
| 361 |
-
|
| 362 |
-
Timeout behavior varies between transport types:
|
| 363 |
|
| 364 |
-
|
| 365 |
-
- With **HTTP** transport, the **lower** of the two timeouts (client or tool call) takes precedence.
|
| 366 |
|
| 367 |
-
|
| 368 |
-
|
|
|
|
|
|
|
| 369 |
|
| 370 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
async def safe_call_tool():
|
| 376 |
-
async with client:
|
| 377 |
-
try:
|
| 378 |
-
# Assume 'divide' tool exists and might raise ZeroDivisionError
|
| 379 |
-
result = await client.call_tool("divide", {"a": 10, "b": 0})
|
| 380 |
-
print(f"Result: {result}")
|
| 381 |
-
except ClientError as e:
|
| 382 |
-
print(f"Tool call failed: {e}")
|
| 383 |
-
except ConnectionError as e:
|
| 384 |
-
print(f"Connection failed: {e}")
|
| 385 |
-
except Exception as e:
|
| 386 |
-
print(f"An unexpected error occurred: {e}")
|
| 387 |
-
|
| 388 |
-
# Example Output if division by zero occurs:
|
| 389 |
-
# Tool call failed: Division by zero is not allowed.
|
| 390 |
-
```
|
| 391 |
-
|
| 392 |
-
Other errors, like connection failures, will raise standard Python exceptions (e.g., `ConnectionError`, `TimeoutError`).
|
| 393 |
|
| 394 |
<Tip>
|
| 395 |
-
The
|
| 396 |
-
</Tip>
|
|
|
|
| 1 |
---
|
| 2 |
title: Client Overview
|
| 3 |
sidebarTitle: Overview
|
| 4 |
+
description: Learn how to use the FastMCP Client to programmatically interact with MCP servers.
|
| 5 |
icon: user-robot
|
| 6 |
---
|
| 7 |
|
|
|
|
| 9 |
|
| 10 |
<VersionBadge version="2.0.0" />
|
| 11 |
|
| 12 |
+
The `fastmcp.Client` is a **programmatic client** for interacting with any Model Context Protocol (MCP) server. It provides a high-level, well-typed, Pythonic interface for deterministic MCP access, making it ideal for:
|
| 13 |
|
| 14 |
+
- **Testing MCP servers** during development
|
| 15 |
+
- **Building deterministic applications** that need reliable MCP interactions
|
| 16 |
+
- **Creating the foundation for agentic or LLM-based clients** with structured, type-safe operations
|
| 17 |
|
| 18 |
+
All client operations require using the `async with` context manager for proper connection lifecycle management.
|
| 19 |
|
| 20 |
+
<Note>
|
| 21 |
+
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.
|
| 22 |
+
</Note>
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
## Quick Start
|
| 25 |
|
| 26 |
+
The client uses transport inference to automatically determine the connection method:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
```python
|
| 29 |
import asyncio
|
| 30 |
from fastmcp import Client, FastMCP
|
| 31 |
|
| 32 |
+
# In-memory server (ideal for testing)
|
| 33 |
+
server = FastMCP("TestServer")
|
| 34 |
+
client = Client(server)
|
|
|
|
| 35 |
|
| 36 |
+
# HTTP server
|
| 37 |
+
client = Client("https://example.com/mcp")
|
|
|
|
| 38 |
|
| 39 |
+
# Local Python script
|
| 40 |
+
client = Client("my_mcp_server.py")
|
| 41 |
|
| 42 |
+
async def main():
|
| 43 |
+
async with client:
|
| 44 |
+
# Basic server interaction
|
| 45 |
+
await client.ping()
|
| 46 |
+
|
| 47 |
+
# List available operations
|
| 48 |
+
tools = await client.list_tools()
|
| 49 |
+
resources = await client.list_resources()
|
| 50 |
+
prompts = await client.list_prompts()
|
| 51 |
+
|
| 52 |
+
# Execute operations
|
| 53 |
+
result = await client.call_tool("example_tool", {"param": "value"})
|
| 54 |
+
print(result)
|
| 55 |
|
| 56 |
+
asyncio.run(main())
|
|
|
|
|
|
|
|
|
|
| 57 |
```
|
| 58 |
|
| 59 |
+
## Client-Transport Architecture
|
| 60 |
|
| 61 |
+
The FastMCP Client separates concerns between protocol and connection:
|
|
|
|
| 62 |
|
| 63 |
+
- **`Client`**: Handles MCP protocol operations (tools, resources, prompts) and manages callbacks
|
| 64 |
+
- **`Transport`**: Establishes and maintains the connection (WebSockets, HTTP, Stdio, in-memory)
|
| 65 |
+
|
| 66 |
+
### Transport Inference
|
| 67 |
+
|
| 68 |
+
The client automatically infers the appropriate transport based on the input:
|
| 69 |
|
| 70 |
+
1. **`FastMCP` instance** → In-memory transport (perfect for testing)
|
| 71 |
+
2. **File path ending in `.py`** → Python Stdio transport
|
| 72 |
+
3. **File path ending in `.js`** → Node.js Stdio transport
|
| 73 |
+
4. **URL starting with `http://` or `https://`** → HTTP transport
|
| 74 |
+
5. **`MCPConfig` dictionary** → Multi-server client
|
| 75 |
+
|
| 76 |
+
```python
|
| 77 |
+
from fastmcp import Client, FastMCP
|
| 78 |
+
|
| 79 |
+
# Examples of transport inference
|
| 80 |
+
client_memory = Client(FastMCP("TestServer"))
|
| 81 |
+
client_script = Client("./server.py")
|
| 82 |
+
client_http = Client("https://api.example.com/mcp")
|
| 83 |
```
|
| 84 |
+
|
| 85 |
<Tip>
|
| 86 |
+
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.
|
| 87 |
</Tip>
|
| 88 |
|
| 89 |
+
## Multi-Server Clients
|
| 90 |
|
| 91 |
<VersionBadge version="2.4.0" />
|
| 92 |
|
| 93 |
+
Connect to multiple MCP servers through a single client using MCP configuration:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
```python
|
|
|
|
|
|
|
|
|
|
| 96 |
config = {
|
| 97 |
"mcpServers": {
|
| 98 |
+
"weather": {"url": "https://weather-api.example.com/mcp"},
|
| 99 |
+
"assistant": {"command": "python", "args": ["./assistant_server.py"]}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
}
|
| 101 |
}
|
| 102 |
|
|
|
|
| 103 |
client = Client(config)
|
| 104 |
|
| 105 |
+
async with client:
|
| 106 |
+
# Tools are prefixed with server names
|
| 107 |
+
weather_data = await client.call_tool("weather_get_forecast", {"city": "London"})
|
| 108 |
+
response = await client.call_tool("assistant_answer_question", {"question": "What's the capital of France?"})
|
| 109 |
+
|
| 110 |
+
# Resources use prefixed URIs
|
| 111 |
+
icons = await client.read_resource("weather://weather/icons/sunny")
|
| 112 |
+
templates = await client.read_resource("resource://assistant/templates/list")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
```
|
| 114 |
|
| 115 |
+
## Connection Lifecycle
|
| 116 |
|
| 117 |
+
The client operates asynchronously and uses context managers for connection management:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
```python
|
| 120 |
+
async def example():
|
| 121 |
+
client = Client("my_mcp_server.py")
|
| 122 |
+
|
| 123 |
+
# Connection established here
|
|
|
|
|
|
|
|
|
|
| 124 |
async with client:
|
| 125 |
+
print(f"Connected: {client.is_connected()}")
|
| 126 |
+
|
| 127 |
+
# Make multiple calls within the same session
|
| 128 |
tools = await client.list_tools()
|
| 129 |
+
result = await client.call_tool("greet", {"name": "World"})
|
| 130 |
+
|
| 131 |
+
# Connection closed automatically here
|
| 132 |
+
print(f"Connected: {client.is_connected()}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
```
|
| 134 |
|
| 135 |
+
## Core Operations
|
|
|
|
|
|
|
| 136 |
|
| 137 |
+
The client provides methods for all standard MCP operations:
|
| 138 |
|
| 139 |
+
| Operation | Method | Description |
|
| 140 |
+
|-----------|--------|-------------|
|
| 141 |
+
| **Tools** | `list_tools()`, `call_tool()` | Execute server-side functions |
|
| 142 |
+
| **Resources** | `list_resources()`, `read_resource()` | Access server data sources |
|
| 143 |
+
| **Prompts** | `list_prompts()`, `get_prompt()` | Retrieve message templates |
|
| 144 |
+
| **Utility** | `ping()` | Test server connectivity |
|
| 145 |
|
| 146 |
+
### Quick Examples
|
| 147 |
|
| 148 |
+
```python
|
| 149 |
+
async with client:
|
| 150 |
+
# Tool operations
|
| 151 |
tools = await client.list_tools()
|
| 152 |
+
result = await client.call_tool("calculate", {"a": 5, "b": 3})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
|
| 154 |
+
# Resource operations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
resources = await client.list_resources()
|
| 156 |
+
content = await client.read_resource("file:///config/settings.json")
|
| 157 |
+
|
| 158 |
+
# Prompt operations
|
| 159 |
+
prompts = await client.list_prompts()
|
| 160 |
+
messages = await client.get_prompt("welcome", {"name": "Alice"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
```
|
| 162 |
|
| 163 |
+
## Advanced Configuration
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
+
The client supports additional configuration for specialized use cases:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
|
| 167 |
```python
|
| 168 |
from fastmcp import Client
|
| 169 |
+
from fastmcp.client.logging import LogMessage
|
| 170 |
|
| 171 |
+
async def log_handler(message: LogMessage):
|
| 172 |
+
print(f"Server log: {message.data}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
+
async def progress_handler(progress: float, total: float | None, message: str | None):
|
| 175 |
+
print(f"Progress: {progress}/{total} - {message}")
|
| 176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
client = Client(
|
| 178 |
+
"my_mcp_server.py",
|
| 179 |
+
log_handler=log_handler, # Handle server logs
|
| 180 |
+
progress_handler=progress_handler, # Monitor long operations
|
| 181 |
+
timeout=30.0 # Set request timeout
|
| 182 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
```
|
| 184 |
|
| 185 |
+
## Next Steps
|
|
|
|
| 186 |
|
| 187 |
+
Explore the detailed documentation for each operation type:
|
|
|
|
| 188 |
|
| 189 |
+
### Core Interactions
|
| 190 |
+
- **[Tools](/clients/tools)** - Execute server-side functions and handle results
|
| 191 |
+
- **[Resources](/clients/resources)** - Access static and templated resources
|
| 192 |
+
- **[Prompts](/clients/prompts)** - Work with message templates and argument serialization
|
| 193 |
|
| 194 |
+
### Advanced Features
|
| 195 |
+
- **[Logging](/clients/logging)** - Handle server log messages
|
| 196 |
+
- **[Progress](/clients/progress)** - Monitor long-running operations
|
| 197 |
+
- **[Sampling](/clients/sampling)** - Respond to server LLM requests
|
| 198 |
+
- **[Roots](/clients/roots)** - Provide local context to servers
|
| 199 |
|
| 200 |
+
### Connection Details
|
| 201 |
+
- **[Transports](/clients/transports)** - Configure connection methods and parameters
|
| 202 |
+
- **[Authentication](/clients/auth/oauth)** - Set up OAuth and bearer token authentication
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
|
| 204 |
<Tip>
|
| 205 |
+
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.
|
| 206 |
+
</Tip>
|
docs/clients/logging.mdx
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Server Logging
|
| 3 |
+
sidebarTitle: Logging
|
| 4 |
+
description: Learn how to receive and handle log messages from MCP servers.
|
| 5 |
+
icon: file-text
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.0.0" />
|
| 11 |
+
|
| 12 |
+
MCP servers can emit log messages to clients. The client can handle these logs through a log handler callback.
|
| 13 |
+
|
| 14 |
+
## Setting Up Log Handling
|
| 15 |
+
|
| 16 |
+
Provide a `log_handler` function when creating the client:
|
| 17 |
+
|
| 18 |
+
```python
|
| 19 |
+
from fastmcp import Client
|
| 20 |
+
from fastmcp.client.logging import LogMessage
|
| 21 |
+
|
| 22 |
+
async def log_handler(message: LogMessage):
|
| 23 |
+
level = message.level.upper()
|
| 24 |
+
logger = message.logger or 'server'
|
| 25 |
+
data = message.data
|
| 26 |
+
print(f"[{level}] {logger}: {data}")
|
| 27 |
+
|
| 28 |
+
client = Client(
|
| 29 |
+
"my_mcp_server.py",
|
| 30 |
+
log_handler=log_handler,
|
| 31 |
+
)
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
## LogMessage Structure
|
| 35 |
+
|
| 36 |
+
The `log_handler` receives a `LogMessage` object with:
|
| 37 |
+
|
| 38 |
+
- **`level`**: Log level (e.g., "debug", "info", "warning", "error")
|
| 39 |
+
- **`logger`**: Logger name (optional, may be None)
|
| 40 |
+
- **`data`**: The actual log message content
|
| 41 |
+
|
| 42 |
+
```python
|
| 43 |
+
async def detailed_log_handler(message: LogMessage):
|
| 44 |
+
if message.level == "error":
|
| 45 |
+
print(f"ERROR: {message.data}")
|
| 46 |
+
elif message.level == "warning":
|
| 47 |
+
print(f"WARNING: {message.data}")
|
| 48 |
+
else:
|
| 49 |
+
print(f"{message.level.upper()}: {message.data}")
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
## Default Log Handling
|
| 53 |
+
|
| 54 |
+
If you don't provide a custom `log_handler`, FastMCP uses a default handler that emits DEBUG level logs:
|
| 55 |
+
|
| 56 |
+
```python
|
| 57 |
+
# Without custom handler - uses default DEBUG logging
|
| 58 |
+
client = Client("my_mcp_server.py")
|
| 59 |
+
|
| 60 |
+
async with client:
|
| 61 |
+
# Server logs will be emitted at DEBUG level
|
| 62 |
+
await client.call_tool("some_tool")
|
| 63 |
+
```
|
docs/clients/progress.mdx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Progress Monitoring
|
| 3 |
+
sidebarTitle: Progress
|
| 4 |
+
description: Learn how to handle progress notifications from long-running server operations.
|
| 5 |
+
icon: chart-line
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.3.5" />
|
| 11 |
+
|
| 12 |
+
MCP servers can report progress during long-running operations. The client can receive these updates through a progress handler.
|
| 13 |
+
|
| 14 |
+
## Setting Up Progress Handling
|
| 15 |
+
|
| 16 |
+
Set a progress handler when creating the client:
|
| 17 |
+
|
| 18 |
+
```python
|
| 19 |
+
from fastmcp import Client
|
| 20 |
+
|
| 21 |
+
async def my_progress_handler(
|
| 22 |
+
progress: float,
|
| 23 |
+
total: float | None,
|
| 24 |
+
message: str | None
|
| 25 |
+
) -> None:
|
| 26 |
+
if total is not None:
|
| 27 |
+
percentage = (progress / total) * 100
|
| 28 |
+
print(f"Progress: {percentage:.1f}% - {message or ''}")
|
| 29 |
+
else:
|
| 30 |
+
print(f"Progress: {progress} - {message or ''}")
|
| 31 |
+
|
| 32 |
+
client = Client(
|
| 33 |
+
"my_mcp_server.py",
|
| 34 |
+
progress_handler=my_progress_handler
|
| 35 |
+
)
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
## Per-Call Progress Handler
|
| 39 |
+
|
| 40 |
+
Override the progress handler for specific tool calls:
|
| 41 |
+
|
| 42 |
+
```python
|
| 43 |
+
async with client:
|
| 44 |
+
# Override with specific progress handler for this call
|
| 45 |
+
result = await client.call_tool(
|
| 46 |
+
"long_running_task",
|
| 47 |
+
{"param": "value"},
|
| 48 |
+
progress_handler=my_progress_handler
|
| 49 |
+
)
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
## Handler Parameters
|
| 53 |
+
|
| 54 |
+
The progress handler receives:
|
| 55 |
+
|
| 56 |
+
- **`progress`** (float): Current progress value
|
| 57 |
+
- **`total`** (float | None): Expected total value (may be None)
|
| 58 |
+
- **`message`** (str | None): Optional status message (may be None)
|
| 59 |
+
|
docs/clients/prompts.mdx
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Prompt Operations
|
| 3 |
+
sidebarTitle: Prompts
|
| 4 |
+
description: Learn how to list and use server-side prompts with automatic argument serialization.
|
| 5 |
+
icon: message-square
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.0.0" />
|
| 11 |
+
|
| 12 |
+
Prompts are reusable message templates exposed by MCP servers. They can accept arguments to generate personalized message sequences for LLM interactions.
|
| 13 |
+
|
| 14 |
+
## Listing Prompts
|
| 15 |
+
|
| 16 |
+
Use `list_prompts()` to retrieve all available prompt templates:
|
| 17 |
+
|
| 18 |
+
```python
|
| 19 |
+
async with client:
|
| 20 |
+
prompts = await client.list_prompts()
|
| 21 |
+
# prompts -> list[mcp.types.Prompt]
|
| 22 |
+
|
| 23 |
+
for prompt in prompts:
|
| 24 |
+
print(f"Prompt: {prompt.name}")
|
| 25 |
+
print(f"Description: {prompt.description}")
|
| 26 |
+
if prompt.arguments:
|
| 27 |
+
print(f"Arguments: {[arg.name for arg in prompt.arguments]}")
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
## Using Prompts
|
| 31 |
+
|
| 32 |
+
### Basic Usage
|
| 33 |
+
|
| 34 |
+
Request a rendered prompt using `get_prompt()` with the prompt name and arguments:
|
| 35 |
+
|
| 36 |
+
```python
|
| 37 |
+
async with client:
|
| 38 |
+
# Simple prompt without arguments
|
| 39 |
+
result = await client.get_prompt("welcome_message")
|
| 40 |
+
# result -> mcp.types.GetPromptResult
|
| 41 |
+
|
| 42 |
+
# Access the generated messages
|
| 43 |
+
for message in result.messages:
|
| 44 |
+
print(f"Role: {message.role}")
|
| 45 |
+
print(f"Content: {message.content}")
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
### Prompts with Arguments
|
| 49 |
+
|
| 50 |
+
Pass arguments as a dictionary to customize the prompt:
|
| 51 |
+
|
| 52 |
+
```python
|
| 53 |
+
async with client:
|
| 54 |
+
# Prompt with simple arguments
|
| 55 |
+
result = await client.get_prompt("user_greeting", {
|
| 56 |
+
"name": "Alice",
|
| 57 |
+
"role": "administrator"
|
| 58 |
+
})
|
| 59 |
+
|
| 60 |
+
# Access the personalized messages
|
| 61 |
+
for message in result.messages:
|
| 62 |
+
print(f"Generated message: {message.content}")
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
## Automatic Argument Serialization
|
| 66 |
+
|
| 67 |
+
<VersionBadge version="2.9.0" />
|
| 68 |
+
|
| 69 |
+
FastMCP automatically serializes complex arguments to JSON strings as required by the MCP specification. This allows you to pass typed objects directly:
|
| 70 |
+
|
| 71 |
+
```python
|
| 72 |
+
from dataclasses import dataclass
|
| 73 |
+
|
| 74 |
+
@dataclass
|
| 75 |
+
class UserData:
|
| 76 |
+
name: str
|
| 77 |
+
age: int
|
| 78 |
+
|
| 79 |
+
async with client:
|
| 80 |
+
# Complex arguments are automatically serialized
|
| 81 |
+
result = await client.get_prompt("analyze_user", {
|
| 82 |
+
"user": UserData(name="Alice", age=30), # Automatically serialized to JSON
|
| 83 |
+
"preferences": {"theme": "dark"}, # Dict serialized to JSON string
|
| 84 |
+
"scores": [85, 92, 78], # List serialized to JSON string
|
| 85 |
+
"simple_name": "Bob" # Strings passed through unchanged
|
| 86 |
+
})
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
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.
|
| 90 |
+
|
| 91 |
+
### Serialization Examples
|
| 92 |
+
|
| 93 |
+
```python
|
| 94 |
+
async with client:
|
| 95 |
+
result = await client.get_prompt("data_analysis", {
|
| 96 |
+
# These will be automatically serialized to JSON strings:
|
| 97 |
+
"config": {
|
| 98 |
+
"format": "csv",
|
| 99 |
+
"include_headers": True,
|
| 100 |
+
"delimiter": ","
|
| 101 |
+
},
|
| 102 |
+
"filters": [
|
| 103 |
+
{"field": "age", "operator": ">", "value": 18},
|
| 104 |
+
{"field": "status", "operator": "==", "value": "active"}
|
| 105 |
+
],
|
| 106 |
+
# This remains a string:
|
| 107 |
+
"report_title": "Monthly Analytics Report"
|
| 108 |
+
})
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
## Working with Prompt Results
|
| 112 |
+
|
| 113 |
+
The `get_prompt()` method returns a `GetPromptResult` object containing a list of messages:
|
| 114 |
+
|
| 115 |
+
```python
|
| 116 |
+
async with client:
|
| 117 |
+
result = await client.get_prompt("conversation_starter", {"topic": "climate"})
|
| 118 |
+
|
| 119 |
+
# Access individual messages
|
| 120 |
+
for i, message in enumerate(result.messages):
|
| 121 |
+
print(f"Message {i + 1}:")
|
| 122 |
+
print(f" Role: {message.role}")
|
| 123 |
+
print(f" Content: {message.content.text if hasattr(message.content, 'text') else message.content}")
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
## Raw MCP Protocol Access
|
| 127 |
+
|
| 128 |
+
For access to the complete MCP protocol objects, use the `*_mcp` methods:
|
| 129 |
+
|
| 130 |
+
```python
|
| 131 |
+
async with client:
|
| 132 |
+
# Raw MCP method returns full protocol object
|
| 133 |
+
prompts_result = await client.list_prompts_mcp()
|
| 134 |
+
# prompts_result -> mcp.types.ListPromptsResult
|
| 135 |
+
|
| 136 |
+
prompt_result = await client.get_prompt_mcp("example_prompt", {"arg": "value"})
|
| 137 |
+
# prompt_result -> mcp.types.GetPromptResult
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
## Multi-Server Clients
|
| 141 |
+
|
| 142 |
+
When using multi-server clients, prompts are accessible without prefixing (unlike tools):
|
| 143 |
+
|
| 144 |
+
```python
|
| 145 |
+
async with client: # Multi-server client
|
| 146 |
+
# Prompts from any server are directly accessible
|
| 147 |
+
result1 = await client.get_prompt("weather_prompt", {"city": "London"})
|
| 148 |
+
result2 = await client.get_prompt("assistant_prompt", {"query": "help"})
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
## Common Prompt Patterns
|
| 152 |
+
|
| 153 |
+
### System Messages
|
| 154 |
+
|
| 155 |
+
Many prompts generate system messages for LLM configuration:
|
| 156 |
+
|
| 157 |
+
```python
|
| 158 |
+
async with client:
|
| 159 |
+
result = await client.get_prompt("system_configuration", {
|
| 160 |
+
"role": "helpful assistant",
|
| 161 |
+
"expertise": "python programming"
|
| 162 |
+
})
|
| 163 |
+
|
| 164 |
+
# Typically returns messages with role="system"
|
| 165 |
+
system_message = result.messages[0]
|
| 166 |
+
print(f"System prompt: {system_message.content}")
|
| 167 |
+
```
|
| 168 |
+
|
| 169 |
+
### Conversation Templates
|
| 170 |
+
|
| 171 |
+
Prompts can generate multi-turn conversation templates:
|
| 172 |
+
|
| 173 |
+
```python
|
| 174 |
+
async with client:
|
| 175 |
+
result = await client.get_prompt("interview_template", {
|
| 176 |
+
"candidate_name": "Alice",
|
| 177 |
+
"position": "Senior Developer"
|
| 178 |
+
})
|
| 179 |
+
|
| 180 |
+
# Multiple messages for a conversation flow
|
| 181 |
+
for message in result.messages:
|
| 182 |
+
print(f"{message.role}: {message.content}")
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
+
<Tip>
|
| 186 |
+
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.
|
| 187 |
+
</Tip>
|
docs/clients/resources.mdx
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Resource Operations
|
| 3 |
+
sidebarTitle: Resources
|
| 4 |
+
description: Learn how to list and read static and templated resources from MCP servers.
|
| 5 |
+
icon: folder-open
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.0.0" />
|
| 11 |
+
|
| 12 |
+
Resources are data sources exposed by MCP servers. They can be static files or dynamic templates that generate content based on parameters.
|
| 13 |
+
|
| 14 |
+
## Types of Resources
|
| 15 |
+
|
| 16 |
+
MCP servers expose two types of resources:
|
| 17 |
+
|
| 18 |
+
- **Static Resources**: Fixed content accessible via URI (e.g., configuration files, documentation)
|
| 19 |
+
- **Resource Templates**: Dynamic resources that accept parameters to generate content (e.g., API endpoints, database queries)
|
| 20 |
+
|
| 21 |
+
## Listing Resources
|
| 22 |
+
|
| 23 |
+
### Static Resources
|
| 24 |
+
|
| 25 |
+
Use `list_resources()` to retrieve all static resources available on the server:
|
| 26 |
+
|
| 27 |
+
```python
|
| 28 |
+
async with client:
|
| 29 |
+
resources = await client.list_resources()
|
| 30 |
+
# resources -> list[mcp.types.Resource]
|
| 31 |
+
|
| 32 |
+
for resource in resources:
|
| 33 |
+
print(f"Resource URI: {resource.uri}")
|
| 34 |
+
print(f"Name: {resource.name}")
|
| 35 |
+
print(f"Description: {resource.description}")
|
| 36 |
+
print(f"MIME Type: {resource.mimeType}")
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
### Resource Templates
|
| 40 |
+
|
| 41 |
+
Use `list_resource_templates()` to retrieve available resource templates:
|
| 42 |
+
|
| 43 |
+
```python
|
| 44 |
+
async with client:
|
| 45 |
+
templates = await client.list_resource_templates()
|
| 46 |
+
# templates -> list[mcp.types.ResourceTemplate]
|
| 47 |
+
|
| 48 |
+
for template in templates:
|
| 49 |
+
print(f"Template URI: {template.uriTemplate}")
|
| 50 |
+
print(f"Name: {template.name}")
|
| 51 |
+
print(f"Description: {template.description}")
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
## Reading Resources
|
| 55 |
+
|
| 56 |
+
### Static Resources
|
| 57 |
+
|
| 58 |
+
Read a static resource using its URI:
|
| 59 |
+
|
| 60 |
+
```python
|
| 61 |
+
async with client:
|
| 62 |
+
# Read a static resource
|
| 63 |
+
content = await client.read_resource("file:///path/to/README.md")
|
| 64 |
+
# content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
|
| 65 |
+
|
| 66 |
+
# Access text content
|
| 67 |
+
if hasattr(content[0], 'text'):
|
| 68 |
+
print(content[0].text)
|
| 69 |
+
|
| 70 |
+
# Access binary content
|
| 71 |
+
if hasattr(content[0], 'blob'):
|
| 72 |
+
print(f"Binary data: {len(content[0].blob)} bytes")
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
### Resource Templates
|
| 76 |
+
|
| 77 |
+
Read from a resource template by providing the URI with parameters:
|
| 78 |
+
|
| 79 |
+
```python
|
| 80 |
+
async with client:
|
| 81 |
+
# Read a resource generated from a template
|
| 82 |
+
# For example, a template like "weather://{{city}}/current"
|
| 83 |
+
weather_content = await client.read_resource("weather://london/current")
|
| 84 |
+
|
| 85 |
+
# Access the generated content
|
| 86 |
+
print(weather_content[0].text) # Assuming text JSON response
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
## Content Types
|
| 90 |
+
|
| 91 |
+
Resources can return different content types:
|
| 92 |
+
|
| 93 |
+
### Text Resources
|
| 94 |
+
|
| 95 |
+
```python
|
| 96 |
+
async with client:
|
| 97 |
+
content = await client.read_resource("resource://config/settings.json")
|
| 98 |
+
|
| 99 |
+
for item in content:
|
| 100 |
+
if hasattr(item, 'text'):
|
| 101 |
+
print(f"Text content: {item.text}")
|
| 102 |
+
print(f"MIME type: {item.mimeType}")
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
### Binary Resources
|
| 106 |
+
|
| 107 |
+
```python
|
| 108 |
+
async with client:
|
| 109 |
+
content = await client.read_resource("resource://images/logo.png")
|
| 110 |
+
|
| 111 |
+
for item in content:
|
| 112 |
+
if hasattr(item, 'blob'):
|
| 113 |
+
print(f"Binary content: {len(item.blob)} bytes")
|
| 114 |
+
print(f"MIME type: {item.mimeType}")
|
| 115 |
+
|
| 116 |
+
# Save to file
|
| 117 |
+
with open("downloaded_logo.png", "wb") as f:
|
| 118 |
+
f.write(item.blob)
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
## Working with Multi-Server Clients
|
| 122 |
+
|
| 123 |
+
When using multi-server clients, resource URIs are automatically prefixed with the server name:
|
| 124 |
+
|
| 125 |
+
```python
|
| 126 |
+
async with client: # Multi-server client
|
| 127 |
+
# Access resources from different servers
|
| 128 |
+
weather_icons = await client.read_resource("weather://weather/icons/sunny")
|
| 129 |
+
templates = await client.read_resource("resource://assistant/templates/list")
|
| 130 |
+
|
| 131 |
+
print(f"Weather icon: {weather_icons[0].blob}")
|
| 132 |
+
print(f"Templates: {templates[0].text}")
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
## Raw MCP Protocol Access
|
| 136 |
+
|
| 137 |
+
For access to the complete MCP protocol objects, use the `*_mcp` methods:
|
| 138 |
+
|
| 139 |
+
```python
|
| 140 |
+
async with client:
|
| 141 |
+
# Raw MCP methods return full protocol objects
|
| 142 |
+
resources_result = await client.list_resources_mcp()
|
| 143 |
+
# resources_result -> mcp.types.ListResourcesResult
|
| 144 |
+
|
| 145 |
+
templates_result = await client.list_resource_templates_mcp()
|
| 146 |
+
# templates_result -> mcp.types.ListResourceTemplatesResult
|
| 147 |
+
|
| 148 |
+
content_result = await client.read_resource_mcp("resource://example")
|
| 149 |
+
# content_result -> mcp.types.ReadResourceResult
|
| 150 |
+
```
|
| 151 |
+
|
| 152 |
+
## Common Resource URI Patterns
|
| 153 |
+
|
| 154 |
+
Different MCP servers may use various URI schemes:
|
| 155 |
+
|
| 156 |
+
```python
|
| 157 |
+
# File system resources
|
| 158 |
+
"file:///path/to/file.txt"
|
| 159 |
+
|
| 160 |
+
# Custom protocol resources
|
| 161 |
+
"weather://london/current"
|
| 162 |
+
"database://users/123"
|
| 163 |
+
|
| 164 |
+
# Generic resource protocol
|
| 165 |
+
"resource://config/settings"
|
| 166 |
+
"resource://templates/email"
|
| 167 |
+
```
|
| 168 |
+
|
| 169 |
+
<Tip>
|
| 170 |
+
Resource URIs and their formats depend on the specific MCP server implementation. Check the server's documentation for available resources and their URI patterns.
|
| 171 |
+
</Tip>
|
docs/clients/roots.mdx
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Client Roots
|
| 3 |
+
sidebarTitle: Roots
|
| 4 |
+
description: Learn how to provide local context to MCP servers.
|
| 5 |
+
icon: tree
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.0.0" />
|
| 11 |
+
|
| 12 |
+
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.
|
| 13 |
+
|
| 14 |
+
## Setting Static Roots
|
| 15 |
+
|
| 16 |
+
Provide a list of roots when creating the client:
|
| 17 |
+
|
| 18 |
+
<CodeGroup>
|
| 19 |
+
```python Static Roots
|
| 20 |
+
from fastmcp import Client
|
| 21 |
+
|
| 22 |
+
client = Client(
|
| 23 |
+
"my_mcp_server.py",
|
| 24 |
+
roots=["/path/to/root1", "/path/to/root2"]
|
| 25 |
+
)
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
```python Dynamic Roots Callback
|
| 29 |
+
from fastmcp import Client
|
| 30 |
+
from fastmcp.client.roots import RequestContext
|
| 31 |
+
|
| 32 |
+
async def roots_callback(context: RequestContext) -> list[str]:
|
| 33 |
+
print(f"Server requested roots (Request ID: {context.request_id})")
|
| 34 |
+
return ["/path/to/root1", "/path/to/root2"]
|
| 35 |
+
|
| 36 |
+
client = Client(
|
| 37 |
+
"my_mcp_server.py",
|
| 38 |
+
roots=roots_callback
|
| 39 |
+
)
|
| 40 |
+
```
|
| 41 |
+
</CodeGroup>
|
| 42 |
+
|
docs/clients/sampling.mdx
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: LLM Sampling
|
| 3 |
+
sidebarTitle: Sampling
|
| 4 |
+
description: Learn how to handle server-initiated LLM sampling requests.
|
| 5 |
+
icon: brain
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.0.0" />
|
| 11 |
+
|
| 12 |
+
MCP servers can request LLM completions from clients. The client handles these requests through a sampling handler callback.
|
| 13 |
+
|
| 14 |
+
## Setting Up Sampling Handling
|
| 15 |
+
|
| 16 |
+
Provide a `sampling_handler` function when creating the client:
|
| 17 |
+
|
| 18 |
+
```python
|
| 19 |
+
from fastmcp import Client
|
| 20 |
+
from fastmcp.client.sampling import (
|
| 21 |
+
SamplingMessage,
|
| 22 |
+
SamplingParams,
|
| 23 |
+
RequestContext,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
async def sampling_handler(
|
| 27 |
+
messages: list[SamplingMessage],
|
| 28 |
+
params: SamplingParams,
|
| 29 |
+
context: RequestContext
|
| 30 |
+
) -> str:
|
| 31 |
+
# Your LLM integration logic here
|
| 32 |
+
# Extract text from messages and generate a response
|
| 33 |
+
return "Generated response based on the messages"
|
| 34 |
+
|
| 35 |
+
client = Client(
|
| 36 |
+
"my_mcp_server.py",
|
| 37 |
+
sampling_handler=sampling_handler,
|
| 38 |
+
)
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
## Handler Parameters
|
| 42 |
+
|
| 43 |
+
The sampling handler receives:
|
| 44 |
+
|
| 45 |
+
- **`messages`**: List of `SamplingMessage` objects representing the conversation
|
| 46 |
+
- **`params`**: `SamplingParams` object with generation parameters (systemPrompt, maxTokens, temperature, etc.)
|
| 47 |
+
- **`context`**: `RequestContext` object with request metadata
|
| 48 |
+
|
| 49 |
+
## Basic Example
|
| 50 |
+
|
| 51 |
+
```python
|
| 52 |
+
async def basic_sampling_handler(
|
| 53 |
+
messages: list[SamplingMessage],
|
| 54 |
+
params: SamplingParams,
|
| 55 |
+
context: RequestContext
|
| 56 |
+
) -> str:
|
| 57 |
+
# Extract message content
|
| 58 |
+
conversation = []
|
| 59 |
+
for message in messages:
|
| 60 |
+
content = message.content.text if hasattr(message.content, 'text') else str(message.content)
|
| 61 |
+
conversation.append(f"{message.role}: {content}")
|
| 62 |
+
|
| 63 |
+
# Use the system prompt if provided
|
| 64 |
+
system_prompt = params.systemPrompt or "You are a helpful assistant."
|
| 65 |
+
|
| 66 |
+
# Here you would integrate with your preferred LLM service
|
| 67 |
+
# This is just a placeholder response
|
| 68 |
+
return f"Response based on conversation: {' | '.join(conversation)}"
|
| 69 |
+
|
| 70 |
+
client = Client(
|
| 71 |
+
"my_mcp_server.py",
|
| 72 |
+
sampling_handler=basic_sampling_handler
|
| 73 |
+
)
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
## Accessing Parameters
|
| 77 |
+
|
| 78 |
+
```python
|
| 79 |
+
async def parameter_handler(
|
| 80 |
+
messages: list[SamplingMessage],
|
| 81 |
+
params: SamplingParams,
|
| 82 |
+
context: RequestContext
|
| 83 |
+
) -> str:
|
| 84 |
+
# Available parameters from the server
|
| 85 |
+
system_prompt = params.systemPrompt
|
| 86 |
+
max_tokens = params.maxTokens
|
| 87 |
+
temperature = params.temperature
|
| 88 |
+
top_p = params.topP
|
| 89 |
+
stop_sequences = params.stopSequences
|
| 90 |
+
|
| 91 |
+
# Use these parameters with your LLM service
|
| 92 |
+
return "Generated response"
|
| 93 |
+
```
|
| 94 |
+
|
docs/clients/tools.mdx
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Tool Operations
|
| 3 |
+
sidebarTitle: Tools
|
| 4 |
+
description: Learn how to discover and execute tools on MCP servers.
|
| 5 |
+
icon: wrench
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.0.0" />
|
| 11 |
+
|
| 12 |
+
Tools are executable functions exposed by MCP servers. The FastMCP client provides methods to discover available tools and execute them with arguments.
|
| 13 |
+
|
| 14 |
+
## Discovering Tools
|
| 15 |
+
|
| 16 |
+
Use `list_tools()` to retrieve all tools available on the server:
|
| 17 |
+
|
| 18 |
+
```python
|
| 19 |
+
async with client:
|
| 20 |
+
tools = await client.list_tools()
|
| 21 |
+
# tools -> list[mcp.types.Tool]
|
| 22 |
+
|
| 23 |
+
for tool in tools:
|
| 24 |
+
print(f"Tool: {tool.name}")
|
| 25 |
+
print(f"Description: {tool.description}")
|
| 26 |
+
if tool.inputSchema:
|
| 27 |
+
print(f"Parameters: {tool.inputSchema}")
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
## Executing Tools
|
| 31 |
+
|
| 32 |
+
### Basic Execution
|
| 33 |
+
|
| 34 |
+
Execute a tool using `call_tool()` with the tool name and arguments:
|
| 35 |
+
|
| 36 |
+
```python
|
| 37 |
+
async with client:
|
| 38 |
+
# Simple tool call
|
| 39 |
+
result = await client.call_tool("add", {"a": 5, "b": 3})
|
| 40 |
+
# result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
|
| 41 |
+
|
| 42 |
+
# Access the result content
|
| 43 |
+
print(result[0].text) # Assuming TextContent, e.g., '8'
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
### Advanced Execution Options
|
| 47 |
+
|
| 48 |
+
The `call_tool()` method supports additional parameters for timeout control and progress monitoring:
|
| 49 |
+
|
| 50 |
+
```python
|
| 51 |
+
async with client:
|
| 52 |
+
# With timeout (aborts if execution takes longer than 2 seconds)
|
| 53 |
+
result = await client.call_tool(
|
| 54 |
+
"long_running_task",
|
| 55 |
+
{"param": "value"},
|
| 56 |
+
timeout=2.0
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
# With progress handler (to track execution progress)
|
| 60 |
+
result = await client.call_tool(
|
| 61 |
+
"long_running_task",
|
| 62 |
+
{"param": "value"},
|
| 63 |
+
progress_handler=my_progress_handler
|
| 64 |
+
)
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
**Parameters:**
|
| 68 |
+
- `name`: The tool name (string)
|
| 69 |
+
- `arguments`: Dictionary of arguments to pass to the tool (optional)
|
| 70 |
+
- `timeout`: Maximum execution time in seconds (optional, overrides client-level timeout)
|
| 71 |
+
- `progress_handler`: Progress callback function (optional, overrides client-level handler)
|
| 72 |
+
|
| 73 |
+
## Handling Results
|
| 74 |
+
|
| 75 |
+
Tool execution returns a list of content objects. The most common types are:
|
| 76 |
+
|
| 77 |
+
- **`TextContent`**: Text-based results with a `.text` attribute
|
| 78 |
+
- **`ImageContent`**: Image data with image-specific attributes
|
| 79 |
+
- **`BlobContent`**: Binary data content
|
| 80 |
+
|
| 81 |
+
```python
|
| 82 |
+
async with client:
|
| 83 |
+
result = await client.call_tool("get_weather", {"city": "London"})
|
| 84 |
+
|
| 85 |
+
for content in result:
|
| 86 |
+
if hasattr(content, 'text'):
|
| 87 |
+
print(f"Text result: {content.text}")
|
| 88 |
+
elif hasattr(content, 'data'):
|
| 89 |
+
print(f"Binary data: {len(content.data)} bytes")
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
## Error Handling
|
| 93 |
+
|
| 94 |
+
### Exception-Based Error Handling
|
| 95 |
+
|
| 96 |
+
By default, `call_tool()` raises a `ToolError` if the tool execution fails:
|
| 97 |
+
|
| 98 |
+
```python
|
| 99 |
+
from fastmcp.exceptions import ToolError
|
| 100 |
+
|
| 101 |
+
async with client:
|
| 102 |
+
try:
|
| 103 |
+
result = await client.call_tool("potentially_failing_tool", {"param": "value"})
|
| 104 |
+
print("Tool succeeded:", result)
|
| 105 |
+
except ToolError as e:
|
| 106 |
+
print(f"Tool failed: {e}")
|
| 107 |
+
```
|
| 108 |
+
|
| 109 |
+
### Manual Error Checking
|
| 110 |
+
|
| 111 |
+
For more granular control, use `call_tool_mcp()` which returns the raw MCP protocol object with an `isError` flag:
|
| 112 |
+
|
| 113 |
+
```python
|
| 114 |
+
async with client:
|
| 115 |
+
result = await client.call_tool_mcp("potentially_failing_tool", {"param": "value"})
|
| 116 |
+
# result -> mcp.types.CallToolResult
|
| 117 |
+
|
| 118 |
+
if result.isError:
|
| 119 |
+
print(f"Tool failed: {result.content}")
|
| 120 |
+
else:
|
| 121 |
+
print(f"Tool succeeded: {result.content}")
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
## Argument Handling
|
| 125 |
+
|
| 126 |
+
Arguments are passed as a dictionary to the tool:
|
| 127 |
+
|
| 128 |
+
```python
|
| 129 |
+
async with client:
|
| 130 |
+
# Simple arguments
|
| 131 |
+
result = await client.call_tool("greet", {"name": "World"})
|
| 132 |
+
|
| 133 |
+
# Complex arguments
|
| 134 |
+
result = await client.call_tool("process_data", {
|
| 135 |
+
"config": {"format": "json", "validate": True},
|
| 136 |
+
"items": [1, 2, 3, 4, 5],
|
| 137 |
+
"metadata": {"source": "api", "version": "1.0"}
|
| 138 |
+
})
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
<Tip>
|
| 142 |
+
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).
|
| 143 |
+
</Tip>
|
docs/docs.json
CHANGED
|
@@ -94,13 +94,31 @@
|
|
| 94 |
"group": "Clients",
|
| 95 |
"pages": [
|
| 96 |
"clients/client",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
"clients/transports",
|
| 98 |
{
|
| 99 |
"group": "Authentication",
|
| 100 |
"icon": "user-shield",
|
| 101 |
"pages": ["clients/auth/oauth", "clients/auth/bearer"]
|
| 102 |
-
}
|
| 103 |
-
"clients/advanced-features"
|
| 104 |
]
|
| 105 |
},
|
| 106 |
{
|
|
|
|
| 94 |
"group": "Clients",
|
| 95 |
"pages": [
|
| 96 |
"clients/client",
|
| 97 |
+
{
|
| 98 |
+
"group": "Core Interactions",
|
| 99 |
+
"icon": "handshake",
|
| 100 |
+
"pages": [
|
| 101 |
+
"clients/tools",
|
| 102 |
+
"clients/resources",
|
| 103 |
+
"clients/prompts"
|
| 104 |
+
]
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"group": "Advanced Features",
|
| 108 |
+
"icon": "stars",
|
| 109 |
+
"pages": [
|
| 110 |
+
"clients/logging",
|
| 111 |
+
"clients/progress",
|
| 112 |
+
"clients/sampling",
|
| 113 |
+
"clients/roots"
|
| 114 |
+
]
|
| 115 |
+
},
|
| 116 |
"clients/transports",
|
| 117 |
{
|
| 118 |
"group": "Authentication",
|
| 119 |
"icon": "user-shield",
|
| 120 |
"pages": ["clients/auth/oauth", "clients/auth/bearer"]
|
| 121 |
+
}
|
|
|
|
| 122 |
]
|
| 123 |
},
|
| 124 |
{
|
justfile
CHANGED
|
@@ -24,4 +24,7 @@ api-ref *MODULES:
|
|
| 24 |
|
| 25 |
# Clean up API reference documentation
|
| 26 |
api-ref-clean:
|
| 27 |
-
rm -rf docs/python-sdk
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
# Clean up API reference documentation
|
| 26 |
api-ref-clean:
|
| 27 |
+
rm -rf docs/python-sdk
|
| 28 |
+
|
| 29 |
+
copy-context:
|
| 30 |
+
uvx --with-editable . --refresh-package copychat copychat@latest src/ docs/ -x changelog.mdx -x python-sdk/ -v
|