Skip to content

feat(mcp): add list and get tools for row level security and plugins#40347

Open
aminghadersohi wants to merge 5 commits into
apache:masterfrom
aminghadersohi:amin/mcp-rls-plugins
Open

feat(mcp): add list and get tools for row level security and plugins#40347
aminghadersohi wants to merge 5 commits into
apache:masterfrom
aminghadersohi:amin/mcp-rls-plugins

Conversation

@aminghadersohi
Copy link
Copy Markdown
Contributor

SUMMARY

Adds four new MCP (Model Context Protocol) tools to Apache Superset across two new domains:

Row Level Security (superset/mcp_service/rls/):

  • list_rls_filters — list RLS filters with filtering, search, column selection, sorting, and pagination. Requires admin access.
  • get_rls_filter_info — get full RLS filter details by ID (name, type, tables, roles, clause, group key). Requires admin access.

Dynamic Plugins (superset/mcp_service/plugin/):

  • list_plugins — list registered dynamic plugins with filtering, search, column selection, sorting, and pagination.
  • get_plugin_info — get plugin details by ID (name, key, bundle_url, timestamps).

Both domains follow the established MCP service patterns:

  • @tool decorator with class_permission_name for RBAC ("Row Level Security", "DynamicPlugin")
  • ModelListCore / ModelGetInfoCore from mcp_core.py for reusable list/get logic
  • Pydantic schemas with model_serializer for column-filtered responses
  • ColumnOperator/ColumnOperatorEnum for structured filter objects
  • event_logger instrumentation and FastMCP ctx logging

The DynamicPluginDAO is co-located in superset/mcp_service/plugin/dao.py since no top-level DAO existed for the DynamicPlugin model.

The DEFAULT_INSTRUCTIONS in app.py are updated to clarify that get_schema covers chart/dataset/dashboard/database resource types only; RLS and plugin tools document their filterable/sortable columns inline in their docstrings.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — backend MCP tools only.

TESTING INSTRUCTIONS

  1. Start the MCP service
  2. Connect with an MCP client (e.g. Claude Desktop)
  3. Call list_rls_filters — returns paginated RLS filters with id, name, filter_type, clause by default
  4. Call list_rls_filters with select_columns: ["id", "name", "tables", "roles"] — includes relationship data
  5. Call get_rls_filter_info with a valid RLS filter ID — returns full filter details
  6. Call list_plugins — returns paginated plugin list with id, name, key, bundle_url by default
  7. Call get_plugin_info with a valid plugin ID — returns full plugin details

Unit tests: pytest tests/unit_tests/mcp_service/rls/ tests/unit_tests/mcp_service/plugin/

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

aminghadersohi and others added 5 commits May 21, 2026 20:10
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er roles to be returned

RLS filter `roles` (which roles a filter applies to) are core RLS data,
not user-directory metadata. Including 'roles' in USER_DIRECTORY_FIELDS
caused filter_user_directory_columns() to strip it from any requested
select_columns list, making it impossible to retrieve via list_rls_filters.

No dashboard/chart/dataset schema defines a 'roles' field, so removing it
from the block set has no privacy impact on other tools.

Fixes test_list_rls_filters_returns_tables_and_roles.
…in RLS list tool

'roles' on a dashboard/chart exposes who has access to the resource and
should be stripped by the USER_DIRECTORY_FIELDS privacy filter.

'roles' in an RLS filter is which roles the filter applies to — it is
core filter data, not user-directory metadata. The RLS list tool now
derives its column selection directly from ALL_RLS_COLUMNS (bypassing
ModelListCore's USER_DIRECTORY_FIELDS filtering) so that RLS roles are
selectable while dashboard roles remain hidden.

Fixes three failing unit tests:
- test_list_dashboards_omits_requested_user_directory_fields
- test_get_allowed_fields_always_denies_user_directory_fields
- test_filter_sensitive_data_strips_user_directory_fields_even_if_allowed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@aminghadersohi aminghadersohi marked this pull request as ready for review May 22, 2026 04:04
@dosubot dosubot Bot added api Related to the REST API authentication:row-level-security Related to Row Level Security plugins labels May 22, 2026
@aminghadersohi aminghadersohi requested a review from geido May 22, 2026 04:05
Copy link
Copy Markdown
Member

@geido geido left a comment

Choose a reason for hiding this comment

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

Reviewed carefully given the sensitivity of RLS data. class_permission_name="Row Level Security" is in ADMIN_ONLY_VIEW_MENUS, so non-admins cannot see filter clauses, tables, or associated roles. MCP_RBAC_ENABLED defaults to True and check_tool_permission enforces can_read at call time. The deliberate bypass of USER_DIRECTORY_FIELDS for the RLS roles column is correct — that field represents policy scope, not user directory metadata, and the bypass only applies after the Admin gate has passed. Plugin tools expose only non-sensitive metadata.

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review Agent Run #f6e20e

Actionable Suggestions - 1
  • superset/mcp_service/plugin/tool/list_plugins.py - 1
    • Type mismatch in serializer signature · Line 78-78
Additional Suggestions - 1
  • superset/mcp_service/rls/tool/list_rls_filters.py - 1
    • Serializer naming inconsistency · Line 78-78
      Rename the `_serialize` function to `_serialize_rls_filter` and update the `item_serializer` argument accordingly to match naming conventions used by other list tools.
Filtered by Review Rules

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

  • superset/mcp_service/rls/tool/get_rls_filter_info.py - 1
    • Async/sync context manager mismatch · Line 69-78
  • superset/mcp_service/plugin/schemas.py - 1
    • Missing filterable columns in schema · Line 62-62
Review Details
  • Files reviewed - 19 · Commit Range: 10ea6da..6e3a325
    • superset/mcp_service/app.py
    • superset/mcp_service/plugin/__init__.py
    • superset/mcp_service/plugin/dao.py
    • superset/mcp_service/plugin/schemas.py
    • superset/mcp_service/plugin/tool/__init__.py
    • superset/mcp_service/plugin/tool/get_plugin_info.py
    • superset/mcp_service/plugin/tool/list_plugins.py
    • superset/mcp_service/privacy.py
    • superset/mcp_service/rls/__init__.py
    • superset/mcp_service/rls/schemas.py
    • superset/mcp_service/rls/tool/__init__.py
    • superset/mcp_service/rls/tool/get_rls_filter_info.py
    • superset/mcp_service/rls/tool/list_rls_filters.py
    • tests/unit_tests/mcp_service/plugin/__init__.py
    • tests/unit_tests/mcp_service/plugin/tool/__init__.py
    • tests/unit_tests/mcp_service/plugin/tool/test_plugin_tools.py
    • tests/unit_tests/mcp_service/rls/__init__.py
    • tests/unit_tests/mcp_service/rls/tool/__init__.py
    • tests/unit_tests/mcp_service/rls/tool/test_rls_tools.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

try:
from superset.mcp_service.plugin.dao import DynamicPluginDAO

def _serialize(obj: object, cols: list[str] | None) -> PluginInfo | None:
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.

Type mismatch in serializer signature

The _serialize function has cols: list[str] | None but ModelListCore expects item_serializer: Callable[[T, List[str]], S | None] where the second argument is List[str] (non-optional). The cols parameter is also unused in the function body.

Code suggestion
Check the AI-generated fix before applying
 --- a/superset/mcp_service/plugin/tool/list_plugins.py
 +++ b/superset/mcp_service/plugin/tool/list_plugins.py
 @@ -75,7 +75,7 @@ async def list_plugins(
          from superset.mcp_service.plugin.dao import DynamicPluginDAO
 
 -        def _serialize(obj: object, cols: list[str] | None) -> PluginInfo | None:
 +        def _serialize(obj: object, cols: list[str]) -> PluginInfo | None:
              return serialize_plugin_object(obj)
 
          list_tool = ModelListCore(

Code Review Run #f6e20e


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

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

Labels

api Related to the REST API authentication:row-level-security Related to Row Level Security plugins size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants