Compare commits

...

6 Commits

Author SHA1 Message Date
Evan Rusackas
24a791f304 fix(mcp): drop remaining ENABLE_VIEWERS no-effect claims in tool metadata
Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 04:17:13 -07:00
Evan Rusackas
4ebe508d9f docs(mcp): align dashboard roles/owners tool text with ENABLE_VIEWERS and error-path behavior
Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 00:55:16 -07:00
Evan Rusackas
aa31bfde37 docs(mcp): don't claim governance tools block directory lookups
Removal of an unassigned ID returns the current owner/role ID list in the error.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 11:29:10 -07:00
Evan Rusackas
f34049e925 fix(mcp): correct ENABLE_VIEWERS docs to match raise_for_access behavior
An empty ROLE-type list only restores normal permissions if no USER-
or GROUP-type viewers remain, since raise_for_access checks the whole
viewers list, not just roles.

Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 06:53:43 -07:00
Evan Rusackas
d0c8a71ccb fix(mcp): correct ENABLE_VIEWERS docs to match raise_for_access behavior
Co-Authored-By: Evan Rusackas <evan@preset.io>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-23 06:53:42 -07:00
rusackas
34f5025398 docs(mcp): document dashboard owner/role/certification governance tools
The manage_dashboard_owners, manage_dashboard_roles, and
manage_dashboard_certification MCP tools (#41606) added safety semantics
(owner-removal guards, ENABLE_VIEWERS gating, certified_by
clear-vs-unchanged convention) that weren't covered anywhere in the MCP
tools reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 06:53:42 -07:00
5 changed files with 89 additions and 16 deletions

View File

@@ -462,6 +462,78 @@ async def metrics_guide(ctx: Context) -> str:
"""
```
## Built-in Dashboard Governance Tools
Superset ships three built-in MCP tools for dashboard governance:
`manage_dashboard_owners`, `manage_dashboard_roles`, and
`manage_dashboard_certification`. They exist alongside the generic
`update_dashboard` tool because a few governance fields are unsafe to expose
as full-replacement lists to an LLM caller — each tool instead takes
explicit, narrow operations with its own safety semantics.
### `manage_dashboard_owners`
Adds or removes dashboard owners (the USER-type entries in the dashboard's
Subject-based `editors` list) via `add_owner_ids`/`remove_owner_ids`, never
a full-replacement list. This guards against the "empty owners" footgun:
`update_dashboard` intentionally dropped its `owners` field because a
full-replacement list has no way to prevent an admin from emptying it
outright.
- **Owner-removal guard**: a request that would leave the dashboard with
zero owners is rejected. To transfer ownership, add the new owner in the
same call as removing the last existing one.
- **Self-removal protection**: a non-admin caller who removes themselves is
automatically re-added, mirroring the lockout protection
`update_dashboard` already relies on (unless an `EXTRA_EDITORS_RESOLVER`
is configured). The response's `warnings` reports when this happens.
- ROLE- or GROUP-type editors already on the dashboard are left untouched.
- A no-op request (e.g. "adding" an ID that's already an owner) returns an
empty `owners` list rather than the full current set. Requesting removal
of an ID that isn't an owner is rejected with an error that lists the
current owner IDs, so an editor can still read that list.
### `manage_dashboard_roles`
Adds or removes dashboard access roles (the ROLE-type entries in the
dashboard's Subject-based `viewers` list) via
`add_role_ids`/`remove_role_ids`, never a full-replacement list. This
guards against silently widening or narrowing who can see a dashboard,
which is why `update_dashboard` dropped its `roles` field.
- **`ENABLE_VIEWERS` reporting**: the response reports the flag's current
state via `viewers_enabled` and `warnings` notes when it's disabled — but
that warning is informational only. `raise_for_access` enforces a
nonempty `viewers` list on a published dashboard regardless of the flag,
so a role added here restricts access immediately even with
`ENABLE_VIEWERS` off. Removing every ROLE-type entry only restores normal
permissions if no USER- or GROUP-type viewers remain — since
`raise_for_access` checks the whole `viewers` list, not just roles, any
such subjects left on the dashboard keep it restricted.
- USER- or GROUP-type viewers already on the dashboard are left untouched.
- Like `manage_dashboard_owners`, a no-op request returns an empty `roles`
list instead of the full current set. Requesting removal of an unassigned
role ID is rejected with an error that lists the current role IDs, so an
editor can still read that list.
### `manage_dashboard_certification`
Sets or clears the `certified_by`/`certification_details` badge fields,
split out from `update_dashboard` because certification is a distinct
governance concern from layout/theme/metadata edits.
- **Clear-vs-unchanged convention**: `certified_by` and
`certification_details` are independent optional fields — omit a field
(`None`) to leave it unchanged, pass an empty string to clear it, or pass
a value to set it. This mirrors the `slug`/`css` clear-with-empty-string
convention already used by `update_dashboard`.
- Both fields are sanitized before being stored, since they render as a UI
badge/tooltip next to the dashboard title. An input that sanitizes down
to nothing (HTML-only or whitespace-only) is rejected rather than
silently clearing the field — callers must pass an explicit empty string
to clear.
- The response's `changed_fields` lists which fields were actually applied.
## Next Steps
- **[Development](./development.md)** - Project structure, APIs, and dev workflow

View File

@@ -139,7 +139,7 @@ Dashboard Management:
- remove_chart_from_dashboard: Remove a chart from an existing dashboard (requires write access)
- restore_dashboard: Restore a soft-deleted dashboard from trash by ID/UUID (requires editor rights — owner or Admin; only applies to dashboards trashed under the SOFT_DELETE feature flag)
- manage_dashboard_owners: Add/remove dashboard owners by explicit operation (requires write access; rejects changes that would leave zero owners)
- manage_dashboard_roles: Add/remove dashboard access roles by explicit operation (requires write access; only affects access when ENABLE_VIEWERS is enabled)
- manage_dashboard_roles: Add/remove dashboard access roles by explicit operation (requires write access; applies regardless of the ENABLE_VIEWERS flag)
- manage_dashboard_certification: Set or clear a dashboard's certified_by/certification_details badge (requires write access)
Annotation Layers:

View File

@@ -1224,9 +1224,9 @@ class ManageDashboardRolesResponse(DashboardMutationErrorFields):
default=False,
description=(
"Whether the ENABLE_VIEWERS feature flag is enabled on this "
"instance. When False, dashboard viewers are stored but have no "
"effect on access control — access still follows normal "
"Superset permissions/editorship."
"instance. Access checks restrict access to any nonempty viewers "
"list regardless of this flag, so role changes take effect "
"either way."
),
)
warnings: list[str] = Field(

View File

@@ -279,8 +279,8 @@ def manage_dashboard_owners(
up current owners — those remain off-limits per the server instructions.
A request that has no effective change (e.g. "adding" an ID that is
already an owner) returns an empty ``owners`` list rather than the full
current set, so this tool cannot be used as a disguised directory
lookup.
current set. Requesting removal of an unassigned owner ID is an error
that includes the current owner IDs.
Example::

View File

@@ -171,12 +171,13 @@ def manage_dashboard_roles(
Add or remove dashboard access roles with explicit operations.
Dashboard access roles restrict who can view a dashboard to members of
the listed roles, on top of normal Superset permissions. An empty roles
list means "no role restriction" — the dashboard is visible per standard
permissions instead. This only takes effect when the ``ENABLE_VIEWERS``
feature flag is enabled; the response's ``viewers_enabled`` field
reports whether it is, and ``warnings`` notes when a change was applied
but has no live effect.
the listed roles, on top of normal Superset permissions. Removing every
role only restores standard permissions if no USER- or GROUP-type
viewers remain on the dashboard — ``raise_for_access`` restricts access
whenever the ``viewers`` list is nonempty, regardless of subject type.
This applies regardless of the ``ENABLE_VIEWERS`` feature flag; the
response's ``viewers_enabled`` field reports the flag's state, and
``warnings`` notes when it is disabled.
Roles are the ROLE-type entries in the dashboard's Subject-based
``viewers`` list. Any USER- or GROUP-type viewers already on the
@@ -193,8 +194,8 @@ def manage_dashboard_roles(
look up current roles — those remain off-limits per the server
instructions. A request that has no effective change (e.g. "adding" a
role that is already assigned) returns an empty ``roles`` list rather
than the full current set, so this tool cannot be used as a disguised
directory lookup.
than the full current set. Requesting removal of an unassigned role ID
is an error that includes the current role IDs.
Example::
@@ -222,8 +223,8 @@ def manage_dashboard_roles(
if not viewers_enabled:
warnings.append(
"The ENABLE_VIEWERS feature flag is disabled on this instance; "
"dashboard viewers will be stored but have no effect on access "
"control until it is enabled."
"dashboard access checks still restrict access to any nonempty "
"viewers list, so role changes take effect regardless."
)
current_role_ids, new_role_ids, compute_error = _compute_new_role_ids(