Skip to content

feat(mcp): support owners in update_dashboard tool#42002

Open
guilleov wants to merge 1 commit into
apache:masterfrom
guilleov:feat/mcp-update-dashboard-owners
Open

feat(mcp): support owners in update_dashboard tool#42002
guilleov wants to merge 1 commit into
apache:masterfrom
guilleov:feat/mcp-update-dashboard-owners

Conversation

@guilleov

Copy link
Copy Markdown

SUMMARY

The MCP update_dashboard tool can edit a dashboard's title, description, slug, published flag, tags, CSS, layout and json_metadata, but it had no way to set the dashboard's owners (and no other MCP tool sets owners either).

Because of this, a dashboard created through the MCP (generate_dashboard) is owned only by the MCP agent's user. An unpublished dashboard is visible only to its owners, so the human driving the agent cannot see or manage their own new dashboard, and no MCP client can transfer ownership to them. The only workarounds today are the Superset UI or direct metadata-DB access.

This PR adds an optional owners field to update_dashboard: a full-replacement list of user IDs, mirroring the REST dashboard owners semantics.

  • Unknown user IDs are rejected up front with an OwnersNotFound error.
  • An empty list clears all owners.
  • Omitting the field leaves owners unchanged.

Editorship is still enforced via raise_for_editorship, so only current owners or an Admin can change owners (same guarantee as the REST PUT).

Discussion: #42001

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — MCP tool schema/behavior change, no UI.

TESTING INSTRUCTIONS

Unit tests added in tests/unit_tests/mcp_service/dashboard/tool/test_update_dashboard.py:

  • test_update_owners_replaces_list — owners fully replaced with the resolved users; owners appears in changed_fields.
  • test_update_owners_unknown_id_errors — an unknown ID returns OwnersNotFound with no mutation and no commit.

Manual: call update_dashboard with {"identifier": <id>, "owners": [<user_id>, ...]} and verify owners change; a non-existent ID returns OwnersNotFound; an empty list clears owners; omitting the field leaves owners unchanged.

ADDITIONAL INFORMATION

Note: unit tests were added and the changed files pass py_compile, but the full test suite / pre-commit were not run locally (no dev environment available in this setup). Relying on CI to validate formatting, typing and tests.

The update_dashboard MCP tool could edit title, description, slug,
published, tags, css, layout and json_metadata, but there was no way to
change a dashboard's owners from MCP. A dashboard created by an MCP agent
is therefore owned only by that agent, and no MCP client can hand it to a
human owner.

Add an optional owners field (full-replacement list of user IDs, mirroring
the REST dashboard owners semantics): unknown IDs are rejected up front
with an OwnersNotFound error, an empty list clears owners, and omitting it
leaves owners unchanged. Editorship is still enforced via
raise_for_editorship, so only current owners or an Admin can change owners.
@dosubot dosubot Bot added the api:dashboard Related to the REST endpoints of the Dashboard label Jul 13, 2026
@bito-code-review

bito-code-review Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #3e475d

Actionable Suggestions - 0
Additional Suggestions - 2
  • superset/mcp_service/dashboard/tool/update_dashboard.py - 1
    • Missing tests for dedup and empty-clear · Line 156-177
      The deduplication behavior implemented in `_resolve_owners` (lines 167-171) and the empty-list clearing path (line 230: `if request.owners is not None`) are both untested despite being shipped in the diff.
  • superset/mcp_service/dashboard/schemas.py - 1
    • Missing test for owners clear edge case · Line 883-891
      The `owners` field documents that "An empty list clears all owners" but this behavior is not covered by any existing test. According to BITO.md rule [11730], new tools must cover edge cases. Add a test that passes `owners: []` to verify the clear-all-owners behavior is implemented correctly.
Filtered by Review Rules

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

  • tests/unit_tests/mcp_service/dashboard/tool/test_update_dashboard.py - 2
Review Details
  • Files reviewed - 3 · Commit Range: 438497f..438497f
    • superset/mcp_service/dashboard/schemas.py
    • superset/mcp_service/dashboard/tool/update_dashboard.py
    • tests/unit_tests/mcp_service/dashboard/tool/test_update_dashboard.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

# Full replacement of owners (empty list clears them). IDs are
# validated up front in _validate_update_request, so resolving here
# only maps the already-verified IDs to user objects.
users, _missing = _resolve_owners(request.owners)

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.

Suggestion: Add an explicit type annotation for the resolved owners variables (or annotate the retained variable after unpacking) so this new assignment is fully typed. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

The new unpacking assignment introduces local variables without an explicit type annotation even though the variable shapes are known from the helper return type. This matches the rule requiring type hints for new or modified Python variables that can be annotated.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/mcp_service/dashboard/tool/update_dashboard.py
**Line:** 234:234
**Comment:**
	*Custom Rule: Add an explicit type annotation for the resolved owners variables (or annotate the retained variable after unpacking) so this new assignment is fully typed.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

# A non-empty owners list must reference existing users. An empty list is
# a valid "clear all owners" request and needs no lookup.
if request.owners:
_users, missing = _resolve_owners(request.owners)

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.

Suggestion: Annotate the variable holding missing owner IDs in this new unpacking statement so the added validation path remains type-explicit. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

This is another new unpacking assignment that omits an explicit type annotation for the local variable missing, which can be annotated. That is a real violation of the type-hint rule.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/mcp_service/dashboard/tool/update_dashboard.py
**Line:** 298:298
**Comment:**
	*Custom Rule: Annotate the variable holding missing owner IDs in this new unpacking statement so the added validation path remains type-explicit.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +297 to +307
if request.owners:
_users, missing = _resolve_owners(request.owners)
if missing:
return DashboardError(
error=(
"Unknown owner user IDs: "
+ ", ".join(str(m) for m in missing)
+ ". Find valid IDs with list_users."
),
error_type="OwnersNotFound",
)

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.

Suggestion: Owner validation does database lookups without handling database exceptions, and this block runs before the tool’s SQLAlchemy error wrapper. If the user lookup query fails (for example during transient DB issues), the exception escapes as an unhandled tool failure instead of returning the structured DatabaseError response used elsewhere in this function. Wrap owner resolution in except SQLAlchemyError and return a consistent DashboardError. [logic error]

Severity Level: Major ⚠️
- ⚠️ Owner validation can crash update_dashboard on database errors.
- ⚠️ MCP clients receive unstructured internal errors for owner lookups.
- ⚠️ Owner changes unavailable during transient database connectivity issues.
Steps of Reproduction ✅
1. Invoke the MCP `update_dashboard` tool defined at
`superset/mcp_service/dashboard/tool/update_dashboard.py:63` with a request containing a
non-empty `owners` list (e.g. via FastMCP client invoking the `update_dashboard` tool).

2. The tool resolves the target dashboard via `_find_and_authorize_dashboard` at
`update_dashboard.py:53-99`, then calls `_validate_update_request(dashboard, request)` at
`update_dashboard.py:362-364` **outside** the surrounding `try/except SQLAlchemyError`
block that begins at `update_dashboard.py:112`.

3. Inside `_validate_update_request` at `update_dashboard.py:241-250`, when it reaches the
owners validation block at `update_dashboard.py:295-308`, it calls
`_resolve_owners(request.owners)` (definition at `update_dashboard.py:156-177`), which in
turn performs `security_manager.get_user_by_id(uid)` using SQLAlchemy-backed models.

4. During this lookup, simulate a transient database failure (e.g. disconnect or statement
error) so that `security_manager.get_user_by_id` raises `SQLAlchemyError`; because this
occurs in `_validate_update_request` before any local `try/except SQLAlchemyError` and
before the main `update_dashboard` `try/except SQLAlchemyError` (which only wraps
`_apply_field_updates` and `db.session.commit` at `update_dashboard.py:112-153`), the
exception bubbles up unhandled, causing the MCP tool call to fail with an internal error
instead of returning a structured `DashboardError(error_type="DatabaseError")` like the
tag-validation block at `update_dashboard.py:22-34`.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/mcp_service/dashboard/tool/update_dashboard.py
**Line:** 297:307
**Comment:**
	*Logic Error: Owner validation does database lookups without handling database exceptions, and this block runs before the tool’s SQLAlchemy error wrapper. If the user lookup query fails (for example during transient DB issues), the exception escapes as an unhandled tool failure instead of returning the structured `DatabaseError` response used elsewhere in this function. Wrap owner resolution in `except SQLAlchemyError` and return a consistent `DashboardError`.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 8.69565% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.00%. Comparing base (8f75f1a) to head (438497f).

Files with missing lines Patch % Lines
...set/mcp_service/dashboard/tool/update_dashboard.py 4.54% 21 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42002      +/-   ##
==========================================
+ Coverage   64.21%   65.00%   +0.78%     
==========================================
  Files        2744     2744              
  Lines      153571   153594      +23     
  Branches    35212    35218       +6     
==========================================
+ Hits        98621    99844    +1223     
+ Misses      53053    51843    -1210     
- Partials     1897     1907      +10     
Flag Coverage Δ
hive 39.05% <8.69%> (-0.01%) ⬇️
mysql 57.91% <8.69%> (?)
postgres 57.96% <8.69%> (?)
presto 41.04% <8.69%> (-0.01%) ⬇️
python 59.34% <8.69%> (+1.61%) ⬆️
sqlite 57.57% <8.69%> (-0.02%) ⬇️
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.

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

Labels

api:dashboard Related to the REST endpoints of the Dashboard size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant