mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-19 20:14:17 +02:00
Deprecate passing settings to the FastMCP instance
This commit is contained in:
parent
bb2dfe80f2
commit
db3c59c098
4 changed files with 81 additions and 46 deletions
|
|
@ -3,7 +3,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import inspect
|
||||
import warnings
|
||||
from collections.abc import AsyncIterator, Awaitable, Callable
|
||||
from contextlib import (
|
||||
|
|
@ -54,7 +53,7 @@ from fastmcp.tools import ToolManager
|
|||
from fastmcp.tools.tool import Tool
|
||||
from fastmcp.utilities.cache import TimedCache
|
||||
from fastmcp.utilities.decorators import DecoratedFunction
|
||||
from fastmcp.utilities.logging import configure_logging, get_logger
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastmcp.client import Client
|
||||
|
|
@ -63,6 +62,8 @@ if TYPE_CHECKING:
|
|||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def default_lifespan(server: FastMCP) -> AsyncIterator[Any]:
|
||||
|
|
@ -107,40 +108,52 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
| None
|
||||
) = None,
|
||||
tags: set[str] | None = None,
|
||||
dependencies: list[str] | None = None,
|
||||
tool_serializer: Callable[[Any], str] | None = None,
|
||||
cache_expiration_seconds: float | None = None,
|
||||
on_duplicate_tools: DuplicateBehavior | None = None,
|
||||
on_duplicate_resources: DuplicateBehavior | None = None,
|
||||
on_duplicate_prompts: DuplicateBehavior | None = None,
|
||||
**settings: Any,
|
||||
):
|
||||
self.tags: set[str] = tags or set()
|
||||
self.settings = fastmcp.settings.ServerSettings(**settings)
|
||||
self._cache = TimedCache(
|
||||
expiration=datetime.timedelta(
|
||||
seconds=self.settings.cache_expiration_seconds
|
||||
if settings:
|
||||
# TODO: remove settings. Deprecated since 2.3.4
|
||||
warnings.warn(
|
||||
"Passing transport-specific and other runtime settings as kwargs "
|
||||
"to the FastMCP constructor is deprecated (as of 2.3.4), "
|
||||
"including most transport settings. Provide settings when calling "
|
||||
"run() instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
)
|
||||
self.settings = fastmcp.settings.ServerSettings(**settings)
|
||||
|
||||
self.tags: set[str] = tags or set()
|
||||
self.dependencies = dependencies
|
||||
self._cache = TimedCache(
|
||||
expiration=datetime.timedelta(seconds=cache_expiration_seconds or 0)
|
||||
)
|
||||
self._mounted_servers: dict[str, MountedServer] = {}
|
||||
self._additional_http_routes: list[BaseRoute] = []
|
||||
self._tool_manager = ToolManager(
|
||||
duplicate_behavior=on_duplicate_tools,
|
||||
serializer=tool_serializer,
|
||||
)
|
||||
self._resource_manager = ResourceManager(
|
||||
duplicate_behavior=on_duplicate_resources
|
||||
)
|
||||
self._prompt_manager = PromptManager(duplicate_behavior=on_duplicate_prompts)
|
||||
|
||||
if lifespan is None:
|
||||
self._has_lifespan = False
|
||||
lifespan = default_lifespan
|
||||
else:
|
||||
self._has_lifespan = True
|
||||
|
||||
self._mcp_server = MCPServer[LifespanResultT](
|
||||
name=name or "FastMCP",
|
||||
instructions=instructions,
|
||||
lifespan=_lifespan_wrapper(self, lifespan),
|
||||
)
|
||||
self._tool_manager = ToolManager(
|
||||
duplicate_behavior=self.settings.on_duplicate_tools,
|
||||
serializer=tool_serializer,
|
||||
)
|
||||
self._resource_manager = ResourceManager(
|
||||
duplicate_behavior=self.settings.on_duplicate_resources
|
||||
)
|
||||
self._prompt_manager = PromptManager(
|
||||
duplicate_behavior=self.settings.on_duplicate_prompts
|
||||
)
|
||||
|
||||
if (self.settings.auth is not None) != (auth_server_provider is not None):
|
||||
# TODO: after we support separate authorization servers (see
|
||||
|
|
@ -150,15 +163,9 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
)
|
||||
self._auth_server_provider = auth_server_provider
|
||||
|
||||
self._additional_http_routes: list[BaseRoute] = []
|
||||
self.dependencies = self.settings.dependencies
|
||||
|
||||
# Set up MCP protocol handlers
|
||||
self._setup_handlers()
|
||||
|
||||
# Configure logging
|
||||
configure_logging(self.settings.log_level)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{type(self).__name__}({self.name!r})"
|
||||
|
||||
|
|
@ -764,15 +771,14 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
uvicorn_config: dict | None = None,
|
||||
) -> None:
|
||||
"""Run the server using SSE transport."""
|
||||
|
||||
# Deprecated since 2.3.2
|
||||
warnings.warn(
|
||||
inspect.cleandoc(
|
||||
"""
|
||||
The run_sse_async method is deprecated. Use run_http_async for a
|
||||
modern (non-SSE) alternative, or create an SSE app with
|
||||
`fastmcp.server.http.create_sse_app` and run it directly.
|
||||
"""
|
||||
),
|
||||
"The run_sse_async method is deprecated (as of 2.3.2). Use run_http_async for a "
|
||||
"modern (non-SSE) alternative, or create an SSE app with "
|
||||
"`fastmcp.server.http.create_sse_app` and run it directly.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
await self.run_http_async(
|
||||
transport="sse",
|
||||
|
|
@ -797,14 +803,12 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
message_path: The path to the message endpoint
|
||||
middleware: A list of middleware to apply to the app
|
||||
"""
|
||||
# Deprecated since 2.3.2
|
||||
warnings.warn(
|
||||
inspect.cleandoc(
|
||||
"""
|
||||
The sse_app method is deprecated. Use http_app as a modern (non-SSE)
|
||||
alternative, or call `fastmcp.server.http.create_sse_app` directly.
|
||||
"""
|
||||
),
|
||||
"The sse_app method is deprecated (as of 2.3.2). Use http_app as a modern (non-SSE) "
|
||||
"alternative, or call `fastmcp.server.http.create_sse_app` directly.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return create_sse_app(
|
||||
server=self,
|
||||
|
|
@ -829,9 +833,11 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
path: The path to the StreamableHTTP endpoint
|
||||
middleware: A list of middleware to apply to the app
|
||||
"""
|
||||
# Deprecated since 2.3.2
|
||||
warnings.warn(
|
||||
"The streamable_http_app method is deprecated. Use http_app() instead.",
|
||||
"The streamable_http_app method is deprecated (as of 2.3.2). Use http_app() instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self.http_app(path=path, middleware=middleware)
|
||||
|
||||
|
|
@ -886,9 +892,12 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
path: str | None = None,
|
||||
uvicorn_config: dict | None = None,
|
||||
) -> None:
|
||||
# Deprecated since 2.3.2
|
||||
warnings.warn(
|
||||
"The run_streamable_http_async method is deprecated. Use run_http_async instead.",
|
||||
"The run_streamable_http_async method is deprecated (as of 2.3.2). "
|
||||
"Use run_http_async instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
await self.run_http_async(
|
||||
transport="streamable-http",
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ from __future__ import annotations as _annotations
|
|||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from mcp.server.auth.settings import AuthSettings
|
||||
from pydantic import Field
|
||||
from pydantic import Field, model_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from typing_extensions import Self
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
|
@ -38,6 +39,15 @@ class Settings(BaseSettings):
|
|||
Defaults to False.""",
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def setup_logging(self) -> Self:
|
||||
"""Finalize the settings."""
|
||||
from fastmcp.utilities.logging import configure_logging
|
||||
|
||||
configure_logging(self.log_level)
|
||||
|
||||
return self
|
||||
|
||||
|
||||
class ServerSettings(BaseSettings):
|
||||
"""FastMCP server settings.
|
||||
|
|
|
|||
|
|
@ -21,22 +21,27 @@ def get_logger(name: str) -> logging.Logger:
|
|||
|
||||
def configure_logging(
|
||||
level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | int = "INFO",
|
||||
logger: logging.Logger | None = None,
|
||||
) -> None:
|
||||
"""Configure logging for FastMCP.
|
||||
"""
|
||||
Configure logging for FastMCP.
|
||||
|
||||
Args:
|
||||
logger: the logger to configure
|
||||
level: the log level to use
|
||||
"""
|
||||
if logger is None:
|
||||
logger = logging.getLogger("FastMCP")
|
||||
|
||||
# Only configure the FastMCP logger namespace
|
||||
handler = RichHandler(console=Console(stderr=True), rich_tracebacks=True)
|
||||
formatter = logging.Formatter("%(message)s")
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
fastmcp_logger = logging.getLogger("FastMCP")
|
||||
fastmcp_logger.setLevel(level)
|
||||
logger.setLevel(level)
|
||||
|
||||
# Remove any existing handlers to avoid duplicates on reconfiguration
|
||||
for hdlr in fastmcp_logger.handlers[:]:
|
||||
fastmcp_logger.removeHandler(hdlr)
|
||||
for hdlr in logger.handlers[:]:
|
||||
logger.removeHandler(hdlr)
|
||||
|
||||
fastmcp_logger.addHandler(handler)
|
||||
logger.addHandler(handler)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,17 @@ from starlette.applications import Starlette
|
|||
from fastmcp import FastMCP
|
||||
|
||||
|
||||
def test_fastmcp_kwargs_settings_deprecation_warning():
|
||||
"""Test that passing settings as kwargs to FastMCP raises a deprecation warning."""
|
||||
with pytest.warns(
|
||||
DeprecationWarning,
|
||||
match="Passing settings as kwargs to the FastMCP constructor is deprecated",
|
||||
):
|
||||
server = FastMCP("TestServer", host="127.0.0.2", port=8001)
|
||||
assert server.settings.host == "127.0.0.2"
|
||||
assert server.settings.port == 8001
|
||||
|
||||
|
||||
def test_sse_app_deprecation_warning():
|
||||
"""Test that sse_app raises a deprecation warning."""
|
||||
server = FastMCP("TestServer")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue