diff --git a/superset/mcp_service/app.py b/superset/mcp_service/app.py index 1e579a0ee64..361f3d105c6 100644 --- a/superset/mcp_service/app.py +++ b/superset/mcp_service/app.py @@ -314,6 +314,24 @@ from superset.core.mcp.core_mcp_injection import ( # noqa: E402 initialize_core_mcp_dependencies() +# Suppress known third-party deprecation warnings that leak to MCP clients. +# The MCP SDK captures Python warnings and forwards them to clients via +# server log entries, wasting LLM tokens and causing clients to act on +# irrelevant internal warnings. These warnings come from transitive imports +# triggered by tool/schema registration below. +import warnings # noqa: E402 + +warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + module=r"marshmallow\..*", +) +warnings.filterwarnings( + "ignore", + category=FutureWarning, + module=r"google\..*", +) + # Import all MCP tools to register them with the mcp instance # NOTE: Always add new tool imports here when creating new MCP tools. # Tools use the @tool decorator from `superset-core` and register automatically diff --git a/superset/mcp_service/server.py b/superset/mcp_service/server.py index 9e9af7dd47b..f2ee1d69a0d 100644 --- a/superset/mcp_service/server.py +++ b/superset/mcp_service/server.py @@ -36,6 +36,32 @@ from superset.mcp_service.storage import _create_redis_store logger = logging.getLogger(__name__) +def _suppress_third_party_warnings() -> None: + """Suppress known third-party deprecation warnings from MCP responses. + + The MCP SDK captures Python warnings and forwards them to clients via + ``mcp.server.lowlevel.server:Warning:`` log entries. This wastes LLM + tokens and causes clients to try to "fix" irrelevant internal warnings. + + Suppressed warnings: + - marshmallow ``RemovedInMarshmallow4Warning`` (triggered during + database engine schema instantiation) + - google.api_core ``FutureWarning`` (Python version support notices) + """ + import warnings + + warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + module=r"marshmallow\..*", + ) + warnings.filterwarnings( + "ignore", + category=FutureWarning, + module=r"google\..*", + ) + + def configure_logging(debug: bool = False) -> None: """Configure logging for the MCP service.""" import sys @@ -179,6 +205,7 @@ def run_server( """ configure_logging(debug) + _suppress_third_party_warnings() # DO NOT IMPORT TOOLS HERE!! IMPORT THEM IN app.py!!!!! diff --git a/tests/unit_tests/mcp_service/test_mcp_server.py b/tests/unit_tests/mcp_service/test_mcp_server.py index 0d7922a296a..e74313444ee 100644 --- a/tests/unit_tests/mcp_service/test_mcp_server.py +++ b/tests/unit_tests/mcp_service/test_mcp_server.py @@ -109,6 +109,40 @@ def test_create_event_store_uses_default_config_values(): ) +def test_suppress_third_party_warnings(): + """Third-party deprecation warnings filters are installed.""" + import re + import warnings + + from superset.mcp_service.server import _suppress_third_party_warnings + + _suppress_third_party_warnings() + + # Verify marshmallow DeprecationWarning filter is installed + marshmallow_filters = [ + f + for f in warnings.filters + if f[0] == "ignore" + and f[2] is DeprecationWarning + and isinstance(f[3], re.Pattern) + and f[3].pattern == r"marshmallow\..*" + ] + assert len(marshmallow_filters) >= 1, ( + "Expected marshmallow DeprecationWarning filter" + ) + + # Verify google FutureWarning filter is installed + google_filters = [ + f + for f in warnings.filters + if f[0] == "ignore" + and f[2] is FutureWarning + and isinstance(f[3], re.Pattern) + and f[3].pattern == r"google\..*" + ] + assert len(google_filters) >= 1, "Expected google FutureWarning filter" + + def test_create_event_store_returns_none_when_redis_store_fails(): """EventStore returns None when Redis store creation fails.""" config = {