Spaces:
Running
Running
Jeremiah Lowin commited on
Commit ·
d8a9a0b
1
Parent(s): 0b2f7ac
Add documentation
Browse files- docs/deployment/authentication.mdx +15 -0
- docs/deployment/running-server.mdx +192 -0
- docs/docs.json +10 -3
- docs/getting-started/quickstart.mdx +1 -1
- docs/patterns/proxy.mdx +2 -2
- docs/servers/fastmcp.mdx +26 -143
- src/fastmcp/cli/cli.py +1 -1
- src/fastmcp/client/transports.py +17 -4
- src/fastmcp/server/server.py +14 -7
- src/fastmcp/settings.py +2 -2
- test.py +12 -0
docs/deployment/authentication.mdx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Authentication
|
| 3 |
+
sidebarTitle: Authentication
|
| 4 |
+
description: Secure your FastMCP server with authentication.
|
| 5 |
+
icon: lock
|
| 6 |
+
---
|
| 7 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 8 |
+
|
| 9 |
+
<VersionBadge version="2.2.7" />
|
| 10 |
+
|
| 11 |
+
This document will cover how to implement authentication for your FastMCP servers.
|
| 12 |
+
|
| 13 |
+
FastMCP leverages the OAuth 2.0 support provided by the underlying Model Context Protocol (MCP) SDK.
|
| 14 |
+
|
| 15 |
+
For now, refer to the [MCP Server Authentication documentation](/servers/fastmcp#authentication) for initial details and the [official MCP SDK documentation](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for more.
|
docs/deployment/running-server.mdx
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Running Your FastMCP Server
|
| 3 |
+
sidebarTitle: Running the Server
|
| 4 |
+
description: Learn how to run and deploy your FastMCP server using various transport protocols like STDIO, Streamable HTTP, and SSE.
|
| 5 |
+
icon: circle-play
|
| 6 |
+
---
|
| 7 |
+
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
FastMCP servers can be run in different ways depending on your application's needs, from local command-line tools to persistent web services. This guide covers the primary methods for running your server, focusing on the available transport protocols: STDIO, Streamable HTTP, and SSE.
|
| 11 |
+
|
| 12 |
+
## The `run()` Method
|
| 13 |
+
|
| 14 |
+
The main way to run a FastMCP server from a Python script is by calling the `run()` method on a `FastMCP` instance.
|
| 15 |
+
|
| 16 |
+
<Tip>
|
| 17 |
+
For maximum compatibility, it's best practice to place the `run()` call within an `if __name__ == "__main__":` block. This ensures the server starts only when the script is executed directly, not when imported as a module.
|
| 18 |
+
</Tip>
|
| 19 |
+
|
| 20 |
+
```python {9-10} my_server.py
|
| 21 |
+
from fastmcp import FastMCP
|
| 22 |
+
|
| 23 |
+
mcp = FastMCP(name="MyServer")
|
| 24 |
+
|
| 25 |
+
@mcp.tool()
|
| 26 |
+
def hello(name: str) -> str:
|
| 27 |
+
return f"Hello, {name}!"
|
| 28 |
+
|
| 29 |
+
if __name__ == "__main__":
|
| 30 |
+
mcp.run()
|
| 31 |
+
```
|
| 32 |
+
You can now run this MCP server by executing `python my_server.py`.
|
| 33 |
+
|
| 34 |
+
MCP servers can be run with a variety of different transport options, depending on your application's requirements. The `run()` method can take a `transport` argument and other transport-specific keyword arguments to configure how the server operates.
|
| 35 |
+
|
| 36 |
+
## Transport Options
|
| 37 |
+
|
| 38 |
+
Below is a comparison of available transport options to help you choose the right one for your needs:
|
| 39 |
+
|
| 40 |
+
| Transport | Use Cases | Recommendation |
|
| 41 |
+
| --------- | --------- | -------------- |
|
| 42 |
+
| **STDIO** | Local tools, command-line scripts, and integrations with clients like Claude Desktop | Best for local tools and when clients manage server processes |
|
| 43 |
+
| **Streamable HTTP** | Web-based deployments, microservices, exposing MCP over a network | Recommended choice for new web-based deployments |
|
| 44 |
+
| **SSE** | Existing web-based deployments that rely on SSE | Suitable for compatibility with SSE clients; prefer Streamable HTTP for new projects |
|
| 45 |
+
|
| 46 |
+
### STDIO
|
| 47 |
+
|
| 48 |
+
The STDIO transport is the default and most widely compatible option for local MCP server execution. It is ideal for local tools, command-line integrations, and clients like Claude Desktop. However, it has the disadvantage of having to run the MCP code locally, which can introduce security concerns with third-party servers.
|
| 49 |
+
|
| 50 |
+
STDIO is the default transport, so you don't need to specify it when calling `run()`. However, you can specify it explicitly to make your intent clear:
|
| 51 |
+
|
| 52 |
+
```python {6}
|
| 53 |
+
from fastmcp import FastMCP
|
| 54 |
+
|
| 55 |
+
mcp = FastMCP()
|
| 56 |
+
|
| 57 |
+
if __name__ == "__main__":
|
| 58 |
+
mcp.run(transport="stdio")
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
When using Stdio transport, you will typically *not* run the server yourself as a separate process. Rather, your *clients* will spin up a new server process for each session. As such, no additional configuration is required.
|
| 62 |
+
|
| 63 |
+
### Streamable HTTP
|
| 64 |
+
|
| 65 |
+
<VersionBadge version="2.3.0" />
|
| 66 |
+
|
| 67 |
+
Streamable HTTP is a modern, efficient transport for exposing your MCP server via HTTP. It is generally recommended over SSE for new web-based deployments.
|
| 68 |
+
|
| 69 |
+
To run a server using Streamable HTTP, you can use the `run()` method with the `transport` argument set to `"streamable-http"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and path (`/mcp/`).
|
| 70 |
+
<CodeGroup>
|
| 71 |
+
```python {6} server.py
|
| 72 |
+
from fastmcp import FastMCP
|
| 73 |
+
|
| 74 |
+
mcp = FastMCP()
|
| 75 |
+
|
| 76 |
+
if __name__ == "__main__":
|
| 77 |
+
mcp.run(transport="streamable-http")
|
| 78 |
+
```
|
| 79 |
+
```python {5} client.py
|
| 80 |
+
import asyncio
|
| 81 |
+
from fastmcp import Client
|
| 82 |
+
|
| 83 |
+
async def example():
|
| 84 |
+
async with Client("http://127.0.0.1:8000/mcp/") as client:
|
| 85 |
+
await client.ping()
|
| 86 |
+
|
| 87 |
+
if __name__ == "__main__":
|
| 88 |
+
asyncio.run(example())
|
| 89 |
+
```
|
| 90 |
+
</CodeGroup>
|
| 91 |
+
|
| 92 |
+
To customize the host, port, path, or log level, provide appropriate keyword arguments to the `run()` method.
|
| 93 |
+
|
| 94 |
+
<CodeGroup>
|
| 95 |
+
```python {8-11} server.py
|
| 96 |
+
from fastmcp import FastMCP
|
| 97 |
+
|
| 98 |
+
mcp = FastMCP()
|
| 99 |
+
|
| 100 |
+
if __name__ == "__main__":
|
| 101 |
+
mcp.run(
|
| 102 |
+
transport="streamable-http",
|
| 103 |
+
host="127.0.0.1",
|
| 104 |
+
port=4200,
|
| 105 |
+
path="/my-custom-path/",
|
| 106 |
+
log_level="debug",
|
| 107 |
+
)
|
| 108 |
+
```
|
| 109 |
+
```python {5} client.py
|
| 110 |
+
import asyncio
|
| 111 |
+
from fastmcp import Client
|
| 112 |
+
|
| 113 |
+
async def example():
|
| 114 |
+
async with Client("http://127.0.0.1:4200/my-custom-path/") as client:
|
| 115 |
+
await client.ping()
|
| 116 |
+
|
| 117 |
+
if __name__ == "__main__":
|
| 118 |
+
asyncio.run(example())
|
| 119 |
+
```
|
| 120 |
+
</CodeGroup>
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
### SSE
|
| 124 |
+
|
| 125 |
+
Server-Sent Events (SSE) is an HTTP-based protocol for server-to-client streaming. While FastMCP supports SSE, Streamable HTTP is preferred for new projects.
|
| 126 |
+
|
| 127 |
+
To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse/`) and message path (`/messages/`).
|
| 128 |
+
|
| 129 |
+
<CodeGroup>
|
| 130 |
+
```python {6} server.py
|
| 131 |
+
from fastmcp import FastMCP
|
| 132 |
+
|
| 133 |
+
mcp = FastMCP()
|
| 134 |
+
|
| 135 |
+
if __name__ == "__main__":
|
| 136 |
+
mcp.run(transport="sse")
|
| 137 |
+
```
|
| 138 |
+
```python {3,7} client.py
|
| 139 |
+
import asyncio
|
| 140 |
+
from fastmcp import Client
|
| 141 |
+
from fastmcp.client.transports import SSETransport
|
| 142 |
+
|
| 143 |
+
async def example():
|
| 144 |
+
async with Client(
|
| 145 |
+
transport=SSETransport("http://127.0.0.1:8000/sse/")
|
| 146 |
+
) as client:
|
| 147 |
+
await client.ping()
|
| 148 |
+
|
| 149 |
+
if __name__ == "__main__":
|
| 150 |
+
asyncio.run(example())
|
| 151 |
+
```
|
| 152 |
+
</CodeGroup>
|
| 153 |
+
|
| 154 |
+
<Tip>
|
| 155 |
+
Notice that the client in the above example uses an explicit `SSETransport` to connect to the server. FastMCP will attempt to infer the appropriate transport from the provided configuration, but HTTP URLs are assumed to be Streamable HTTP (as of FastMCP 2.3.0).
|
| 156 |
+
</Tip>
|
| 157 |
+
|
| 158 |
+
To customize the host, port, or log level, provide appropriate keyword arguments to the `run()` method. You can also adjust the SSE path (which clients should connect to) and the message POST endpoint (which clients use to send subsequent messages).
|
| 159 |
+
|
| 160 |
+
<CodeGroup>
|
| 161 |
+
```python {8-12} server.py
|
| 162 |
+
from fastmcp import FastMCP
|
| 163 |
+
|
| 164 |
+
mcp = FastMCP()
|
| 165 |
+
|
| 166 |
+
if __name__ == "__main__":
|
| 167 |
+
mcp.run(
|
| 168 |
+
transport="sse",
|
| 169 |
+
host="127.0.0.1",
|
| 170 |
+
port=4200,
|
| 171 |
+
log_level="debug",
|
| 172 |
+
path="/my-custom-sse-path/",
|
| 173 |
+
message_path="/my-custom-message-path/",
|
| 174 |
+
)
|
| 175 |
+
```
|
| 176 |
+
```python {7} client.py
|
| 177 |
+
import asyncio
|
| 178 |
+
from fastmcp import Client
|
| 179 |
+
from fastmcp.client.transports import SSETransport
|
| 180 |
+
|
| 181 |
+
async def example():
|
| 182 |
+
async with Client(
|
| 183 |
+
transport=SSETransport("http://127.0.0.1:4200/my-custom-sse-path/")
|
| 184 |
+
) as client:
|
| 185 |
+
await client.ping()
|
| 186 |
+
|
| 187 |
+
if __name__ == "__main__":
|
| 188 |
+
asyncio.run(example())
|
| 189 |
+
```
|
| 190 |
+
</CodeGroup>
|
| 191 |
+
|
| 192 |
+
Your client only needs to know the host, port, and "main" path; the message path will be transmitted to it as part of the connection handshake.
|
docs/docs.json
CHANGED
|
@@ -49,7 +49,16 @@
|
|
| 49 |
"servers/tools",
|
| 50 |
"servers/resources",
|
| 51 |
"servers/prompts",
|
| 52 |
-
"servers/context"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
]
|
| 54 |
},
|
| 55 |
{
|
|
@@ -62,8 +71,6 @@
|
|
| 62 |
{
|
| 63 |
"group": "Patterns",
|
| 64 |
"pages": [
|
| 65 |
-
"patterns/proxy",
|
| 66 |
-
"patterns/composition",
|
| 67 |
"patterns/decorating-methods",
|
| 68 |
"patterns/http-requests",
|
| 69 |
"patterns/openapi",
|
|
|
|
| 49 |
"servers/tools",
|
| 50 |
"servers/resources",
|
| 51 |
"servers/prompts",
|
| 52 |
+
"servers/context",
|
| 53 |
+
"patterns/proxy",
|
| 54 |
+
"patterns/composition"
|
| 55 |
+
]
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"group": "Deployment",
|
| 59 |
+
"pages": [
|
| 60 |
+
"deployment/running-server",
|
| 61 |
+
"deployment/authentication"
|
| 62 |
]
|
| 63 |
},
|
| 64 |
{
|
|
|
|
| 71 |
{
|
| 72 |
"group": "Patterns",
|
| 73 |
"pages": [
|
|
|
|
|
|
|
| 74 |
"patterns/decorating-methods",
|
| 75 |
"patterns/http-requests",
|
| 76 |
"patterns/openapi",
|
docs/getting-started/quickstart.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
---
|
| 2 |
title: Quickstart
|
| 3 |
-
icon: rocket
|
| 4 |
---
|
| 5 |
|
| 6 |
Welcome! This guide will help you quickly set up FastMCP and run your first MCP server.
|
|
|
|
| 1 |
---
|
| 2 |
title: Quickstart
|
| 3 |
+
icon: rocket-launch
|
| 4 |
---
|
| 5 |
|
| 6 |
Welcome! This guide will help you quickly set up FastMCP and run your first MCP server.
|
docs/patterns/proxy.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
sidebarTitle:
|
| 4 |
description: Use FastMCP to act as an intermediary or change transport for other MCP servers.
|
| 5 |
icon: arrows-retweet
|
| 6 |
---
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Proxy Servers
|
| 3 |
+
sidebarTitle: Proxy Servers
|
| 4 |
description: Use FastMCP to act as an intermediary or change transport for other MCP servers.
|
| 5 |
icon: arrows-retweet
|
| 6 |
---
|
docs/servers/fastmcp.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
---
|
| 2 |
title: The FastMCP Server
|
| 3 |
-
sidebarTitle: FastMCP
|
| 4 |
description: Learn about the core FastMCP server class and how to run it.
|
| 5 |
icon: server
|
| 6 |
---
|
|
@@ -97,11 +97,7 @@ See [Prompts](/servers/prompts) for detailed documentation.
|
|
| 97 |
|
| 98 |
## Running the Server
|
| 99 |
|
| 100 |
-
FastMCP servers need a transport mechanism to communicate with clients.
|
| 101 |
-
|
| 102 |
-
### The `__main__` Block Pattern
|
| 103 |
-
|
| 104 |
-
The standard way to make your server executable is to include a `run()` call inside an `if __name__ == "__main__":` block:
|
| 105 |
|
| 106 |
```python
|
| 107 |
# my_server.py
|
|
@@ -115,118 +111,17 @@ def greet(name: str) -> str:
|
|
| 115 |
return f"Hello, {name}!"
|
| 116 |
|
| 117 |
if __name__ == "__main__":
|
| 118 |
-
# This
|
| 119 |
-
|
| 120 |
-
# Basic run with default settings (stdio transport)
|
| 121 |
mcp.run()
|
| 122 |
|
| 123 |
-
#
|
| 124 |
-
# mcp.run(transport="
|
| 125 |
-
```
|
| 126 |
-
|
| 127 |
-
This pattern is important because:
|
| 128 |
-
|
| 129 |
-
1. **Client Compatibility**: Standard MCP clients (like Claude Desktop) expect to execute your server file directly with `python my_server.py`
|
| 130 |
-
2. **Process Isolation**: Each server runs in its own process, allowing clients to manage multiple servers independently
|
| 131 |
-
3. **Import Safety**: The main block prevents the server from running when the file is imported by other code
|
| 132 |
-
|
| 133 |
-
While this pattern is technically optional when using FastMCP's CLI, it's considered a best practice for maximum compatibility with all MCP clients.
|
| 134 |
-
|
| 135 |
-
### Transport Options
|
| 136 |
-
|
| 137 |
-
FastMCP supports two transport mechanisms:
|
| 138 |
-
|
| 139 |
-
#### STDIO Transport (Default)
|
| 140 |
-
|
| 141 |
-
The standard input/output (STDIO) transport is the default and most widely compatible option:
|
| 142 |
-
|
| 143 |
-
```python
|
| 144 |
-
# Run with stdio (default)
|
| 145 |
-
mcp.run() # or explicitly: mcp.run(transport="stdio")
|
| 146 |
-
```
|
| 147 |
-
|
| 148 |
-
With STDIO:
|
| 149 |
-
- The client starts a new server process for each session
|
| 150 |
-
- Communication happens through standard input/output streams
|
| 151 |
-
- The server process terminates when the client disconnects
|
| 152 |
-
- This is ideal for integrations with tools like Claude Desktop, where each conversation gets its own server instance
|
| 153 |
-
|
| 154 |
-
#### SSE Transport (Server-Sent Events)
|
| 155 |
-
|
| 156 |
-
For long-running servers that serve multiple clients, FastMCP supports SSE:
|
| 157 |
-
|
| 158 |
-
```python
|
| 159 |
-
# Run with SSE on default host/port (0.0.0.0:8000)
|
| 160 |
-
mcp.run(transport="sse")
|
| 161 |
-
```
|
| 162 |
-
|
| 163 |
-
With SSE:
|
| 164 |
-
- The server runs as a persistent web server
|
| 165 |
-
- Multiple clients can connect simultaneously
|
| 166 |
-
- The server stays running until explicitly terminated
|
| 167 |
-
- This is ideal for remote access to services
|
| 168 |
-
|
| 169 |
-
You can configure transport parameters directly when running the server:
|
| 170 |
-
|
| 171 |
-
```python
|
| 172 |
-
# Configure with specific parameters
|
| 173 |
-
mcp.run(
|
| 174 |
-
transport="sse",
|
| 175 |
-
host="127.0.0.1", # Override default host
|
| 176 |
-
port=8888, # Override default port
|
| 177 |
-
log_level="debug" # Set logging level
|
| 178 |
-
)
|
| 179 |
-
|
| 180 |
-
# You can also run asynchronously with the same parameters
|
| 181 |
-
import asyncio
|
| 182 |
-
asyncio.run(
|
| 183 |
-
mcp.run_sse_async(
|
| 184 |
-
host="127.0.0.1",
|
| 185 |
-
port=8888,
|
| 186 |
-
log_level="debug"
|
| 187 |
-
)
|
| 188 |
-
)
|
| 189 |
```
|
| 190 |
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
- `host`: Host to bind to (default: "0.0.0.0")
|
| 194 |
-
- `port`: Port to bind to (default: 8000)
|
| 195 |
-
- `log_level`: Logging level (default: "INFO")
|
| 196 |
-
|
| 197 |
-
#### Advanced Transport Configuration
|
| 198 |
-
|
| 199 |
-
Under the hood, FastMCP's `run()` method accepts arbitrary keyword arguments (`**transport_kwargs`) that are passed to the transport-specific run methods:
|
| 200 |
-
|
| 201 |
-
```python
|
| 202 |
-
# For SSE transport, kwargs are passed to run_sse_async()
|
| 203 |
-
mcp.run(transport="sse", **transport_kwargs)
|
| 204 |
-
|
| 205 |
-
# For stdio transport, kwargs are passed to run_stdio_async()
|
| 206 |
-
mcp.run(transport="stdio", **transport_kwargs)
|
| 207 |
-
```
|
| 208 |
-
|
| 209 |
-
This means that any future transport-specific options will be automatically available through the same interface without requiring changes to your code.
|
| 210 |
-
|
| 211 |
-
### Using the FastMCP CLI
|
| 212 |
-
|
| 213 |
-
The FastMCP CLI provides a convenient way to run servers:
|
| 214 |
|
| 215 |
-
|
| 216 |
-
# Run a server (defaults to stdio transport)
|
| 217 |
-
fastmcp run my_server.py:mcp
|
| 218 |
|
| 219 |
-
# Explicitly specify a transport
|
| 220 |
-
fastmcp run my_server.py:mcp --transport sse
|
| 221 |
-
|
| 222 |
-
# Configure SSE transport with host and port
|
| 223 |
-
fastmcp run my_server.py:mcp --transport sse --host 127.0.0.1 --port 8888
|
| 224 |
-
|
| 225 |
-
# With log level
|
| 226 |
-
fastmcp run my_server.py:mcp --transport sse --log-level DEBUG
|
| 227 |
-
```
|
| 228 |
-
|
| 229 |
-
The CLI can dynamically find and run FastMCP server objects in your files, but including the `if __name__ == "__main__":` block ensures compatibility with all clients.
|
| 230 |
|
| 231 |
## Composing Servers
|
| 232 |
|
|
@@ -289,7 +184,7 @@ print(mcp.settings.on_duplicate_tools) # Output: "error"
|
|
| 289 |
|
| 290 |
### Key Configuration Options
|
| 291 |
|
| 292 |
-
- **`host`**: Host address for SSE transport (default: "
|
| 293 |
- **`port`**: Port number for SSE transport (default: 8000)
|
| 294 |
- **`log_level`**: Logging level (default: "INFO")
|
| 295 |
- **`on_duplicate_tools`**: How to handle duplicate tool registrations
|
|
@@ -336,36 +231,24 @@ If the serializer function raises an exception, the tool will fall back to the d
|
|
| 336 |
|
| 337 |
<VersionBadge version="2.2.7" />
|
| 338 |
|
| 339 |
-
FastMCP
|
| 340 |
-
|
| 341 |
-
### OAuth 2.0 Support
|
| 342 |
-
|
| 343 |
-
The `mcp.server.auth` module implements an OAuth 2.0 server interface that servers can use by providing an implementation of the `OAuthServerProvider` protocol.
|
| 344 |
|
| 345 |
```python
|
| 346 |
from fastmcp import FastMCP
|
| 347 |
-
from mcp.server.auth.settings import
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
#
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
enabled=True,
|
| 365 |
-
valid_scopes=["myscope", "myotherscope"],
|
| 366 |
-
default_scopes=["myscope"],
|
| 367 |
-
),
|
| 368 |
-
required_scopes=["myscope"],
|
| 369 |
-
),
|
| 370 |
-
)
|
| 371 |
-
```
|
|
|
|
| 1 |
---
|
| 2 |
title: The FastMCP Server
|
| 3 |
+
sidebarTitle: FastMCP Servers
|
| 4 |
description: Learn about the core FastMCP server class and how to run it.
|
| 5 |
icon: server
|
| 6 |
---
|
|
|
|
| 97 |
|
| 98 |
## Running the Server
|
| 99 |
|
| 100 |
+
FastMCP servers need a transport mechanism to communicate with clients. You typically start your server by calling the `mcp.run()` method on your `FastMCP` instance, often within an `if __name__ == "__main__":` block in your main server script. This pattern ensures compatibility with various MCP clients.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
```python
|
| 103 |
# my_server.py
|
|
|
|
| 111 |
return f"Hello, {name}!"
|
| 112 |
|
| 113 |
if __name__ == "__main__":
|
| 114 |
+
# This runs the server, defaulting to STDIO transport
|
|
|
|
|
|
|
| 115 |
mcp.run()
|
| 116 |
|
| 117 |
+
# To use a different transport, e.g., Streamable HTTP:
|
| 118 |
+
# mcp.run(transport="streamable-http", host="127.0.0.1", port=9000)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
```
|
| 120 |
|
| 121 |
+
FastMCP supports several transport options like STDIO (default, for local tools), Streamable HTTP (recommended for web services), and SSE (legacy web transport). The server can also be run using the FastMCP CLI.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
+
For detailed information on each transport, how to configure them (host, port, paths), and when to use which, please refer to the [**Running Your FastMCP Server**](/deployment/running-server) guide.
|
|
|
|
|
|
|
| 124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
## Composing Servers
|
| 127 |
|
|
|
|
| 184 |
|
| 185 |
### Key Configuration Options
|
| 186 |
|
| 187 |
+
- **`host`**: Host address for SSE transport (default: "127.0.0.1")
|
| 188 |
- **`port`**: Port number for SSE transport (default: 8000)
|
| 189 |
- **`log_level`**: Logging level (default: "INFO")
|
| 190 |
- **`on_duplicate_tools`**: How to handle duplicate tool registrations
|
|
|
|
| 231 |
|
| 232 |
<VersionBadge version="2.2.7" />
|
| 233 |
|
| 234 |
+
FastMCP supports OAuth 2.0 authentication, allowing servers to protect their tools and resources. This is configured by providing an `auth_server_provider` and `auth` settings during `FastMCP` initialization.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
|
| 236 |
```python
|
| 237 |
from fastmcp import FastMCP
|
| 238 |
+
from mcp.server.auth.settings import AuthSettings #, ... other auth imports
|
| 239 |
+
# from your_auth_implementation import MyOAuthServerProvider # Placeholder
|
| 240 |
+
|
| 241 |
+
# Create a server with authentication (conceptual example)
|
| 242 |
+
# mcp = FastMCP(
|
| 243 |
+
# name="SecureApp",
|
| 244 |
+
# auth_server_provider=MyOAuthServerProvider(),
|
| 245 |
+
# auth=AuthSettings(
|
| 246 |
+
# issuer_url="https://myapp.com",
|
| 247 |
+
# # ... other OAuth settings ...
|
| 248 |
+
# required_scopes=["myscope"],
|
| 249 |
+
# ),
|
| 250 |
+
# )
|
| 251 |
+
```
|
| 252 |
+
Due to the low-level nature of the current MCP SDK's auth provider interface, detailed implementation is beyond a quick example. Refer to the [MCP SDK documentation](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for specifics on implementing an `OAuthAuthorizationServerProvider`. FastMCP integrates with this by passing the provider and settings to the underlying MCP server.
|
| 253 |
+
|
| 254 |
+
A dedicated [Authentication guide](/deployment/authentication) will cover this in more detail once higher-level abstractions are available in FastMCP.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/fastmcp/cli/cli.py
CHANGED
|
@@ -334,7 +334,7 @@ def run(
|
|
| 334 |
str | None,
|
| 335 |
typer.Option(
|
| 336 |
"--host",
|
| 337 |
-
help="Host to bind to when using sse transport (default:
|
| 338 |
),
|
| 339 |
] = None,
|
| 340 |
port: Annotated[
|
|
|
|
| 334 |
str | None,
|
| 335 |
typer.Option(
|
| 336 |
"--host",
|
| 337 |
+
help="Host to bind to when using sse transport (default: 127.0.0.1)",
|
| 338 |
),
|
| 339 |
] = None,
|
| 340 |
port: Annotated[
|
src/fastmcp/client/transports.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
| 1 |
import abc
|
| 2 |
import contextlib
|
| 3 |
import datetime
|
|
|
|
| 4 |
import os
|
| 5 |
import shutil
|
| 6 |
import sys
|
|
|
|
| 7 |
from collections.abc import AsyncIterator
|
| 8 |
from pathlib import Path
|
| 9 |
from typing import Any, TypedDict
|
|
@@ -450,6 +452,8 @@ def infer_transport(
|
|
| 450 |
This function attempts to infer the correct transport type from the provided
|
| 451 |
argument, handling various input types and converting them to the appropriate
|
| 452 |
ClientTransport subclass.
|
|
|
|
|
|
|
| 453 |
"""
|
| 454 |
# the transport is already a ClientTransport
|
| 455 |
if isinstance(transport, ClientTransport):
|
|
@@ -470,10 +474,19 @@ def infer_transport(
|
|
| 470 |
|
| 471 |
# the transport is an http(s) URL
|
| 472 |
elif isinstance(transport, AnyUrl | str) and str(transport).startswith("http"):
|
| 473 |
-
if str(transport).endswith("/sse"):
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
|
| 478 |
# the transport is a websocket URL
|
| 479 |
elif isinstance(transport, AnyUrl | str) and str(transport).startswith("ws"):
|
|
|
|
| 1 |
import abc
|
| 2 |
import contextlib
|
| 3 |
import datetime
|
| 4 |
+
import inspect
|
| 5 |
import os
|
| 6 |
import shutil
|
| 7 |
import sys
|
| 8 |
+
import warnings
|
| 9 |
from collections.abc import AsyncIterator
|
| 10 |
from pathlib import Path
|
| 11 |
from typing import Any, TypedDict
|
|
|
|
| 452 |
This function attempts to infer the correct transport type from the provided
|
| 453 |
argument, handling various input types and converting them to the appropriate
|
| 454 |
ClientTransport subclass.
|
| 455 |
+
|
| 456 |
+
For HTTP URLs, they are assumed to be Streamable HTTP URLs unless they end in `/sse`.
|
| 457 |
"""
|
| 458 |
# the transport is already a ClientTransport
|
| 459 |
if isinstance(transport, ClientTransport):
|
|
|
|
| 474 |
|
| 475 |
# the transport is an http(s) URL
|
| 476 |
elif isinstance(transport, AnyUrl | str) and str(transport).startswith("http"):
|
| 477 |
+
if str(transport).rstrip("/").endswith("/sse"):
|
| 478 |
+
warnings.warn(
|
| 479 |
+
inspect.cleandoc(
|
| 480 |
+
"""
|
| 481 |
+
As of FastMCP 2.3.0, HTTP URLs are inferred to use Streamable HTTP.
|
| 482 |
+
The provided URL ends in `/sse`, so you may encounter unexpected behavior.
|
| 483 |
+
If you intended to use SSE, please use the `SSETransport` class directly.
|
| 484 |
+
"""
|
| 485 |
+
),
|
| 486 |
+
category=UserWarning,
|
| 487 |
+
stacklevel=2,
|
| 488 |
+
)
|
| 489 |
+
return StreamableHttpTransport(url=transport)
|
| 490 |
|
| 491 |
# the transport is a websocket URL
|
| 492 |
elif isinstance(transport, AnyUrl | str) and str(transport).startswith("ws"):
|
src/fastmcp/server/server.py
CHANGED
|
@@ -718,6 +718,8 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 718 |
host: str | None = None,
|
| 719 |
port: int | None = None,
|
| 720 |
log_level: str | None = None,
|
|
|
|
|
|
|
| 721 |
uvicorn_config: dict | None = None,
|
| 722 |
) -> None:
|
| 723 |
"""Run the server using SSE transport."""
|
|
@@ -726,7 +728,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 726 |
# timeout to make it possible to close immediately. see
|
| 727 |
# https://github.com/jlowin/fastmcp/issues/296
|
| 728 |
uvicorn_config.setdefault("timeout_graceful_shutdown", 0)
|
| 729 |
-
app = self.sse_app()
|
| 730 |
|
| 731 |
config = uvicorn.Config(
|
| 732 |
app,
|
|
@@ -738,25 +740,29 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 738 |
server = uvicorn.Server(config)
|
| 739 |
await server.serve()
|
| 740 |
|
| 741 |
-
def sse_app(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 742 |
"""Return an instance of the SSE server app."""
|
| 743 |
return create_sse_app(
|
| 744 |
server=self,
|
| 745 |
-
message_path=self.settings.message_path,
|
| 746 |
-
sse_path=self.settings.sse_path,
|
| 747 |
auth_server_provider=self._auth_server_provider,
|
| 748 |
auth_settings=self.settings.auth,
|
| 749 |
debug=self.settings.debug,
|
| 750 |
additional_routes=self._additional_http_routes,
|
| 751 |
)
|
| 752 |
|
| 753 |
-
def streamable_http_app(self) -> Starlette:
|
| 754 |
"""Return an instance of the StreamableHTTP server app."""
|
| 755 |
from fastmcp.server.http import create_streamable_http_app
|
| 756 |
|
| 757 |
return create_streamable_http_app(
|
| 758 |
server=self,
|
| 759 |
-
streamable_http_path=self.settings.streamable_http_path,
|
| 760 |
event_store=None,
|
| 761 |
auth_server_provider=self._auth_server_provider,
|
| 762 |
auth_settings=self.settings.auth,
|
|
@@ -771,13 +777,14 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 771 |
host: str | None = None,
|
| 772 |
port: int | None = None,
|
| 773 |
log_level: str | None = None,
|
|
|
|
| 774 |
uvicorn_config: dict | None = None,
|
| 775 |
) -> None:
|
| 776 |
"""Run the server using StreamableHTTP transport."""
|
| 777 |
uvicorn_config = uvicorn_config or {}
|
| 778 |
uvicorn_config.setdefault("timeout_graceful_shutdown", 0)
|
| 779 |
|
| 780 |
-
app = self.streamable_http_app()
|
| 781 |
|
| 782 |
config = uvicorn.Config(
|
| 783 |
app,
|
|
|
|
| 718 |
host: str | None = None,
|
| 719 |
port: int | None = None,
|
| 720 |
log_level: str | None = None,
|
| 721 |
+
path: str | None = None,
|
| 722 |
+
message_path: str | None = None,
|
| 723 |
uvicorn_config: dict | None = None,
|
| 724 |
) -> None:
|
| 725 |
"""Run the server using SSE transport."""
|
|
|
|
| 728 |
# timeout to make it possible to close immediately. see
|
| 729 |
# https://github.com/jlowin/fastmcp/issues/296
|
| 730 |
uvicorn_config.setdefault("timeout_graceful_shutdown", 0)
|
| 731 |
+
app = self.sse_app(path=path, message_path=message_path)
|
| 732 |
|
| 733 |
config = uvicorn.Config(
|
| 734 |
app,
|
|
|
|
| 740 |
server = uvicorn.Server(config)
|
| 741 |
await server.serve()
|
| 742 |
|
| 743 |
+
def sse_app(
|
| 744 |
+
self,
|
| 745 |
+
path: str | None = None,
|
| 746 |
+
message_path: str | None = None,
|
| 747 |
+
) -> Starlette:
|
| 748 |
"""Return an instance of the SSE server app."""
|
| 749 |
return create_sse_app(
|
| 750 |
server=self,
|
| 751 |
+
message_path=message_path or self.settings.message_path,
|
| 752 |
+
sse_path=path or self.settings.sse_path,
|
| 753 |
auth_server_provider=self._auth_server_provider,
|
| 754 |
auth_settings=self.settings.auth,
|
| 755 |
debug=self.settings.debug,
|
| 756 |
additional_routes=self._additional_http_routes,
|
| 757 |
)
|
| 758 |
|
| 759 |
+
def streamable_http_app(self, path: str | None = None) -> Starlette:
|
| 760 |
"""Return an instance of the StreamableHTTP server app."""
|
| 761 |
from fastmcp.server.http import create_streamable_http_app
|
| 762 |
|
| 763 |
return create_streamable_http_app(
|
| 764 |
server=self,
|
| 765 |
+
streamable_http_path=path or self.settings.streamable_http_path,
|
| 766 |
event_store=None,
|
| 767 |
auth_server_provider=self._auth_server_provider,
|
| 768 |
auth_settings=self.settings.auth,
|
|
|
|
| 777 |
host: str | None = None,
|
| 778 |
port: int | None = None,
|
| 779 |
log_level: str | None = None,
|
| 780 |
+
path: str | None = None,
|
| 781 |
uvicorn_config: dict | None = None,
|
| 782 |
) -> None:
|
| 783 |
"""Run the server using StreamableHTTP transport."""
|
| 784 |
uvicorn_config = uvicorn_config or {}
|
| 785 |
uvicorn_config.setdefault("timeout_graceful_shutdown", 0)
|
| 786 |
|
| 787 |
+
app = self.streamable_http_app(path=path)
|
| 788 |
|
| 789 |
config = uvicorn.Config(
|
| 790 |
app,
|
src/fastmcp/settings.py
CHANGED
|
@@ -59,9 +59,9 @@ class ServerSettings(BaseSettings):
|
|
| 59 |
# HTTP settings
|
| 60 |
host: str = "127.0.0.1"
|
| 61 |
port: int = 8000
|
| 62 |
-
sse_path: str = "/sse"
|
| 63 |
message_path: str = "/messages/"
|
| 64 |
-
streamable_http_path: str = "/mcp"
|
| 65 |
debug: bool = False
|
| 66 |
|
| 67 |
# resource settings
|
|
|
|
| 59 |
# HTTP settings
|
| 60 |
host: str = "127.0.0.1"
|
| 61 |
port: int = 8000
|
| 62 |
+
sse_path: str = "/sse/"
|
| 63 |
message_path: str = "/messages/"
|
| 64 |
+
streamable_http_path: str = "/mcp/"
|
| 65 |
debug: bool = False
|
| 66 |
|
| 67 |
# resource settings
|
test.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastmcp import FastMCP
|
| 2 |
+
|
| 3 |
+
mcp = FastMCP()
|
| 4 |
+
|
| 5 |
+
if __name__ == "__main__":
|
| 6 |
+
mcp.run(
|
| 7 |
+
transport="streamable-http",
|
| 8 |
+
host="127.0.0.1",
|
| 9 |
+
port=4200,
|
| 10 |
+
path="/my-custom-path/",
|
| 11 |
+
log_level="debug",
|
| 12 |
+
)
|