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()