From 9dc4b3b31bae63615c0a5dde804716fe51a0246d Mon Sep 17 00:00:00 2001 From: "joksan.flores" Date: Wed, 25 Feb 2026 12:33:54 -0500 Subject: [PATCH] fix: replace removed FastMCP include_tags/exclude_tags kwargs with enable/disable API FastMCP 3.x removed include_tags and exclude_tags from the constructor. Passing them (even as None) raises TypeError. Replace with the new post-creation API: server.enable(tags=..., only=True) for include and server.disable(tags=...) for exclude. --- src/itential_mcp/server/server.py | 11 +++++++++-- tests/test_server.py | 21 +++++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/itential_mcp/server/server.py b/src/itential_mcp/server/server.py index 02381fcf..de3dfe90 100644 --- a/src/itential_mcp/server/server.py +++ b/src/itential_mcp/server/server.py @@ -152,10 +152,17 @@ def parse_tags(tags: str | None) -> list[str] | None: instructions=inspect.cleandoc(INSTRUCTIONS), lifespan=lifespan, auth=auth_provider, - include_tags=parse_tags(self.config.server.include_tags), - exclude_tags=parse_tags(self.config.server.exclude_tags), ) + # Apply tag filtering after server creation (FastMCP 3.x API) + include_tags = parse_tags(self.config.server.include_tags) + exclude_tags = parse_tags(self.config.server.exclude_tags) + + if include_tags: + self.mcp.enable(tags=set(include_tags), only=True) + if exclude_tags: + self.mcp.disable(tags=set(exclude_tags)) + logger = logging.get_logger() self.mcp.add_middleware(ErrorHandlingMiddleware(logger=logger)) diff --git a/tests/test_server.py b/tests/test_server.py index d0d07697..e479f6ca 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -685,11 +685,13 @@ async def empty_aiter(): name="Itential Platform MCP", instructions=server_module.inspect.cleandoc(server_module.INSTRUCTIONS), lifespan=server_module.lifespan, - include_tags=["system"], - exclude_tags=["deprecated"], auth=None, ) + # Verify tag filtering was applied after creation + mock_mcp.enable.assert_called_once_with(tags={"system"}, only=True) + mock_mcp.disable.assert_called_once_with(tags={"deprecated"}) + # Verify tool registration mock_mcp.tool.assert_called_once_with( mock_func, tags={"system", "test", "default"} @@ -1026,7 +1028,7 @@ async def test_init_server_auth_transport_incompatibility( @pytest.mark.asyncio @patch("itential_mcp.server.auth.build_auth_provider") async def test_init_server_parse_tags_with_none(self, mock_auth_builder): - """Test __init_server__ with None tags returns None""" + """Test __init_server__ with None tags does not call enable/disable""" from itential_mcp.config.models import Config, ServerConfig, AuthConfig mock_config = Config( @@ -1044,12 +1046,19 @@ async def test_init_server_parse_tags_with_none(self, mock_auth_builder): server_instance = server_module.Server(mock_config) with patch("itential_mcp.server.server.FastMCP") as mock_fastmcp: + mock_mcp = MagicMock() + mock_fastmcp.return_value = mock_mcp + await server_instance.__init_server__() - # Verify FastMCP was called with None for tags + # Verify tags are not passed to FastMCP constructor call_kwargs = mock_fastmcp.call_args.kwargs - assert call_kwargs["include_tags"] is None - assert call_kwargs["exclude_tags"] is None + assert "include_tags" not in call_kwargs + assert "exclude_tags" not in call_kwargs + + # Verify enable/disable were not called when tags are None + mock_mcp.enable.assert_not_called() + mock_mcp.disable.assert_not_called() @pytest.mark.asyncio @patch("itential_mcp.server.auth.build_auth_provider")