Merge pull request #401 from jlowin/deprecate-methods

Deprecate transport-specific methods on FastMCP server
This commit is contained in:
Jeremiah Lowin 2025-05-10 12:18:20 -04:00 committed by GitHub
commit aa6dde48cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 293 additions and 79 deletions

View file

@ -5,6 +5,9 @@ description: Integrate FastMCP servers into existing Starlette, FastAPI, or othe
icon: plug
---
import { VersionBadge } from '/snippets/version-badge.mdx'
While FastMCP provides standalone server capabilities, you can also integrate your FastMCP server into existing web applications. This approach is useful for:
- Adding MCP functionality to an existing website or API
@ -16,10 +19,13 @@ Please note that all FastMCP servers have a `run()` method that can be used to s
## ASGI Server
FastMCP servers can be created as [Starlette](https://www.starlette.io/) ASGI apps for straightforward hosting or integration into existing applications.
The first step is to obtain a Starlette application instance from your FastMCP server using either the `streamable_http_app()` (preferred) or `sse_app()` (legacy) methods:
The first step is to obtain a Starlette application instance from your FastMCP server using the `http_app()` method:
<Tip>
The `http_app()` method is new in FastMCP 2.3.2. In older versions, use `sse_app()` for SSE transport or `streamable_http_app()` for Streamable HTTP transport.
</Tip>
```python
from fastmcp import FastMCP
@ -30,18 +36,23 @@ mcp = FastMCP("MyServer")
def hello(name: str) -> str:
return f"Hello, {name}!"
# Get a Starlette app instance for the preferred transport
http_app = mcp.streamable_http_app() # For Streamable HTTP transport
sse_app = mcp.sse_app() # For SSE transport
# Get a Starlette app instance for Streamable HTTP transport (recommended)
http_app = mcp.http_app()
# For legacy SSE transport (deprecated)
sse_app = mcp.http_app(transport="sse")
```
Both methods return a Starlette application that can be integrated with other ASGI-compatible web frameworks.
Both approaches return a Starlette application that can be integrated with other ASGI-compatible web frameworks.
The MCP server's endpoint is mounted at the root path `/mcp` for Streamable HTTP transport, and `/sse` for SSE transport, though you can change these paths by passing a `path` argument to the `streamable_http_app()` or `sse_app()` methods:
The MCP server's endpoint is mounted at the root path `/mcp` for Streamable HTTP transport, and `/sse` for SSE transport, though you can change these paths by passing a `path` argument to the `http_app()` method:
```python
http_app = mcp.streamable_http_app(path="/custom-mcp-path")
sse_app = mcp.sse_app(path="/custom-sse-path")
# For Streamable HTTP transport
http_app = mcp.http_app(path="/custom-mcp-path")
# For SSE transport (deprecated)
sse_app = mcp.http_app(path="/custom-sse-path", transport="sse")
```
### Running the Server
@ -49,9 +60,12 @@ sse_app = mcp.sse_app(path="/custom-sse-path")
To run the FastMCP server, you can use the `uvicorn` ASGI server:
```python
from fastmcp import FastMCP
import uvicorn
# (define the app here)
mcp = FastMCP("MyServer")
http_app = mcp.http_app()
if __name__ == "__main__":
uvicorn.run(http_app, host="0.0.0.0", port=8000)
@ -83,7 +97,7 @@ custom_middleware = [
]
# Create ASGI app with custom middleware
http_app = mcp.streamable_http_app(middleware=custom_middleware)
http_app = mcp.http_app(middleware=custom_middleware)
```
@ -91,7 +105,7 @@ http_app = mcp.streamable_http_app(middleware=custom_middleware)
<VersionBadge version="2.3.1" />
You can mount your FastMCP server in another Starlette application using the `Mount` class.
You can mount your FastMCP server in another Starlette application:
```python
from fastmcp import FastMCP
@ -102,7 +116,7 @@ from starlette.routing import Mount
mcp = FastMCP("MyServer")
# Create the ASGI app
mcp_app = mcp.streamable_http_app(path='/mcp')
mcp_app = mcp.http_app(path='/mcp')
# Create a Starlette app and mount the MCP server
app = Starlette(
@ -134,7 +148,7 @@ from starlette.routing import Mount
mcp = FastMCP("MyServer")
# Create the ASGI app
mcp_app = mcp.streamable_http_app(path='/mcp')
mcp_app = mcp.http_app(path='/mcp')
# Create nested application structure
inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
@ -164,7 +178,7 @@ from starlette.routing import Mount
mcp = FastMCP("MyServer")
# Create the ASGI app
mcp_app = mcp.streamable_http_app(path='/mcp')
mcp_app = mcp.http_app(path='/mcp')
# Create a FastAPI app and mount the MCP server
app = FastAPI(lifespan=mcp_app.router.lifespan_context)

View file

@ -68,8 +68,8 @@ Below is a comparison of available transport options to help you choose the righ
| Transport | Use Cases | Recommendation |
| --------- | --------- | -------------- |
| **STDIO** | Local tools, command-line scripts, and integrations with clients like Claude Desktop | Best for local tools and when clients manage server processes |
| **Streamable HTTP** | Web-based deployments, microservices, exposing MCP over a network | Recommended choice for new web-based deployments |
| **SSE** | Existing web-based deployments that rely on SSE | Suitable for compatibility with SSE clients; prefer Streamable HTTP for new projects |
| **Streamable HTTP** | Web-based deployments, microservices, exposing MCP over a network | Recommended choice for web-based deployments |
| **SSE** | Existing web-based deployments that rely on SSE | Deprecated - prefer Streamable HTTP for new projects |
### STDIO
@ -92,7 +92,7 @@ When using Stdio transport, you will typically *not* run the server yourself as
<VersionBadge version="2.3.0" />
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.
Streamable HTTP is a modern, efficient transport for exposing your MCP server via HTTP. It is the recommended transport for web-based deployments.
To run a server using Streamable HTTP, you can use the `run()` method with the `transport` argument set to `"streamable-http"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and path (`/mcp`).
<CodeGroup>
@ -150,7 +150,12 @@ if __name__ == "__main__":
### SSE
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.
<Warning>
The SSE transport is deprecated and may be removed in a future version.
New applications should use Streamable HTTP transport instead.
</Warning>
Server-Sent Events (SSE) is an HTTP-based protocol for server-to-client streaming. While FastMCP still supports SSE, it is deprecated and Streamable HTTP is preferred for new projects.
To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse`) and message path (`/messages/`).
@ -198,7 +203,6 @@ if __name__ == "__main__":
port=4200,
log_level="debug",
path="/my-custom-sse-path",
message_path="/my-custom-message-path/",
)
```
```python {7} client.py
@ -217,9 +221,37 @@ if __name__ == "__main__":
```
</CodeGroup>
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.
## Async Usage
FastMCP provides both synchronous and asynchronous APIs for running your server. The `run()` method seen in previous examples is a synchronous method that internally uses `anyio.run()` to run the asynchronous server. For applications that are already running in an async context, FastMCP provides the `run_async()` method.
```python {10-12}
from fastmcp import FastMCP
import asyncio
mcp = FastMCP(name="MyServer")
@mcp.tool()
def hello(name: str) -> str:
return f"Hello, {name}!"
async def main():
# Use run_async() in async contexts
await mcp.run_async(transport="streamable-http")
if __name__ == "__main__":
asyncio.run(main())
```
<Warning>
The `run()` method cannot be called from inside an async function because it already creates its own async event loop internally. If you attempt to call `run()` from inside an async function, you'll get an error about the event loop already running.
Always use `run_async()` inside async functions and `run()` in synchronous contexts.
</Warning>
Both `run()` and `run_async()` accept the same transport arguments, so all the examples above apply to both methods.
## Custom Routes