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

aminghadersohi pushed a commit to branch mcp-rls-plugins-99978
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 776fcf2f8be0e32b608a708930ca195a274f7604
Author: Mehmet Salih Yavuz <[email protected]>
AuthorDate: Thu May 21 18:01:59 2026 +0300

    feat(mcp): make config optional in generate_explore_link (#39559)
---
 superset/mcp_service/chart/schemas.py              |  9 ++++-
 .../explore/tool/generate_explore_link.py          | 41 ++++++++++++++++---
 .../explore/tool/test_generate_explore_link.py     | 46 ++++++++++++++++++++++
 3 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/superset/mcp_service/chart/schemas.py 
b/superset/mcp_service/chart/schemas.py
index 6e9e67d8722..d598536d4db 100644
--- a/superset/mcp_service/chart/schemas.py
+++ b/superset/mcp_service/chart/schemas.py
@@ -1712,7 +1712,14 @@ class GenerateChartRequest(QueryCacheControl):
 
 class GenerateExploreLinkRequest(FormDataCacheControl):
     dataset_id: int | str = Field(..., description="Dataset identifier (ID, 
UUID)")
-    config: ChartConfig = Field(..., description="Chart configuration")
+    config: ChartConfig | None = Field(
+        None,
+        description=(
+            "Chart configuration. Optional; omit to get a default "
+            "explore URL that opens the dataset in Superset without a "
+            "preconfigured chart."
+        ),
+    )
 
 
 class UpdateChartRequest(QueryCacheControl):
diff --git a/superset/mcp_service/explore/tool/generate_explore_link.py 
b/superset/mcp_service/explore/tool/generate_explore_link.py
index 6e29158b6e6..68ed36c037a 100644
--- a/superset/mcp_service/explore/tool/generate_explore_link.py
+++ b/superset/mcp_service/explore/tool/generate_explore_link.py
@@ -65,10 +65,12 @@ async def generate_explore_link(
     - "Visualize [data]"
     - General data exploration
     - When user wants to SEE data visually
+    - Opening a dataset in Explore without a preconfigured chart (omit config)
 
     IMPORTANT:
     - Use numeric dataset ID or UUID (NOT schema.table_name format)
-    - MUST include chart_type in config (either 'xy' or 'table')
+    - When config is provided, MUST include chart_type (e.g. 'xy' or 'table')
+    - Omit config entirely to return a default explore URL for the dataset
 
     Example usage:
     ```json
@@ -83,6 +85,11 @@ async def generate_explore_link(
     }
     ```
 
+    Or with no config to simply open the dataset in Explore:
+    ```json
+    {"dataset_id": 123}
+    ```
+
     Better UX because:
     - Users can interact with chart before saving
     - Easy to modify parameters instantly
@@ -93,9 +100,10 @@ async def generate_explore_link(
 
     Returns explore URL for immediate use.
     """
+    chart_type = request.config.chart_type if request.config else "none"
     await ctx.info(
         "Generating explore link for dataset_id=%s, chart_type=%s"
-        % (request.dataset_id, request.config.chart_type)
+        % (request.dataset_id, chart_type)
     )
     await ctx.debug(
         "Configuration details: use_cache=%s, force_refresh=%s, 
cache_form_data=%s"
@@ -103,9 +111,6 @@ async def generate_explore_link(
     )
 
     try:
-        # config is already a typed ChartConfig (validated by Pydantic)
-        config = request.config
-
         await ctx.report_progress(1, 4, "Validating dataset exists")
         with 
event_logger.log_context(action="mcp.generate_explore_link.dataset_check"):
             from superset.daos.dataset import DatasetDAO
@@ -157,8 +162,32 @@ async def generate_explore_link(
                     ),
                 }
 
+        # When no config is provided, return a default explore URL that opens
+        # the dataset in Superset without a preconfigured chart.
+        if request.config is None:
+            await ctx.report_progress(4, 4, "URL generation complete")
+            from superset.mcp_service.utils.url_utils import 
get_superset_base_url
+
+            base_url = get_superset_base_url()
+            default_url = (
+                
f"{base_url}/explore/?datasource_type=table&datasource_id={dataset.id}"
+            )
+            await ctx.info(
+                "Default explore link generated: dataset_id=%s" % 
(request.dataset_id,)
+            )
+            return {
+                "url": default_url,
+                "form_data": {},
+                "form_data_key": None,
+                "chart_type_label": None,
+                "error": None,
+            }
+
         await ctx.report_progress(2, 4, "Converting configuration to form 
data")
         with 
event_logger.log_context(action="mcp.generate_explore_link.form_data"):
+            # config is already a typed ChartConfig (validated by Pydantic)
+            config = request.config
+
             # Normalize column names to match canonical dataset column names
             # This fixes case sensitivity issues (e.g., 'order_date' vs 
'OrderDate')
             try:
@@ -256,7 +285,7 @@ async def generate_explore_link(
             "Explore link generation failed for dataset_id=%s, chart_type=%s: 
%s: %s"
             % (
                 request.dataset_id,
-                request.config.chart_type,
+                chart_type,
                 type(e).__name__,
                 str(e),
             )
diff --git 
a/tests/unit_tests/mcp_service/explore/tool/test_generate_explore_link.py 
b/tests/unit_tests/mcp_service/explore/tool/test_generate_explore_link.py
index 5a22d2dc6c3..a4dc86f86a3 100644
--- a/tests/unit_tests/mcp_service/explore/tool/test_generate_explore_link.py
+++ b/tests/unit_tests/mcp_service/explore/tool/test_generate_explore_link.py
@@ -810,6 +810,52 @@ class TestGenerateExploreLink:
             assert "Dataset not found: 99999" in result.data["error"]
             assert "list_datasets" in result.data["error"]
 
+    @patch("superset.daos.dataset.DatasetDAO.find_by_id")
+    @pytest.mark.asyncio
+    async def test_generate_explore_link_without_config(
+        self, mock_find_dataset, mcp_server
+    ):
+        """Omitting config returns a default dataset explore URL."""
+        mock_find_dataset.return_value = _mock_dataset(id=42)
+
+        request = GenerateExploreLinkRequest(dataset_id="42")
+
+        async with Client(mcp_server) as client:
+            result = await client.call_tool(
+                "generate_explore_link", {"request": request.model_dump()}
+            )
+
+            assert result.data["error"] is None
+            assert (
+                result.data["url"]
+                == "http://localhost:9001/explore/?datasource_type=table";
+                "&datasource_id=42"
+            )
+            assert result.data["form_data"] == {}
+            assert result.data["form_data_key"] is None
+            assert result.data["chart_type_label"] is None
+
+    @patch("superset.daos.dataset.DatasetDAO.find_by_id")
+    @pytest.mark.asyncio
+    async def test_generate_explore_link_without_config_missing_dataset(
+        self, mock_find_dataset, mcp_server
+    ):
+        """Omitting config still surfaces a dataset-not-found error."""
+        mock_find_dataset.return_value = None
+
+        request = GenerateExploreLinkRequest(dataset_id="99999")
+
+        async with Client(mcp_server) as client:
+            result = await client.call_tool(
+                "generate_explore_link", {"request": request.model_dump()}
+            )
+
+            assert result.data["url"] == ""
+            assert result.data["form_data"] == {}
+            assert result.data["form_data_key"] is None
+            assert result.data["chart_type_label"] is None
+            assert "Dataset not found: 99999" in result.data["error"]
+
     @patch("superset.daos.dataset.DatasetDAO.find_by_id")
     @pytest.mark.asyncio
     async def test_generate_explore_link_nonexistent_uuid_dataset(

Reply via email to