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

richardfogaca pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new fa06989ed71 fix(mcp): return requested update chart previews (#40077)
fa06989ed71 is described below

commit fa06989ed71b2d7233b2e5faeff91816d1da6574
Author: Richard Fogaca Nienkotter 
<[email protected]>
AuthorDate: Tue May 12 21:23:49 2026 -0300

    fix(mcp): return requested update chart previews (#40077)
---
 superset/mcp_service/chart/preview_utils.py        |  2 +
 superset/mcp_service/chart/tool/generate_chart.py  |  7 +-
 .../mcp_service/chart/tool/update_chart_preview.py | 39 +++++++++++-
 .../chart/tool/test_update_chart_preview.py        | 74 ++++++++++++++++++++++
 4 files changed, 115 insertions(+), 7 deletions(-)

diff --git a/superset/mcp_service/chart/preview_utils.py 
b/superset/mcp_service/chart/preview_utils.py
index d585e3cda99..a5ae10b3de3 100644
--- a/superset/mcp_service/chart/preview_utils.py
+++ b/superset/mcp_service/chart/preview_utils.py
@@ -36,6 +36,8 @@ from superset.mcp_service.chart.schemas import (
 
 logger = logging.getLogger(__name__)
 
+SUPPORTED_FORM_DATA_PREVIEW_FORMATS = frozenset({"ascii", "table", 
"vega_lite"})
+
 
 def _build_query_columns(form_data: Dict[str, Any]) -> list[str]:
     """Build query columns list from form_data, including both x_axis and 
groupby."""
diff --git a/superset/mcp_service/chart/tool/generate_chart.py 
b/superset/mcp_service/chart/tool/generate_chart.py
index 8ad907c1abe..aa31afecc4b 100644
--- a/superset/mcp_service/chart/tool/generate_chart.py
+++ b/superset/mcp_service/chart/tool/generate_chart.py
@@ -43,6 +43,7 @@ from superset.mcp_service.chart.compile import (
     CompileResult,
     validate_and_compile,
 )
+from superset.mcp_service.chart.preview_utils import 
SUPPORTED_FORM_DATA_PREVIEW_FORMATS
 from superset.mcp_service.chart.schemas import (
     AccessibilityMetadata,
     CHART_FORM_DATA_EXCLUDED_FIELD_NAMES,
@@ -630,11 +631,7 @@ async def generate_chart(  # noqa: C901
                             # For preview-only mode (save_chart=false)
                             # Note: Screenshot-based URL previews are not
                             # supported. Use explore_url to view interactively.
-                            if format_type in [
-                                "ascii",
-                                "table",
-                                "vega_lite",
-                            ]:
+                            if format_type in 
SUPPORTED_FORM_DATA_PREVIEW_FORMATS:
                                 # Generate preview from form data
                                 from superset.mcp_service.chart.preview_utils 
import (
                                     generate_preview_from_form_data,
diff --git a/superset/mcp_service/chart/tool/update_chart_preview.py 
b/superset/mcp_service/chart/tool/update_chart_preview.py
index 82ace0df0ad..d130411f677 100644
--- a/superset/mcp_service/chart/tool/update_chart_preview.py
+++ b/superset/mcp_service/chart/tool/update_chart_preview.py
@@ -40,8 +40,13 @@ from superset.mcp_service.chart.chart_utils import (
     map_config_to_form_data,
 )
 from superset.mcp_service.chart.compile import validate_and_compile
+from superset.mcp_service.chart.preview_utils import (
+    generate_preview_from_form_data,
+    SUPPORTED_FORM_DATA_PREVIEW_FORMATS,
+)
 from superset.mcp_service.chart.schemas import (
     AccessibilityMetadata,
+    ChartError,
     PerformanceMetadata,
     UpdateChartPreviewRequest,
 )
@@ -229,9 +234,39 @@ def update_chart_preview(  # noqa: C901
             high_contrast_available=False,
         )
 
-        # Note: Screenshot-based previews are not supported.
-        # Use the explore_url to view the chart interactively.
         previews: Dict[str, Any] = {}
+        if request.generate_preview:
+            try:
+                with event_logger.log_context(
+                    action="mcp.update_chart_preview.preview"
+                ):
+                    for format_type in request.preview_formats:
+                        # URL previews are represented by 
explore_url/chart.url.
+                        # Screenshot-based previews are not supported.
+                        if format_type not in 
SUPPORTED_FORM_DATA_PREVIEW_FORMATS:
+                            continue
+
+                        preview_result = generate_preview_from_form_data(
+                            form_data=new_form_data,
+                            dataset_id=dataset.id,
+                            preview_format=format_type,
+                        )
+
+                        if isinstance(preview_result, ChartError):
+                            logger.warning(
+                                "Preview '%s' failed: %s",
+                                format_type,
+                                preview_result.error,
+                            )
+                        else:
+                            previews[format_type] = (
+                                preview_result.model_dump(mode="json")
+                                if hasattr(preview_result, "model_dump")
+                                else preview_result
+                            )
+
+            except (CommandException, ValueError, KeyError) as e:
+                logger.warning("Preview generation failed: %s", e)
 
         # Return enhanced data
         result = {
diff --git 
a/tests/unit_tests/mcp_service/chart/tool/test_update_chart_preview.py 
b/tests/unit_tests/mcp_service/chart/tool/test_update_chart_preview.py
index 2aa63efdf1c..caeb1e7a747 100644
--- a/tests/unit_tests/mcp_service/chart/tool/test_update_chart_preview.py
+++ b/tests/unit_tests/mcp_service/chart/tool/test_update_chart_preview.py
@@ -32,6 +32,7 @@ from superset.mcp_service.chart.schemas import (
     FilterConfig,
     LegendConfig,
     TableChartConfig,
+    TablePreview,
     UpdateChartPreviewRequest,
     XYChartConfig,
 )
@@ -698,6 +699,79 @@ class TestUpdateChartPreview:
         assert result["warnings"] == []
         mock_get_previous_form_data.assert_called_once_with("valid_key_12345")
 
+    @patch.object(update_chart_preview_module, "validate_and_compile")
+    @patch.object(update_chart_preview_module, "has_dataset_access", 
return_value=True)
+    @patch("superset.daos.dataset.DatasetDAO.find_by_id")
+    @patch.object(update_chart_preview_module, 
"generate_preview_from_form_data")
+    @patch.object(update_chart_preview_module, "analyze_chart_semantics")
+    @patch.object(update_chart_preview_module, "analyze_chart_capabilities")
+    @patch.object(update_chart_preview_module, "generate_explore_link")
+    @patch.object(update_chart_preview_module, "_get_previous_form_data")
+    @patch("superset.mcp_service.auth.get_user_from_request")
+    @pytest.mark.asyncio
+    async def test_returns_requested_table_preview(
+        self,
+        mock_get_user_from_request,
+        mock_get_previous_form_data,
+        mock_generate_explore_link,
+        mock_analyze_chart_capabilities,
+        mock_analyze_chart_semantics,
+        mock_generate_preview_from_form_data,
+        mock_find_by_id,
+        unused_access_mock,
+        mock_validate_and_compile,
+    ) -> None:
+        """Preview updates honor supported preview_formats."""
+        mock_user = Mock()
+        mock_user.id = 1
+        mock_get_user_from_request.return_value = mock_user
+        mock_find_by_id.return_value = _mock_dataset(id=3)
+        mock_validate_and_compile.return_value = Mock(success=True)
+        mock_get_previous_form_data.return_value = {}
+        mock_generate_explore_link.return_value = (
+            "http://localhost:8088/explore/?form_data_key=new_preview_key";
+        )
+        mock_analyze_chart_capabilities.return_value = None
+        mock_analyze_chart_semantics.return_value = None
+        table_preview = TablePreview(
+            table_data="Table Preview",
+            row_count=1,
+            supports_sorting=True,
+        )
+        expected_table_preview = {
+            "type": "table",
+            "table_data": "Table Preview",
+            "row_count": 1,
+            "supports_sorting": True,
+        }
+        mock_generate_preview_from_form_data.return_value = table_preview
+
+        request = UpdateChartPreviewRequest(
+            form_data_key="valid_key_12345",
+            dataset_id=3,
+            config=TableChartConfig(
+                chart_type="table",
+                columns=[
+                    ColumnRef(name="country", label="Country"),
+                    ColumnRef(name="sales", label="Sales", aggregate="SUM"),
+                ],
+            ),
+            generate_preview=True,
+            preview_formats=["url", "table"],
+        )
+
+        result = update_chart_preview_module.update_chart_preview(
+            request=request, ctx=Mock()
+        )
+
+        assert result["success"] is True
+        assert result["previews"] == {"table": expected_table_preview}
+        mock_generate_preview_from_form_data.assert_called_once()
+        preview_kwargs = mock_generate_preview_from_form_data.call_args.kwargs
+        assert preview_kwargs["dataset_id"] == 3
+        assert preview_kwargs["preview_format"] == "table"
+        assert preview_kwargs["form_data"]["viz_type"] == "table"
+
 
 class TestUpdateChartPreviewValidation:
     """Tier-1 validation gate and dataset access checks."""

Reply via email to