mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-27 07:50:43 +02:00
Remove open-ended and server-specific settings
This commit is contained in:
parent
94a931f2f3
commit
aaa09ca7a3
16 changed files with 507 additions and 119 deletions
|
|
@ -1,6 +1,9 @@
|
|||
"""FastMCP - An ergonomic MCP interface."""
|
||||
|
||||
from importlib.metadata import version
|
||||
from fastmcp.settings import Settings
|
||||
|
||||
settings = Settings()
|
||||
|
||||
from fastmcp.server.server import FastMCP
|
||||
from fastmcp.server.context import Context
|
||||
|
|
@ -8,7 +11,7 @@ import fastmcp.server
|
|||
|
||||
from fastmcp.client import Client
|
||||
from fastmcp.utilities.types import Image
|
||||
from . import client, settings
|
||||
from . import client
|
||||
|
||||
__version__ = version("fastmcp")
|
||||
__all__ = [
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from typer import Context, Exit
|
|||
import fastmcp
|
||||
from fastmcp.cli import claude
|
||||
from fastmcp.cli import run as run_module
|
||||
from fastmcp.server.server import FastMCP
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
logger = get_logger("cli")
|
||||
|
|
@ -165,8 +166,8 @@ def dev(
|
|||
|
||||
try:
|
||||
# Import server to get dependencies
|
||||
server = run_module.import_server(file, server_object)
|
||||
if hasattr(server, "dependencies") and server.dependencies is not None:
|
||||
server: FastMCP = run_module.import_server(file, server_object)
|
||||
if server.dependencies is not None:
|
||||
with_packages = list(set(with_packages + server.dependencies))
|
||||
|
||||
env_vars = {}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ from mcp.shared.auth import (
|
|||
)
|
||||
from pydantic import AnyHttpUrl, ValidationError
|
||||
|
||||
from fastmcp import settings as fastmcp_global_settings
|
||||
from fastmcp.client.oauth_callback import (
|
||||
create_oauth_callback_server,
|
||||
)
|
||||
from fastmcp.settings import settings as fastmcp_global_settings
|
||||
from fastmcp.utilities.http import find_available_port
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ class Client(Generic[ClientTransportT]):
|
|||
|
||||
# handle init handshake timeout
|
||||
if init_timeout is None:
|
||||
init_timeout = fastmcp.settings.settings.client_init_timeout
|
||||
init_timeout = fastmcp.settings.client_init_timeout
|
||||
if isinstance(init_timeout, datetime.timedelta):
|
||||
init_timeout = init_timeout.total_seconds()
|
||||
elif not init_timeout:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any
|
|||
|
||||
from mcp import GetPromptResult
|
||||
|
||||
from fastmcp import settings
|
||||
from fastmcp.exceptions import NotFoundError, PromptError
|
||||
from fastmcp.prompts.prompt import FunctionPrompt, Prompt, PromptResult
|
||||
from fastmcp.settings import DuplicateBehavior
|
||||
|
|
@ -23,10 +24,10 @@ class PromptManager:
|
|||
def __init__(
|
||||
self,
|
||||
duplicate_behavior: DuplicateBehavior | None = None,
|
||||
mask_error_details: bool = False,
|
||||
mask_error_details: bool | None = None,
|
||||
):
|
||||
self._prompts: dict[str, Prompt] = {}
|
||||
self.mask_error_details = mask_error_details
|
||||
self.mask_error_details = mask_error_details or settings.mask_error_details
|
||||
|
||||
# Default to "warn" if None is provided
|
||||
if duplicate_behavior is None:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from typing import Any
|
|||
|
||||
from pydantic import AnyUrl
|
||||
|
||||
from fastmcp import settings
|
||||
from fastmcp.exceptions import NotFoundError, ResourceError
|
||||
from fastmcp.resources.resource import Resource
|
||||
from fastmcp.resources.template import (
|
||||
|
|
@ -25,7 +26,7 @@ class ResourceManager:
|
|||
def __init__(
|
||||
self,
|
||||
duplicate_behavior: DuplicateBehavior | None = None,
|
||||
mask_error_details: bool = False,
|
||||
mask_error_details: bool | None = None,
|
||||
):
|
||||
"""Initialize the ResourceManager.
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ class ResourceManager:
|
|||
"""
|
||||
self._resources: dict[str, Resource] = {}
|
||||
self._templates: dict[str, ResourceTemplate] = {}
|
||||
self.mask_error_details = mask_error_details
|
||||
self.mask_error_details = mask_error_details or settings.mask_error_details
|
||||
|
||||
# Default to "warn" if None is provided
|
||||
if duplicate_behavior is None:
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ from starlette.routing import BaseRoute, Route
|
|||
|
||||
import fastmcp
|
||||
import fastmcp.server
|
||||
import fastmcp.settings
|
||||
from fastmcp.exceptions import NotFoundError
|
||||
from fastmcp.prompts import Prompt, PromptManager
|
||||
from fastmcp.prompts.prompt import FunctionPrompt
|
||||
|
|
@ -56,6 +55,7 @@ from fastmcp.server.http import (
|
|||
create_sse_app,
|
||||
create_streamable_http_app,
|
||||
)
|
||||
from fastmcp.settings import Settings
|
||||
from fastmcp.tools import ToolManager
|
||||
from fastmcp.tools.tool import FunctionTool, Tool
|
||||
from fastmcp.utilities.cache import TimedCache
|
||||
|
|
@ -121,7 +121,6 @@ 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,
|
||||
|
|
@ -130,44 +129,44 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
resource_prefix_format: Literal["protocol", "path"] | None = None,
|
||||
mask_error_details: bool | None = None,
|
||||
tools: list[Tool | Callable[..., Any]] | None = None,
|
||||
**settings: Any,
|
||||
dependencies: list[str] | None = None,
|
||||
# ---
|
||||
# ---
|
||||
# --- The following arguments are DEPRECATED ---
|
||||
# ---
|
||||
# ---
|
||||
log_level: str | None = None,
|
||||
debug: bool | None = None,
|
||||
host: str | None = None,
|
||||
port: int | None = None,
|
||||
sse_path: str | None = None,
|
||||
message_path: str | None = None,
|
||||
streamable_http_path: str | None = None,
|
||||
json_response: bool | None = None,
|
||||
stateless_http: bool | None = None,
|
||||
):
|
||||
if cache_expiration_seconds is not None:
|
||||
settings["cache_expiration_seconds"] = cache_expiration_seconds
|
||||
self.settings = fastmcp.settings.ServerSettings(**settings)
|
||||
|
||||
# If mask_error_details is provided, override the settings value
|
||||
if mask_error_details is not None:
|
||||
self.settings.mask_error_details = mask_error_details
|
||||
|
||||
self.resource_prefix_format: Literal["protocol", "path"]
|
||||
if resource_prefix_format is None:
|
||||
self.resource_prefix_format = (
|
||||
fastmcp.settings.settings.resource_prefix_format
|
||||
)
|
||||
else:
|
||||
self.resource_prefix_format = resource_prefix_format
|
||||
self.resource_prefix_format: Literal["protocol", "path"] = (
|
||||
resource_prefix_format or fastmcp.settings.resource_prefix_format
|
||||
)
|
||||
|
||||
self.tags: set[str] = tags or set()
|
||||
self.dependencies = dependencies
|
||||
|
||||
self._cache = TimedCache(
|
||||
expiration=datetime.timedelta(
|
||||
seconds=self.settings.cache_expiration_seconds
|
||||
)
|
||||
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,
|
||||
mask_error_details=self.settings.mask_error_details,
|
||||
mask_error_details=mask_error_details,
|
||||
)
|
||||
self._resource_manager = ResourceManager(
|
||||
duplicate_behavior=on_duplicate_resources,
|
||||
mask_error_details=self.settings.mask_error_details,
|
||||
mask_error_details=mask_error_details,
|
||||
)
|
||||
self._prompt_manager = PromptManager(
|
||||
duplicate_behavior=on_duplicate_prompts,
|
||||
mask_error_details=self.settings.mask_error_details,
|
||||
mask_error_details=mask_error_details,
|
||||
)
|
||||
self._tool_serializer = tool_serializer
|
||||
|
||||
|
|
@ -182,7 +181,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
lifespan=_lifespan_wrapper(self, lifespan),
|
||||
)
|
||||
|
||||
if auth is None and self.settings.default_auth_provider == "bearer_env":
|
||||
if auth is None and fastmcp.settings.default_auth_provider == "bearer_env":
|
||||
auth = EnvBearerAuthProvider()
|
||||
self.auth = auth
|
||||
|
||||
|
|
@ -194,10 +193,62 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
|
||||
# Set up MCP protocol handlers
|
||||
self._setup_handlers()
|
||||
self.dependencies = dependencies or fastmcp.settings.server_dependencies
|
||||
|
||||
# handle deprecated settings
|
||||
self._handle_deprecated_settings(
|
||||
log_level=log_level,
|
||||
debug=debug,
|
||||
host=host,
|
||||
port=port,
|
||||
sse_path=sse_path,
|
||||
message_path=message_path,
|
||||
streamable_http_path=streamable_http_path,
|
||||
json_response=json_response,
|
||||
stateless_http=stateless_http,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{type(self).__name__}({self.name!r})"
|
||||
|
||||
def _handle_deprecated_settings(
|
||||
self,
|
||||
log_level: str | None,
|
||||
debug: bool | None,
|
||||
host: str | None,
|
||||
port: int | None,
|
||||
sse_path: str | None,
|
||||
message_path: str | None,
|
||||
streamable_http_path: str | None,
|
||||
json_response: bool | None,
|
||||
stateless_http: bool | None,
|
||||
) -> None:
|
||||
"""Handle deprecated settings. Deprecated in 2.8.0."""
|
||||
deprecated_settings: dict[str, Any] = {}
|
||||
|
||||
for name, arg in [
|
||||
("log_level", log_level),
|
||||
("debug", debug),
|
||||
("host", host),
|
||||
("port", port),
|
||||
("sse_path", sse_path),
|
||||
("message_path", message_path),
|
||||
("streamable_http_path", streamable_http_path),
|
||||
("json_response", json_response),
|
||||
("stateless_http", stateless_http),
|
||||
]:
|
||||
if arg is not None:
|
||||
# Deprecated in 2.8.0
|
||||
warnings.warn(
|
||||
f"Providing `{name}` when creating a server is deprecated. Provide it when calling `run` or as a global setting instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
deprecated_settings[name] = arg
|
||||
|
||||
combined_settings = fastmcp.settings.model_dump() | deprecated_settings
|
||||
self._deprecated_settings = Settings(**combined_settings)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._mcp_server.name
|
||||
|
|
@ -1008,9 +1059,11 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
path: Path for the endpoint (defaults to settings.streamable_http_path or settings.sse_path)
|
||||
uvicorn_config: Additional configuration for the Uvicorn server
|
||||
"""
|
||||
host = host or self.settings.host
|
||||
port = port or self.settings.port
|
||||
default_log_level_to_use = (log_level or self.settings.log_level).lower()
|
||||
host = host or self._deprecated_settings.host
|
||||
port = port or self._deprecated_settings.port
|
||||
default_log_level_to_use = (
|
||||
log_level or self._deprecated_settings.log_level
|
||||
).lower()
|
||||
|
||||
app = self.http_app(path=path, transport=transport, middleware=middleware)
|
||||
|
||||
|
|
@ -1084,10 +1137,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
)
|
||||
return create_sse_app(
|
||||
server=self,
|
||||
message_path=message_path or self.settings.message_path,
|
||||
sse_path=path or self.settings.sse_path,
|
||||
message_path=message_path or self._deprecated_settings.message_path,
|
||||
sse_path=path or self._deprecated_settings.sse_path,
|
||||
auth=self.auth,
|
||||
debug=self.settings.debug,
|
||||
debug=self._deprecated_settings.debug,
|
||||
middleware=middleware,
|
||||
)
|
||||
|
||||
|
|
@ -1115,6 +1168,8 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
self,
|
||||
path: str | None = None,
|
||||
middleware: list[Middleware] | None = None,
|
||||
json_response: bool | None = None,
|
||||
stateless_http: bool | None = None,
|
||||
transport: Literal["streamable-http", "sse"] = "streamable-http",
|
||||
) -> StarletteWithLifespan:
|
||||
"""Create a Starlette app using the specified HTTP transport.
|
||||
|
|
@ -1131,21 +1186,22 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
if transport == "streamable-http":
|
||||
return create_streamable_http_app(
|
||||
server=self,
|
||||
streamable_http_path=path or self.settings.streamable_http_path,
|
||||
streamable_http_path=path
|
||||
or self._deprecated_settings.streamable_http_path,
|
||||
event_store=None,
|
||||
auth=self.auth,
|
||||
json_response=self.settings.json_response,
|
||||
stateless_http=self.settings.stateless_http,
|
||||
debug=self.settings.debug,
|
||||
json_response=self._deprecated_settings.json_response,
|
||||
stateless_http=self._deprecated_settings.stateless_http,
|
||||
debug=self._deprecated_settings.debug,
|
||||
middleware=middleware,
|
||||
)
|
||||
elif transport == "sse":
|
||||
return create_sse_app(
|
||||
server=self,
|
||||
message_path=self.settings.message_path,
|
||||
sse_path=path or self.settings.sse_path,
|
||||
message_path=self._deprecated_settings.message_path,
|
||||
sse_path=path or self._deprecated_settings.sse_path,
|
||||
auth=self.auth,
|
||||
debug=self.settings.debug,
|
||||
debug=self._deprecated_settings.debug,
|
||||
middleware=middleware,
|
||||
)
|
||||
|
||||
|
|
@ -1597,7 +1653,7 @@ def add_resource_prefix(
|
|||
# Get the server settings to check for legacy format preference
|
||||
|
||||
if prefix_format is None:
|
||||
prefix_format = fastmcp.settings.settings.resource_prefix_format
|
||||
prefix_format = fastmcp.settings.resource_prefix_format
|
||||
|
||||
if prefix_format == "protocol":
|
||||
# Legacy style: prefix+protocol://path
|
||||
|
|
@ -1646,7 +1702,7 @@ def remove_resource_prefix(
|
|||
return uri
|
||||
|
||||
if prefix_format is None:
|
||||
prefix_format = fastmcp.settings.settings.resource_prefix_format
|
||||
prefix_format = fastmcp.settings.resource_prefix_format
|
||||
|
||||
if prefix_format == "protocol":
|
||||
# Legacy style: prefix+protocol://path
|
||||
|
|
@ -1706,7 +1762,7 @@ def has_resource_prefix(
|
|||
# Get the server settings to check for legacy format preference
|
||||
|
||||
if prefix_format is None:
|
||||
prefix_format = fastmcp.settings.settings.resource_prefix_format
|
||||
prefix_format = fastmcp.settings.resource_prefix_format
|
||||
|
||||
if prefix_format == "protocol":
|
||||
# Legacy style: prefix+protocol://path
|
||||
|
|
|
|||
|
|
@ -1,16 +1,47 @@
|
|||
from __future__ import annotations as _annotations
|
||||
|
||||
import inspect
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Literal
|
||||
from typing import Annotated, Any, Literal
|
||||
|
||||
from pydantic import Field, model_validator
|
||||
from pydantic.fields import FieldInfo
|
||||
from pydantic_settings import (
|
||||
BaseSettings,
|
||||
EnvSettingsSource,
|
||||
PydanticBaseSettingsSource,
|
||||
SettingsConfigDict,
|
||||
)
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
class ExtendedEnvSettingsSource(EnvSettingsSource):
|
||||
def get_field_value(
|
||||
self, field: FieldInfo, field_name: str
|
||||
) -> tuple[Any, str, bool]:
|
||||
if prefixes := self.config.get("env_prefixes"):
|
||||
for prefix in prefixes:
|
||||
self.env_prefix = prefix
|
||||
env_val, field_key, value_is_complex = super().get_field_value(
|
||||
field, field_name
|
||||
)
|
||||
if env_val is not None:
|
||||
if prefix == "FASTMCP_SERVER_":
|
||||
warnings.warn(
|
||||
"Using `FASTMCP_SERVER_` environment variables is deprecated. Use `FASTMCP_` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return env_val, field_key, value_is_complex
|
||||
|
||||
return super().get_field_value(field, field_name)
|
||||
|
||||
|
||||
class ExtendedSettingsConfigDict(SettingsConfigDict, total=False):
|
||||
env_prefixes: list[str] | None
|
||||
|
||||
|
||||
LOG_LEVEL = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
|
||||
DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
|
||||
|
|
@ -19,14 +50,30 @@ DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
|
|||
class Settings(BaseSettings):
|
||||
"""FastMCP settings."""
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_prefix="FASTMCP_",
|
||||
model_config = ExtendedSettingsConfigDict(
|
||||
env_prefixes=["FASTMCP_", "FASTMCP_SERVER_"],
|
||||
env_file=".env",
|
||||
extra="ignore",
|
||||
env_nested_delimiter="__",
|
||||
nested_model_default_partial_update=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def settings_customise_sources(
|
||||
cls,
|
||||
settings_cls: type[BaseSettings],
|
||||
init_settings: PydanticBaseSettingsSource,
|
||||
env_settings: PydanticBaseSettingsSource,
|
||||
dotenv_settings: PydanticBaseSettingsSource,
|
||||
file_secret_settings: PydanticBaseSettingsSource,
|
||||
) -> tuple[PydanticBaseSettingsSource, ...]:
|
||||
return (
|
||||
init_settings,
|
||||
ExtendedEnvSettingsSource(settings_cls),
|
||||
dotenv_settings,
|
||||
file_secret_settings,
|
||||
)
|
||||
|
||||
home: Path = Path.home() / ".fastmcp"
|
||||
|
||||
test_mode: bool = False
|
||||
|
|
@ -107,27 +154,6 @@ class Settings(BaseSettings):
|
|||
|
||||
return self
|
||||
|
||||
|
||||
class ServerSettings(BaseSettings):
|
||||
"""FastMCP server settings.
|
||||
|
||||
All settings can be configured via environment variables with the prefix FASTMCP_.
|
||||
For example, FASTMCP_DEBUG=true will set debug=True.
|
||||
"""
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_prefix="FASTMCP_SERVER_",
|
||||
env_file=".env",
|
||||
extra="ignore",
|
||||
env_nested_delimiter="__",
|
||||
nested_model_default_partial_update=True,
|
||||
)
|
||||
|
||||
log_level: Annotated[
|
||||
LOG_LEVEL,
|
||||
Field(default_factory=lambda: Settings().log_level),
|
||||
]
|
||||
|
||||
# HTTP settings
|
||||
host: str = "127.0.0.1"
|
||||
port: int = 8000
|
||||
|
|
@ -136,15 +162,6 @@ class ServerSettings(BaseSettings):
|
|||
streamable_http_path: str = "/mcp"
|
||||
debug: bool = False
|
||||
|
||||
# resource settings
|
||||
on_duplicate_resources: DuplicateBehavior = "warn"
|
||||
|
||||
# tool settings
|
||||
on_duplicate_tools: DuplicateBehavior = "warn"
|
||||
|
||||
# prompt settings
|
||||
on_duplicate_prompts: DuplicateBehavior = "warn"
|
||||
|
||||
# error handling
|
||||
mask_error_details: Annotated[
|
||||
bool,
|
||||
|
|
@ -162,7 +179,7 @@ class ServerSettings(BaseSettings):
|
|||
),
|
||||
] = False
|
||||
|
||||
dependencies: Annotated[
|
||||
server_dependencies: Annotated[
|
||||
list[str],
|
||||
Field(
|
||||
default_factory=list,
|
||||
|
|
@ -170,9 +187,6 @@ class ServerSettings(BaseSettings):
|
|||
),
|
||||
] = []
|
||||
|
||||
# cache settings (for getting attributes from servers, used to avoid repeated calls)
|
||||
cache_expiration_seconds: float = 0
|
||||
|
||||
# StreamableHTTP settings
|
||||
json_response: bool = False
|
||||
stateless_http: bool = (
|
||||
|
|
@ -197,6 +211,3 @@ class ServerSettings(BaseSettings):
|
|||
),
|
||||
),
|
||||
] = None
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ class FunctionTool(Tool):
|
|||
if context_kwarg and context_kwarg not in arguments:
|
||||
arguments[context_kwarg] = get_context()
|
||||
|
||||
if fastmcp.settings.settings.tool_attempt_parse_json_args:
|
||||
if fastmcp.settings.tool_attempt_parse_json_args:
|
||||
# Pre-parse data from JSON in order to handle cases like `["a", "b", "c"]`
|
||||
# being passed in as JSON inside a string rather than an actual list.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any
|
|||
|
||||
from mcp.types import EmbeddedResource, ImageContent, TextContent, ToolAnnotations
|
||||
|
||||
from fastmcp import settings
|
||||
from fastmcp.exceptions import NotFoundError, ToolError
|
||||
from fastmcp.settings import DuplicateBehavior
|
||||
from fastmcp.tools.tool import Tool
|
||||
|
|
@ -23,10 +24,10 @@ class ToolManager:
|
|||
def __init__(
|
||||
self,
|
||||
duplicate_behavior: DuplicateBehavior | None = None,
|
||||
mask_error_details: bool = False,
|
||||
mask_error_details: bool | None = None,
|
||||
):
|
||||
self._tools: dict[str, Tool] = {}
|
||||
self.mask_error_details = mask_error_details
|
||||
self.mask_error_details = mask_error_details or settings.mask_error_details
|
||||
|
||||
# Default to "warn" if None is provided
|
||||
if duplicate_behavior is None:
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ def get_catch_handlers() -> Mapping[
|
|||
type[BaseException] | Iterable[type[BaseException]],
|
||||
Callable[[BaseExceptionGroup[Any]], Any],
|
||||
]:
|
||||
if fastmcp.settings.settings.client_raise_first_exceptiongroup_error:
|
||||
if fastmcp.settings.client_raise_first_exceptiongroup_error:
|
||||
return _catch_handlers
|
||||
else:
|
||||
return {}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Any, Literal
|
|||
|
||||
import uvicorn
|
||||
|
||||
from fastmcp.settings import settings
|
||||
from fastmcp import settings
|
||||
from fastmcp.utilities.http import find_available_port
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -32,8 +32,8 @@ def temporary_settings(**kwargs: Any):
|
|||
from fastmcp.utilities.tests import temporary_settings
|
||||
|
||||
with temporary_settings(log_level='DEBUG'):
|
||||
assert fastmcp.settings.settings.log_level == 'DEBUG'
|
||||
assert fastmcp.settings.settings.log_level == 'INFO'
|
||||
assert fastmcp.settings.log_level == 'DEBUG'
|
||||
assert fastmcp.settings.log_level == 'INFO'
|
||||
```
|
||||
"""
|
||||
old_settings = copy.deepcopy(settings.model_dump())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue