Jeremiah Lowin Jeremiah Lowin marvin-context-protocol[bot] commited on
Commit
28c234f
·
unverified ·
1 Parent(s): f5238eb

Add mounted_components_raise_on_load_error setting for debugging (#1534)

Browse files

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>

src/fastmcp/prompts/prompt_manager.py CHANGED
@@ -80,6 +80,8 @@ class PromptManager:
80
  logger.warning(
81
  f"Failed to get prompts from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
82
  )
 
 
83
  continue
84
 
85
  # Finally, add local prompts, which always take precedence
 
80
  logger.warning(
81
  f"Failed to get prompts from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
82
  )
83
+ if settings.mounted_components_raise_on_load_error:
84
+ raise
85
  continue
86
 
87
  # Finally, add local prompts, which always take precedence
src/fastmcp/resources/resource_manager.py CHANGED
@@ -114,6 +114,8 @@ class ResourceManager:
114
  logger.warning(
115
  f"Failed to get resources from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
116
  )
 
 
117
  continue
118
 
119
  # Finally, add local resources, which always take precedence
@@ -165,6 +167,8 @@ class ResourceManager:
165
  logger.warning(
166
  f"Failed to get templates from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
167
  )
 
 
168
  continue
169
 
170
  # Finally, add local templates, which always take precedence
 
114
  logger.warning(
115
  f"Failed to get resources from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
116
  )
117
+ if settings.mounted_components_raise_on_load_error:
118
+ raise
119
  continue
120
 
121
  # Finally, add local resources, which always take precedence
 
167
  logger.warning(
168
  f"Failed to get templates from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
169
  )
170
+ if settings.mounted_components_raise_on_load_error:
171
+ raise
172
  continue
173
 
174
  # Finally, add local templates, which always take precedence
src/fastmcp/settings.py CHANGED
@@ -320,6 +320,20 @@ class Settings(BaseSettings):
320
  ),
321
  ] = True
322
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
  def __getattr__(name: str):
325
  """
 
320
  ),
321
  ] = True
322
 
323
+ mounted_components_raise_on_load_error: Annotated[
324
+ bool,
325
+ Field(
326
+ default=False,
327
+ description=inspect.cleandoc(
328
+ """
329
+ If True, errors encountered when loading mounted components (tools, resources, prompts)
330
+ will be raised instead of logged as warnings. This is useful for debugging
331
+ but will interrupt normal operation.
332
+ """
333
+ ),
334
+ ),
335
+ ] = False
336
+
337
 
338
  def __getattr__(name: str):
339
  """
src/fastmcp/tools/tool_manager.py CHANGED
@@ -86,6 +86,8 @@ class ToolManager:
86
  logger.warning(
87
  f"Failed to get tools from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
88
  )
 
 
89
  continue
90
 
91
  # Finally, add local tools, which always take precedence
 
86
  logger.warning(
87
  f"Failed to get tools from server: {mounted.server.name!r}, mounted at: {mounted.prefix!r}: {e}"
88
  )
89
+ if settings.mounted_components_raise_on_load_error:
90
+ raise
91
  continue
92
 
93
  # Finally, add local tools, which always take precedence
tests/tools/test_tool_manager.py CHANGED
@@ -13,7 +13,7 @@ from fastmcp.exceptions import NotFoundError, ToolError
13
  from fastmcp.tools import FunctionTool, ToolManager
14
  from fastmcp.tools.tool import Tool
15
  from fastmcp.tools.tool_transform import ArgTransformConfig, ToolTransformConfig
16
- from fastmcp.utilities.tests import caplog_for_fastmcp
17
  from fastmcp.utilities.types import Image
18
 
19
 
@@ -996,3 +996,42 @@ class TestToolErrorHandling:
996
  # Exception message should contain the tool name but not the internal details
997
  assert "Error calling tool 'async_buggy_tool'" in str(excinfo.value)
998
  assert "Internal async error details" not in str(excinfo.value)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  from fastmcp.tools import FunctionTool, ToolManager
14
  from fastmcp.tools.tool import Tool
15
  from fastmcp.tools.tool_transform import ArgTransformConfig, ToolTransformConfig
16
+ from fastmcp.utilities.tests import caplog_for_fastmcp, temporary_settings
17
  from fastmcp.utilities.types import Image
18
 
19
 
 
996
  # Exception message should contain the tool name but not the internal details
997
  assert "Error calling tool 'async_buggy_tool'" in str(excinfo.value)
998
  assert "Internal async error details" not in str(excinfo.value)
999
+
1000
+
1001
+ class TestMountedComponentsRaiseOnLoadError:
1002
+ """Test the mounted_components_raise_on_load_error setting."""
1003
+
1004
+ async def test_mounted_components_raise_on_load_error_default_false(self):
1005
+ """Test that by default, mounted component load errors are warned and not raised."""
1006
+ import fastmcp
1007
+
1008
+ # Ensure default setting is False
1009
+ assert fastmcp.settings.mounted_components_raise_on_load_error is False
1010
+
1011
+ parent_mcp = FastMCP("ParentServer")
1012
+ child_mcp = FastMCP("FailingChildServer")
1013
+
1014
+ # Create a failing mounted server by corrupting it
1015
+ parent_mcp.mount(child_mcp, prefix="child")
1016
+ # Corrupt the child server to make it fail during tool loading
1017
+ child_mcp._tool_manager._mounted_servers.append("invalid") # type: ignore
1018
+
1019
+ # Should not raise, just warn
1020
+ tools = await parent_mcp._tool_manager.list_tools()
1021
+ assert isinstance(tools, list) # Should return empty list, not raise
1022
+
1023
+ async def test_mounted_components_raise_on_load_error_true(self):
1024
+ """Test that when enabled, mounted component load errors are raised."""
1025
+ parent_mcp = FastMCP("ParentServer")
1026
+ child_mcp = FastMCP("FailingChildServer")
1027
+
1028
+ # Create a failing mounted server
1029
+ parent_mcp.mount(child_mcp, prefix="child")
1030
+ # Corrupt the child server to make it fail during tool loading
1031
+ child_mcp._tool_manager._mounted_servers.append("invalid") # type: ignore
1032
+
1033
+ # Use temporary settings context manager
1034
+ with temporary_settings(mounted_components_raise_on_load_error=True):
1035
+ # Should raise the exception
1036
+ with pytest.raises(AttributeError, match=""):
1037
+ await parent_mcp._tool_manager.list_tools()