diff --git a/docs/deployment/running-server.mdx b/docs/deployment/running-server.mdx index edcebcd16..254caf2b3 100644 --- a/docs/deployment/running-server.mdx +++ b/docs/deployment/running-server.mdx @@ -170,4 +170,117 @@ New applications should use Streamable HTTP transport instead. Server-Sent Events (SSE) is an HTTP-based protocol for server-to-client streaming. While FastMCP still supports SSE, it is deprecated and Streamable HTTP is preferred for new projects. -To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse`) and message path (`/messages/` \ No newline at end of file +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/`). + + +```python {6} server.py +from fastmcp import FastMCP + +mcp = FastMCP() + +if __name__ == "__main__": + mcp.run(transport="sse") +``` +```python {3,7} client.py +import asyncio +from fastmcp import Client +from fastmcp.client.transports import SSETransport + +async def example(): + async with Client( + transport=SSETransport("http://127.0.0.1:8000/sse") + ) as client: + await client.ping() + +if __name__ == "__main__": + asyncio.run(example()) +``` + + + +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). + + +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). + + +```python {8-12} server.py +from fastmcp import FastMCP + +mcp = FastMCP() + +if __name__ == "__main__": + mcp.run( + transport="sse", + host="127.0.0.1", + port=4200, + log_level="debug", + path="/my-custom-sse-path", + ) +``` +```python {7} client.py +import asyncio +from fastmcp import Client +from fastmcp.client.transports import SSETransport + +async def example(): + async with Client( + transport=SSETransport("http://127.0.0.1:4200/my-custom-sse-path") + ) as client: + await client.ping() + +if __name__ == "__main__": + asyncio.run(example()) +``` + + + + +## 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()) +``` + + +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. + + +Both `run()` and `run_async()` accept the same transport arguments, so all the examples above apply to both methods. + +## Custom Routes + +You can also add custom web routes to your FastMCP server, which will be exposed alongside the MCP endpoint. To do so, use the `@custom_route` decorator. Note that this is less flexible than using a full ASGI framework, but can be useful for adding simple endpoints like health checks to your standalone server. + +```python +from fastmcp import FastMCP +from starlette.requests import Request +from starlette.responses import PlainTextResponse + +mcp = FastMCP("MyServer") + +@mcp.custom_route("/health", methods=["GET"]) +async def health_check(request: Request) -> PlainTextResponse: + return PlainTextResponse("OK") + +if __name__ == "__main__": + mcp.run() +``` \ No newline at end of file