This is an automated email from the ASF dual-hosted git repository.
aminghadersohi 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 460992d89b3 fix(mcp): improve not-found errors to suggest
corresponding list_* tools (#39919)
460992d89b3 is described below
commit 460992d89b33ba07f3a22cb9a5b4cd83c27a768c
Author: Amin Ghadersohi <[email protected]>
AuthorDate: Tue May 12 02:38:10 2026 -0400
fix(mcp): improve not-found errors to suggest corresponding list_* tools
(#39919)
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---
superset/mcp_service/chart/tool/get_chart_data.py | 11 +++++++++--
.../mcp_service/chart/tool/get_chart_preview.py | 21 +++++++++++++++++++--
superset/mcp_service/chart/tool/update_chart.py | 14 ++++++++------
.../tool/add_chart_to_existing_dashboard.py | 10 ++++++++--
.../dashboard/tool/generate_dashboard.py | 5 ++++-
superset/mcp_service/dataset/tool/query_dataset.py | 5 ++++-
superset/mcp_service/sql_lab/tool/execute_sql.py | 5 ++++-
.../sql_lab/tool/open_sql_lab_with_context.py | 3 ++-
.../sql_lab/tool/test_open_sql_lab_with_context.py | 3 ++-
9 files changed, 60 insertions(+), 17 deletions(-)
diff --git a/superset/mcp_service/chart/tool/get_chart_data.py
b/superset/mcp_service/chart/tool/get_chart_data.py
index 7421ab0cdc0..a14fdcb73cb 100644
--- a/superset/mcp_service/chart/tool/get_chart_data.py
+++ b/superset/mcp_service/chart/tool/get_chart_data.py
@@ -46,7 +46,10 @@ from superset.mcp_service.chart.schemas import (
GetChartDataRequest,
PerformanceMetadata,
)
-from superset.mcp_service.utils import sanitize_for_llm_context
+from superset.mcp_service.utils import (
+ escape_llm_context_delimiters,
+ sanitize_for_llm_context,
+)
from superset.mcp_service.utils.cache_utils import get_cache_status_from_result
from superset.mcp_service.utils.oauth2_utils import (
build_oauth2_redirect_message,
@@ -199,8 +202,12 @@ async def get_chart_data( # noqa: C901
if not chart:
await ctx.warning("Chart not found: identifier=%s" %
(request.identifier,))
+ safe_id =
escape_llm_context_delimiters(str(request.identifier)[:200])
return ChartError(
- error=f"No chart found with identifier: {request.identifier}",
+ error=(
+ f"No chart found with identifier: {safe_id}."
+ " Use list_charts to get valid chart IDs."
+ ),
error_type="NotFound",
)
diff --git a/superset/mcp_service/chart/tool/get_chart_preview.py
b/superset/mcp_service/chart/tool/get_chart_preview.py
index 7ab8b29473a..33281ec6fb2 100644
--- a/superset/mcp_service/chart/tool/get_chart_preview.py
+++ b/superset/mcp_service/chart/tool/get_chart_preview.py
@@ -47,7 +47,10 @@ from superset.mcp_service.chart.schemas import (
URLPreview,
VegaLitePreview,
)
-from superset.mcp_service.utils import sanitize_for_llm_context
+from superset.mcp_service.utils import (
+ escape_llm_context_delimiters,
+ sanitize_for_llm_context,
+)
from superset.mcp_service.utils.oauth2_utils import (
build_oauth2_redirect_message,
OAUTH2_CONFIG_ERROR_MESSAGE,
@@ -1201,8 +1204,22 @@ async def _get_chart_preview_internal( # noqa: C901
if not chart:
await ctx.warning("Chart not found: identifier=%s" %
(request.identifier,))
+ is_form_data_key = (
+ isinstance(request.identifier, str)
+ and len(request.identifier) > 8
+ and not request.identifier.isdigit()
+ )
+ if is_form_data_key:
+ recovery = (
+ "If using a form_data_key, it may have expired — "
+ "use generate_explore_link to get a fresh key, "
+ "or use list_charts to find a saved chart by ID."
+ )
+ else:
+ recovery = "Use list_charts to get valid chart IDs."
+ safe_id =
escape_llm_context_delimiters(str(request.identifier)[:200])
return ChartError(
- error=f"No chart found with identifier: {request.identifier}",
+ error=f"No chart found with identifier: {safe_id}. {recovery}",
error_type="NotFound",
)
diff --git a/superset/mcp_service/chart/tool/update_chart.py
b/superset/mcp_service/chart/tool/update_chart.py
index 3e3057bdd2e..9fd0986a29a 100644
--- a/superset/mcp_service/chart/tool/update_chart.py
+++ b/superset/mcp_service/chart/tool/update_chart.py
@@ -47,6 +47,7 @@ from superset.mcp_service.chart.schemas import (
PerformanceMetadata,
UpdateChartRequest,
)
+from superset.mcp_service.utils import escape_llm_context_delimiters
from superset.mcp_service.utils.oauth2_utils import (
build_oauth2_redirect_message,
OAUTH2_CONFIG_ERROR_MESSAGE,
@@ -337,17 +338,18 @@ async def update_chart( # noqa: C901
chart = find_chart_by_identifier(request.identifier)
if not chart:
+ safe_id =
escape_llm_context_delimiters(str(request.identifier)[:200])
+ not_found_msg = (
+ f"No chart found with identifier: {safe_id}."
+ " Use list_charts to get valid chart IDs."
+ )
return GenerateChartResponse.model_validate(
{
"chart": None,
"error": {
"error_type": "NotFound",
- "message": (
- f"No chart found with identifier:
{request.identifier}"
- ),
- "details": (
- f"No chart found with identifier:
{request.identifier}"
- ),
+ "message": not_found_msg,
+ "details": not_found_msg,
},
"success": False,
"schema_version": "2.0",
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 026275c9e4b..72fcd482a55 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
@@ -334,7 +334,10 @@ def _find_and_authorize_dashboard(
dashboard=None,
dashboard_url=None,
position=None,
- error=f"Dashboard with ID {dashboard_id} not found",
+ error=(
+ f"Dashboard with ID {dashboard_id} not found."
+ " Use list_dashboards to get valid dashboard IDs."
+ ),
)
try:
@@ -392,7 +395,10 @@ def add_chart_to_existing_dashboard(
dashboard=None,
dashboard_url=None,
position=None,
- error=f"Chart with ID {request.chart_id} not found",
+ error=(
+ f"Chart with ID {request.chart_id} not found."
+ " Use list_charts to get valid chart IDs."
+ ),
)
# Validate dataset access for the chart.
diff --git a/superset/mcp_service/dashboard/tool/generate_dashboard.py
b/superset/mcp_service/dashboard/tool/generate_dashboard.py
index 968d5ca74f2..31c038e05a7 100644
--- a/superset/mcp_service/dashboard/tool/generate_dashboard.py
+++ b/superset/mcp_service/dashboard/tool/generate_dashboard.py
@@ -230,7 +230,10 @@ def generate_dashboard( # noqa: C901
return GenerateDashboardResponse(
dashboard=None,
dashboard_url=None,
- error=f"Charts not found: {list(missing_chart_ids)}",
+ error=(
+ f"Charts not found: {sorted(missing_chart_ids)}."
+ " Use list_charts to get valid chart IDs."
+ ),
)
# Validate dataset access for each chart.
diff --git a/superset/mcp_service/dataset/tool/query_dataset.py
b/superset/mcp_service/dataset/tool/query_dataset.py
index d62c7fd9d2d..da7f2d227f6 100644
--- a/superset/mcp_service/dataset/tool/query_dataset.py
+++ b/superset/mcp_service/dataset/tool/query_dataset.py
@@ -183,7 +183,10 @@ async def query_dataset( # noqa: C901
if dataset is None:
await ctx.error("Dataset not found: identifier=%s" %
(request.dataset_id,))
return DatasetError.create(
- error=f"No dataset found with identifier:
{request.dataset_id}",
+ error=(
+ f"No dataset found with identifier: {request.dataset_id}."
+ " Use list_datasets to get valid dataset IDs."
+ ),
error_type="NotFound",
)
diff --git a/superset/mcp_service/sql_lab/tool/execute_sql.py
b/superset/mcp_service/sql_lab/tool/execute_sql.py
index c1000320a4d..b5f01e8afa7 100644
--- a/superset/mcp_service/sql_lab/tool/execute_sql.py
+++ b/superset/mcp_service/sql_lab/tool/execute_sql.py
@@ -100,7 +100,10 @@ async def execute_sql(request: ExecuteSqlRequest, ctx:
Context) -> ExecuteSqlRes
)
return ExecuteSqlResponse(
success=False,
- error=f"Database with ID {request.database_id} not found",
+ error=(
+ f"Database with ID {request.database_id} not found."
+ " Use list_databases to get valid database IDs."
+ ),
error_type=SupersetErrorType.DATABASE_NOT_FOUND_ERROR.value,
)
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 1d7af0ba6f2..17485b67da3 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
@@ -103,7 +103,8 @@ def open_sql_lab_with_context(
database = DatabaseDAO.find_by_id(request.database_connection_id)
if not database:
error_message = (
- f"Database with ID {request.database_connection_id} not found"
+ f"Database with ID {request.database_connection_id} not found."
+ " Use list_databases to get valid database IDs."
)
return _sanitize_sql_lab_response_for_llm_context(
SqlLabResponse(
diff --git
a/tests/unit_tests/mcp_service/sql_lab/tool/test_open_sql_lab_with_context.py
b/tests/unit_tests/mcp_service/sql_lab/tool/test_open_sql_lab_with_context.py
index f6feacc0f01..fd67fd2bff2 100644
---
a/tests/unit_tests/mcp_service/sql_lab/tool/test_open_sql_lab_with_context.py
+++
b/tests/unit_tests/mcp_service/sql_lab/tool/test_open_sql_lab_with_context.py
@@ -298,7 +298,8 @@ class TestOpenSqlLabWithContext:
field_path=("title",),
)
assert response.error == sanitize_for_llm_context(
- "Database with ID 404 not found",
+ "Database with ID 404 not found."
+ " Use list_databases to get valid database IDs.",
field_path=("error",),
)
finally: