diff --git a/superset/mcp_service/auth.py b/superset/mcp_service/auth.py index b18b4b5768d..3ad24988024 100644 --- a/superset/mcp_service/auth.py +++ b/superset/mcp_service/auth.py @@ -370,6 +370,26 @@ def mcp_auth_hook(tool_func: F) -> F: # noqa: C901 is_async = inspect.iscoroutinefunction(tool_func) + # Detect if the original function expects a ctx: Context parameter. + # If so, we inject it via get_context() at call time so tool functions + # don't need @parse_request to handle Context injection. + from fastmcp import Context as FMContext + + _tool_sig = inspect.signature(tool_func) + _needs_ctx = any( + p.annotation is FMContext + or (hasattr(p.annotation, "__name__") and p.annotation.__name__ == "Context") + for p in _tool_sig.parameters.values() + ) + + def _inject_ctx(kwargs: dict[str, Any]) -> dict[str, Any]: + """Inject FastMCP Context into kwargs if the tool function expects it.""" + if _needs_ctx and "ctx" not in kwargs: + from fastmcp.server.dependencies import get_context + + kwargs["ctx"] = get_context() + return kwargs + if is_async: @functools.wraps(tool_func) @@ -384,7 +404,7 @@ def mcp_auth_hook(tool_func: F) -> F: # noqa: C901 "MCP internal call without Flask context: tool=%s", tool_func.__name__, ) - return await tool_func(*args, **kwargs) + return await tool_func(*args, **_inject_ctx(kwargs)) # RBAC permission check if not check_tool_permission(tool_func): @@ -402,7 +422,7 @@ def mcp_auth_hook(tool_func: F) -> F: # noqa: C901 user.username, tool_func.__name__, ) - result = await tool_func(*args, **kwargs) + result = await tool_func(*args, **_inject_ctx(kwargs)) return result except Exception: _cleanup_session_on_error() @@ -424,7 +444,7 @@ def mcp_auth_hook(tool_func: F) -> F: # noqa: C901 "MCP internal call without Flask context: tool=%s", tool_func.__name__, ) - return tool_func(*args, **kwargs) + return tool_func(*args, **_inject_ctx(kwargs)) # RBAC permission check if not check_tool_permission(tool_func): @@ -442,7 +462,7 @@ def mcp_auth_hook(tool_func: F) -> F: # noqa: C901 user.username, tool_func.__name__, ) - result = tool_func(*args, **kwargs) + result = tool_func(*args, **_inject_ctx(kwargs)) return result except Exception: _cleanup_session_on_error() @@ -481,17 +501,15 @@ def mcp_auth_hook(tool_func: F) -> F: # noqa: C901 # Set __signature__ from the original function, but: # 1. Remove ctx parameter - FastMCP tools don't expose it to clients # 2. Skip if original has *args (parse_request output has its own handling) - from fastmcp import Context as FMContext - - tool_sig = inspect.signature(tool_func) has_var_positional = any( - p.kind == inspect.Parameter.VAR_POSITIONAL for p in tool_sig.parameters.values() + p.kind == inspect.Parameter.VAR_POSITIONAL + for p in _tool_sig.parameters.values() ) if not has_var_positional: # For functions without *args, preserve signature but remove ctx new_params = [] - for _name, param in tool_sig.parameters.items(): + for _name, param in _tool_sig.parameters.items(): # Skip ctx parameter - FastMCP tools don't expose it to clients if param.annotation is FMContext or ( hasattr(param.annotation, "__name__") @@ -499,7 +517,7 @@ def mcp_auth_hook(tool_func: F) -> F: # noqa: C901 ): continue new_params.append(param) - new_wrapper.__signature__ = tool_sig.replace( # type: ignore[attr-defined] + new_wrapper.__signature__ = _tool_sig.replace( # type: ignore[attr-defined] parameters=new_params ) diff --git a/superset/mcp_service/chart/tool/generate_chart.py b/superset/mcp_service/chart/tool/generate_chart.py index c637d3d60b2..2ba89cd71a8 100644 --- a/superset/mcp_service/chart/tool/generate_chart.py +++ b/superset/mcp_service/chart/tool/generate_chart.py @@ -45,7 +45,6 @@ from superset.mcp_service.chart.schemas import ( GenerateChartResponse, PerformanceMetadata, ) -from superset.mcp_service.utils.schema_utils import parse_request from superset.mcp_service.utils.url_utils import get_superset_base_url from superset.utils import json @@ -133,7 +132,6 @@ def _compile_chart( destructiveHint=False, ), ) -@parse_request(GenerateChartRequest) async def generate_chart( # noqa: C901 request: GenerateChartRequest, ctx: Context ) -> GenerateChartResponse: diff --git a/superset/mcp_service/chart/tool/get_chart_data.py b/superset/mcp_service/chart/tool/get_chart_data.py index 8bacd35217f..a958433b9f8 100644 --- a/superset/mcp_service/chart/tool/get_chart_data.py +++ b/superset/mcp_service/chart/tool/get_chart_data.py @@ -43,7 +43,6 @@ from superset.mcp_service.chart.schemas import ( PerformanceMetadata, ) from superset.mcp_service.utils.cache_utils import get_cache_status_from_result -from superset.mcp_service.utils.schema_utils import parse_request from superset.utils.core import merge_extra_filters logger = logging.getLogger(__name__) @@ -83,7 +82,6 @@ def _get_cached_form_data(form_data_key: str) -> str | None: destructiveHint=False, ), ) -@parse_request(GetChartDataRequest) async def get_chart_data( # noqa: C901 request: GetChartDataRequest, ctx: Context ) -> ChartData | ChartError: diff --git a/superset/mcp_service/chart/tool/get_chart_info.py b/superset/mcp_service/chart/tool/get_chart_info.py index e1cb5442003..a550cf9d6fd 100644 --- a/superset/mcp_service/chart/tool/get_chart_info.py +++ b/superset/mcp_service/chart/tool/get_chart_info.py @@ -36,7 +36,6 @@ from superset.mcp_service.chart.schemas import ( serialize_chart_object, ) from superset.mcp_service.mcp_core import ModelGetInfoCore -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -124,7 +123,6 @@ def _apply_unsaved_state_override(result: ChartInfo, form_data_key: str) -> None destructiveHint=False, ), ) -@parse_request(GetChartInfoRequest) async def get_chart_info( request: GetChartInfoRequest, ctx: Context ) -> ChartInfo | ChartError: diff --git a/superset/mcp_service/chart/tool/get_chart_preview.py b/superset/mcp_service/chart/tool/get_chart_preview.py index f844fcc630e..548898e03b4 100644 --- a/superset/mcp_service/chart/tool/get_chart_preview.py +++ b/superset/mcp_service/chart/tool/get_chart_preview.py @@ -41,7 +41,6 @@ from superset.mcp_service.chart.schemas import ( URLPreview, VegaLitePreview, ) -from superset.mcp_service.utils.schema_utils import parse_request from superset.mcp_service.utils.url_utils import get_superset_base_url logger = logging.getLogger(__name__) @@ -2186,7 +2185,6 @@ async def _get_chart_preview_internal( # noqa: C901 destructiveHint=False, ), ) -@parse_request(GetChartPreviewRequest) async def get_chart_preview( request: GetChartPreviewRequest, ctx: Context ) -> ChartPreview | ChartError: diff --git a/superset/mcp_service/chart/tool/list_charts.py b/superset/mcp_service/chart/tool/list_charts.py index ed92f1d057b..3f9bcfe88eb 100644 --- a/superset/mcp_service/chart/tool/list_charts.py +++ b/superset/mcp_service/chart/tool/list_charts.py @@ -38,7 +38,6 @@ from superset.mcp_service.chart.schemas import ( serialize_chart_object, ) from superset.mcp_service.mcp_core import ModelListCore -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -71,7 +70,6 @@ SORTABLE_CHART_COLUMNS = [ destructiveHint=False, ), ) -@parse_request(ListChartsRequest) async def list_charts(request: ListChartsRequest, ctx: Context) -> ChartList: """List charts with filtering and search. diff --git a/superset/mcp_service/chart/tool/update_chart.py b/superset/mcp_service/chart/tool/update_chart.py index c59c8a1485f..a8f522c8758 100644 --- a/superset/mcp_service/chart/tool/update_chart.py +++ b/superset/mcp_service/chart/tool/update_chart.py @@ -39,7 +39,6 @@ from superset.mcp_service.chart.schemas import ( PerformanceMetadata, UpdateChartRequest, ) -from superset.mcp_service.utils.schema_utils import parse_request from superset.mcp_service.utils.url_utils import get_superset_base_url from superset.utils import json @@ -55,7 +54,6 @@ logger = logging.getLogger(__name__) destructiveHint=True, ), ) -@parse_request(UpdateChartRequest) async def update_chart( request: UpdateChartRequest, ctx: Context ) -> GenerateChartResponse: diff --git a/superset/mcp_service/chart/tool/update_chart_preview.py b/superset/mcp_service/chart/tool/update_chart_preview.py index 007be88cc1e..4adcdad93af 100644 --- a/superset/mcp_service/chart/tool/update_chart_preview.py +++ b/superset/mcp_service/chart/tool/update_chart_preview.py @@ -39,7 +39,6 @@ from superset.mcp_service.chart.schemas import ( PerformanceMetadata, UpdateChartPreviewRequest, ) -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -54,7 +53,6 @@ logger = logging.getLogger(__name__) destructiveHint=True, ), ) -@parse_request(UpdateChartPreviewRequest) def update_chart_preview( request: UpdateChartPreviewRequest, ctx: Context ) -> Dict[str, Any]: diff --git a/superset/mcp_service/dashboard/tool/add_chart_to_existing_dashboard.py b/superset/mcp_service/dashboard/tool/add_chart_to_existing_dashboard.py index 2262bdb722f..ede847b8580 100644 --- a/superset/mcp_service/dashboard/tool/add_chart_to_existing_dashboard.py +++ b/superset/mcp_service/dashboard/tool/add_chart_to_existing_dashboard.py @@ -40,7 +40,6 @@ from superset.mcp_service.dashboard.schemas import ( AddChartToDashboardResponse, DashboardInfo, ) -from superset.mcp_service.utils.schema_utils import parse_request from superset.mcp_service.utils.url_utils import get_superset_base_url from superset.utils import json @@ -314,7 +313,6 @@ def _ensure_layout_structure( destructiveHint=False, ), ) -@parse_request(AddChartToDashboardRequest) def add_chart_to_existing_dashboard( request: AddChartToDashboardRequest, ctx: Context ) -> AddChartToDashboardResponse: diff --git a/superset/mcp_service/dashboard/tool/generate_dashboard.py b/superset/mcp_service/dashboard/tool/generate_dashboard.py index bd9ce924709..e4559d9f0f8 100644 --- a/superset/mcp_service/dashboard/tool/generate_dashboard.py +++ b/superset/mcp_service/dashboard/tool/generate_dashboard.py @@ -40,7 +40,6 @@ from superset.mcp_service.dashboard.schemas import ( GenerateDashboardRequest, GenerateDashboardResponse, ) -from superset.mcp_service.utils.schema_utils import parse_request from superset.mcp_service.utils.url_utils import get_superset_base_url from superset.utils import json @@ -188,7 +187,6 @@ def _generate_title_from_charts(chart_objects: List[Any]) -> str: destructiveHint=False, ), ) -@parse_request(GenerateDashboardRequest) def generate_dashboard( request: GenerateDashboardRequest, ctx: Context ) -> GenerateDashboardResponse: diff --git a/superset/mcp_service/dashboard/tool/get_dashboard_info.py b/superset/mcp_service/dashboard/tool/get_dashboard_info.py index 7ad42e97955..46f5ae4eedd 100644 --- a/superset/mcp_service/dashboard/tool/get_dashboard_info.py +++ b/superset/mcp_service/dashboard/tool/get_dashboard_info.py @@ -39,7 +39,6 @@ from superset.mcp_service.dashboard.schemas import ( GetDashboardInfoRequest, ) from superset.mcp_service.mcp_core import ModelGetInfoCore -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -68,7 +67,6 @@ def _get_permalink_state(permalink_key: str) -> DashboardPermalinkValue | None: destructiveHint=False, ), ) -@parse_request(GetDashboardInfoRequest) async def get_dashboard_info( request: GetDashboardInfoRequest, ctx: Context ) -> DashboardInfo | DashboardError: diff --git a/superset/mcp_service/dashboard/tool/list_dashboards.py b/superset/mcp_service/dashboard/tool/list_dashboards.py index 1feee0d2b6e..ff277406140 100644 --- a/superset/mcp_service/dashboard/tool/list_dashboards.py +++ b/superset/mcp_service/dashboard/tool/list_dashboards.py @@ -40,7 +40,6 @@ from superset.mcp_service.dashboard.schemas import ( serialize_dashboard_object, ) from superset.mcp_service.mcp_core import ModelListCore -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -72,7 +71,6 @@ SORTABLE_DASHBOARD_COLUMNS = [ destructiveHint=False, ), ) -@parse_request(ListDashboardsRequest) async def list_dashboards( request: ListDashboardsRequest, ctx: Context ) -> DashboardList: diff --git a/superset/mcp_service/dataset/tool/get_dataset_info.py b/superset/mcp_service/dataset/tool/get_dataset_info.py index 888e1452d7e..ee74db8c1a0 100644 --- a/superset/mcp_service/dataset/tool/get_dataset_info.py +++ b/superset/mcp_service/dataset/tool/get_dataset_info.py @@ -37,7 +37,6 @@ from superset.mcp_service.dataset.schemas import ( serialize_dataset_object, ) from superset.mcp_service.mcp_core import ModelGetInfoCore -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -51,7 +50,6 @@ logger = logging.getLogger(__name__) destructiveHint=False, ), ) -@parse_request(GetDatasetInfoRequest) async def get_dataset_info( request: GetDatasetInfoRequest, ctx: Context ) -> DatasetInfo | DatasetError: diff --git a/superset/mcp_service/dataset/tool/list_datasets.py b/superset/mcp_service/dataset/tool/list_datasets.py index 97a8b0597a3..95e37fcaece 100644 --- a/superset/mcp_service/dataset/tool/list_datasets.py +++ b/superset/mcp_service/dataset/tool/list_datasets.py @@ -40,7 +40,6 @@ from superset.mcp_service.dataset.schemas import ( serialize_dataset_object, ) from superset.mcp_service.mcp_core import ModelListCore -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -70,7 +69,6 @@ SORTABLE_DATASET_COLUMNS = [ destructiveHint=False, ), ) -@parse_request(ListDatasetsRequest) async def list_datasets(request: ListDatasetsRequest, ctx: Context) -> DatasetList: """List datasets with filtering and search. diff --git a/superset/mcp_service/explore/tool/generate_explore_link.py b/superset/mcp_service/explore/tool/generate_explore_link.py index 0c5f11824ea..988ca58541e 100644 --- a/superset/mcp_service/explore/tool/generate_explore_link.py +++ b/superset/mcp_service/explore/tool/generate_explore_link.py @@ -36,7 +36,6 @@ from superset.mcp_service.chart.chart_utils import ( from superset.mcp_service.chart.schemas import ( GenerateExploreLinkRequest, ) -from superset.mcp_service.utils.schema_utils import parse_request @tool( @@ -48,7 +47,6 @@ from superset.mcp_service.utils.schema_utils import parse_request destructiveHint=False, ), ) -@parse_request(GenerateExploreLinkRequest) async def generate_explore_link( request: GenerateExploreLinkRequest, ctx: Context ) -> Dict[str, Any]: diff --git a/superset/mcp_service/sql_lab/tool/execute_sql.py b/superset/mcp_service/sql_lab/tool/execute_sql.py index 8617bbcf9f1..18d89b3b271 100644 --- a/superset/mcp_service/sql_lab/tool/execute_sql.py +++ b/superset/mcp_service/sql_lab/tool/execute_sql.py @@ -22,8 +22,6 @@ Tool for executing SQL queries against databases using the unified Database.execute() API with RLS, template rendering, and security validation. """ -from __future__ import annotations - import logging from decimal import Decimal from typing import Any @@ -48,7 +46,6 @@ from superset.mcp_service.sql_lab.schemas import ( StatementData, StatementInfo, ) -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -63,7 +60,6 @@ logger = logging.getLogger(__name__) destructiveHint=True, ), ) -@parse_request(ExecuteSqlRequest) async def execute_sql(request: ExecuteSqlRequest, ctx: Context) -> ExecuteSqlResponse: """Execute SQL query against database using the unified Database.execute() API.""" await ctx.info( diff --git a/superset/mcp_service/sql_lab/tool/open_sql_lab_with_context.py b/superset/mcp_service/sql_lab/tool/open_sql_lab_with_context.py index 6e46b483eed..cf09b2ceb04 100644 --- a/superset/mcp_service/sql_lab/tool/open_sql_lab_with_context.py +++ b/superset/mcp_service/sql_lab/tool/open_sql_lab_with_context.py @@ -32,7 +32,6 @@ from superset.mcp_service.sql_lab.schemas import ( OpenSqlLabRequest, SqlLabResponse, ) -from superset.mcp_service.utils.schema_utils import parse_request from superset.mcp_service.utils.url_utils import get_superset_base_url logger = logging.getLogger(__name__) @@ -48,7 +47,6 @@ logger = logging.getLogger(__name__) destructiveHint=False, ), ) -@parse_request(OpenSqlLabRequest) def open_sql_lab_with_context( request: OpenSqlLabRequest, ctx: Context ) -> SqlLabResponse: diff --git a/superset/mcp_service/sql_lab/tool/save_sql_query.py b/superset/mcp_service/sql_lab/tool/save_sql_query.py index 66a8a5e99f8..8126bf19efb 100644 --- a/superset/mcp_service/sql_lab/tool/save_sql_query.py +++ b/superset/mcp_service/sql_lab/tool/save_sql_query.py @@ -23,8 +23,6 @@ so it appears in SQL Lab's "Saved Queries" list and can be reloaded/shared via URL. """ -from __future__ import annotations - import logging from fastmcp import Context @@ -38,7 +36,6 @@ from superset.mcp_service.sql_lab.schemas import ( SaveSqlQueryRequest, SaveSqlQueryResponse, ) -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -53,7 +50,6 @@ logger = logging.getLogger(__name__) destructiveHint=False, ), ) -@parse_request(SaveSqlQueryRequest) async def save_sql_query( request: SaveSqlQueryRequest, ctx: Context ) -> SaveSqlQueryResponse: diff --git a/superset/mcp_service/system/tool/get_instance_info.py b/superset/mcp_service/system/tool/get_instance_info.py index f6f162570e1..36cbbbb9ecf 100644 --- a/superset/mcp_service/system/tool/get_instance_info.py +++ b/superset/mcp_service/system/tool/get_instance_info.py @@ -40,7 +40,6 @@ from superset.mcp_service.system.system_utils import ( calculate_popular_content, calculate_recent_activity, ) -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -73,6 +72,9 @@ _instance_info_core = InstanceInfoCore( ) +_DEFAULT_INSTANCE_INFO_REQUEST = GetSupersetInstanceInfoRequest() + + @tool( tags=["core"], annotations=ToolAnnotations( @@ -81,9 +83,9 @@ _instance_info_core = InstanceInfoCore( destructiveHint=False, ), ) -@parse_request(GetSupersetInstanceInfoRequest) def get_instance_info( - request: GetSupersetInstanceInfoRequest, ctx: Context + request: GetSupersetInstanceInfoRequest = _DEFAULT_INSTANCE_INFO_REQUEST, + ctx: Context = None, ) -> InstanceInfo: """Get instance statistics. diff --git a/superset/mcp_service/system/tool/get_schema.py b/superset/mcp_service/system/tool/get_schema.py index 0757b028e05..57b8909c301 100644 --- a/superset/mcp_service/system/tool/get_schema.py +++ b/superset/mcp_service/system/tool/get_schema.py @@ -48,7 +48,6 @@ from superset.mcp_service.common.schema_discovery import ( ModelSchemaInfo, ) from superset.mcp_service.mcp_core import ModelGetSchemaCore -from superset.mcp_service.utils.schema_utils import parse_request logger = logging.getLogger(__name__) @@ -129,7 +128,6 @@ _SCHEMA_CORE_FACTORIES: dict[ destructiveHint=False, ), ) -@parse_request(GetSchemaRequest) async def get_schema(request: GetSchemaRequest, ctx: Context) -> GetSchemaResponse: """ Get comprehensive schema metadata for a model type. diff --git a/tests/unit_tests/mcp_service/sql_lab/tool/test_save_sql_query.py b/tests/unit_tests/mcp_service/sql_lab/tool/test_save_sql_query.py index 53b242c440e..9bffcf66c88 100644 --- a/tests/unit_tests/mcp_service/sql_lab/tool/test_save_sql_query.py +++ b/tests/unit_tests/mcp_service/sql_lab/tool/test_save_sql_query.py @@ -228,9 +228,8 @@ class TestSaveSqlQueryToolLogic: from superset import db, etc.). We patch at the import source so that when the function runs, it picks up our mocks. - The @parse_request decorator injects ctx via get_context() and strips - __wrapped__, so we mock get_context and call the decorated function - directly (without unwrapping). + With passthrough decorators, we call the tool function directly + and pass a mock Context as the second argument. """ @pytest.mark.anyio @@ -280,10 +279,6 @@ class TestSaveSqlQueryToolLogic: ) with ( - patch( - "fastmcp.server.dependencies.get_context", - return_value=mock_ctx, - ), patch("superset.db", mock_db_session), patch("superset.security_manager", mock_sm), patch("superset.daos.query.SavedQueryDAO", mock_dao), @@ -294,7 +289,7 @@ class TestSaveSqlQueryToolLogic: patch("flask.g", mock_g), patch.object(mod, "event_logger", mock_event_logger), ): - result = await mod.save_sql_query(request) + result = await mod.save_sql_query(request, mock_ctx) assert result.id == 42 assert result.label == "Revenue Query" @@ -336,10 +331,6 @@ class TestSaveSqlQueryToolLogic: ) with ( - patch( - "fastmcp.server.dependencies.get_context", - return_value=mock_ctx, - ), patch("superset.db", mock_db_session), patch("flask.g", mock_g), patch.object(mod, "event_logger", mock_event_logger), @@ -347,7 +338,7 @@ class TestSaveSqlQueryToolLogic: from superset.exceptions import SupersetErrorException with pytest.raises(SupersetErrorException, match="not found"): - await mod.save_sql_query(request) + await mod.save_sql_query(request, mock_ctx) finally: _restore_modules(saved) @@ -385,10 +376,6 @@ class TestSaveSqlQueryToolLogic: ) with ( - patch( - "fastmcp.server.dependencies.get_context", - return_value=mock_ctx, - ), patch("superset.db", mock_db_session), patch("superset.security_manager", mock_sm), patch("flask.g", mock_g), @@ -397,7 +384,7 @@ class TestSaveSqlQueryToolLogic: from superset.exceptions import SupersetSecurityException with pytest.raises(SupersetSecurityException, match="Access denied"): - await mod.save_sql_query(request) + await mod.save_sql_query(request, mock_ctx) finally: _restore_modules(saved) @@ -449,10 +436,6 @@ class TestSaveSqlQueryToolLogic: ) with ( - patch( - "fastmcp.server.dependencies.get_context", - return_value=mock_ctx, - ), patch("superset.db", mock_db_session), patch("superset.security_manager", mock_sm), patch("superset.daos.query.SavedQueryDAO", mock_dao), @@ -463,7 +446,7 @@ class TestSaveSqlQueryToolLogic: patch("flask.g", mock_g), patch.object(mod, "event_logger", mock_event_logger), ): - result = await mod.save_sql_query(request) + result = await mod.save_sql_query(request, mock_ctx) assert result.id == 10 call_attrs = mock_dao.create.call_args[1]["attributes"] diff --git a/tests/unit_tests/mcp_service/system/tool/test_get_schema.py b/tests/unit_tests/mcp_service/system/tool/test_get_schema.py index 1d4e857be33..ef4c26a79cd 100644 --- a/tests/unit_tests/mcp_service/system/tool/test_get_schema.py +++ b/tests/unit_tests/mcp_service/system/tool/test_get_schema.py @@ -269,28 +269,6 @@ class TestGetSchemaToolViaClient: assert "dashboard_title" in info["sortable_columns"] assert "changed_on" in info["sortable_columns"] - @patch( - "superset.mcp_service.utils.schema_utils._is_parse_request_enabled", - return_value=True, - ) - @patch("superset.daos.chart.ChartDAO.get_filterable_columns_and_operators") - @pytest.mark.asyncio - async def test_get_schema_with_json_string_request( - self, mock_filters, mock_parse_enabled, mcp_server - ): - """Test get_schema accepts JSON string request (Claude Code compatibility).""" - mock_filters.return_value = {"slice_name": ["eq"]} - - async with Client(mcp_server) as client: - # Send request as JSON string (Claude Code bug workaround) - result = await client.call_tool( - "get_schema", {"request": '{"model_type": "chart"}'} - ) - - assert result.content is not None - data = json.loads(result.content[0].text) - assert data["schema_info"]["model_type"] == "chart" - @patch("superset.daos.chart.ChartDAO.get_filterable_columns_and_operators") @pytest.mark.asyncio async def test_get_schema_select_columns_have_metadata(