diff --git a/docs/deployment/http.mdx b/docs/deployment/http.mdx
index 2383d987d..ca636cb17 100644
--- a/docs/deployment/http.mdx
+++ b/docs/deployment/http.mdx
@@ -198,6 +198,79 @@ Without `expose_headers=["mcp-session-id"]`, browsers will receive the session I
**Production Security**: Never use `allow_origins=["*"]` in production. Specify the exact origins of your browser-based clients. Using wildcards exposes your server to unauthorized access from any website.
+### SSE Polling for Long-Running Operations
+
+
+
+
+This feature only applies to the **StreamableHTTP transport** (the default for `http_app()`). It does not apply to the legacy SSE transport (`transport="sse"`).
+
+
+When running tools that take a long time to complete, you may encounter issues with load balancers or proxies terminating connections that stay idle too long. [SEP-1699](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1699) introduces SSE polling to solve this by allowing the server to gracefully close connections and have clients automatically reconnect.
+
+To enable SSE polling, configure an `EventStore` when creating your HTTP application:
+
+```python
+from fastmcp import FastMCP, Context
+from fastmcp.server.event_store import EventStore
+
+mcp = FastMCP("My Server")
+
+@mcp.tool
+async def long_running_task(ctx: Context) -> str:
+ """A task that takes several minutes to complete."""
+ for i in range(100):
+ await ctx.report_progress(i, 100)
+
+ # Periodically close the connection to avoid load balancer timeouts
+ # Client will automatically reconnect and resume receiving progress
+ if i % 30 == 0 and i > 0:
+ await ctx.close_sse_stream()
+
+ await do_expensive_work()
+
+ return "Done!"
+
+# Configure with EventStore for resumability
+event_store = EventStore()
+app = mcp.http_app(
+ event_store=event_store,
+ retry_interval=2000, # Client reconnects after 2 seconds
+)
+```
+
+**How it works:**
+
+1. When `event_store` is configured, the server stores all events (progress updates, results) with unique IDs
+2. Calling `ctx.close_sse_stream()` gracefully closes the HTTP connection
+3. The client automatically reconnects with a `Last-Event-ID` header
+4. The server replays any events the client missed during the disconnection
+
+The `retry_interval` parameter (in milliseconds) controls how long clients wait before reconnecting. Choose a value that balances responsiveness with server load.
+
+
+`close_sse_stream()` is a no-op if called without an `EventStore` configured, so you can safely include it in tools that may run in different deployment configurations.
+
+
+#### Custom Storage Backends
+
+By default, `EventStore` uses in-memory storage. For production deployments with multiple server instances, you can provide a custom storage backend using the `key_value` package:
+
+```python
+from fastmcp.server.event_store import EventStore
+from key_value.aio.stores.redis import RedisStore
+
+# Use Redis for distributed deployments
+redis_store = RedisStore(url="redis://localhost:6379")
+event_store = EventStore(
+ storage=redis_store,
+ max_events_per_stream=100, # Keep last 100 events per stream
+ ttl=3600, # Events expire after 1 hour
+)
+
+app = mcp.http_app(event_store=event_store)
+```
+
## Integration with Web Frameworks
If you already have a web application running, you can add MCP capabilities by mounting a FastMCP server as a sub-application. This allows you to expose MCP tools alongside your existing API endpoints, sharing the same domain and infrastructure. The MCP server becomes just another route in your application, making it easy to manage and deploy.
@@ -513,11 +586,66 @@ When deploying to production, you'll want to optimize your server for performanc
# Run with basic configuration
uvicorn app:app --host 0.0.0.0 --port 8000
-# Ensure stateless HTTP mode is enabled (stateless_http=True)
-# Run with multiple workers for production
+# Run with multiple workers for production (requires stateless mode - see below)
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
```
+### Horizontal Scaling
+
+
+
+When deploying FastMCP behind a load balancer or running multiple server instances, you need to understand how the HTTP transport handles sessions and configure your server appropriately.
+
+#### Understanding Sessions
+
+By default, FastMCP's Streamable HTTP transport maintains server-side sessions. Sessions enable stateful MCP features like [elicitation](/servers/elicitation) and [sampling](/servers/sampling), where the server needs to maintain context across multiple requests from the same client.
+
+This works perfectly for single-instance deployments. However, sessions are stored in memory on each server instance, which creates challenges when scaling horizontally.
+
+#### Without Stateless Mode
+
+When running multiple server instances behind a load balancer (Traefik, nginx, HAProxy, Kubernetes, etc.), requests from the same client may be routed to different instances:
+
+1. Client connects to Instance A → session created on Instance A
+2. Next request routes to Instance B → session doesn't exist → **request fails**
+
+You might expect sticky sessions (session affinity) to solve this, but they don't work reliably with MCP clients.
+
+
+**Why sticky sessions don't work:** Most MCP clients—including Cursor and Claude Code—use `fetch()` internally and don't properly forward `Set-Cookie` headers. Without cookies, load balancers can't identify which instance should handle subsequent requests. This is a limitation in how these clients implement HTTP, not something you can fix with load balancer configuration.
+
+
+#### Enabling Stateless Mode
+
+For horizontally scaled deployments, enable stateless HTTP mode. In stateless mode, each request creates a fresh transport context, eliminating the need for session affinity entirely.
+
+**Option 1: Via constructor**
+
+```python
+from fastmcp import FastMCP
+
+mcp = FastMCP("My Server", stateless_http=True)
+
+@mcp.tool
+def process(data: str) -> str:
+ return f"Processed: {data}"
+
+app = mcp.http_app()
+```
+
+**Option 2: Via `run()`**
+
+```python
+if __name__ == "__main__":
+ mcp.run(transport="http", stateless_http=True)
+```
+
+**Option 3: Via environment variable**
+
+```bash
+FASTMCP_STATELESS_HTTP=true uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
+```
+
### Environment Variables
Production deployments should never hardcode sensitive information like API keys or authentication tokens. Instead, use environment variables to configure your server at runtime. This keeps your code secure and makes it easy to deploy the same code to different environments with different configurations.