mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 23:29:10 +02:00
Compare commits
1 commit
main
...
fix-settin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
577d23d5c3 |
5 changed files with 84 additions and 27 deletions
|
|
@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any
|
|||
|
||||
from mcp import GetPromptResult
|
||||
|
||||
from fastmcp import settings
|
||||
import fastmcp
|
||||
from fastmcp.exceptions import NotFoundError, PromptError
|
||||
from fastmcp.prompts.prompt import FunctionPrompt, Prompt, PromptResult
|
||||
from fastmcp.settings import DuplicateBehavior
|
||||
|
|
@ -28,7 +28,9 @@ class PromptManager:
|
|||
):
|
||||
self._prompts: dict[str, Prompt] = {}
|
||||
self._mounted_servers: list[MountedServer] = []
|
||||
self.mask_error_details = mask_error_details or settings.mask_error_details
|
||||
self.mask_error_details = (
|
||||
mask_error_details or fastmcp.settings.mask_error_details
|
||||
)
|
||||
|
||||
# Default to "warn" if None is provided
|
||||
if duplicate_behavior is None:
|
||||
|
|
@ -80,7 +82,7 @@ class PromptManager:
|
|||
logger.warning(
|
||||
f"Failed to get prompts from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
|
||||
)
|
||||
if settings.mounted_components_raise_on_load_error:
|
||||
if fastmcp.settings.mounted_components_raise_on_load_error:
|
||||
raise
|
||||
continue
|
||||
|
||||
|
|
@ -122,7 +124,7 @@ class PromptManager:
|
|||
) -> FunctionPrompt:
|
||||
"""Create a prompt from a function."""
|
||||
# deprecated in 2.7.0
|
||||
if settings.deprecation_warnings:
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"PromptManager.add_prompt_from_fn() is deprecated. Use Prompt.from_function() and call add_prompt() instead.",
|
||||
DeprecationWarning,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any
|
|||
|
||||
from pydantic import AnyUrl
|
||||
|
||||
from fastmcp import settings
|
||||
import fastmcp
|
||||
from fastmcp.exceptions import NotFoundError, ResourceError
|
||||
from fastmcp.resources.resource import Resource
|
||||
from fastmcp.resources.template import (
|
||||
|
|
@ -44,7 +44,9 @@ class ResourceManager:
|
|||
self._resources: dict[str, Resource] = {}
|
||||
self._templates: dict[str, ResourceTemplate] = {}
|
||||
self._mounted_servers: list[MountedServer] = []
|
||||
self.mask_error_details = mask_error_details or settings.mask_error_details
|
||||
self.mask_error_details = (
|
||||
mask_error_details or fastmcp.settings.mask_error_details
|
||||
)
|
||||
|
||||
# Default to "warn" if None is provided
|
||||
if duplicate_behavior is None:
|
||||
|
|
@ -114,7 +116,7 @@ class ResourceManager:
|
|||
logger.warning(
|
||||
f"Failed to get resources from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
|
||||
)
|
||||
if settings.mounted_components_raise_on_load_error:
|
||||
if fastmcp.settings.mounted_components_raise_on_load_error:
|
||||
raise
|
||||
continue
|
||||
|
||||
|
|
@ -167,7 +169,7 @@ class ResourceManager:
|
|||
logger.warning(
|
||||
f"Failed to get templates from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
|
||||
)
|
||||
if settings.mounted_components_raise_on_load_error:
|
||||
if fastmcp.settings.mounted_components_raise_on_load_error:
|
||||
raise
|
||||
continue
|
||||
|
||||
|
|
@ -261,7 +263,7 @@ class ResourceManager:
|
|||
returns the existing resource.
|
||||
"""
|
||||
# deprecated in 2.7.0
|
||||
if settings.deprecation_warnings:
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"add_resource_from_fn is deprecated. Use Resource.from_function() and call add_resource() instead.",
|
||||
DeprecationWarning,
|
||||
|
|
@ -310,7 +312,7 @@ class ResourceManager:
|
|||
) -> ResourceTemplate:
|
||||
"""Create a template from a function."""
|
||||
# deprecated in 2.7.0
|
||||
if settings.deprecation_warnings:
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"add_template_from_fn is deprecated. Use ResourceTemplate.from_function() and call add_template() instead.",
|
||||
DeprecationWarning,
|
||||
|
|
|
|||
|
|
@ -86,31 +86,38 @@ class Settings(BaseSettings):
|
|||
validate_assignment=True,
|
||||
)
|
||||
|
||||
def _find_setting_level(self, attr: str) -> Any:
|
||||
"""
|
||||
Get the parent of a setting. If the setting contains one or more `__`, it will be
|
||||
treated as a nested setting.
|
||||
"""
|
||||
current_settings_level = self
|
||||
|
||||
while "__" in attr:
|
||||
parent_attr, attr = attr.split("__", 1)
|
||||
if not hasattr(current_settings_level, parent_attr):
|
||||
raise AttributeError(f"Setting {parent_attr} does not exist.")
|
||||
|
||||
# Go one level deeper into the nested settings
|
||||
current_settings_level = getattr(current_settings_level, parent_attr)
|
||||
|
||||
return current_settings_level
|
||||
|
||||
def get_setting(self, attr: str) -> Any:
|
||||
"""
|
||||
Get a setting. If the setting contains one or more `__`, it will be
|
||||
treated as a nested setting.
|
||||
"""
|
||||
settings = self
|
||||
while "__" in attr:
|
||||
parent_attr, attr = attr.split("__", 1)
|
||||
if not hasattr(settings, parent_attr):
|
||||
raise AttributeError(f"Setting {parent_attr} does not exist.")
|
||||
settings = getattr(settings, parent_attr)
|
||||
return getattr(settings, attr)
|
||||
settings_level = self._find_setting_level(attr)
|
||||
return getattr(settings_level, attr)
|
||||
|
||||
def set_setting(self, attr: str, value: Any) -> None:
|
||||
"""
|
||||
Set a setting. If the setting contains one or more `__`, it will be
|
||||
treated as a nested setting.
|
||||
"""
|
||||
settings = self
|
||||
while "__" in attr:
|
||||
parent_attr, attr = attr.split("__", 1)
|
||||
if not hasattr(settings, parent_attr):
|
||||
raise AttributeError(f"Setting {parent_attr} does not exist.")
|
||||
settings = getattr(settings, parent_attr)
|
||||
setattr(settings, attr, value)
|
||||
settings_level = self._find_setting_level(attr)
|
||||
setattr(settings_level, attr, value)
|
||||
|
||||
@classmethod
|
||||
def settings_customise_sources(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any
|
|||
|
||||
from mcp.types import ToolAnnotations
|
||||
|
||||
from fastmcp import settings
|
||||
import fastmcp
|
||||
from fastmcp.exceptions import NotFoundError, ToolError
|
||||
from fastmcp.settings import DuplicateBehavior
|
||||
from fastmcp.tools.tool import Tool, ToolResult
|
||||
|
|
@ -33,7 +33,9 @@ class ToolManager:
|
|||
):
|
||||
self._tools: dict[str, Tool] = {}
|
||||
self._mounted_servers: list[MountedServer] = []
|
||||
self.mask_error_details = mask_error_details or settings.mask_error_details
|
||||
self.mask_error_details = (
|
||||
mask_error_details or fastmcp.settings.mask_error_details
|
||||
)
|
||||
self.transformations = transformations or {}
|
||||
|
||||
# Default to "warn" if None is provided
|
||||
|
|
@ -86,7 +88,7 @@ class ToolManager:
|
|||
logger.warning(
|
||||
f"Failed to get tools from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
|
||||
)
|
||||
if settings.mounted_components_raise_on_load_error:
|
||||
if fastmcp.settings.mounted_components_raise_on_load_error:
|
||||
raise
|
||||
continue
|
||||
|
||||
|
|
@ -146,7 +148,7 @@ class ToolManager:
|
|||
) -> Tool:
|
||||
"""Add a tool to the server."""
|
||||
# deprecated in 2.7.0
|
||||
if settings.deprecation_warnings:
|
||||
if fastmcp.settings.deprecation_warnings:
|
||||
warnings.warn(
|
||||
"ToolManager.add_tool_from_fn() is deprecated. Use Tool.from_function() and call add_tool() instead.",
|
||||
DeprecationWarning,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
import os
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from textwrap import dedent
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.client import Client
|
||||
from fastmcp.client.transports import PythonStdioTransport
|
||||
from fastmcp.settings import Settings
|
||||
from fastmcp.utilities.tests import caplog_for_fastmcp
|
||||
|
||||
|
|
@ -12,6 +17,45 @@ from fastmcp.utilities.tests import caplog_for_fastmcp
|
|||
pytestmark = pytest.mark.filterwarnings("default::DeprecationWarning")
|
||||
|
||||
|
||||
class TestSettingsImport:
|
||||
"""Test settings import."""
|
||||
|
||||
async def test_settings_from_environment_issue_1749(self):
|
||||
"""Test that when auth is enabled, the server starts."""
|
||||
|
||||
script = dedent("""
|
||||
import os
|
||||
|
||||
os.environ["FASTMCP_SERVER_AUTH"] = "fastmcp.server.auth.providers.azure.AzureProvider"
|
||||
|
||||
os.environ["FASTMCP_SERVER_AUTH_AZURE_TENANT_ID"] = "A_Valid_Value"
|
||||
os.environ["FASTMCP_SERVER_AUTH_AZURE_CLIENT_ID"] = "A_Valid_Value"
|
||||
os.environ["FASTMCP_SERVER_AUTH_AZURE_CLIENT_SECRET"] = "A_Valid_Value"
|
||||
os.environ["FASTMCP_SERVER_AUTH_AZURE_REDIRECT_PATH"] = "/auth/callback"
|
||||
os.environ["FASTMCP_SERVER_AUTH_AZURE_BASE_URL"] = "http://localhost:8000"
|
||||
os.environ["FASTMCP_SERVER_AUTH_AZURE_REQUIRED_SCOPES"] = "User.Read,email,profile"
|
||||
|
||||
import fastmcp
|
||||
|
||||
mcp = fastmcp.FastMCP("TestServer")
|
||||
|
||||
mcp.run()
|
||||
""")
|
||||
|
||||
with TemporaryDirectory() as temp_dir:
|
||||
server_file = Path(temp_dir) / "server.py"
|
||||
server_file.write_text(script)
|
||||
|
||||
transport: PythonStdioTransport = PythonStdioTransport(
|
||||
script_path=server_file
|
||||
)
|
||||
|
||||
async with Client[PythonStdioTransport](transport=transport) as client:
|
||||
tools = await client.list_tools()
|
||||
|
||||
assert tools == []
|
||||
|
||||
|
||||
class TestDeprecatedServerInitKwargs:
|
||||
"""Test deprecated server initialization keyword arguments."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue