You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by jl...@apache.org on 2026/10/13 18:06:13 UTC
(superset) branch ultraset-mcp-chart-data-stale-query-context created (now c8c40182d91)
This is an automated email from the ASF dual-hosted git repository.
sadpandajoe pushed a change to branch ultraset-mcp-chart-data-stale-query-context
in repository https://gitbox.apache.org/repos/asf/superset.git
at c8c40182d91 Merge remote-tracking branch 'origin/master' into ultraset-mcp-chart-data-stale-query-context
This branch includes the following new commits:
new 6e0efe69002 fix(mcp): surface missing/stale query_context accuracy warnings in get_chart_data
new c8c40182d91 Merge remote-tracking branch 'origin/master' into ultraset-mcp-chart-data-stale-query-context
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(superset) 02/02: Merge remote-tracking branch 'origin/master' into ultraset-mcp-chart-data-stale-query-context
Posted by jl...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
sadpandajoe pushed a commit to branch ultraset-mcp-chart-data-stale-query-context
in repository https://gitbox.apache.org/repos/asf/superset.git
commit c8c40182d91c371e3acb58f758ce4940e90a3c8a
Merge: 6e0efe69002 718572cf29a
Author: Joe Li <47...@users.noreply.github.com>
AuthorDate: Tue Oct 13 17:57:36 2026 +0000
Merge remote-tracking branch 'origin/master' into ultraset-mcp-chart-data-stale-query-context
docs/src/components/databases/DatabaseLogo.tsx | 62 ++++++++++++++++
docs/src/components/databases/DatabaseLogoWall.tsx | 9 ++-
docs/src/components/databases/DatabasePage.tsx | 32 +++++++--
docs/src/components/databases/types.ts | 2 +
docs/src/data/databases.json | 53 +++++++++-----
docs/src/pages/index.tsx | 11 ++-
docs/static/img/databases/clickhouse-dark.svg | Bin 0 -> 4480 bytes
docs/static/img/databases/clickhouse-mark-dark.svg | Bin 0 -> 960 bytes
docs/static/img/databases/clickhouse-mark.svg | Bin 0 -> 963 bytes
docs/static/img/databases/clickhouse.svg | Bin 0 -> 4483 bytes
pyproject.toml | 2 +-
superset/db_engine_specs/README.md | 1 +
superset/db_engine_specs/base.py | 2 +
superset/db_engine_specs/clickhouse.py | 78 +++++++++++++++++----
superset/db_engine_specs/postgres.py | 3 +-
15 files changed, 210 insertions(+), 45 deletions(-)
(superset) 01/02: fix(mcp): surface missing/stale query_context accuracy warnings in get_chart_data
Posted by jl...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
sadpandajoe pushed a commit to branch ultraset-mcp-chart-data-stale-query-context
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 6e0efe69002b20814926e90fd80fea4696f6d1bf
Author: Joe Li <47...@users.noreply.github.com>
AuthorDate: Tue Oct 13 17:57:30 2026 +0000
fix(mcp): surface missing/stale query_context accuracy warnings in get_chart_data
Chart has no saved query_context or the saved one no longer matches its
current params, get_chart_data only called ctx.warning(), an MCP log
notification that does not reach the tool's structured result. An LLM
client had no way to know the returned numbers skip Explore's buildQuery
logic (time grain, trend line, period comparison) or came from a stale
saved query_context. Thread the same warning into the response's
insights field so it travels with the data.
Fixes #45237
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
superset/mcp_service/chart/tool/get_chart_data.py | 24 +++-
.../mcp_service/chart/tool/test_get_chart_data.py | 148 +++++++++++++++++++++
2 files changed, 171 insertions(+), 1 deletion(-)
diff --git a/superset/mcp_service/chart/tool/get_chart_data.py b/superset/mcp_service/chart/tool/get_chart_data.py
index 26d6d578082..af22b3082b3 100644
--- a/superset/mcp_service/chart/tool/get_chart_data.py
+++ b/superset/mcp_service/chart/tool/get_chart_data.py
@@ -699,6 +699,14 @@ async def _get_chart_data( # noqa: C901
# Use the chart's saved query_context - this is the key!
# The query_context contains all the information needed to reproduce
# the chart's data exactly as shown in the visualization
+ #
+ # Surfaced in the response's ``insights`` (not only via
+ # ctx.warning, which is an MCP log notification and does not
+ # reach the client's tool result) whenever the returned data may
+ # not match what Explore/the dashboard would show: no saved
+ # query_context to begin with, or one that no longer matches the
+ # chart's current configuration.
+ query_context_accuracy_warning: str | None = None
query_context_json = None
if using_unsaved_state and cached_form_data_dict is not None:
form_data = cached_form_data_dict
@@ -783,6 +791,17 @@ async def _get_chart_data( # noqa: C901
await ctx.debug(
"Using chart's saved query_context for data retrieval"
)
+ if (
+ isinstance(query_form_data, dict)
+ and query_form_data != form_data
+ ):
+ query_context_accuracy_warning = (
+ "Chart's saved query_context is stale: it no longer "
+ "matches the chart's current configuration. Data may "
+ "not reflect the chart's latest settings. Re-open and "
+ "re-save the chart in Explore to refresh it."
+ )
+ await ctx.warning(query_context_accuracy_warning)
except (TypeError, ValueError) as exc:
if not _is_expected_json_load_error(exc):
raise
@@ -791,11 +810,12 @@ async def _get_chart_data( # noqa: C901
if query_context_json is None and not using_unsaved_state:
# Fallback: Chart has no saved query_context
# This can happen with older charts that haven't been re-saved
- await ctx.warning(
+ query_context_accuracy_warning = (
"Chart has no saved query_context. "
"Data may not match the chart visualization exactly. "
"Consider re-saving the chart to enable full data retrieval."
)
+ await ctx.warning(query_context_accuracy_warning)
# Try to construct from form_data as a fallback
effective_form_data = canonicalize_operation_form_data(
effective_form_data,
@@ -1049,6 +1069,8 @@ async def _get_chart_data( # noqa: C901
# Generate insights and recommendations
insights = []
+ if query_context_accuracy_warning:
+ insights.append(query_context_accuracy_warning)
if len(data) > 100:
insights.append(
"Large dataset - consider filtering for better performance"
diff --git a/tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py b/tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py
index 300f1df8abf..cd04e73b16b 100644
--- a/tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py
+++ b/tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py
@@ -5276,6 +5276,154 @@ class TestSavedDataFallbackSortDirection:
assert captured["order_desc"] is False
+class TestQueryContextAccuracyWarning:
+ """When get_chart_data cannot use a fresh, saved query_context, the
+ structured response must say so. ``ctx.warning`` alone is an MCP log
+ notification, not part of the tool result, so an LLM client has no way
+ to know the returned numbers skip the frontend's buildQuery logic
+ (time grain, trend line, period comparison) or came from a query_context
+ that no longer matches the chart's current params (#45237).
+ """
+
+ @pytest.mark.asyncio
+ async def test_missing_query_context_fallback_is_visible_in_response(
+ self, mcp_server: Any, mock_auth: Any, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
+ module = importlib.import_module(
+ "superset.mcp_service.chart.tool.get_chart_data"
+ )
+ chart = SimpleNamespace(
+ id=201,
+ slice_name="QoQ growth",
+ viz_type="big_number",
+ datasource_id=1,
+ datasource_type="table",
+ query_context=None,
+ params=json.dumps(
+ {
+ "viz_type": "big_number",
+ "metric": {"label": "SUM(revenue)"},
+ "time_grain_sqla": "P3M",
+ "compare_lag": 1,
+ "show_trend_line": True,
+ }
+ ),
+ )
+
+ class Command:
+ def __init__(self, _context: Any) -> None: ...
+ def validate(self) -> None: ...
+ def run(self) -> dict[str, Any]:
+ return chart_data_command_result(
+ [{"SUM(revenue)": 100}],
+ columns=["SUM(revenue)"],
+ coltypes=[GenericDataType.NUMERIC],
+ )
+
+ class _Factory:
+ def create(self, **_kwargs: Any) -> object:
+ return object()
+
+ monkeypatch.setattr(module, "find_chart_by_identifier", lambda *a, **k: chart)
+ monkeypatch.setattr(
+ module,
+ "validate_chart_dataset",
+ lambda *a, **k: SimpleNamespace(is_valid=True, warnings=[], error=None),
+ )
+ monkeypatch.setattr(
+ "superset.commands.chart.data.get_data_command.ChartDataCommand", Command
+ )
+ monkeypatch.setattr(
+ "superset.common.query_context_factory.QueryContextFactory", _Factory
+ )
+ monkeypatch.setattr(module, "set_query_context_form_data", lambda *_a: None)
+
+ async with Client(mcp_server) as client:
+ response = await client.call_tool(
+ "get_chart_data", {"request": {"identifier": 201}}
+ )
+
+ data = json.loads(response.content[0].text)
+ assert any(
+ "query_context" in insight.lower() for insight in data["insights"]
+ ), data["insights"]
+
+ @pytest.mark.asyncio
+ async def test_stale_saved_query_context_is_flagged_in_response(
+ self, mcp_server: Any, mock_auth: Any, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
+ module = importlib.import_module(
+ "superset.mcp_service.chart.tool.get_chart_data"
+ )
+ chart = SimpleNamespace(
+ id=202,
+ slice_name="QoQ growth",
+ viz_type="big_number",
+ datasource_id=1,
+ datasource_type="table",
+ # The saved query_context's embedded form_data still reflects the
+ # chart's old configuration (a plain sum, no comparison), while
+ # chart.params has since been edited (e.g. via the REST API) to
+ # add a period comparison. The saved context no longer matches.
+ query_context=json.dumps(
+ {
+ "datasource": {"id": 1, "type": "table"},
+ "form_data": {
+ "viz_type": "big_number",
+ "metric": {"label": "SUM(revenue)"},
+ },
+ "queries": [{"metrics": [{"label": "SUM(revenue)"}]}],
+ }
+ ),
+ params=json.dumps(
+ {
+ "viz_type": "big_number",
+ "metric": {"label": "SUM(revenue)"},
+ "time_grain_sqla": "P3M",
+ "compare_lag": 1,
+ "show_trend_line": True,
+ }
+ ),
+ )
+
+ class Command:
+ def __init__(self, _context: Any) -> None: ...
+ def validate(self) -> None: ...
+ def run(self) -> dict[str, Any]:
+ return chart_data_command_result(
+ [{"SUM(revenue)": 100}],
+ columns=["SUM(revenue)"],
+ coltypes=[GenericDataType.NUMERIC],
+ )
+
+ monkeypatch.setattr(module, "find_chart_by_identifier", lambda *a, **k: chart)
+ monkeypatch.setattr(
+ module,
+ "validate_chart_dataset",
+ lambda *a, **k: SimpleNamespace(is_valid=True, warnings=[], error=None),
+ )
+ monkeypatch.setattr(
+ "superset.commands.chart.data.get_data_command.ChartDataCommand", Command
+ )
+ monkeypatch.setattr(
+ "superset.charts.schemas.ChartDataQueryContextSchema.load",
+ lambda self, payload: SimpleNamespace(
+ queries=payload.get("queries", []), form_data=payload.get("form_data")
+ ),
+ )
+ monkeypatch.setattr(module, "set_query_context_form_data", lambda *_a: None)
+
+ async with Client(mcp_server) as client:
+ response = await client.call_tool(
+ "get_chart_data", {"request": {"identifier": 202}}
+ )
+
+ data = json.loads(response.content[0].text)
+ assert any("stale" in insight.lower() for insight in data["insights"]), data[
+ "insights"
+ ]
+
+
@pytest.mark.asyncio
@pytest.mark.parametrize(
"invalid_fields, message",