Spaces:
Running
Running
Merge pull request #483 from jlowin/lifecycle
Browse files
docs/deployment/asgi.mdx
CHANGED
|
@@ -124,7 +124,7 @@ app = Starlette(
|
|
| 124 |
Mount("/mcp-server", app=mcp_app),
|
| 125 |
# Add other routes as needed
|
| 126 |
],
|
| 127 |
-
lifespan=mcp_app.
|
| 128 |
)
|
| 129 |
```
|
| 130 |
|
|
@@ -154,7 +154,7 @@ mcp_app = mcp.http_app(path='/mcp')
|
|
| 154 |
inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
|
| 155 |
app = Starlette(
|
| 156 |
routes=[Mount("/outer", app=inner_app)],
|
| 157 |
-
lifespan=mcp_app.
|
| 158 |
)
|
| 159 |
```
|
| 160 |
|
|
@@ -181,7 +181,7 @@ mcp = FastMCP("MyServer")
|
|
| 181 |
mcp_app = mcp.http_app(path='/mcp')
|
| 182 |
|
| 183 |
# Create a FastAPI app and mount the MCP server
|
| 184 |
-
app = FastAPI(lifespan=mcp_app.
|
| 185 |
app.mount("/mcp-server", mcp_app)
|
| 186 |
```
|
| 187 |
|
|
|
|
| 124 |
Mount("/mcp-server", app=mcp_app),
|
| 125 |
# Add other routes as needed
|
| 126 |
],
|
| 127 |
+
lifespan=mcp_app.lifespan,
|
| 128 |
)
|
| 129 |
```
|
| 130 |
|
|
|
|
| 154 |
inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
|
| 155 |
app = Starlette(
|
| 156 |
routes=[Mount("/outer", app=inner_app)],
|
| 157 |
+
lifespan=mcp_app.lifespan,
|
| 158 |
)
|
| 159 |
```
|
| 160 |
|
|
|
|
| 181 |
mcp_app = mcp.http_app(path='/mcp')
|
| 182 |
|
| 183 |
# Create a FastAPI app and mount the MCP server
|
| 184 |
+
app = FastAPI(lifespan=mcp_app.lifespan)
|
| 185 |
app.mount("/mcp-server", mcp_app)
|
| 186 |
```
|
| 187 |
|
src/fastmcp/server/http.py
CHANGED
|
@@ -27,7 +27,7 @@ from starlette.middleware.authentication import AuthenticationMiddleware
|
|
| 27 |
from starlette.requests import Request
|
| 28 |
from starlette.responses import Response
|
| 29 |
from starlette.routing import BaseRoute, Mount, Route
|
| 30 |
-
from starlette.types import Receive, Scope, Send
|
| 31 |
|
| 32 |
from fastmcp.utilities.logging import get_logger
|
| 33 |
|
|
@@ -43,6 +43,12 @@ _current_http_request: ContextVar[Request | None] = ContextVar(
|
|
| 43 |
)
|
| 44 |
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
@contextmanager
|
| 47 |
def set_http_request(request: Request) -> Generator[Request, None, None]:
|
| 48 |
token = _current_http_request.set(request)
|
|
@@ -122,7 +128,7 @@ def create_base_app(
|
|
| 122 |
middleware: list[Middleware],
|
| 123 |
debug: bool = False,
|
| 124 |
lifespan: Callable | None = None,
|
| 125 |
-
) ->
|
| 126 |
"""Create a base Starlette app with common middleware and routes.
|
| 127 |
|
| 128 |
Args:
|
|
@@ -137,7 +143,7 @@ def create_base_app(
|
|
| 137 |
# Always add RequestContextMiddleware as the outermost middleware
|
| 138 |
middleware.append(Middleware(RequestContextMiddleware))
|
| 139 |
|
| 140 |
-
return
|
| 141 |
routes=routes,
|
| 142 |
middleware=middleware,
|
| 143 |
debug=debug,
|
|
@@ -157,7 +163,7 @@ def create_sse_app(
|
|
| 157 |
debug: bool = False,
|
| 158 |
routes: list[BaseRoute] | None = None,
|
| 159 |
middleware: list[Middleware] | None = None,
|
| 160 |
-
) ->
|
| 161 |
"""Return an instance of the SSE server app.
|
| 162 |
|
| 163 |
Args:
|
|
@@ -262,7 +268,7 @@ def create_streamable_http_app(
|
|
| 262 |
debug: bool = False,
|
| 263 |
routes: list[BaseRoute] | None = None,
|
| 264 |
middleware: list[Middleware] | None = None,
|
| 265 |
-
) ->
|
| 266 |
"""Return an instance of the StreamableHTTP server app.
|
| 267 |
|
| 268 |
Args:
|
|
|
|
| 27 |
from starlette.requests import Request
|
| 28 |
from starlette.responses import Response
|
| 29 |
from starlette.routing import BaseRoute, Mount, Route
|
| 30 |
+
from starlette.types import Lifespan, Receive, Scope, Send
|
| 31 |
|
| 32 |
from fastmcp.utilities.logging import get_logger
|
| 33 |
|
|
|
|
| 43 |
)
|
| 44 |
|
| 45 |
|
| 46 |
+
class StarletteWithLifespan(Starlette):
|
| 47 |
+
@property
|
| 48 |
+
def lifespan(self) -> Lifespan:
|
| 49 |
+
return self.router.lifespan_context
|
| 50 |
+
|
| 51 |
+
|
| 52 |
@contextmanager
|
| 53 |
def set_http_request(request: Request) -> Generator[Request, None, None]:
|
| 54 |
token = _current_http_request.set(request)
|
|
|
|
| 128 |
middleware: list[Middleware],
|
| 129 |
debug: bool = False,
|
| 130 |
lifespan: Callable | None = None,
|
| 131 |
+
) -> StarletteWithLifespan:
|
| 132 |
"""Create a base Starlette app with common middleware and routes.
|
| 133 |
|
| 134 |
Args:
|
|
|
|
| 143 |
# Always add RequestContextMiddleware as the outermost middleware
|
| 144 |
middleware.append(Middleware(RequestContextMiddleware))
|
| 145 |
|
| 146 |
+
return StarletteWithLifespan(
|
| 147 |
routes=routes,
|
| 148 |
middleware=middleware,
|
| 149 |
debug=debug,
|
|
|
|
| 163 |
debug: bool = False,
|
| 164 |
routes: list[BaseRoute] | None = None,
|
| 165 |
middleware: list[Middleware] | None = None,
|
| 166 |
+
) -> StarletteWithLifespan:
|
| 167 |
"""Return an instance of the SSE server app.
|
| 168 |
|
| 169 |
Args:
|
|
|
|
| 268 |
debug: bool = False,
|
| 269 |
routes: list[BaseRoute] | None = None,
|
| 270 |
middleware: list[Middleware] | None = None,
|
| 271 |
+
) -> StarletteWithLifespan:
|
| 272 |
"""Return an instance of the StreamableHTTP server app.
|
| 273 |
|
| 274 |
Args:
|
src/fastmcp/server/server.py
CHANGED
|
@@ -35,7 +35,6 @@ from mcp.types import Resource as MCPResource
|
|
| 35 |
from mcp.types import ResourceTemplate as MCPResourceTemplate
|
| 36 |
from mcp.types import Tool as MCPTool
|
| 37 |
from pydantic import AnyUrl
|
| 38 |
-
from starlette.applications import Starlette
|
| 39 |
from starlette.middleware import Middleware
|
| 40 |
from starlette.requests import Request
|
| 41 |
from starlette.responses import Response
|
|
@@ -48,7 +47,11 @@ from fastmcp.prompts import Prompt, PromptManager
|
|
| 48 |
from fastmcp.prompts.prompt import PromptResult
|
| 49 |
from fastmcp.resources import Resource, ResourceManager
|
| 50 |
from fastmcp.resources.template import ResourceTemplate
|
| 51 |
-
from fastmcp.server.http import
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
from fastmcp.tools import ToolManager
|
| 53 |
from fastmcp.tools.tool import Tool
|
| 54 |
from fastmcp.utilities.cache import TimedCache
|
|
@@ -59,7 +62,6 @@ if TYPE_CHECKING:
|
|
| 59 |
from fastmcp.client import Client
|
| 60 |
from fastmcp.server.openapi import FastMCPOpenAPI
|
| 61 |
from fastmcp.server.proxy import FastMCPProxy
|
| 62 |
-
|
| 63 |
logger = get_logger(__name__)
|
| 64 |
|
| 65 |
DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
|
|
@@ -806,7 +808,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 806 |
path: str | None = None,
|
| 807 |
message_path: str | None = None,
|
| 808 |
middleware: list[Middleware] | None = None,
|
| 809 |
-
) ->
|
| 810 |
"""
|
| 811 |
Create a Starlette app for the SSE server.
|
| 812 |
|
|
@@ -837,7 +839,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 837 |
self,
|
| 838 |
path: str | None = None,
|
| 839 |
middleware: list[Middleware] | None = None,
|
| 840 |
-
) ->
|
| 841 |
"""
|
| 842 |
Create a Starlette app for the StreamableHTTP server.
|
| 843 |
|
|
@@ -858,7 +860,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 858 |
path: str | None = None,
|
| 859 |
middleware: list[Middleware] | None = None,
|
| 860 |
transport: Literal["streamable-http", "sse"] = "streamable-http",
|
| 861 |
-
) ->
|
| 862 |
"""Create a Starlette app using the specified HTTP transport.
|
| 863 |
|
| 864 |
Args:
|
|
@@ -869,7 +871,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 869 |
Returns:
|
| 870 |
A Starlette application configured with the specified transport
|
| 871 |
"""
|
| 872 |
-
from fastmcp.server.http import create_streamable_http_app
|
| 873 |
|
| 874 |
if transport == "streamable-http":
|
| 875 |
return create_streamable_http_app(
|
|
|
|
| 35 |
from mcp.types import ResourceTemplate as MCPResourceTemplate
|
| 36 |
from mcp.types import Tool as MCPTool
|
| 37 |
from pydantic import AnyUrl
|
|
|
|
| 38 |
from starlette.middleware import Middleware
|
| 39 |
from starlette.requests import Request
|
| 40 |
from starlette.responses import Response
|
|
|
|
| 47 |
from fastmcp.prompts.prompt import PromptResult
|
| 48 |
from fastmcp.resources import Resource, ResourceManager
|
| 49 |
from fastmcp.resources.template import ResourceTemplate
|
| 50 |
+
from fastmcp.server.http import (
|
| 51 |
+
StarletteWithLifespan,
|
| 52 |
+
create_sse_app,
|
| 53 |
+
create_streamable_http_app,
|
| 54 |
+
)
|
| 55 |
from fastmcp.tools import ToolManager
|
| 56 |
from fastmcp.tools.tool import Tool
|
| 57 |
from fastmcp.utilities.cache import TimedCache
|
|
|
|
| 62 |
from fastmcp.client import Client
|
| 63 |
from fastmcp.server.openapi import FastMCPOpenAPI
|
| 64 |
from fastmcp.server.proxy import FastMCPProxy
|
|
|
|
| 65 |
logger = get_logger(__name__)
|
| 66 |
|
| 67 |
DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
|
|
|
|
| 808 |
path: str | None = None,
|
| 809 |
message_path: str | None = None,
|
| 810 |
middleware: list[Middleware] | None = None,
|
| 811 |
+
) -> StarletteWithLifespan:
|
| 812 |
"""
|
| 813 |
Create a Starlette app for the SSE server.
|
| 814 |
|
|
|
|
| 839 |
self,
|
| 840 |
path: str | None = None,
|
| 841 |
middleware: list[Middleware] | None = None,
|
| 842 |
+
) -> StarletteWithLifespan:
|
| 843 |
"""
|
| 844 |
Create a Starlette app for the StreamableHTTP server.
|
| 845 |
|
|
|
|
| 860 |
path: str | None = None,
|
| 861 |
middleware: list[Middleware] | None = None,
|
| 862 |
transport: Literal["streamable-http", "sse"] = "streamable-http",
|
| 863 |
+
) -> StarletteWithLifespan:
|
| 864 |
"""Create a Starlette app using the specified HTTP transport.
|
| 865 |
|
| 866 |
Args:
|
|
|
|
| 871 |
Returns:
|
| 872 |
A Starlette application configured with the specified transport
|
| 873 |
"""
|
|
|
|
| 874 |
|
| 875 |
if transport == "streamable-http":
|
| 876 |
return create_streamable_http_app(
|
tests/client/test_streamable_http.py
CHANGED
|
@@ -119,7 +119,7 @@ def run_nested_server(host: str, port: int) -> None:
|
|
| 119 |
mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
|
| 120 |
mount2 = Starlette(
|
| 121 |
routes=[Mount("/nest-outer", app=mount)],
|
| 122 |
-
lifespan=mcp_app.
|
| 123 |
)
|
| 124 |
server = uvicorn.Server(
|
| 125 |
config=uvicorn.Config(
|
|
|
|
| 119 |
mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
|
| 120 |
mount2 = Starlette(
|
| 121 |
routes=[Mount("/nest-outer", app=mount)],
|
| 122 |
+
lifespan=mcp_app.lifespan,
|
| 123 |
)
|
| 124 |
server = uvicorn.Server(
|
| 125 |
config=uvicorn.Config(
|