Add mounted_components_raise_on_load_error setting for debugging (#1534)

Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
This commit is contained in:
Jeremiah Lowin 2025-08-18 13:42:33 -04:00 committed by GitHub
commit 26969e80c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 62 additions and 1 deletions

View file

@ -80,6 +80,8 @@ 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:
raise
continue
# Finally, add local prompts, which always take precedence

View file

@ -114,6 +114,8 @@ 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:
raise
continue
# Finally, add local resources, which always take precedence
@ -165,6 +167,8 @@ 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:
raise
continue
# Finally, add local templates, which always take precedence

View file

@ -320,6 +320,20 @@ class Settings(BaseSettings):
),
] = True
mounted_components_raise_on_load_error: Annotated[
bool,
Field(
default=False,
description=inspect.cleandoc(
"""
If True, errors encountered when loading mounted components (tools, resources, prompts)
will be raised instead of logged as warnings. This is useful for debugging
but will interrupt normal operation.
"""
),
),
] = False
def __getattr__(name: str):
"""

View file

@ -86,6 +86,8 @@ 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:
raise
continue
# Finally, add local tools, which always take precedence

View file

@ -13,7 +13,7 @@ from fastmcp.exceptions import NotFoundError, ToolError
from fastmcp.tools import FunctionTool, ToolManager
from fastmcp.tools.tool import Tool
from fastmcp.tools.tool_transform import ArgTransformConfig, ToolTransformConfig
from fastmcp.utilities.tests import caplog_for_fastmcp
from fastmcp.utilities.tests import caplog_for_fastmcp, temporary_settings
from fastmcp.utilities.types import Image
@ -996,3 +996,42 @@ class TestToolErrorHandling:
# Exception message should contain the tool name but not the internal details
assert "Error calling tool 'async_buggy_tool'" in str(excinfo.value)
assert "Internal async error details" not in str(excinfo.value)
class TestMountedComponentsRaiseOnLoadError:
"""Test the mounted_components_raise_on_load_error setting."""
async def test_mounted_components_raise_on_load_error_default_false(self):
"""Test that by default, mounted component load errors are warned and not raised."""
import fastmcp
# Ensure default setting is False
assert fastmcp.settings.mounted_components_raise_on_load_error is False
parent_mcp = FastMCP("ParentServer")
child_mcp = FastMCP("FailingChildServer")
# Create a failing mounted server by corrupting it
parent_mcp.mount(child_mcp, prefix="child")
# Corrupt the child server to make it fail during tool loading
child_mcp._tool_manager._mounted_servers.append("invalid") # type: ignore
# Should not raise, just warn
tools = await parent_mcp._tool_manager.list_tools()
assert isinstance(tools, list) # Should return empty list, not raise
async def test_mounted_components_raise_on_load_error_true(self):
"""Test that when enabled, mounted component load errors are raised."""
parent_mcp = FastMCP("ParentServer")
child_mcp = FastMCP("FailingChildServer")
# Create a failing mounted server
parent_mcp.mount(child_mcp, prefix="child")
# Corrupt the child server to make it fail during tool loading
child_mcp._tool_manager._mounted_servers.append("invalid") # type: ignore
# Use temporary settings context manager
with temporary_settings(mounted_components_raise_on_load_error=True):
# Should raise the exception
with pytest.raises(AttributeError, match=""):
await parent_mcp._tool_manager.list_tools()