Spaces:
Running
Running
Jeremiah Lowin commited on
Commit ·
9b4ba17
1
Parent(s): 11e3870
add client docs
Browse files- docs/clients/overview.mdx +252 -0
- docs/clients/transports.mdx +241 -0
- docs/docs.json +7 -2
- docs/style.css +10 -10
docs/clients/overview.mdx
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
|
| 8 |
+
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.
|
| 9 |
+
|
| 10 |
+
## FastMCP Client
|
| 11 |
+
|
| 12 |
+
The FastMCP Client architecture separates the protocol logic (`Client`) from the connection mechanism (`Transport`).
|
| 13 |
+
|
| 14 |
+
- **`Client`**: Handles sending MCP requests (like `tools/call`, `resources/read`), receiving responses, and managing callbacks.
|
| 15 |
+
- **`Transport`**: Responsible for establishing and maintaining the connection to the server (e.g., via WebSockets, SSE, Stdio, or in-memory).
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
### Transports
|
| 19 |
+
|
| 20 |
+
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.
|
| 21 |
+
|
| 22 |
+
The following inference rules are used to determine the appropriate `ClientTransport` based on the input type:
|
| 23 |
+
|
| 24 |
+
1. **`ClientTransport` Instance**: If you provide an already instantiated transport object, it's used directly.
|
| 25 |
+
2. **`FastMCP` Instance**: Creates a `FastMCPTransport` for efficient in-memory communication (ideal for testing).
|
| 26 |
+
3. **`Path` or `str` pointing to an existing file**:
|
| 27 |
+
* If it ends with `.py`: Creates a `PythonStdioTransport` to run the script using `python`.
|
| 28 |
+
* If it ends with `.js`: Creates a `NodeStdioTransport` to run the script using `node`.
|
| 29 |
+
4. **`AnyUrl` or `str` pointing to a URL**:
|
| 30 |
+
* If it starts with `http://` or `https://`: Creates an `SSETransport`.
|
| 31 |
+
* If it starts with `ws://` or `wss://`: Creates a `WSTransport`.
|
| 32 |
+
5. **Other**: Raises a `ValueError` if the type cannot be inferred.
|
| 33 |
+
|
| 34 |
+
```python
|
| 35 |
+
import asyncio
|
| 36 |
+
from fastmcp import Client, FastMCP
|
| 37 |
+
|
| 38 |
+
# Example transports (more details in Transports page)
|
| 39 |
+
server_instance = FastMCP(name="TestServer") # In-memory server
|
| 40 |
+
sse_url = "http://localhost:8000/sse" # SSE server URL
|
| 41 |
+
ws_url = "ws://localhost:9000" # WebSocket server URL
|
| 42 |
+
server_script = "my_mcp_server.py" # Path to a Python server file
|
| 43 |
+
|
| 44 |
+
# Client automatically infers the transport type
|
| 45 |
+
client_in_memory = Client(server_instance)
|
| 46 |
+
client_sse = Client(sse_url)
|
| 47 |
+
client_ws = Client(ws_url)
|
| 48 |
+
client_stdio = Client(server_script)
|
| 49 |
+
|
| 50 |
+
print(client_in_memory.transport)
|
| 51 |
+
print(client_sse.transport)
|
| 52 |
+
print(client_ws.transport)
|
| 53 |
+
print(client_stdio.transport)
|
| 54 |
+
|
| 55 |
+
# Expected Output (types may vary slightly based on environment):
|
| 56 |
+
# <FastMCP(server='TestServer')>
|
| 57 |
+
# <SSE(url='http://localhost:8000/sse')>
|
| 58 |
+
# <WebSocket(url='ws://localhost:9000')>
|
| 59 |
+
# <PythonStdioTransport(command='python', args=['/path/to/your/my_mcp_server.py'])>
|
| 60 |
+
```
|
| 61 |
+
<Tip>
|
| 62 |
+
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.
|
| 63 |
+
</Tip>
|
| 64 |
+
|
| 65 |
+
## Client Usage
|
| 66 |
+
|
| 67 |
+
### Connection Lifecycle
|
| 68 |
+
|
| 69 |
+
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.
|
| 70 |
+
|
| 71 |
+
```python
|
| 72 |
+
import asyncio
|
| 73 |
+
from fastmcp import Client
|
| 74 |
+
|
| 75 |
+
client = Client("my_mcp_server.py") # Assumes my_mcp_server.py exists
|
| 76 |
+
|
| 77 |
+
async def main():
|
| 78 |
+
# Connection is established here
|
| 79 |
+
async with client:
|
| 80 |
+
print(f"Client connected: {client.is_connected()}")
|
| 81 |
+
|
| 82 |
+
# Make MCP calls within the context
|
| 83 |
+
tools = await client.list_tools()
|
| 84 |
+
print(f"Available tools: {tools}")
|
| 85 |
+
|
| 86 |
+
if any(tool.name == "greet" for tool in tools):
|
| 87 |
+
result = await client.call_tool("greet", {"name": "World"})
|
| 88 |
+
print(f"Greet result: {result}")
|
| 89 |
+
|
| 90 |
+
# Connection is closed automatically here
|
| 91 |
+
print(f"Client connected: {client.is_connected()}")
|
| 92 |
+
|
| 93 |
+
if __name__ == "__main__":
|
| 94 |
+
asyncio.run(main())
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
You can make multiple calls to the server within the same `async with` block using the established session.
|
| 98 |
+
|
| 99 |
+
### Client Methods
|
| 100 |
+
|
| 101 |
+
The `Client` provides methods corresponding to standard MCP requests:
|
| 102 |
+
|
| 103 |
+
#### Tool Operations
|
| 104 |
+
|
| 105 |
+
* **`list_tools()`**: Retrieves a list of tools available on the server.
|
| 106 |
+
```python
|
| 107 |
+
tools = await client.list_tools()
|
| 108 |
+
# tools -> list[mcp.types.Tool]
|
| 109 |
+
```
|
| 110 |
+
* **`call_tool(name: str, arguments: dict[str, Any] | None = None)`**: Executes a tool on the server.
|
| 111 |
+
```python
|
| 112 |
+
result = await client.call_tool("add", {"a": 5, "b": 3})
|
| 113 |
+
# result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
|
| 114 |
+
print(result[0].text) # Assuming TextContent, e.g., '8'
|
| 115 |
+
```
|
| 116 |
+
* Arguments are passed as a dictionary. FastMCP servers automatically handle JSON string parsing for complex types if needed.
|
| 117 |
+
* Returns a list of content objects (usually `TextContent` or `ImageContent`).
|
| 118 |
+
|
| 119 |
+
#### Resource Operations
|
| 120 |
+
|
| 121 |
+
* **`list_resources()`**: Retrieves a list of static resources.
|
| 122 |
+
```python
|
| 123 |
+
resources = await client.list_resources()
|
| 124 |
+
# resources -> list[mcp.types.Resource]
|
| 125 |
+
```
|
| 126 |
+
* **`list_resource_templates()`**: Retrieves a list of resource templates.
|
| 127 |
+
```python
|
| 128 |
+
templates = await client.list_resource_templates()
|
| 129 |
+
# templates -> list[mcp.types.ResourceTemplate]
|
| 130 |
+
```
|
| 131 |
+
* **`read_resource(uri: str | AnyUrl)`**: Reads the content of a resource or a resolved template.
|
| 132 |
+
```python
|
| 133 |
+
# Read a static resource
|
| 134 |
+
readme_content = await client.read_resource("file:///path/to/README.md")
|
| 135 |
+
# readme_content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
|
| 136 |
+
print(readme_content[0].text) # Assuming text
|
| 137 |
+
|
| 138 |
+
# Read a resource generated from a template
|
| 139 |
+
weather_content = await client.read_resource("data://weather/london")
|
| 140 |
+
print(weather_content[0].text) # Assuming text JSON
|
| 141 |
+
```
|
| 142 |
+
|
| 143 |
+
#### Prompt Operations
|
| 144 |
+
|
| 145 |
+
* **`list_prompts()`**: Retrieves available prompt templates.
|
| 146 |
+
* **`get_prompt(name: str, arguments: dict[str, Any] | None = None)`**: Retrieves a rendered prompt message list.
|
| 147 |
+
|
| 148 |
+
### Callbacks
|
| 149 |
+
|
| 150 |
+
MCP allows servers to make requests *back* to the client for certain capabilities. The `Client` constructor accepts callback functions to handle these server requests:
|
| 151 |
+
|
| 152 |
+
#### Roots
|
| 153 |
+
|
| 154 |
+
* **`roots: RootsList | RootsHandler | None`**: Provides the server with a list of root directories the client grants access to. This can be a static list or a function that dynamically determines roots.
|
| 155 |
+
```python
|
| 156 |
+
from pathlib import Path
|
| 157 |
+
from fastmcp.client.roots import RootsHandler, RootsList
|
| 158 |
+
from mcp.shared.context import RequestContext # For type hint
|
| 159 |
+
|
| 160 |
+
# Option 1: Static list
|
| 161 |
+
static_roots: RootsList = [str(Path.home() / "Documents")]
|
| 162 |
+
|
| 163 |
+
# Option 2: Dynamic function
|
| 164 |
+
def dynamic_roots_handler(context: RequestContext) -> RootsList:
|
| 165 |
+
# Logic to determine accessible roots based on context
|
| 166 |
+
print(f"Server requested roots (Request ID: {context.request_id})")
|
| 167 |
+
return [str(Path.home() / "Downloads")]
|
| 168 |
+
|
| 169 |
+
client_with_roots = Client(
|
| 170 |
+
"my_server.py",
|
| 171 |
+
roots=dynamic_roots_handler # or roots=static_roots
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
# Tell the server the roots might have changed (if needed)
|
| 175 |
+
# async with client_with_roots:
|
| 176 |
+
# await client_with_roots.send_roots_list_changed()
|
| 177 |
+
```
|
| 178 |
+
See `fastmcp.client.roots` for helpers.
|
| 179 |
+
|
| 180 |
+
#### LLM Sampling
|
| 181 |
+
|
| 182 |
+
* **`sampling_handler: SamplingHandler | None`**: Handles `sampling/createMessage` requests from the server. This callback receives messages from the server and should return an LLM completion.
|
| 183 |
+
```python
|
| 184 |
+
from fastmcp.client.sampling import SamplingHandler, MessageResult
|
| 185 |
+
from mcp.types import SamplingMessage, SamplingParams, TextContent
|
| 186 |
+
from mcp.shared.context import RequestContext # For type hint
|
| 187 |
+
|
| 188 |
+
async def my_llm_handler(
|
| 189 |
+
messages: list[SamplingMessage],
|
| 190 |
+
params: SamplingParams,
|
| 191 |
+
context: RequestContext
|
| 192 |
+
) -> str | MessageResult:
|
| 193 |
+
print(f"Server requested sampling (Request ID: {context.request_id})")
|
| 194 |
+
# In a real scenario, call your LLM API here
|
| 195 |
+
last_user_message = next((m for m in reversed(messages) if m.role == 'user'), None)
|
| 196 |
+
prompt = last_user_message.content.text if last_user_message and isinstance(last_user_message.content, TextContent) else "Default prompt"
|
| 197 |
+
|
| 198 |
+
# Simulate LLM response
|
| 199 |
+
response_text = f"LLM processed: {prompt[:50]}..."
|
| 200 |
+
# Return simple string (becomes TextContent) or a MessageResult object
|
| 201 |
+
return response_text
|
| 202 |
+
|
| 203 |
+
client_with_sampling = Client(
|
| 204 |
+
"my_server.py",
|
| 205 |
+
sampling_handler=my_llm_handler
|
| 206 |
+
)
|
| 207 |
+
```
|
| 208 |
+
See `fastmcp.client.sampling` for helpers.
|
| 209 |
+
|
| 210 |
+
#### Logging
|
| 211 |
+
|
| 212 |
+
* **`log_handler: LoggingFnT | None`**: Receives log messages sent from the server (`ctx.info`, `ctx.error`, etc.).
|
| 213 |
+
```python
|
| 214 |
+
from mcp.client.session import LoggingFnT, LogLevel
|
| 215 |
+
|
| 216 |
+
def my_log_handler(level: LogLevel, message: str, logger_name: str | None):
|
| 217 |
+
print(f"[Server Log - {level.upper()}] {logger_name or 'default'}: {message}")
|
| 218 |
+
|
| 219 |
+
client_with_logging = Client(
|
| 220 |
+
"my_server.py",
|
| 221 |
+
log_handler=my_log_handler
|
| 222 |
+
)
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
### Error Handling
|
| 227 |
+
|
| 228 |
+
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.client.ClientError`.
|
| 229 |
+
|
| 230 |
+
```python
|
| 231 |
+
async def safe_call_tool():
|
| 232 |
+
async with client:
|
| 233 |
+
try:
|
| 234 |
+
# Assume 'divide' tool exists and might raise ZeroDivisionError
|
| 235 |
+
result = await client.call_tool("divide", {"a": 10, "b": 0})
|
| 236 |
+
print(f"Result: {result}")
|
| 237 |
+
except ClientError as e:
|
| 238 |
+
print(f"Tool call failed: {e}")
|
| 239 |
+
except ConnectionError as e:
|
| 240 |
+
print(f"Connection failed: {e}")
|
| 241 |
+
except Exception as e:
|
| 242 |
+
print(f"An unexpected error occurred: {e}")
|
| 243 |
+
|
| 244 |
+
# Example Output if division by zero occurs:
|
| 245 |
+
# Tool call failed: Division by zero is not allowed.
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
Other errors, like connection failures, will raise standard Python exceptions (e.g., `ConnectionError`, `TimeoutError`).
|
| 249 |
+
|
| 250 |
+
<Tip>
|
| 251 |
+
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 call `call_tools(..., return_raw_result=True)` to get the raw result object and handle errors yourself by checking its `isError` attribute.
|
| 252 |
+
</Tip>
|
docs/clients/transports.mdx
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Client Transports
|
| 3 |
+
sidebarTitle: Transports
|
| 4 |
+
description: Understand the different ways FastMCP Clients can connect to servers.
|
| 5 |
+
icon: link
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
The FastMCP `Client` relies on a `ClientTransport` object to handle the specifics of connecting to and communicating with an MCP server. FastMCP provides several built-in transport implementations for common connection methods.
|
| 9 |
+
|
| 10 |
+
While the `Client` often infers the correct transport automatically (see [Client Overview](/clients/overview#transport-inference)), you can also instantiate transports explicitly for more control.
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
## Stdio Transports
|
| 14 |
+
|
| 15 |
+
These transports manage an MCP server running as a subprocess, communicating with it via standard input (stdin) and standard output (stdout). This is the standard mechanism used by clients like Claude Desktop.
|
| 16 |
+
|
| 17 |
+
### Python Stdio
|
| 18 |
+
|
| 19 |
+
* **Class:** `fastmcp.client.transports.PythonStdioTransport`
|
| 20 |
+
* **Inferred From:** Paths to `.py` files.
|
| 21 |
+
* **Use Case:** Running a Python-based MCP server script (like one using FastMCP or the base `mcp` library) in a subprocess.
|
| 22 |
+
|
| 23 |
+
This is the most common way to interact with local FastMCP servers during development or when integrating with tools that expect to launch a server script.
|
| 24 |
+
|
| 25 |
+
```python
|
| 26 |
+
from fastmcp import Client
|
| 27 |
+
from fastmcp.client.transports import PythonStdioTransport
|
| 28 |
+
|
| 29 |
+
server_script = "my_mcp_server.py" # Assumes this file exists and runs mcp.run()
|
| 30 |
+
|
| 31 |
+
# Option 1: Inferred transport
|
| 32 |
+
client_inferred = Client(server_script)
|
| 33 |
+
|
| 34 |
+
# Option 2: Explicit transport (e.g., to use a specific python executable or add args)
|
| 35 |
+
transport_explicit = PythonStdioTransport(
|
| 36 |
+
script_path=server_script,
|
| 37 |
+
python_cmd="/usr/bin/python3.11", # Specify python version
|
| 38 |
+
# args=["--some-server-arg"], # Pass args to the script
|
| 39 |
+
# env={"MY_VAR": "value"}, # Set environment variables
|
| 40 |
+
# cwd="/path/to/run/in" # Set working directory
|
| 41 |
+
)
|
| 42 |
+
client_explicit = Client(transport_explicit)
|
| 43 |
+
|
| 44 |
+
async def use_stdio_client(client):
|
| 45 |
+
async with client:
|
| 46 |
+
tools = await client.list_tools()
|
| 47 |
+
print(f"Connected via Python Stdio, found tools: {tools}")
|
| 48 |
+
|
| 49 |
+
# asyncio.run(use_stdio_client(client_inferred))
|
| 50 |
+
# asyncio.run(use_stdio_client(client_explicit))
|
| 51 |
+
```
|
| 52 |
+
|
| 53 |
+
<Warning>
|
| 54 |
+
The server script (`my_mcp_server.py` in the example) *must* include logic to start the MCP server and listen on stdio, typically via `mcp.run()` or `fastmcp.server.run()`. The `Client` only launches the script; it doesn't inject the server logic.
|
| 55 |
+
</Warning>
|
| 56 |
+
|
| 57 |
+
### Node.js Stdio
|
| 58 |
+
|
| 59 |
+
* **Class:** `fastmcp.client.transports.NodeStdioTransport`
|
| 60 |
+
* **Inferred From:** Paths to `.js` files.
|
| 61 |
+
* **Use Case:** Running a Node.js-based MCP server script in a subprocess.
|
| 62 |
+
|
| 63 |
+
Similar to the Python transport, but for JavaScript servers.
|
| 64 |
+
|
| 65 |
+
```python
|
| 66 |
+
from fastmcp import Client
|
| 67 |
+
from fastmcp.client.transports import NodeStdioTransport
|
| 68 |
+
|
| 69 |
+
node_server_script = "my_mcp_server.js" # Assumes this JS file starts an MCP server on stdio
|
| 70 |
+
|
| 71 |
+
# Option 1: Inferred transport
|
| 72 |
+
client_inferred = Client(node_server_script)
|
| 73 |
+
|
| 74 |
+
# Option 2: Explicit transport
|
| 75 |
+
transport_explicit = NodeStdioTransport(
|
| 76 |
+
script_path=node_server_script,
|
| 77 |
+
node_cmd="node" # Or specify path to Node executable
|
| 78 |
+
)
|
| 79 |
+
client_explicit = Client(transport_explicit)
|
| 80 |
+
|
| 81 |
+
# Usage is the same as other clients
|
| 82 |
+
# async with client_explicit:
|
| 83 |
+
# tools = await client_explicit.list_tools()
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
### UVX Stdio (Experimental)
|
| 87 |
+
|
| 88 |
+
* **Class:** `fastmcp.client.transports.UvxStdioTransport`
|
| 89 |
+
* **Inferred From:** Not automatically inferred. Must be instantiated explicitly.
|
| 90 |
+
* **Use Case:** Running an MCP server packaged as a Python tool using [`uvx`](https://docs.astral.sh/uv/reference/cli/#uvx) (part of the `uv` toolchain). This allows running tools without explicitly installing them into the current environment.
|
| 91 |
+
|
| 92 |
+
This is useful for executing MCP servers distributed as command-line tools or packages.
|
| 93 |
+
|
| 94 |
+
```python
|
| 95 |
+
from fastmcp.client.transports import UvxStdioTransport
|
| 96 |
+
|
| 97 |
+
# Example: Run a hypothetical 'cloud-analyzer-mcp' tool via uvx
|
| 98 |
+
# Assume this tool, when run, starts an MCP server on stdio
|
| 99 |
+
transport = UvxStdioTransport(
|
| 100 |
+
tool_name="cloud-analyzer-mcp",
|
| 101 |
+
# from_package="cloud-analyzer-cli", # Optionally specify package if tool name differs
|
| 102 |
+
# with_packages=["boto3", "requests"], # Add dependencies if needed
|
| 103 |
+
# tool_args=["--config", "prod.yaml"] # Pass args to the tool itself
|
| 104 |
+
)
|
| 105 |
+
client = Client(transport)
|
| 106 |
+
|
| 107 |
+
# async with client:
|
| 108 |
+
# analysis = await client.call_tool("analyze_bucket", {"name": "my-data"})
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
### NPX Stdio (Experimental)
|
| 112 |
+
|
| 113 |
+
* **Class:** `fastmcp.client.transports.NpxStdioTransport`
|
| 114 |
+
* **Inferred From:** Not automatically inferred. Must be instantiated explicitly.
|
| 115 |
+
* **Use Case:** Running an MCP server packaged as an NPM package using `npx`.
|
| 116 |
+
|
| 117 |
+
Similar to `UvxStdioTransport`, but for the Node.js ecosystem.
|
| 118 |
+
|
| 119 |
+
```python
|
| 120 |
+
from fastmcp.client.transports import NpxStdioTransport
|
| 121 |
+
|
| 122 |
+
# Example: Run a hypothetical 'npm-mcp-server-package' via npx
|
| 123 |
+
transport = NpxStdioTransport(
|
| 124 |
+
package="npm-mcp-server-package",
|
| 125 |
+
# args=["--port", "stdio"] # Args passed to the package script
|
| 126 |
+
)
|
| 127 |
+
client = Client(transport)
|
| 128 |
+
|
| 129 |
+
# async with client:
|
| 130 |
+
# response = await client.call_tool("get_npm_data", {})
|
| 131 |
+
```
|
| 132 |
+
## Network Transports
|
| 133 |
+
|
| 134 |
+
These transports connect to servers running over a network, typically long-running services accessible via URLs.
|
| 135 |
+
|
| 136 |
+
### SSE (Server-Sent Events)
|
| 137 |
+
|
| 138 |
+
* **Class:** `fastmcp.client.transports.SSETransport`
|
| 139 |
+
* **Inferred From:** `http://` or `https://` URLs
|
| 140 |
+
* **Use Case:** Connecting to persistent MCP servers exposed over HTTP/S, often using FastMCP's `mcp.run(transport="sse")` mode.
|
| 141 |
+
|
| 142 |
+
SSE is a simple, unidirectional protocol where the server pushes messages to the client over a standard HTTP connection.
|
| 143 |
+
|
| 144 |
+
```python
|
| 145 |
+
from fastmcp import Client
|
| 146 |
+
from fastmcp.client.transports import SSETransport
|
| 147 |
+
|
| 148 |
+
sse_url = "http://localhost:8000/sse"
|
| 149 |
+
|
| 150 |
+
# Option 1: Inferred transport
|
| 151 |
+
client_inferred = Client(sse_url)
|
| 152 |
+
|
| 153 |
+
# Option 2: Explicit transport (e.g., to add custom headers)
|
| 154 |
+
headers = {"Authorization": "Bearer mytoken"}
|
| 155 |
+
transport_explicit = SSETransport(url=sse_url, headers=headers)
|
| 156 |
+
client_explicit = Client(transport_explicit)
|
| 157 |
+
|
| 158 |
+
async def use_sse_client(client):
|
| 159 |
+
async with client:
|
| 160 |
+
tools = await client.list_tools()
|
| 161 |
+
print(f"Connected via SSE, found tools: {tools}")
|
| 162 |
+
|
| 163 |
+
# asyncio.run(use_sse_client(client_inferred))
|
| 164 |
+
# asyncio.run(use_sse_client(client_explicit))
|
| 165 |
+
```
|
| 166 |
+
|
| 167 |
+
### WebSocket
|
| 168 |
+
|
| 169 |
+
* **Class:** `fastmcp.client.transports.WSTransport`
|
| 170 |
+
* **Inferred From:** `ws://` or `wss://` URLs
|
| 171 |
+
* **Use Case:** Connecting to MCP servers using the WebSocket protocol for bidirectional communication.
|
| 172 |
+
|
| 173 |
+
WebSockets provide a persistent, full-duplex connection between client and server.
|
| 174 |
+
|
| 175 |
+
```python
|
| 176 |
+
from fastmcp import Client
|
| 177 |
+
from fastmcp.client.transports import WSTransport
|
| 178 |
+
|
| 179 |
+
ws_url = "ws://localhost:9000"
|
| 180 |
+
|
| 181 |
+
# Option 1: Inferred transport
|
| 182 |
+
client_inferred = Client(ws_url)
|
| 183 |
+
|
| 184 |
+
# Option 2: Explicit transport
|
| 185 |
+
transport_explicit = WSTransport(url=ws_url)
|
| 186 |
+
client_explicit = Client(transport_explicit)
|
| 187 |
+
|
| 188 |
+
async def use_ws_client(client):
|
| 189 |
+
async with client:
|
| 190 |
+
tools = await client.list_tools()
|
| 191 |
+
print(f"Connected via WebSocket, found tools: {tools}")
|
| 192 |
+
|
| 193 |
+
# asyncio.run(use_ws_client(client_inferred))
|
| 194 |
+
# asyncio.run(use_ws_client(client_explicit))
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
## In-Memory Transports
|
| 198 |
+
|
| 199 |
+
### FastMCP Transport
|
| 200 |
+
|
| 201 |
+
* **Class:** `fastmcp.client.transports.FastMCPTransport`
|
| 202 |
+
* **Inferred From:** An instance of `fastmcp.server.FastMCP`.
|
| 203 |
+
* **Use Case:** Connecting directly to a `FastMCP` server instance running in the *same Python process*.
|
| 204 |
+
|
| 205 |
+
This is extremely useful for:
|
| 206 |
+
* **Testing:** Writing unit or integration tests for your FastMCP server without needing subprocesses or network connections.
|
| 207 |
+
* **Embedding:** Using an MCP server as a component within a larger application.
|
| 208 |
+
|
| 209 |
+
```python
|
| 210 |
+
from fastmcp import FastMCP, Client
|
| 211 |
+
from fastmcp.client.transports import FastMCPTransport
|
| 212 |
+
|
| 213 |
+
# 1. Create your FastMCP server instance
|
| 214 |
+
server = FastMCP(name="InMemoryServer")
|
| 215 |
+
@server.tool()
|
| 216 |
+
def ping(): return "pong"
|
| 217 |
+
|
| 218 |
+
# 2. Create a client pointing directly to the server instance
|
| 219 |
+
# Option A: Inferred
|
| 220 |
+
client_inferred = Client(server)
|
| 221 |
+
|
| 222 |
+
# Option B: Explicit
|
| 223 |
+
transport_explicit = FastMCPTransport(mcp=server)
|
| 224 |
+
client_explicit = Client(transport_explicit)
|
| 225 |
+
|
| 226 |
+
# 3. Use the client (no subprocess or network involved)
|
| 227 |
+
async def test_in_memory():
|
| 228 |
+
async with client_inferred: # Or client_explicit
|
| 229 |
+
result = await client_inferred.call_tool("ping")
|
| 230 |
+
print(f"In-memory call result: {result[0].text}") # Output: pong
|
| 231 |
+
|
| 232 |
+
# asyncio.run(test_in_memory())
|
| 233 |
+
```
|
| 234 |
+
Communication happens through efficient in-memory queues, making it very fast.
|
| 235 |
+
|
| 236 |
+
## Choosing a Transport
|
| 237 |
+
|
| 238 |
+
* **Local Development/Testing:** Use `PythonStdioTransport` (inferred from `.py` files) or `FastMCPTransport` (for same-process testing).
|
| 239 |
+
* **Connecting to Remote/Persistent Servers:** Use `SSETransport` (for `http/s`) or `WSTransport` (for `ws/s`).
|
| 240 |
+
* **Running Packaged Tools:** Use `UvxStdioTransport` (Python/uv) or `NpxStdioTransport` (Node/npm) if you need to run MCP servers without local installation.
|
| 241 |
+
* **Integrating with Claude Desktop (or similar):** These tools typically expect to run a Python script, so your server should be runnable via `python your_server.py`, making `PythonStdioTransport` the relevant mechanism on the client side.
|
docs/docs.json
CHANGED
|
@@ -15,7 +15,9 @@
|
|
| 15 |
"description": "The fast, Pythonic way to build MCP servers.",
|
| 16 |
"footer": {
|
| 17 |
"socials": {
|
| 18 |
-
"
|
|
|
|
|
|
|
| 19 |
}
|
| 20 |
},
|
| 21 |
"name": "FastMCP",
|
|
@@ -47,7 +49,10 @@
|
|
| 47 |
},
|
| 48 |
{
|
| 49 |
"group": "Clients",
|
| 50 |
-
"pages": [
|
|
|
|
|
|
|
|
|
|
| 51 |
},
|
| 52 |
{
|
| 53 |
"group": "Deployment",
|
|
|
|
| 15 |
"description": "The fast, Pythonic way to build MCP servers.",
|
| 16 |
"footer": {
|
| 17 |
"socials": {
|
| 18 |
+
"bluesky": "https://bsky.app/profile/jlowin.dev",
|
| 19 |
+
"github": "https://github.com/jlowin/fastmcp",
|
| 20 |
+
"x": "https://x.com/jlowin"
|
| 21 |
}
|
| 22 |
},
|
| 23 |
"name": "FastMCP",
|
|
|
|
| 49 |
},
|
| 50 |
{
|
| 51 |
"group": "Clients",
|
| 52 |
+
"pages": [
|
| 53 |
+
"clients/overview",
|
| 54 |
+
"clients/transports"
|
| 55 |
+
]
|
| 56 |
},
|
| 57 |
{
|
| 58 |
"group": "Deployment",
|
docs/style.css
CHANGED
|
@@ -1,13 +1,13 @@
|
|
| 1 |
-
/* Target inline code elements
|
| 2 |
-
p code,
|
| 3 |
-
table code,
|
| 4 |
-
li code,
|
| 5 |
-
h1 code,
|
| 6 |
-
h2 code,
|
| 7 |
-
h3 code,
|
| 8 |
-
h4 code,
|
| 9 |
-
h5 code,
|
| 10 |
-
h6 code {
|
| 11 |
color: #f72585 !important;
|
| 12 |
background-color: #ea54551a !important;
|
| 13 |
}
|
|
|
|
| 1 |
+
/* Target only inline code elements, not code blocks */
|
| 2 |
+
p code:not(pre code),
|
| 3 |
+
table code:not(pre code),
|
| 4 |
+
li code:not(pre code),
|
| 5 |
+
h1 code:not(pre code),
|
| 6 |
+
h2 code:not(pre code),
|
| 7 |
+
h3 code:not(pre code),
|
| 8 |
+
h4 code:not(pre code),
|
| 9 |
+
h5 code:not(pre code),
|
| 10 |
+
h6 code:not(pre code) {
|
| 11 |
color: #f72585 !important;
|
| 12 |
background-color: #ea54551a !important;
|
| 13 |
}
|