Skip to content

feat(mcp): add manage_native_filters tool#40960

Merged
aminghadersohi merged 10 commits into
apache:masterfrom
aminghadersohi:mcp-manage-native-filters
Jun 30, 2026
Merged

feat(mcp): add manage_native_filters tool#40960
aminghadersohi merged 10 commits into
apache:masterfrom
aminghadersohi:mcp-manage-native-filters

Conversation

@aminghadersohi

Copy link
Copy Markdown
Contributor

SUMMARY

Adds a new MCP tool, manage_native_filters, to the Superset MCP service. Native filters are what make dashboards interactive; the MCP server could previously read them (via get_dashboard_info) but not write them.

The tool exposes four operations on a dashboard's native filters, translated into the deleted / modified / reordered payload consumed by the existing UpdateDashboardNativeFiltersCommand (the same command behind the PUT /api/v1/dashboard/{pk}/filters endpoint):

  • add — create new filters from strict Pydantic specs (no raw JSON configs). v1 supports filter_select (dropdown: dataset + column target, multiSelect, defaultToFirstItem, enableEmptyFilter, sortAscending, searchAllOptions) and filter_time (time range with optional default). Other filter types (numerical range, time column, time grain) are documented as not yet supported.
  • update — partial updates addressed by filter ID. Because the backend command substitutes whole config entries (it does not merge deltas), the tool reads the dashboard's current native_filter_configuration and merges changes into FULL filter configs. Type-incompatible fields (e.g. default_time_range on a filter_select) are rejected.
  • remove — delete filters by ID (unknown IDs produce a descriptive error listing valid IDs).
  • reorder — complete ordered ID list. The DAO silently drops any surviving filter missing from the reordered list, so the tool validates the list covers every remaining filter; newly added filters are appended automatically.

Design notes:

  • Filter IDs are server-generated as NATIVE_FILTER-<random>, matching the frontend's generateFilterId convention, and returned in the response (added_filter_ids).
  • New filter configs follow the frontend Filter type shape: {id, type: "NATIVE_FILTER", filterType, name, description, scope, targets, controlValues, defaultDataMask, cascadeParentIds}.
  • Optional scope_chart_ids per filter is translated into the frontend's exclusion-list scope ({rootPath: ["ROOT_ID"], excluded: [...]}); IDs not on the dashboard are rejected. When omitted, the filter applies to all charts.
  • Dataset and column targets are validated via DatasetDAO with an error that lists available columns so LLM callers can self-correct.
  • Decorated with tags=["mutate"], class_permission_name="Dashboard", method_permission_name="write", and ToolAnnotations(readOnlyHint=False, destructiveHint=True) since remove deletes filters. DashboardForbiddenError / TagForbiddenError surface as structured permission_denied responses.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — MCP service only, no UI changes.

TESTING INSTRUCTIONS

  1. pytest tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py
  2. Or manually: start the MCP service, then call manage_native_filters with e.g. {"dashboard_id": 1, "add": [{"filter_type": "filter_select", "name": "Region", "dataset_id": 5, "column": "region"}]} and verify the filter appears on the dashboard's filter bar; exercise update, remove, and reorder similarly.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 35.68465% with 155 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.49%. Comparing base (805c12e) to head (f255d21).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
...cp_service/dashboard/tool/manage_native_filters.py 15.51% 147 Missing ⚠️
superset/mcp_service/dashboard/schemas.py 89.28% 6 Missing ⚠️
superset/daos/dashboard.py 80.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #40960      +/-   ##
==========================================
- Coverage   64.54%   64.49%   -0.05%     
==========================================
  Files        2665     2666       +1     
  Lines      146641   146875     +234     
  Branches    33880    33912      +32     
==========================================
+ Hits        94652    94733      +81     
- Misses      50271    50425     +154     
+ Partials     1718     1717       -1     
Flag Coverage Δ
hive 39.14% <32.36%> (-0.02%) ⬇️
mysql 57.78% <35.68%> (-0.08%) ⬇️
postgres 57.85% <35.68%> (-0.08%) ⬇️
presto 40.70% <32.36%> (-0.03%) ⬇️
python 59.26% <35.68%> (-0.09%) ⬇️
sqlite 57.48% <35.68%> (-0.08%) ⬇️
unit 100.00% <ø> (ø)

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.

@aminghadersohi
aminghadersohi marked this pull request as ready for review June 12, 2026 21:22
@aminghadersohi
aminghadersohi requested a review from eschutho June 12, 2026 21:24
@bito-code-review

bito-code-review Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #13a2f1

Actionable Suggestions - 0
Additional Suggestions - 2
  • superset/mcp_service/dashboard/tool/manage_native_filters.py - 1
    • Dead exception handler · Line 411-417
      The `except DashboardNotFoundError:` handler at line 411 is unreachable. `DashboardDAO.find_by_id` (line 361) delegates to `_find_by_column` in `superset/daos/base.py:313`, which calls `query.filter(...).one_or_none()` and returns `None` — it never raises `DashboardNotFoundError`. The only DAO method that raises this exception is `get_by_id_or_slug` (used elsewhere), not `find_by_id`. The not-found case is already handled by the `if not dashboard:` guard at line 362.
      Code suggestion
      --- a/superset/mcp_service/dashboard/tool/manage_native_filters.py
      +++ b/superset/mcp_service/dashboard/tool/manage_native_filters.py
       @@ -347,7 +347,6 @@ def manage_native_filters(
            from superset.commands.dashboard.exceptions import (
                DashboardForbiddenError,
                DashboardInvalidError,
      -        DashboardNativeFiltersUpdateFailedError,
      -        DashboardNotFoundError,
      +        DashboardNativeFiltersUpdateFailedError,
            )
            from superset.commands.dashboard.update import (
                UpdateDashboardNativeFiltersCommand,
       @@ -408,13 +407,6 @@ def manage_native_filters(
                    removed_filter_ids=list(request.remove),
                    filters=[_filter_summary(conf) for conf in configuration],
                )
      -
      -    except DashboardNotFoundError:
      -        return ManageNativeFiltersResponse(
      -            error=(
      -                f"Dashboard with ID {request.dashboard_id} not found."
      -                " Use list_dashboards to get valid dashboard IDs."
      -            ),
      -        )
            except DashboardForbiddenError:
  • tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py - 1
    • Unused import · Line 44-44
      This import is unused — all error-case tests inject exceptions directly via `patch(COMMAND_PATH, side_effect=)` rather than catching the exception class. Remove it to eliminate dead code and avoid misleading readers.
      Code suggestion
      --- a/tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py
      +++ b/tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py
       @@ -41,7 +41,6 @@ import pytest
        from fastmcp import Client
       
      -from superset.commands.dashboard.exceptions import DashboardForbiddenError
        from superset.mcp_service.app import mcp
        from superset.utils import json
Review Details
  • Files reviewed - 5 · Commit Range: 5bd5c9a..5bd5c9a
    • superset/mcp_service/app.py
    • superset/mcp_service/dashboard/schemas.py
    • superset/mcp_service/dashboard/tool/__init__.py
    • superset/mcp_service/dashboard/tool/manage_native_filters.py
    • tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

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

  • /review - 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

@aminghadersohi
aminghadersohi force-pushed the mcp-manage-native-filters branch from 5bd5c9a to 274f0f3 Compare June 25, 2026 19:04
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
Comment thread tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py Outdated
Comment thread tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py Outdated
Comment thread tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py Outdated
Comment thread tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py Outdated
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
@bito-code-review

bito-code-review Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #739ec1

Actionable Suggestions - 0
Additional Suggestions - 1
  • tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py - 1
    • Missing test coverage · Line 36-36
      The docstring lists 'Permission denied' as a covered case but omits the update+remove conflict, which is a documented validation path in the source (manage_native_filters.py line 312-315). Add a test e.g. `test_update_and_remove_same_filter_rejected` that passes `{"update": [{"id": "NATIVE_FILTER-existing1", "name": "X"}], "remove": ["NATIVE_FILTER-existing1"]}` and asserts the conflict error is returned.
Review Details
  • Files reviewed - 5 · Commit Range: 274f0f3..c76ff7d
    • superset/mcp_service/app.py
    • superset/mcp_service/dashboard/schemas.py
    • superset/mcp_service/dashboard/tool/__init__.py
    • superset/mcp_service/dashboard/tool/manage_native_filters.py
    • tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

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

  • /review - 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

@aminghadersohi
aminghadersohi force-pushed the mcp-manage-native-filters branch from c76ff7d to 45e1fc2 Compare June 26, 2026 14:43
@aminghadersohi

Copy link
Copy Markdown
Contributor Author

Addressed in the latest push (now 45e1fc2cd9): added test_update_and_remove_same_filter_rejected covering the update+remove conflict guard, and refreshed the test module docstring coverage list. The branch was also rebased onto current master.

Comment thread superset/mcp_service/dashboard/schemas.py
Comment thread tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py Outdated
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py Outdated
Comment thread superset/mcp_service/dashboard/schemas.py Outdated
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py Outdated
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py Outdated
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
@bito-code-review

bito-code-review Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #51f943

Actionable Suggestions - 0
Additional Suggestions - 3
  • tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py - 1
    • Incomplete test function stub · Line 349-352
      The test_update_unknown_filter_id function is currently empty. Please add a `with patch(DAO_FIND_BY_ID, return_value=dashboard)` block that calls `await _call` with an update request for a nonexistent filter ID and assert that the returned `data["error"]` contains the unknown filter ID.
  • superset/mcp_service/dashboard/tool/manage_native_filters.py - 2
    • Dead type cast wrapping · Line 267-268
      `cast(list[dict[str, Any]], ...)` at line 267 is a no-op at runtime — `typing.cast` performs no conversion and returns its first argument unchanged. The `sanitize_for_llm_context` return value satisfies the type already; the wrapper is pure dead code that clutters the call site. Additionally, the `cast` import on line 28 becomes unused after removing the wrapper.
      Code suggestion
      --- a/superset/mcp_service/dashboard/tool/manage_native_filters.py
      +++ b/superset/mcp_service/dashboard/tool/manage_native_filters.py
       @@ -25,7 +25,7 @@
        """
       
        import copy
        import logging
      -from typing import Any, cast
      +from typing import Any
       
        from fastmcp import Context
        from superset_core.mcp.decorators import tool, ToolAnnotations
       @@ -264,12 +264,9 @@ def _filter_summary(conf: dict[str, Any]) -> NativeFilterSummary:
                else None,
                filter_type=conf.get("filterType"),
                targets=cast(
      -            list[dict[str, Any]],
                    sanitize_for_llm_context(
                        targets,
                        field_path=("targets",),
                        excluded_field_names=frozenset(),
                    ),
      -        ),
      +        ),
            )
    • Missing reorder duplicate test · Line 367-367
      The duplicate-reorder guard on line 367 is implemented, but no test exercises it — leaving the logic invisible to future maintainers. Adding a `test_reorder_duplicate_filter_ids_rejected` test parallels the existing `test_update_duplicate_filter_ids_rejected` pattern.
Review Details
  • Files reviewed - 5 · Commit Range: b48bc42..99b6007
    • superset/mcp_service/app.py
    • superset/mcp_service/dashboard/schemas.py
    • superset/mcp_service/dashboard/tool/__init__.py
    • superset/mcp_service/dashboard/tool/manage_native_filters.py
    • tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

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

  • /review - 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/daos/dashboard.py Outdated
Comment thread superset/daos/dashboard.py
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
Comment thread superset/mcp_service/dashboard/schemas.py
@netlify

netlify Bot commented Jun 29, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 4b4db14
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a4419ac1a1f250008d778ba
😎 Deploy Preview https://deploy-preview-40960--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.

@aminghadersohi
aminghadersohi force-pushed the mcp-manage-native-filters branch from 0dbe17c to dc721df Compare June 29, 2026 16:47
Comment thread superset/mcp_service/dashboard/schemas.py
Comment thread superset/daos/dashboard.py Outdated
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
Comment thread superset/mcp_service/dashboard/tool/manage_native_filters.py
@aminghadersohi
aminghadersohi force-pushed the mcp-manage-native-filters branch from dc721df to 582ebf8 Compare June 29, 2026 17:32
@aminghadersohi

Copy link
Copy Markdown
Contributor Author

Addressing bito review findings from runs #13a2f1 and #51f943 (commit 582ebf8):

Dead type cast (run #51f943): Fixed — removed the runtime-noop cast(list[dict[str, Any]], ...) wrapper around sanitize_for_llm_context() in _filter_summary and dropped the now-unused cast import.

Missing reorder duplicate test (run #51f943): Fixed — added test_reorder_duplicate_filter_ids_rejected covering the len(set(request.reorder)) != len(request.reorder) guard in _build_native_filters_payload.

Incomplete test stub (run #51f943, test_update_unknown_filter_id): This test is already fully implemented — it patches the DAO, calls _call, and asserts on the error response. Bito flagged it in an earlier draft before the body was committed; the current HEAD is not empty.

Dead exception handler (run #13a2f1, except DashboardNotFoundError): Not dead. UpdateDashboardNativeFiltersCommand.run() calls super().validate() which calls DashboardDAO.find_by_id() and raises DashboardNotFoundError() if it returns None. This is a race-condition guard (dashboard deleted between the initial check and the command execution). Keeping it.

@aminghadersohi

Copy link
Copy Markdown
Contributor Author

Fixed pre-commit CI failures in 49c78fb:

  • ruff-format (test file): removed unnecessary parentheses around single-string assignment — auto-fixed by ruff format.
  • ruff C901 (update_native_filters_config complexity 11 > 10): The previous commit added a try/except guard that pushed McCabe complexity from 10 to 11. Simplified the modify/delete loop by pre-building a deleted_ids set and modified_map dict, replacing two generator-expression if branches and an explicit if modified_filter conditional with a single modified_map.get(conf_id, conf) call. Complexity now 8. No behaviour change — all 22 unit tests pass.

@rusackas rusackas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

UpdateDashboardNativeFiltersCommand doesn't process tags, so TagForbiddenError can't be raised on this path. This except looks copied from a general dashboard-update handler — safe to drop.

@rusackas

Copy link
Copy Markdown
Member

Also, the current_config snapshot read here (T1) and the re-read inside UpdateDashboardNativeFiltersCommand (T2) aren't covered by a shared lock or a version check on json_metadata. If another writer commits in between, a concurrently added filter gets silently dropped when this request also does a reorder (its id is in neither reordered nor modified, so the DAO's rebuild discards it). This mirrors existing REST behavior, but since this tool may be driven by an agent issuing frequent reorders, worth at least a docstring note — ideally re-read inside the command's transaction and reject if changed.

@aminghadersohi

Copy link
Copy Markdown
Contributor Author

Thank you @rusackas I will address your concerns and test end to end

@bito-code-review

bito-code-review Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #1c0c2b

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/mcp_service/dashboard/tool/manage_native_filters.py - 1
    • Unused import Context · Line 30-30
      The `Context` import and `ctx: Context` parameter in `manage_native_filters` are never used. Please remove the unused import from `fastmcp` and the unused `ctx` parameter to clean up dead code.
Review Details
  • Files reviewed - 6 · Commit Range: 1395f04..49c78fb
    • superset/daos/dashboard.py
    • superset/mcp_service/app.py
    • superset/mcp_service/dashboard/schemas.py
    • superset/mcp_service/dashboard/tool/__init__.py
    • superset/mcp_service/dashboard/tool/manage_native_filters.py
    • tests/unit_tests/mcp_service/dashboard/tool/test_manage_native_filters.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 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

aminghadersohi and others added 9 commits June 30, 2026 19:30
Address PR review feedback on manage_native_filters:

- Wrap the user-controlled filter name and target column names as untrusted
  content before returning them in the tool response, mirroring the
  get_dashboard_info read path (prevents prompt-injection via filter metadata).
- Reject duplicate filter IDs in the update list so later updates for the same
  filter are no longer silently dropped by the DAO's first-match resolution.
- Add docstrings to helper functions and type annotations to test helpers.
Add test_update_and_remove_same_filter_rejected and refresh the module
docstring coverage list (update-validation + sanitization cases). The
update+remove conflict guard was previously uncovered.
CodeAnt review: json_metadata can parse to a non-dict (e.g. a legacy
'[]' payload), which made metadata.get(...) raise AttributeError and
escape the structured error path. Extract _current_native_filter_config
to parse defensively (invalid JSON or non-dict -> empty list). Add a
regression test plus docstrings/type annotations flagged in review.
CodeAnt review:
- The request validator rejected an explicit empty reorder list because it
  used a falsy check; the payload builder already treats reorder is not None
  as an operation. Align the validator (check reorder is None) so
  {"reorder": []} is a valid no-op operation.
- native_filter_configuration could be a non-list or contain non-dict items,
  crashing payload building on conf["id"]/conf.get(...). Filter to dict
  entries in _current_native_filter_config.
Add regression tests and two helper docstrings flagged in review.
…ite path

- _filter_summary now applies escape_llm_context_delimiters to the
  operational id and filter_type fields so embedded delimiter tokens
  cannot disrupt outer LLM-context wrappers, while keeping the values
  usable verbatim as identifiers in subsequent tool calls.
  Added test_filter_summary_escapes_delimiter_tokens_in_operational_fields.

- DashboardDAO.update_native_filters_config now guards against non-dict
  json_metadata in the write path (e.g. legacy '[]' payloads), matching
  the robustness already in _current_native_filter_config for the read path.
Extract tampered_id into a local variable so the inline string
literal in the update request stays within the 88-char line limit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Drop the runtime-noop `cast(list[dict[str,Any]], ...)` wrapper around
  `sanitize_for_llm_context` in `_filter_summary` and remove the now-
  unused `cast` import.
- Wrap `json.loads` in `DashboardDAO.update_native_filters_config` with a
  `(json.JSONDecodeError, TypeError)` guard so corrupt `json_metadata`
  degrades to `{}` instead of crashing the write path with an unhandled
  exception (mirrors the existing guard in `_current_native_filter_config`).
- Add `test_reorder_duplicate_filter_ids_rejected` to cover the duplicate-
  ID check in `_build_native_filters_payload`, which was previously
  exercised only by source inspection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ply ruff formatting

`update_native_filters_config` hit complexity 11 (limit 10) after the
json.loads guard was added in the previous commit. Pre-build `deleted_ids`
set and `modified_map` dict to replace the two generator-if expressions and
the conditional append, dropping McCabe complexity from 11 to 8. No
behaviour change — tests confirm identical output.

Also apply ruff auto-format to test file (unnecessary parentheses removal).
@aminghadersohi
aminghadersohi force-pushed the mcp-manage-native-filters branch from 49c78fb to 4b4db14 Compare June 30, 2026 19:31
@aminghadersohi

Copy link
Copy Markdown
Contributor Author

Rebased onto master in 4b4db14 (was 38 commits behind; now clean on top of 805c12e). Conflict in superset/mcp_service/app.py: master had added duplicate_dashboard to the write-tool list; our branch adds manage_native_filters. Merged both. All 22 unit tests pass. All 38 review threads have replies; the one remaining unresolved thread (type-annotation nit, already declined + CodeAnt custom instruction saved) resolved via API.

Comment thread superset/mcp_service/dashboard/schemas.py
Comment thread superset/daos/dashboard.py
Comment thread superset/daos/dashboard.py
Comment thread superset/daos/dashboard.py
Comment thread superset/mcp_service/dashboard/schemas.py
A reorder validated against the pre-transaction snapshot can silently
drop a filter added by a concurrent writer between the snapshot read
(T1) and the DAO write (T2). This mirrors existing REST behaviour.
Document the limitation and the recommended retry pattern in the tool
docstring so callers (including LLM agents) are aware.
@aminghadersohi

Copy link
Copy Markdown
Contributor Author

@rusackas — you're right, and thank you for the detailed write-up. The race is real: a reorder=[F1,F2] validated at T1 can silently drop a filter F3 that another writer committed between T1 and the DAO write at T2, because the DAO's reorder pass only keeps filters in the ordered list.

This mirrors the existing REST write behavior (the REST update endpoint has the same gap), so it's not a regression, but it's worth calling out explicitly since an LLM-driven agent could issue frequent reorders.

For v1, I've added a concurrency note to the tool docstring in f255d21 documenting the limitation and recommending the read-then-compare retry pattern. A proper server-side guard (e.g. compare a hash of json_metadata at T1 and T2, rejecting if changed) would require reworking the DAO/command layer and is tracked as a follow-up.

@aminghadersohi
aminghadersohi merged commit bf88c62 into apache:master Jun 30, 2026
59 checks passed
@bito-code-review

Copy link
Copy Markdown
Contributor

Bito Automatic Review Skipped – PR Already Merged

Bito scheduled an automatic review for this pull request, but the review was skipped because this PR was merged before the review could be run.
No action is needed if you didn't intend to review it. To get a review, you can type /review in a comment and save it

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants