From 7224295f935f363255d76eabbf8d78004e223939 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Tue, 10 Jun 2025 19:32:31 -0400 Subject: [PATCH] Incorporate `enabled` property --- src/fastmcp/server/server.py | 6 ++-- tests/server/test_server.py | 58 ++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py index eeb41d05c..bf2a29d18 100644 --- a/src/fastmcp/server/server.py +++ b/src/fastmcp/server/server.py @@ -555,7 +555,7 @@ class FastMCP(Generic[LifespanResultT]): # Get tool, checking first from our tools, then from the mounted servers if self._tool_manager.has_tool(key): tool = self._tool_manager.get_tool(key) - if not tool.enabled: + if not self._should_enable_component(tool): raise DisabledError(f"Tool {key!r} is disabled") return await self._tool_manager.call_tool(key, arguments) @@ -592,7 +592,7 @@ class FastMCP(Generic[LifespanResultT]): """ if self._resource_manager.has_resource(uri): resource = await self._resource_manager.get_resource(uri) - if not resource.enabled: + if not self._should_enable_component(resource): raise DisabledError(f"Resource {str(uri)!r} is disabled") content = await self._resource_manager.read_resource(uri) return [ @@ -646,7 +646,7 @@ class FastMCP(Generic[LifespanResultT]): # Get prompt, checking first from our prompts, then from the mounted servers if self._prompt_manager.has_prompt(name): prompt = self._prompt_manager.get_prompt(name) - if not prompt.enabled: + if not self._should_enable_component(prompt): raise DisabledError(f"Prompt {name!r} is disabled") return await self._prompt_manager.render_prompt(name, arguments) diff --git a/tests/server/test_server.py b/tests/server/test_server.py index 34f3cd1a7..7a0ee3e4e 100644 --- a/tests/server/test_server.py +++ b/tests/server/test_server.py @@ -1242,7 +1242,7 @@ class TestShouldIncludeComponent: """Test that when no include or exclude filters are provided, always returns True.""" tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={}) mcp = FastMCP(tools=[tool]) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is True def test_exclude_string_tag_present_returns_false(self): @@ -1251,28 +1251,28 @@ class TestShouldIncludeComponent: name="test_tool", tags={"tag1", "tag2", "exclude_me"}, parameters={} ) mcp = FastMCP(tools=[tool], exclude_tags={"exclude_me"}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is False def test_exclude_string_tag_absent_returns_true(self): """Test that when an exclude string tag is not present in tags, returns True.""" tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={}) mcp = FastMCP(tools=[tool], exclude_tags={"exclude_me"}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is True def test_exclude_tuple_all_present_returns_false(self): """Test that when all tags in exclude tuple are present, returns False.""" tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={}) mcp = FastMCP(tools=[tool], exclude_tags={("tag1", "tag2")}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is False def test_exclude_tuple_partial_present_returns_true(self): """Test that when only some tags in exclude tuple are present, returns True.""" tool = Tool(name="test_tool", tags={"tag1", "tag3"}, parameters={}) mcp = FastMCP(tools=[tool], exclude_tags={("tag1", "tag2")}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is True def test_multiple_exclude_tags_any_match_returns_false(self): @@ -1281,7 +1281,7 @@ class TestShouldIncludeComponent: mcp = FastMCP( tools=[tool], exclude_tags={"not_present", "tag2", "also_not_present"} ) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is False def test_include_string_tag_present_returns_true(self): @@ -1290,28 +1290,28 @@ class TestShouldIncludeComponent: name="test_tool", tags={"tag1", "include_me", "tag2"}, parameters={} ) mcp = FastMCP(tools=[tool], include_tags={"include_me"}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is True def test_include_string_tag_absent_returns_false(self): """Test that when an include string tag is not present in tags, returns False.""" tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={}) mcp = FastMCP(tools=[tool], include_tags={"include_me"}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is False def test_include_tuple_all_present_returns_true(self): """Test that when all tags in include tuple are present, returns True.""" tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={}) mcp = FastMCP(tools=[tool], include_tags={("tag1", "tag2")}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is True def test_include_tuple_partial_present_returns_false(self): """Test that when only some tags in include tuple are present, returns False.""" tool = Tool(name="test_tool", tags={"tag1", "tag3"}, parameters={}) mcp = FastMCP(tools=[tool], include_tags={("tag1", "tag2")}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is False def test_multiple_include_tags_any_match_returns_true(self): @@ -1320,14 +1320,14 @@ class TestShouldIncludeComponent: mcp = FastMCP( tools=[tool], include_tags={"not_present", "tag2", "also_not_present"} ) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is True def test_multiple_include_tags_none_match_returns_false(self): """Test that when no include tags match, returns False.""" tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={}) mcp = FastMCP(tools=[tool], include_tags={"not_present", "also_not_present"}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is False def test_exclude_takes_precedence_over_include(self): @@ -1336,7 +1336,7 @@ class TestShouldIncludeComponent: name="test_tool", tags={"tag1", "tag2", "exclude_me"}, parameters={} ) mcp = FastMCP(tools=[tool], include_tags={"tag1"}, exclude_tags={"exclude_me"}) - result = mcp.should_include_component(tool) + result = mcp._should_enable_component(tool) assert result is False def test_mixed_string_and_tuple_exclude_tags(self): @@ -1346,13 +1346,13 @@ class TestShouldIncludeComponent: name="test_tool", tags={"tag1", "tag2", "tag3", "tag4"}, parameters={} ) mcp1 = FastMCP(tools=[tool1], exclude_tags={"tag1", ("tag2", "tag3")}) - result = mcp1.should_include_component(tool1) + result = mcp1._should_enable_component(tool1) assert result is False # Remove tag1, should still be excluded because both tag2 and tag3 are present tool2 = Tool(name="test_tool", tags={"tag2", "tag3", "tag4"}, parameters={}) mcp2 = FastMCP(tools=[tool2], exclude_tags={"tag1", ("tag2", "tag3")}) - result = mcp2.should_include_component(tool2) + result = mcp2._should_enable_component(tool2) assert result is False # Remove tag2, should not be excluded @@ -1360,7 +1360,7 @@ class TestShouldIncludeComponent: name="test_tool", tags={"tag1_removed", "tag3", "tag4"}, parameters={} ) mcp3 = FastMCP(tools=[tool3], exclude_tags={("tag2", "tag3")}) - result = mcp3.should_include_component(tool3) + result = mcp3._should_enable_component(tool3) assert result is True def test_mixed_string_and_tuple_include_tags(self): @@ -1368,7 +1368,7 @@ class TestShouldIncludeComponent: # Should be included because both tag1 and tag2 are present (tuple match) tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={}) mcp1 = FastMCP(tools=[tool1], include_tags={"not_present", ("tag1", "tag2")}) - result = mcp1.should_include_component(tool1) + result = mcp1._should_enable_component(tool1) assert result is True # Should be included because tag1 is present (string match) @@ -1376,7 +1376,7 @@ class TestShouldIncludeComponent: mcp2 = FastMCP( tools=[tool2], include_tags={"tag1", ("not_present1", "not_present2")} ) - result = mcp2.should_include_component(tool2) + result = mcp2._should_enable_component(tool2) assert result is True # Should not be included because no conditions are met @@ -1385,7 +1385,7 @@ class TestShouldIncludeComponent: tools=[tool3], include_tags={"not_present", ("not_present1", "not_present2")}, ) - result = mcp3.should_include_component(tool3) + result = mcp3._should_enable_component(tool3) assert result is False def test_complex_scenario_with_both_filters(self): @@ -1399,7 +1399,7 @@ class TestShouldIncludeComponent: include_tags={"api", ("read", "admin")}, exclude_tags={"sensitive"}, ) - result = mcp1.should_include_component(tool1) + result = mcp1._should_enable_component(tool1) assert result is False # Remove sensitive tag, should now be included @@ -1409,7 +1409,7 @@ class TestShouldIncludeComponent: include_tags={"api", ("read", "admin")}, exclude_tags={"sensitive"}, ) - result = mcp2.should_include_component(tool2) + result = mcp2._should_enable_component(tool2) assert result is True def test_empty_include_exclude_sets(self): @@ -1417,13 +1417,13 @@ class TestShouldIncludeComponent: # Empty include set means nothing matches tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={}) mcp1 = FastMCP(tools=[tool1], include_tags=set()) - result = mcp1.should_include_component(tool1) + result = mcp1._should_enable_component(tool1) assert result is False # Empty exclude set means nothing excluded tool2 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={}) mcp2 = FastMCP(tools=[tool2], exclude_tags=set()) - result = mcp2.should_include_component(tool2) + result = mcp2._should_enable_component(tool2) assert result is True def test_empty_tags_with_filters(self): @@ -1431,24 +1431,24 @@ class TestShouldIncludeComponent: # With include filters, empty tags should not match tool1 = Tool(name="test_tool", tags=set(), parameters={}) mcp1 = FastMCP(tools=[tool1], include_tags={"required_tag"}) - result = mcp1.should_include_component(tool1) + result = mcp1._should_enable_component(tool1) assert result is False # With exclude filters but no include, empty tags should pass tool2 = Tool(name="test_tool", tags=set(), parameters={}) mcp2 = FastMCP(tools=[tool2], exclude_tags={"bad_tag"}) - result = mcp2.should_include_component(tool2) + result = mcp2._should_enable_component(tool2) assert result is True # Tuple filters with empty tags tool3 = Tool(name="test_tool", tags=set(), parameters={}) mcp3 = FastMCP(tools=[tool3], include_tags={("tag1", "tag2")}) - result = mcp3.should_include_component(tool3) + result = mcp3._should_enable_component(tool3) assert result is False tool4 = Tool(name="test_tool", tags=set(), parameters={}) mcp4 = FastMCP(tools=[tool4], exclude_tags={("tag1", "tag2")}) - result = mcp4.should_include_component(tool4) + result = mcp4._should_enable_component(tool4) assert result is True def test_single_element_tuples(self): @@ -1456,10 +1456,10 @@ class TestShouldIncludeComponent: # Single-element tuple should behave like a string tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={}) mcp1 = FastMCP(tools=[tool1], include_tags={("tag1",)}) - result = mcp1.should_include_component(tool1) + result = mcp1._should_enable_component(tool1) assert result is True tool2 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={}) mcp2 = FastMCP(tools=[tool2], exclude_tags={("tag1",)}) - result = mcp2.should_include_component(tool2) + result = mcp2._should_enable_component(tool2) assert result is False