You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/08/21 16:09:40 UTC

(superset) 07/09: fix(mcp): install the auth provider in the module entrypoint's network-transport branch

This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch fix/mcp-request-validation-hardening
in repository https://gitbox.apache.org/repos/asf/superset.git

commit cf5db318984d87834966fae66e3e588965ade027
Author: Superset Dev <de...@superset.apache.org>
AuthorDate: Fri Aug 21 01:06:40 2026 -0700

    fix(mcp): install the auth provider in the module entrypoint's network-transport branch
    
    python -m superset.mcp_service's non-stdio branch called
    init_fastmcp_server() with no auth argument, so any network transport
    (streamable-http, sse, ...) started with no token verifier regardless
    of MCP_AUTH_ENABLED / MCP_API_KEY_ENABLED / FAB_API_KEY_ENABLED /
    guest-auth config. Build the auth provider through the same
    server._create_auth_provider() used by the supported `superset mcp
    run` path and pass it into init_fastmcp_server(); a verifier-
    construction failure now propagates and aborts startup instead of
    silently running unauthenticated.
---
 superset/mcp_service/__main__.py                   | 15 +++-
 .../unit_tests/mcp_service/test_main_entrypoint.py | 97 ++++++++++++++++++++++
 2 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/superset/mcp_service/__main__.py b/superset/mcp_service/__main__.py
index 474d5bcc3f3..2ccb8180e7b 100644
--- a/superset/mcp_service/__main__.py
+++ b/superset/mcp_service/__main__.py
@@ -157,8 +157,19 @@ def main() -> None:
                 sys.stderr.write(f"[MCP] Client disconnected: {e}\n")
                 sys.exit(0)
     else:
-        # For other transports, use normal initialization
-        init_fastmcp_server()
+        # For other transports (network listeners), install the same auth
+        # provider as the supported entry point (`superset mcp run` ->
+        # server.run_server()) instead of starting with no verifier at all.
+        # _create_auth_provider fails closed (raises MCPAuthConfigError) when
+        # auth is configured but a verifier could not be built, so letting
+        # that propagate here refuses to start rather than silently running
+        # this transport unauthenticated.
+        from superset.mcp_service.flask_singleton import get_flask_app
+        from superset.mcp_service.server import _create_auth_provider
+
+        flask_app = get_flask_app()
+        auth_provider = _create_auth_provider(flask_app)
+        init_fastmcp_server(auth=auth_provider)
         _add_default_middlewares()
 
         # Run with specified transport
diff --git a/tests/unit_tests/mcp_service/test_main_entrypoint.py b/tests/unit_tests/mcp_service/test_main_entrypoint.py
new file mode 100644
index 00000000000..a37038b4a4b
--- /dev/null
+++ b/tests/unit_tests/mcp_service/test_main_entrypoint.py
@@ -0,0 +1,97 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Tests for the ``python -m superset.mcp_service`` entrypoint's non-stdio path.
+
+Network transports (streamable-http, sse, ...) must install the same auth
+provider as the supported CLI path (``superset mcp run`` -> server.run_server()
+-> _create_auth_provider()) instead of starting with no verifier at all.
+"""
+
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+
+def test_main_installs_auth_provider_for_network_transport(
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:
+    """The non-stdio branch must call _create_auth_provider and pass its
+    result into init_fastmcp_server, mirroring run_server()."""
+    monkeypatch.setenv("FASTMCP_TRANSPORT", "sse")
+
+    from superset.mcp_service import __main__ as main_module
+
+    flask_app = MagicMock()
+    auth_provider = object()
+    mcp_instance = MagicMock()
+
+    with (
+        patch(
+            "superset.mcp_service.flask_singleton.get_flask_app",
+            return_value=flask_app,
+        ),
+        patch(
+            "superset.mcp_service.server._create_auth_provider",
+            return_value=auth_provider,
+        ) as mock_create_auth_provider,
+        patch.object(
+            main_module, "init_fastmcp_server", return_value=mcp_instance
+        ) as mock_init,
+        patch.object(main_module, "_add_default_middlewares"),
+        patch.object(main_module.mcp, "run") as mock_run,
+    ):
+        main_module.main()
+
+    mock_create_auth_provider.assert_called_once_with(flask_app)
+    mock_init.assert_called_once_with(auth=auth_provider)
+    mock_run.assert_called_once_with(transport="sse")
+
+
+def test_main_propagates_auth_config_error_for_network_transport(
+    monkeypatch: pytest.MonkeyPatch,
+) -> None:
+    """A verifier-construction failure must abort startup, not fall through to
+    an unauthenticated server.
+
+    Before the fix, the non-stdio branch never called _create_auth_provider
+    at all, so MCP_AUTH_ENABLED was silently discarded by this entrypoint.
+    """
+    monkeypatch.setenv("FASTMCP_TRANSPORT", "streamable-http")
+
+    from superset.mcp_service import __main__ as main_module
+    from superset.mcp_service.mcp_config import MCPAuthConfigError
+
+    flask_app = MagicMock()
+
+    with (
+        patch(
+            "superset.mcp_service.flask_singleton.get_flask_app",
+            return_value=flask_app,
+        ),
+        patch(
+            "superset.mcp_service.server._create_auth_provider",
+            side_effect=MCPAuthConfigError("bad auth config"),
+        ),
+        patch.object(main_module, "init_fastmcp_server") as mock_init,
+        patch.object(main_module, "_add_default_middlewares"),
+        patch.object(main_module.mcp, "run") as mock_run,
+    ):
+        with pytest.raises(MCPAuthConfigError):
+            main_module.main()
+
+    mock_init.assert_not_called()
+    mock_run.assert_not_called()