Skip to content

feat(mcp): add typed Bullet chart support - #43770

Open
aminghadersohi wants to merge 108 commits into
apache:masterfrom
aminghadersohi:sc-119157-mcp-bullet
Open

aminghadersohi wants to merge 108 commits into
apache:masterfrom
aminghadersohi:sc-119157-mcp-bullet

Conversation

@aminghadersohi

@aminghadersohi aminghadersohi commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

SUMMARY

Adds typed MCP support for the ECharts Bullet plugin (chart_type: bullet, native viz_type: bullet), including schema discovery, metric/dimension normalization, query compilation, saved/cached updates, and ASCII/Vega-Lite previews.

Preserves the reviewed shared contracts: query-result validation and bounded serialization; pandas/NumPy temporal and numeric conversion; exact-result identity; final-response size preflight; filter/time-binding provenance and explicit-clear behavior; and existing BASE_AXIS, Deck.gl, Pivot, Jinja, and non-Bullet paths.

Finite maintainer pass:

  • Merges current upstream master through e22ce197 into Amin's branch using normal merge commits, with no rebase, force-push, or history rewrite. Conflict resolution retains both Bullet and upstream Gauge registration/schema/discovery, preview renderers, result normalization, and metric sorting.
  • Addresses sadpandajoe's short-label request: missing/empty range labels stay blank; marker/marker-line labels fall back per index to formatted numbers. Extra labels are ignored. This covers typed validation, containing-range tooltips, sorted bands, saved and cached FastMCP previews, and saved updates.
  • Addresses sadpandajoe's exact-case request: Region and region remain distinct through role validation, dataset normalization, sort-target resolution, query columns, and preview result keys. Exact duplicates and ambiguous non-exact lookups remain errors.
  • Fixes the confirmed newer bot report that explicit axis scales were written as truthy strings to native boolean controls.
  • Documents Bullet label and case-sensitive-name compatibility.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Screenshots

Bullet chart created by the MCP generate_chart tool (chart_type bullet), opened in Explore on the cleaned_sales_data examples dataset: SUM(sales) by product_line.

The rendered chart: qualitative range bands (Low / Target / Stretch), the Forecast marker line at 2.5M, and the Goal markers at 3.5M, all from the MCP config.

Captured at 4bec3812. Captured in a local docker-compose-light dev environment with the bundled examples dataset, driving this PR's MCP generate_chart tool.

N/A: MCP contract/query/preview changes; no frontend UI change. The frontend addition is a parity regression test. No live Superset server was available (localhost:8088/health failed), so live/manual UI verification is not claimed.

TESTING INSTRUCTIONS

Run from the repository root:

export PYTHONPATH="$PWD:$PWD/superset-core/src"
PY=/home/agorpg/tmp/sc119154-review-venv/bin/python

$PY -m pytest -q tests/unit_tests/mcp_service
$PY -m pytest -q tests/unit_tests/mcp_service/chart/test_bullet_chart.py \
  tests/unit_tests/mcp_service/chart/tool/test_get_chart_preview.py \
  tests/unit_tests/mcp_service/chart/test_gauge_chart.py \
  tests/unit_tests/mcp_service/chart/test_chart_helpers.py
$PY -m pytest -q tests/unit_tests/common \
  tests/unit_tests/queries/query_object_test.py tests/unit_tests/charts/data \
  tests/unit_tests/dataframe_test.py tests/unit_tests/sql_lab_test.py \
  tests/unit_tests/sql/execution/test_celery_task.py

cd superset-frontend
npm run test -- --maxWorkers=2 \
  plugins/plugin-chart-echarts/test/Bullet plugins/plugin-chart-echarts/test/Gauge \
  plugins/plugin-chart-pivot-table/test/plugin/buildQuery.test.ts \
  plugins/plugin-chart-pivot-table/test/plugin/utilities.test.ts \
  plugins/preset-chart-deckgl/src/layers/Geojson/Geojson.test.tsx \
  plugins/preset-chart-deckgl/src/layers/Polygon/Polygon.test.tsx \
  plugins/preset-chart-deckgl/src/layers/Path/Path.test.tsx
cd ..
pre-commit run --files $(git diff --name-only origin/master HEAD)
git diff --check

Results for this pass:

  • Focused Bullet/Gauge/query-helper/FastMCP suites: 450 passed.
  • Shared backend suites: 427 passed.
  • Frontend Bullet/Gauge/Pivot/Deck.gl: 9 suites, 122 passed.
  • Full MCP suite on the final source/head: 4,414 passed (350.81s).
  • Branch-wide pre-commit: all applicable hooks passed, including MyPy, frontend type check, Ruff, Pylint, and workflow audit. Python compilation and diff checks passed.
  • Local frontend declarations needed regeneration. The broader declaration build exposed an unrelated existing Contour.tsx:170 TS2352 error; no unrelated source change was made. Targeted branch type checks passed after declarations were emitted.
  • Current exact head: d753e0b0647db86223f60be4acb05d2ab09c476e. A focused post-merge Bullet/Gauge/Sunburst-fallback/update-path rerun passed 614 tests (1 skipped). Branch-wide pre-commit also passed. All required exact-head GitHub checks completed successfully.
  • GitHub's merge-state label remains BLOCKED because the review decision is REVIEW_REQUIRED, not because of conflicts or failing CI. No merge or approval is claimed.

ADDITIONAL INFORMATION

Work is confined to Amin's sc-119157-mcp-bullet. No Sunburst or community branch was modified, and no independent reviewer or further review loop was started.

All live reviews, inline comments, issue comments, and review threads were fetched again for this finite pass. No new actionable feedback or unresolved thread was present. Earlier addressed human threads were short labels and case-distinct dimensions. Both received individual fix/evidence replies after the normal push and are resolved. The newer bot's ambiguous temporal-binding coverage suggestion is already covered by test_bullet_rejects_ambiguous_provenance_subject_operator_matches, which exercises the live merge_update_form_data path. Optional deduplication refactors are deferred rather than broadening this finite pass.

  • Has associated issue
  • Introduces new feature or API
  • Changes UI
  • Includes DB Migration

Comment thread superset/mcp_service/chart/tool/get_chart_preview.py Outdated
@bito-code-review

Copy link
Copy Markdown
Contributor

The issue described is a logic error in the Bullet chart configuration mapping where range rectangles lack a category y encoding. This causes thresholds to render as chart-wide bands instead of being scoped to individual bullet rows.

To resolve this, you need to ensure that the range_rectangles (or equivalent threshold configuration) in the form_data mapping includes the necessary y encoding or grouping information that links them to the specific bullet row. Since the file superset/mcp_service/chart/tool/get_chart_preview.py is not present in the provided PR context, I cannot implement the fix directly. Please verify the mapping logic in that file to ensure the y encoding is correctly passed to the frontend configuration.

Comment thread superset/mcp_service/chart/preview_utils.py Outdated
Comment thread superset/mcp_service/chart/preview_utils.py Outdated
Comment thread superset/mcp_service/chart/schemas.py
Comment thread superset/mcp_service/chart/chart_utils.py Outdated
Comment thread superset/mcp_service/chart/plugins/bullet.py Outdated
@codecov

codecov Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.46895% with 381 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.84%. Comparing base (1267bd1) to head (7161e56).
⚠️ Report is 7 commits behind head on master.

Files with missing lines Patch % Lines
superset/mcp_service/chart/preview_utils.py 77.28% 62 Missing and 45 partials ⚠️
superset/mcp_service/chart/compile.py 44.82% 69 Missing and 27 partials ⚠️
superset/mcp_service/chart/schemas.py 79.47% 32 Missing and 30 partials ⚠️
superset/mcp_service/chart/chart_utils.py 86.57% 17 Missing and 23 partials ⚠️
superset/mcp_service/chart/plugins/bullet.py 89.68% 11 Missing and 12 partials ⚠️
superset/mcp_service/chart/chart_helpers.py 47.36% 13 Missing and 7 partials ⚠️
superset/mcp_service/chart/query_result.py 84.21% 14 Missing and 4 partials ⚠️
.../mcp_service/chart/validation/dataset_validator.py 50.00% 3 Missing and 3 partials ⚠️
superset/mcp_service/chart/tool/get_chart_data.py 73.68% 0 Missing and 5 partials ⚠️
superset/mcp_service/chart/validation/pipeline.py 72.72% 2 Missing and 1 partial ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43770      +/-   ##
==========================================
- Coverage   82.85%   82.84%   -0.02%     
==========================================
  Files        3017     3018       +1     
  Lines      192909   194576    +1667     
  Branches    44974    45431     +457     
==========================================
+ Hits       159836   161191    +1355     
- Misses      30034    30206     +172     
- Partials     3039     3179     +140     
Flag Coverage Δ
hive 34.93% <12.41%> (-0.37%) ⬇️
javascript 78.63% <ø> (+0.01%) ⬆️
mysql 53.05% <12.41%> (-0.67%) ⬇️
postgres 53.06% <12.41%> (-0.67%) ⬇️
presto 36.73% <12.41%> (-0.40%) ⬇️
python 86.39% <77.46%> (-0.11%) ⬇️
sqlite 52.81% <12.41%> (-0.67%) ⬇️
unit 80.14% <77.46%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bito-code-review bito-code-review Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #658aec

Actionable Suggestions - 1
  • superset/mcp_service/chart/chart_utils.py - 1
Additional Suggestions - 7
  • superset/mcp_service/chart/preview_utils.py - 2
    • SQL metric label gap · Line 328-341
      `_form_metric_label` reimplements `superset.utils.core.get_metric_name` but drops its SQL branch (`expressionType == "SQL"` returns `sqlExpression`). For a SQL adhoc metric without a `label`, this returns `None`, so `_bullet_result_roles` falls back to the first numeric column — which may be a dimension or a different metric, producing a wrong Bullet preview. Consider reusing `get_metric_name` or adding the SQL branch.
    • Empty ranges band divergence · Line 620-620
      When `ranges` is empty, `_bullet_numeric_tokens` returns `[]` and no range band is drawn. The native Bullet plugin (`transformProps.ts`) falls back to `[0, measure * 1.1]` when ranges are empty, so the preview diverges from the real chart. Consider deriving a default band from the metric values to stay faithful.
  • superset/mcp_service/chart/tool/get_chart_preview.py - 1
    • Duplicated bullet spec logic · Line 704-822
      `_bullet_chart_spec` reimplements metric/dimension resolution (lines 729-745) and the full layer-building already provided by `_bullet_result_roles`/`_canonical_result_field` and `_generate_bullet_vega_lite_preview` in preview_utils.py. The metric fallback also diverges: existing code picks the first quantitative field from the data row, this picks the last from `reversed(fields)`. Reuse the shared helpers to avoid divergent bullet rendering.
  • superset/mcp_service/chart/chart_utils.py - 1
    • Unhandled KeyError · Line 1122-1122
      `dimension_targets[order.column.casefold()]` raises `KeyError` when an `order_by` column matches neither the metric nor any dimension. The schema's `_adapt_native_order_by` accepts arbitrary bare column-name strings, so this is reachable from user input and crashes the update/preview tool. Use `.get(...)` and surface a validation error.
  • superset/mcp_service/chart/schemas.py - 2
    • Incomplete operator mapping · Line 2231-2232
      `_adapt_native_filters` maps `operator` using `{"==": "=", "IS_NOT_NULL": "IS NOT NULL"}`, but saved `adhoc_filters` store the `Operators` enum value (e.g. `'EQUALS'`, `'LESS_THAN'`, `'GREATER_THAN'`, `'IN'`, `'IS_NULL'`). Only `IS_NOT_NULL` maps correctly; `'=='` never occurs, and all other operators pass through unmapped and fail `FilterConfig.op` Literal validation, so saved bullet charts with filters won't load. Map the full enum set.
    • order_by validation/mapper divergence · Line 2371-2379
      `valid_order_targets` adds a dimension's `label` even when the dimension has no `name`, but `map_bullet_config`'s `dimension_targets` dict only maps label→name when `dimension.name` is present (`if candidate and dimension.name`). A dimension `{"label": "Region"}` passes this validation yet crashes the mapper with `KeyError` on `dimension_targets[order.column.casefold()]`. Gate the label on `dimension.name` to match.
  • tests/unit_tests/mcp_service/chart/test_bullet_chart.py - 1
    • Vacuous compile test · Line 347-348
      Within the provided diff, `test_bullet_compile_path_uses_groupby_metric_orderby_and_empty_results` ends at line 348 with `query` assigned but never asserted; `result` (line 347) is also unused. If the function is complete, the test is vacuous and would pass even if compiled queries were wrong. Add assertions on `query`/`result`, or remove the dead assignments. (If assertions follow line 348 and the diff was truncated, please disregard.)
Review Details
  • Files reviewed - 14 · Commit Range: 6411173..6411173
    • superset/mcp_service/chart/chart_helpers.py
    • superset/mcp_service/chart/chart_utils.py
    • superset/mcp_service/chart/plugins/__init__.py
    • superset/mcp_service/chart/plugins/bullet.py
    • superset/mcp_service/chart/preview_utils.py
    • superset/mcp_service/chart/resources/chart_configs.py
    • superset/mcp_service/chart/schemas.py
    • superset/mcp_service/chart/tool/generate_chart.py
    • superset/mcp_service/chart/tool/get_chart_data.py
    • superset/mcp_service/chart/tool/get_chart_preview.py
    • superset/mcp_service/chart/tool/get_chart_type_schema.py
    • superset/mcp_service/chart/tool/update_chart.py
    • superset/mcp_service/chart/tool/update_chart_preview.py
    • tests/unit_tests/mcp_service/chart/test_bullet_chart.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset/mcp_service/chart/chart_utils.py Outdated
@netlify

netlify Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 4d58b59
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ac9baaecfa4cf0008239e34
😎 Deploy Preview https://deploy-preview-43770--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@bito-code-review

bito-code-review Bot commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #d1f367

Actionable Suggestions - 0
Additional Suggestions - 6
  • superset/mcp_service/chart/query_result.py - 1
    • Error misclassified as malformed · Line 125-135
      A non-empty mapping with no recognized message field (e.g. `{"code": 500}`) now returns `malformed`, so `_failure_for_query_payload` short-circuits with `MalformedQueryResult` before checking other fields like a top-level `message`. Previously `_query_error_text` returned `None` and the caller fell through to the real message. Consider `continue` here so legitimate error objects with unrecognized keys still surface the actual message.
  • superset/mcp_service/chart/compile.py - 1
    • HAVING metric fallback unreachable · Line 233-234
      When `resolve_dataset_column` raises `ValueError` (ambiguous casefold match among physical columns), this branch returns `False` before the HAVING metric check at L237-240 runs. A valid HAVING saved-metric reference whose name casefold-matches 2+ physical columns is then rejected, whereas the previous `_column_exists` path accepted it. Fall through for HAVING clauses.
  • superset/mcp_service/chart/schemas.py - 1
    • Case-sensitive alias conflict check · Line 2361-2361
      The conflict check compares canonical names case-sensitively (`dimensions != groupby`), but `validate_roles_and_outputs` treats physical column identity case-insensitively via `name.casefold()` (line 2466), and SQL column identifiers are case-insensitive in most backends. So `dimensions=["Region"]` with `groupby=["region"]` (same column) is falsely rejected. Canonicalize with `name.casefold()` in `_canonical_dimension_alias` for consistency.
  • superset/mcp_service/chart/validation/dataset_validator.py - 1
    • Duplicated column resolution logic · Line 56-88
      `resolve_dataset_column` re-implements the exact-then-casefold matching and ambiguity `ValueError` already in `get_canonical_column_name` (lines 460-490) in this same file. Duplicating the logic risks divergence (e.g. one path changes matching rules, the other doesn't). Consider delegating to `get_canonical_column_name` and mapping the resolved name back to the column dict.
  • superset/mcp_service/chart/tool/update_chart_preview.py - 1
    • Dead code in production · Line 129-141
      `_preserve_previous_adhoc_filters` is never called in production — the update path at line 264 calls `merge_update_form_data` directly, and grep shows only the `def` plus test references. The new `previous_has_new_subject_filter`/`temporal_binding_changed` logic therefore has no effect on real previews, while the tests give false confidence it is active. Wire the wrapper into the production path or remove it.
  • superset/mcp_service/chart/plugins/bullet.py - 1
    • Misleading error message · Line 174-174
      `resolve_dataset_column` raises `ValueError` for two distinct causes: duplicate exact column names (dataset_validator.py:74) and casefold ambiguity (line 86). The hardcoded message "is ambiguous by case" is misleading for the duplicate-exact case. Consider wording that covers both, since `details=str(ex)` already carries the real reason.
Review Details
  • Files reviewed - 23 · Commit Range: 6411173..da1a1f1
    • superset/mcp_service/app.py
    • superset/mcp_service/chart/chart_helpers.py
    • superset/mcp_service/chart/chart_utils.py
    • superset/mcp_service/chart/compile.py
    • superset/mcp_service/chart/plugins/bullet.py
    • superset/mcp_service/chart/preview_utils.py
    • superset/mcp_service/chart/query_result.py
    • superset/mcp_service/chart/schemas.py
    • superset/mcp_service/chart/tool/get_chart_preview.py
    • superset/mcp_service/chart/tool/update_chart.py
    • superset/mcp_service/chart/tool/update_chart_preview.py
    • superset/mcp_service/chart/validation/dataset_validator.py
    • superset/mcp_service/chart/validation/pipeline.py
    • tests/unit_tests/mcp_service/chart/test_bullet_chart.py
    • tests/unit_tests/mcp_service/chart/test_chart_helpers.py
    • tests/unit_tests/mcp_service/chart/tool/test_update_chart.py
    • tests/unit_tests/mcp_service/chart/tool/test_update_chart_preview.py
    • tests/unit_tests/mcp_service/chart/validation/test_column_name_normalization.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_preview.py
    • superset/mcp_service/chart/tool/get_chart_data.py
    • tests/unit_tests/mcp_service/chart/test_query_result.py
    • tests/unit_tests/mcp_service/chart/tool/test_generate_chart.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #76a9c8

Actionable Suggestions - 0
Additional Suggestions - 6
  • superset/mcp_service/chart/chart_utils.py - 2
    • Wrong native control key · Line 1646-1646
      `y_axis_scale` is not a control name anywhere in the frontend plugins (0 matches), so writing it to `new_form_data` is ignored and the y-axis log scale won't apply for "xy" charts. The echarts timeseries control is `logAxis` — the same diff already uses `logAxis`/`logAxisSecondary` for `mixed_timeseries`. Use `logAxis` here for consistency.
    • Granularity clear unreliable · Line 1843-1844
      The current logic sets `granularity_sqla` whenever `is_column_truly_temporal` returns true, but that function defaults to true if the dataset or column isn't found. To ensure non-temporal columns clear `granularity_sqla`, update the code to only set it when the dataset lookup succeeds and `is_column_truly_temporal` explicitly confirms temporality.
  • tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py - 1
    • Decimal serialization mismatch · Line 2634-2634
      This asserts special Decimals serialize to strings (`"sNaN"`, `"NaN"`, `"Infinity"`), but the repo's Decimal encoder in `superset/utils/json.py` (line 93-94) returns `float(obj)`, and `float(Decimal("sNaN"))` raises `ValueError: cannot convert float NaN to integer`. If the tool-result path uses that encoder, this assertion fails. Verify the serializer actually used and align the expectation.
  • superset/mcp_service/chart/preview_utils.py - 1
    • Unsupported datetime dimension · Line 609-646
      The new dispatch raises BulletOutputError for any dimension value that isn't None/str/bool/int/float/Decimal. But the upstream validator `_unsafe_row_value` (query_result.py `_SAFE_ROW_SCALAR_TYPES`) explicitly whitelists `date`, `datetime`, `time`, `timedelta`, and `UUID` as safe row scalars, so a bullet chart with a date/datetime dimension passes validation then hits the `else` branch and fails with "unsupported value type". The prior code passed raw values through (`copied[dimension] = row[row_dimension]`), so this is a regression. Consider handling these types explicitly.
  • superset/mcp_service/chart/query_result.py - 1
    • Wrong metadata key · Line 426-426
      `cache_dttm` is not a key in the chart-data payload: `query_context_processor.py` builds it as `cached_dttm` (line 237). So `dict.get(query, "cache_dttm")` always returns `None` and this validation never fires. Use `cached_dttm` to match the payload key, otherwise the intended cache-timestamp guard is dead.
  • tests/unit_tests/mcp_service/chart/test_dashboard_time_binding.py - 1
    • Misleading test name · Line 489-489
      The test name `test_non_temporal_waterfall_granularity_falls_back_to_dataset_time_column` now contradicts its assertion: `granularity_sqla` is asserted `is None`, not a fallback to `order_date`. The name was left stale when the assertion changed from `== "region"`. Rename it so future maintainers don't misread the intended behavior.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/mcp_service/chart/tool/get_chart_preview.py - 1
Review Details
  • Files reviewed - 19 · Commit Range: da1a1f1..105c0f9
    • superset/mcp_service/chart/chart_utils.py
    • superset/mcp_service/chart/preview_utils.py
    • superset/mcp_service/chart/query_result.py
    • superset/mcp_service/chart/schemas.py
    • superset/mcp_service/chart/tool/get_chart_preview.py
    • tests/unit_tests/mcp_service/chart/test_bullet_chart.py
    • tests/unit_tests/mcp_service/chart/test_query_result.py
    • tests/unit_tests/mcp_service/chart/tool/test_update_chart_preview.py
    • superset/mcp_service/chart/compile.py
    • superset/mcp_service/chart/tool/get_chart_data.py
    • tests/unit_tests/mcp_service/chart/test_compile.py
    • tests/unit_tests/mcp_service/chart/test_preview_utils.py
    • superset/mcp_service/chart/tool/update_chart.py
    • superset/mcp_service/chart/tool/update_chart_preview.py
    • tests/unit_tests/mcp_service/chart/test_dashboard_time_binding.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_preview.py
    • tests/unit_tests/mcp_service/chart/tool/test_update_chart.py
    • superset/mcp_service/chart/plugins/waterfall.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #0e799f

Actionable Suggestions - 0
Additional Suggestions - 2
  • superset/mcp_service/chart/tool/get_chart_data.py - 1
    • Duplicated temporal identity logic · Line 514-561
      The `datetime` (514-535) and `datetime_time` (541-561) branches duplicate the same offset/naive/opaque resolution via `_trusted_utc_offset` and `id(value.tzinfo)`. If one branch is later updated (e.g. a new trusted tzinfo type), the other can silently diverge. Consider extracting a shared helper for the aware/naive/opaque decision.
  • tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py - 1
    • Duplicated test scaffolding · Line 2654-2710
      New test `test_saved_generic_get_data_rejects_misaligned_coltypes` duplicates ~40 lines of scaffolding (imports, `chart` construction, `_Command`, the four `patch` calls, `client.call_tool`) from `test_saved_get_data_rejects_hostile_rows_and_scalars`. Consider a shared helper so both malformed-result tests stay in sync when the tool's error handling changes.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/mcp_service/chart/tool/get_chart_data.py - 1
Review Details
  • Files reviewed - 7 · Commit Range: 105c0f9..ddd3296
    • superset/mcp_service/chart/chart_helpers.py
    • superset/mcp_service/chart/query_result.py
    • superset/mcp_service/chart/tool/get_chart_data.py
    • tests/unit_tests/mcp_service/chart/test_bullet_chart.py
    • tests/unit_tests/mcp_service/chart/test_chart_helpers.py
    • tests/unit_tests/mcp_service/chart/test_query_result.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #400d68

Actionable Suggestions - 0
Additional Suggestions - 6
  • superset/mcp_service/chart/tool/update_chart.py - 1
    • Rebind now fail-closed · Line 719-719
      This changes dataset-only rebinds from 'verify target dataset exists' to a full live compile against the target (`_compile_chart` via `validate_and_compile`). A rebind whose existing form_data references columns/metrics missing in the new dataset will now be rejected with no escape hatch, since `run_compile_check=False` was removed here and at line 755. Confirm this is intended and consider an opt-out for legitimate schema-differing rebinds.
  • superset/mcp_service/utils/cache_utils.py - 1
    • Duplicated security-relevant constants · Line 34-35
      `_TRUSTED_TZINFO_TYPES` and the 4096 length bound are redefined here but already exist in `chart/query_result.py` (`_TRUSTED_TZINFO_TYPES`, `_MAX_CACHE_STRING_LENGTH`). Since this allowlist gates which tzinfo values are accepted, a future change in one file would silently diverge from the other. Consider importing the shared constants.
  • superset/mcp_service/chart/compile.py - 1
    • Error message context lost · Line 551-551
      The refactor into `metric_error()` changes the saved-metric ambiguity error from `"saved metric"` to the generic `"query {query_index} metric"` role. The old message explicitly guided users toward saved metric names; the new one loses that context. Consider retaining the saved-metric qualifier in the role string.
  • superset/mcp_service/chart/tool/update_chart_preview.py - 1
    • Doc/design drift on fast path · Line 294-294
      This flips `update_chart_preview` to run Tier 2, which executes a live DB query via `_compile_chart` (`ChartDataCommand.run()`, row_limit=2) on every call — including when `generate_preview=False`. The `validate_and_compile` docstring (compile.py:750-752) and CLAUDE.md section 11 both still document this tool as opting out of Tier 2 for SLA reasons. If intentional, update both docs; otherwise keep `False`.
  • tests/unit_tests/mcp_service/chart/test_big_number_chart.py - 1
    • Cached branch omits merge chain · Line 102-104
      In the 'cached' branch of the test, after `merge_update_form_data(existing, form_data, config)`, also call `merge_table_column_config(existing, form_data)`, `merge_interactive_pivot_ui_config(existing, form_data)`, and `merge_same_viz_form_data(existing, form_data, config)` to match the merge sequence in update_chart_preview.py.
  • tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py - 1
    • Test coverage gap · Line 638-638
      The test discards the return of `_query_from_form_data`, so the `ChartDataCommand.run()` stub's data never flows through `query_result_data`, column inference, or `get_cache_status_from_result`. A regression in those steps wouldn't be caught. Consider asserting on the returned `ChartData` (e.g. `row_count`, `columns`) in addition to the captured query dicts.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/mcp_service/chart/tool/test_get_chart_preview.py - 1
Review Details
  • Files reviewed - 18 · Commit Range: ddd3296..63558bf
    • superset/mcp_service/chart/chart_helpers.py
    • superset/mcp_service/chart/compile.py
    • superset/mcp_service/chart/query_result.py
    • superset/mcp_service/chart/schemas.py
    • superset/mcp_service/chart/tool/get_chart_data.py
    • superset/mcp_service/chart/tool/update_chart.py
    • superset/mcp_service/chart/tool/update_chart_preview.py
    • tests/unit_tests/mcp_service/chart/test_chart_helpers.py
    • tests/unit_tests/mcp_service/chart/test_compile.py
    • tests/unit_tests/mcp_service/chart/test_query_result.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py
    • tests/unit_tests/mcp_service/chart/tool/test_update_chart.py
    • tests/unit_tests/mcp_service/chart/tool/test_update_chart_preview.py
    • superset/mcp_service/utils/cache_utils.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_preview.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_sql.py
    • tests/unit_tests/mcp_service/utils/test_cache_utils.py
    • tests/unit_tests/mcp_service/chart/test_big_number_chart.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #48577f

Actionable Suggestions - 0
Additional Suggestions - 6
  • superset/mcp_service/chart/tool/get_chart_data.py - 1
    • Nulls inflate unique_count · Line 579-579
      `unique_values.add(_safe_value_identity(value))` runs even when `value is None`, so nulls inflate `unique_count` by 1. This diverges from `format_data_columns` (response_utils.py:217-224), which only adds non-null values, contradicting the docstring's "identical metadata" contract. Use an `else` branch so nulls only increment `null_count`.
  • superset/mcp_service/dataset/tool/query_dataset.py - 1
    • assert-based validation stripped under -O · Line 327-335
      These `assert` statements are stripped when the interpreter runs with `-O`/`-OO`, so the shape guards silently disappear in optimized deployments. Since `query_result_data` already validates the envelope and returns `(None, ChartError)` on any malformation, the asserts are redundant; prefer explicit `DatasetError` checks or remove them so validation survives `-O` runs.
  • superset/mcp_service/chart/chart_helpers.py - 2
    • Inline import in loop · Line 708-708
      Inline import inside loop violates BITO.md rule [12745]. Move to module level (line 33) and remove from loop. No circular dependency exists with superset.utils.
    • Deck query builder too complex · Line 804-804
      `_build_deck_query` has too many branches (20 > 12) and statements (84 > 50). Extract each Deck.gl layer type (arc, geojson, scatter, polygon, path, grid/hex/heatmap/contour/screengrid) into its own helper function to reduce complexity.
  • tests/unit_tests/mcp_service/chart/test_chart_helpers.py - 1
    • Missing return type annotation · Line 2172-2172
      The new test `test_build_query_dicts_deck_geojson_preserves_grain_but_is_not_timeseries` lacks an explicit return annotation. Per the repo's BITO.md rule, all test functions must declare `-> None`. Add it to keep signatures consistent and mypy-clean.
  • superset/mcp_service/semantic_layer/tool/get_table.py - 1
    • Use of assert for type checking · Line 370-370
      The code uses `assert` statements for type narrowing (lines 370, 371, 373, 375, 378). Assertions can be disabled with `-O`, leading to potential runtime errors. Replace them with explicit checks that raise appropriate exceptions.
Review Details
  • Files reviewed - 18 · Commit Range: 63558bf..f8c142a
    • superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/Geojson.test.tsx
    • superset/mcp_service/chart/chart_helpers.py
    • superset/mcp_service/chart/compile.py
    • tests/unit_tests/mcp_service/chart/test_chart_helpers.py
    • tests/unit_tests/mcp_service/chart/test_compile.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_data.py
    • tests/unit_tests/mcp_service/chart/tool/test_get_chart_preview.py
    • tests/unit_tests/mcp_service/chart/tool/test_update_chart.py
    • superset/mcp_service/chart/query_result.py
    • superset/mcp_service/chart/tool/get_chart_data.py
    • superset/mcp_service/semantic_layer/tool/get_table.py
    • tests/unit_tests/mcp_service/chart/test_bullet_chart.py
    • tests/unit_tests/mcp_service/chart/test_query_result.py
    • tests/unit_tests/mcp_service/semantic_layer/tool/test_get_table.py
    • superset/mcp_service/dataset/tool/query_dataset.py
    • superset/mcp_service/utils/response_utils.py
    • tests/unit_tests/mcp_service/dataset/tool/test_query_dataset.py
    • tests/unit_tests/mcp_service/utils/test_response_utils.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

Copy link
Copy Markdown
Contributor

AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR).

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

Comment thread superset/mcp_service/chart/chart_utils.py Outdated
A saved grouped XY chart with truncate_metric strips the metric prefix
from the pivoted columns, so field discovery found no series and the
preview fell back to a single unlabeled series. With one metric and
truncate_metric set, every non-x-axis column is a category series.
The envelope validation in compile and the preview paths ran before the
plugin result normalizer and rejected any non-finite cell, so a grouped
Gauge with one Infinity dial failed instead of rendering its finite
dials. Honor the plugin's preserve_nonfinite_floats contract there, and
render the normalized rows in the saved Vega-Lite preview.
Bullet creation without row_limit saved no limit, unlike the advertised
schema default. The mapper always emits the limit; update merging keeps
the saved value when the caller omits it.
No MCP tool exports Parquet: get_chart_data accepts json, csv, and excel,
and query_dataset and get_table have no export option.
build_mixed_timeseries_secondary and with_x_axis_column have no callers
since the Mixed Timeseries plugin builds both layers through the shared
timeseries query builder.
Chart-data results unescape flattened column names, so a metric label
containing the separator never matched its escaped prefix and the
preview lost its category series. Match the unescaped label as well as
the escaped spelling from raw post-processing output.
Table buildQuery drops a cached duration grain from semantic-view
aggregate queries when no selected column is temporal, because the
semantic layer rejects a grain without a time column. The MCP Table
builder copied the grain into extras, so get_chart_data failed for a
chart that renders in Explore. Apply the same rule to every final query.
Rows that fit the source result budget can still exceed the response
budget once column profiling repeats their cells in sample_values.
Exercise that guard through the registered tool.
Restating the saved dimensions in a Bullet update made the merged
validation copy treat an inherited independent sort, such as a saved
ranking metric, as newly authored and reject it. Exclude the inherited
ordering unless the update supplies order_by, as for groupby and
filters; the native query contract still validates it.
Comment thread superset/mcp_service/chart/preview_utils.py Outdated
Comment thread superset/mcp_service/chart/preview_utils.py Outdated
Comment thread superset/mcp_service/chart/tool/update_chart_preview.py Outdated
A single compared metric renames its shifted series to the bare offset,
so the metric-prefix filter dropped the comparison series from grouped
previews. Also escape the x-axis field in the encoding and tooltip, as
for folded series, so a dotted column name is not read as a nested path.
The rebind prune only checked list-valued column roles, so a saved
Bullet with a scalar groupby kept a hierarchy the replacement dataset
cannot resolve and the query failed on a missing column. Treat a scalar
value as a one-item list, as the frontend ensureIsArray does.
Comment thread superset/mcp_service/chart/chart_utils.py Outdated
Comment thread superset/mcp_service/chart/chart_utils.py Outdated
Comment thread superset/mcp_service/chart/preview_utils.py Outdated
Comment thread superset/mcp_service/chart/query_result.py Outdated
Comment thread superset/mcp_service/chart/chart_helpers.py Outdated
- Treat a saved chart's viz_type column as authoritative when params omit it,
  and treat a missing saved viz_type as a visualization boundary when merging
  filters.
- Drop a null dashboard time-subject marker from the Bullet validation copy
  regardless of filters.
- Infer the XY pivot preview x type from parseable dates instead of a
  character scan, so labels such as "New York" stay nominal.
- Null infinities nested inside array/object cells like NaN instead of
  rejecting the whole result.
- Remove the unused Deck.gl column/metric/null-filter resolvers and tests.
Comment thread superset/mcp_service/chart/preview_utils.py Outdated
Comment thread superset/mcp_service/semantic_layer/tool/get_table.py Outdated
Comment thread superset/mcp_service/chart/chart_helpers.py Outdated
Comment thread superset/mcp_service/chart/preview_utils.py Outdated
Comment thread superset/mcp_service/semantic_layer/tool/get_table.py Outdated
Comment thread tests/unit_tests/mcp_service/chart/tool/test_update_chart_preview.py Outdated
Master landed the typed Sunburst chart (apache#43771), whose shared MCP chart
infrastructure (native query builders in form_data_query_context, result
envelope validation, preview/update hooks, response preflight) overlaps the
shared code this branch had grown independently. Resolve onto master's
shared implementation and port the Bullet-specific behavior onto it:

- Bullet plugin: build_query_dicts mirrors Bullet/buildQuery, table previews
  are rejected through unsupported_preview, null data is malformed, and
  same-viz updates keep unmodeled native controls like the shared overlay.
- Result validation gains an opt-in Chart Data temporal mode (epoch-ms
  numbers and duration text) used by plugins that set temporal_json_numbers.
- get_chart_data honors allows_empty_data_result and sanitize_data_rows, and
  empty results still export their header row.
- Compile checks optionally validate native QueryObject references for
  plugins that set validates_native_references.
- Dataset rebinds follow master's contract (saved query roles are not
  inherited), so Bullet rebind tests that expected inherited hierarchies
  were removed.

Shared-behavior tests from this branch that targeted the superseded
implementation were dropped in favor of master's suites; Bullet tests that
lived in shared test modules moved to test_bullet_tool_paths.py.
…iew regressions

Folded grouped XY Vega-Lite previews encoded a quantitative y with a nominal
color and no stack, so Vega-Lite stacked unstacked grouped bars and areas.
The y encoding now follows the saved stack control (null when unstacked,
zero for Stack, normalize for Expand).

Add regression tests for the other review findings, which the shared
master implementation resolves:
- the compile sample skips the Prophet step for grouped series whose two
  sample rows share one timestamp;
- get_table reports grain-variant temporal columns as temporal for empty
  results too;
- update_chart_preview's access check runs against a real dataset lookup
  and blocks compile and cache writes.
Comment thread superset/mcp_service/chart/preview_utils.py Outdated
Comment thread superset/mcp_service/chart/schemas.py
Comment thread superset/mcp_service/chart/schemas.py Outdated
…order aliases, keep native provenance through request normalization
Comment thread superset/mcp_service/chart/chart_utils.py
# Conflicts:
#	superset/mcp_service/chart/schemas.py
#	superset/mcp_service/chart/tool/generate_chart.py
Comment thread superset/mcp_service/chart/plugins/bullet.py
Comment thread superset/mcp_service/chart/preview_utils.py
Comment thread superset/mcp_service/chart/compile.py Outdated
Comment thread superset/mcp_service/chart/plugins/bullet.py Outdated
… bar previews faithful

- Do not restore the dashboard time-binding marker once the merge decided
  the adhoc filter sequence, so a filters=[] update validates.
- Export every validated ungrouped Bullet source row instead of only the
  rendered first row.
- Offset unstacked folded bars by series so they render side by side.
- Remove unreachable Timeseries/Mixed/Deck raw-control checks from the
  Bullet-only native reference validator.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

doc Namespace | Anything related to documentation plugins size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants