Spaces:
Running
Running
zzstoatzz commited on
Commit ·
d09d7f6
1
Parent(s): 5359040
strict typing for `server.py`
Browse files- pyproject.toml +1 -0
- src/fastmcp/server/http.py +22 -6
- src/fastmcp/server/server.py +13 -11
pyproject.toml
CHANGED
|
@@ -96,6 +96,7 @@ reportMissingTypeStubs = false
|
|
| 96 |
useLibraryCodeForTypes = true
|
| 97 |
venvPath = "."
|
| 98 |
venv = ".venv"
|
|
|
|
| 99 |
|
| 100 |
[tool.ruff.lint]
|
| 101 |
extend-select = ["I", "UP"]
|
|
|
|
| 96 |
useLibraryCodeForTypes = true
|
| 97 |
venvPath = "."
|
| 98 |
venv = ".venv"
|
| 99 |
+
strict = ["src/fastmcp/server/server.py"]
|
| 100 |
|
| 101 |
[tool.ruff.lint]
|
| 102 |
extend-select = ["I", "UP"]
|
src/fastmcp/server/http.py
CHANGED
|
@@ -10,9 +10,15 @@ from mcp.server.auth.middleware.bearer_auth import (
|
|
| 10 |
BearerAuthBackend,
|
| 11 |
RequireAuthMiddleware,
|
| 12 |
)
|
| 13 |
-
from mcp.server.auth.provider import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
from mcp.server.auth.routes import create_auth_routes
|
| 15 |
from mcp.server.auth.settings import AuthSettings
|
|
|
|
| 16 |
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
|
| 17 |
from starlette.applications import Starlette
|
| 18 |
from starlette.middleware import Middleware
|
|
@@ -30,6 +36,7 @@ if TYPE_CHECKING:
|
|
| 30 |
|
| 31 |
logger = get_logger(__name__)
|
| 32 |
|
|
|
|
| 33 |
_current_http_request: ContextVar[Request | None] = ContextVar(
|
| 34 |
"http_request",
|
| 35 |
default=None,
|
|
@@ -62,7 +69,10 @@ class RequestContextMiddleware:
|
|
| 62 |
|
| 63 |
|
| 64 |
def setup_auth_middleware_and_routes(
|
| 65 |
-
auth_server_provider: OAuthAuthorizationServerProvider
|
|
|
|
|
|
|
|
|
|
| 66 |
auth_settings: AuthSettings | None,
|
| 67 |
) -> tuple[list[Middleware], list[BaseRoute], list[str]]:
|
| 68 |
"""Set up authentication middleware and routes if auth is enabled.
|
|
@@ -136,10 +146,13 @@ def create_base_app(
|
|
| 136 |
|
| 137 |
|
| 138 |
def create_sse_app(
|
| 139 |
-
server: FastMCP,
|
| 140 |
message_path: str,
|
| 141 |
sse_path: str,
|
| 142 |
-
auth_server_provider: OAuthAuthorizationServerProvider
|
|
|
|
|
|
|
|
|
|
| 143 |
auth_settings: AuthSettings | None = None,
|
| 144 |
debug: bool = False,
|
| 145 |
routes: list[BaseRoute] | None = None,
|
|
@@ -236,10 +249,13 @@ def create_sse_app(
|
|
| 236 |
|
| 237 |
|
| 238 |
def create_streamable_http_app(
|
| 239 |
-
server: FastMCP,
|
| 240 |
streamable_http_path: str,
|
| 241 |
event_store: None = None,
|
| 242 |
-
auth_server_provider: OAuthAuthorizationServerProvider
|
|
|
|
|
|
|
|
|
|
| 243 |
auth_settings: AuthSettings | None = None,
|
| 244 |
json_response: bool = False,
|
| 245 |
stateless_http: bool = False,
|
|
|
|
| 10 |
BearerAuthBackend,
|
| 11 |
RequireAuthMiddleware,
|
| 12 |
)
|
| 13 |
+
from mcp.server.auth.provider import (
|
| 14 |
+
AccessTokenT,
|
| 15 |
+
AuthorizationCodeT,
|
| 16 |
+
OAuthAuthorizationServerProvider,
|
| 17 |
+
RefreshTokenT,
|
| 18 |
+
)
|
| 19 |
from mcp.server.auth.routes import create_auth_routes
|
| 20 |
from mcp.server.auth.settings import AuthSettings
|
| 21 |
+
from mcp.server.lowlevel.server import LifespanResultT
|
| 22 |
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
|
| 23 |
from starlette.applications import Starlette
|
| 24 |
from starlette.middleware import Middleware
|
|
|
|
| 36 |
|
| 37 |
logger = get_logger(__name__)
|
| 38 |
|
| 39 |
+
|
| 40 |
_current_http_request: ContextVar[Request | None] = ContextVar(
|
| 41 |
"http_request",
|
| 42 |
default=None,
|
|
|
|
| 69 |
|
| 70 |
|
| 71 |
def setup_auth_middleware_and_routes(
|
| 72 |
+
auth_server_provider: OAuthAuthorizationServerProvider[
|
| 73 |
+
AuthorizationCodeT, RefreshTokenT, AccessTokenT
|
| 74 |
+
]
|
| 75 |
+
| None,
|
| 76 |
auth_settings: AuthSettings | None,
|
| 77 |
) -> tuple[list[Middleware], list[BaseRoute], list[str]]:
|
| 78 |
"""Set up authentication middleware and routes if auth is enabled.
|
|
|
|
| 146 |
|
| 147 |
|
| 148 |
def create_sse_app(
|
| 149 |
+
server: FastMCP[LifespanResultT],
|
| 150 |
message_path: str,
|
| 151 |
sse_path: str,
|
| 152 |
+
auth_server_provider: OAuthAuthorizationServerProvider[
|
| 153 |
+
AuthorizationCodeT, RefreshTokenT, AccessTokenT
|
| 154 |
+
]
|
| 155 |
+
| None = None,
|
| 156 |
auth_settings: AuthSettings | None = None,
|
| 157 |
debug: bool = False,
|
| 158 |
routes: list[BaseRoute] | None = None,
|
|
|
|
| 249 |
|
| 250 |
|
| 251 |
def create_streamable_http_app(
|
| 252 |
+
server: FastMCP[LifespanResultT],
|
| 253 |
streamable_http_path: str,
|
| 254 |
event_store: None = None,
|
| 255 |
+
auth_server_provider: OAuthAuthorizationServerProvider[
|
| 256 |
+
AuthorizationCodeT, RefreshTokenT, AccessTokenT
|
| 257 |
+
]
|
| 258 |
+
| None = None,
|
| 259 |
auth_settings: AuthSettings | None = None,
|
| 260 |
json_response: bool = False,
|
| 261 |
stateless_http: bool = False,
|
src/fastmcp/server/server.py
CHANGED
|
@@ -66,7 +66,7 @@ DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
|
|
| 66 |
|
| 67 |
|
| 68 |
@asynccontextmanager
|
| 69 |
-
async def default_lifespan(server: FastMCP) -> AsyncIterator[Any]:
|
| 70 |
"""Default lifespan context manager that does nothing.
|
| 71 |
|
| 72 |
Args:
|
|
@@ -79,8 +79,10 @@ async def default_lifespan(server: FastMCP) -> AsyncIterator[Any]:
|
|
| 79 |
|
| 80 |
|
| 81 |
def _lifespan_wrapper(
|
| 82 |
-
app: FastMCP,
|
| 83 |
-
lifespan: Callable[
|
|
|
|
|
|
|
| 84 |
) -> Callable[
|
| 85 |
[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]
|
| 86 |
]:
|
|
@@ -226,7 +228,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 226 |
async def get_tools(self) -> dict[str, Tool]:
|
| 227 |
"""Get all registered tools, indexed by registered key."""
|
| 228 |
if (tools := self._cache.get("tools")) is self._cache.NOT_FOUND:
|
| 229 |
-
tools = {}
|
| 230 |
for server in self._mounted_servers.values():
|
| 231 |
server_tools = await server.get_tools()
|
| 232 |
tools.update(server_tools)
|
|
@@ -237,7 +239,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 237 |
async def get_resources(self) -> dict[str, Resource]:
|
| 238 |
"""Get all registered resources, indexed by registered key."""
|
| 239 |
if (resources := self._cache.get("resources")) is self._cache.NOT_FOUND:
|
| 240 |
-
resources = {}
|
| 241 |
for server in self._mounted_servers.values():
|
| 242 |
server_resources = await server.get_resources()
|
| 243 |
resources.update(server_resources)
|
|
@@ -250,7 +252,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 250 |
if (
|
| 251 |
templates := self._cache.get("resource_templates")
|
| 252 |
) is self._cache.NOT_FOUND:
|
| 253 |
-
templates = {}
|
| 254 |
for server in self._mounted_servers.values():
|
| 255 |
server_templates = await server.get_resource_templates()
|
| 256 |
templates.update(server_templates)
|
|
@@ -263,7 +265,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 263 |
List all available prompts.
|
| 264 |
"""
|
| 265 |
if (prompts := self._cache.get("prompts")) is self._cache.NOT_FOUND:
|
| 266 |
-
prompts = {}
|
| 267 |
for server in self._mounted_servers.values():
|
| 268 |
server_prompts = await server.get_prompts()
|
| 269 |
prompts.update(server_prompts)
|
|
@@ -741,7 +743,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 741 |
port: int | None = None,
|
| 742 |
log_level: str | None = None,
|
| 743 |
path: str | None = None,
|
| 744 |
-
uvicorn_config: dict | None = None,
|
| 745 |
middleware: list[Middleware] | None = None,
|
| 746 |
) -> None:
|
| 747 |
"""Run the server using HTTP transport.
|
|
@@ -778,7 +780,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 778 |
log_level: str | None = None,
|
| 779 |
path: str | None = None,
|
| 780 |
message_path: str | None = None,
|
| 781 |
-
uvicorn_config: dict | None = None,
|
| 782 |
) -> None:
|
| 783 |
"""Run the server using SSE transport."""
|
| 784 |
|
|
@@ -900,7 +902,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
| 900 |
port: int | None = None,
|
| 901 |
log_level: str | None = None,
|
| 902 |
path: str | None = None,
|
| 903 |
-
uvicorn_config: dict | None = None,
|
| 904 |
) -> None:
|
| 905 |
# Deprecated since 2.3.2
|
| 906 |
warnings.warn(
|
|
@@ -1127,7 +1129,7 @@ class MountedServer:
|
|
| 1127 |
def __init__(
|
| 1128 |
self,
|
| 1129 |
prefix: str,
|
| 1130 |
-
server: FastMCP,
|
| 1131 |
tool_separator: str | None = None,
|
| 1132 |
resource_separator: str | None = None,
|
| 1133 |
prompt_separator: str | None = None,
|
|
|
|
| 66 |
|
| 67 |
|
| 68 |
@asynccontextmanager
|
| 69 |
+
async def default_lifespan(server: FastMCP[LifespanResultT]) -> AsyncIterator[Any]:
|
| 70 |
"""Default lifespan context manager that does nothing.
|
| 71 |
|
| 72 |
Args:
|
|
|
|
| 79 |
|
| 80 |
|
| 81 |
def _lifespan_wrapper(
|
| 82 |
+
app: FastMCP[LifespanResultT],
|
| 83 |
+
lifespan: Callable[
|
| 84 |
+
[FastMCP[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]
|
| 85 |
+
],
|
| 86 |
) -> Callable[
|
| 87 |
[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]
|
| 88 |
]:
|
|
|
|
| 228 |
async def get_tools(self) -> dict[str, Tool]:
|
| 229 |
"""Get all registered tools, indexed by registered key."""
|
| 230 |
if (tools := self._cache.get("tools")) is self._cache.NOT_FOUND:
|
| 231 |
+
tools: dict[str, Tool] = {}
|
| 232 |
for server in self._mounted_servers.values():
|
| 233 |
server_tools = await server.get_tools()
|
| 234 |
tools.update(server_tools)
|
|
|
|
| 239 |
async def get_resources(self) -> dict[str, Resource]:
|
| 240 |
"""Get all registered resources, indexed by registered key."""
|
| 241 |
if (resources := self._cache.get("resources")) is self._cache.NOT_FOUND:
|
| 242 |
+
resources: dict[str, Resource] = {}
|
| 243 |
for server in self._mounted_servers.values():
|
| 244 |
server_resources = await server.get_resources()
|
| 245 |
resources.update(server_resources)
|
|
|
|
| 252 |
if (
|
| 253 |
templates := self._cache.get("resource_templates")
|
| 254 |
) is self._cache.NOT_FOUND:
|
| 255 |
+
templates: dict[str, ResourceTemplate] = {}
|
| 256 |
for server in self._mounted_servers.values():
|
| 257 |
server_templates = await server.get_resource_templates()
|
| 258 |
templates.update(server_templates)
|
|
|
|
| 265 |
List all available prompts.
|
| 266 |
"""
|
| 267 |
if (prompts := self._cache.get("prompts")) is self._cache.NOT_FOUND:
|
| 268 |
+
prompts: dict[str, Prompt] = {}
|
| 269 |
for server in self._mounted_servers.values():
|
| 270 |
server_prompts = await server.get_prompts()
|
| 271 |
prompts.update(server_prompts)
|
|
|
|
| 743 |
port: int | None = None,
|
| 744 |
log_level: str | None = None,
|
| 745 |
path: str | None = None,
|
| 746 |
+
uvicorn_config: dict[str, Any] | None = None,
|
| 747 |
middleware: list[Middleware] | None = None,
|
| 748 |
) -> None:
|
| 749 |
"""Run the server using HTTP transport.
|
|
|
|
| 780 |
log_level: str | None = None,
|
| 781 |
path: str | None = None,
|
| 782 |
message_path: str | None = None,
|
| 783 |
+
uvicorn_config: dict[str, Any] | None = None,
|
| 784 |
) -> None:
|
| 785 |
"""Run the server using SSE transport."""
|
| 786 |
|
|
|
|
| 902 |
port: int | None = None,
|
| 903 |
log_level: str | None = None,
|
| 904 |
path: str | None = None,
|
| 905 |
+
uvicorn_config: dict[str, Any] | None = None,
|
| 906 |
) -> None:
|
| 907 |
# Deprecated since 2.3.2
|
| 908 |
warnings.warn(
|
|
|
|
| 1129 |
def __init__(
|
| 1130 |
self,
|
| 1131 |
prefix: str,
|
| 1132 |
+
server: FastMCP[LifespanResultT],
|
| 1133 |
tool_separator: str | None = None,
|
| 1134 |
resource_separator: str | None = None,
|
| 1135 |
prompt_separator: str | None = None,
|