This is an automated email from the ASF dual-hosted git repository.

aminghadersohi pushed a commit to branch mcp-rls-plugins-99978
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 95b9efe1a3d973545a0edffd2077e037ff18ca36
Author: Amin Ghadersohi <[email protected]>
AuthorDate: Wed May 27 18:33:25 2026 +0000

    fix(mcp): prevent ValueError when select_columns contains only 
USER_DIRECTORY_FIELDS in list_rls_filters
    
    When the caller passes select_columns that consists entirely of 
USER_DIRECTORY_FIELDS
    columns (e.g. ["roles"]), ModelListCore raises ValueError because its 
privacy filter
    strips all columns, leaving an empty list.
    
    Strip USER_DIRECTORY_FIELDS from select_columns before passing to run_tool 
(falling
    back to None/defaults when the filtered list is empty). The existing bypass 
mechanism
    already restores these fields in the final serialized output using 
ALL_RLS_COLUMNS.
    
    Adds a regression test for the ["roles"]-only select_columns edge case.
---
 superset/mcp_service/rls/tool/list_rls_filters.py  | 15 ++++++++++++++-
 .../mcp_service/rls/tool/test_rls_tools.py         | 22 ++++++++++++++++++++++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/superset/mcp_service/rls/tool/list_rls_filters.py 
b/superset/mcp_service/rls/tool/list_rls_filters.py
index 998033f90ad..ae92dcf56df 100644
--- a/superset/mcp_service/rls/tool/list_rls_filters.py
+++ b/superset/mcp_service/rls/tool/list_rls_filters.py
@@ -26,6 +26,7 @@ from superset_core.mcp.decorators import tool, ToolAnnotations
 
 from superset.extensions import event_logger
 from superset.mcp_service.mcp_core import ModelListCore
+from superset.mcp_service.privacy import USER_DIRECTORY_FIELDS
 from superset.mcp_service.rls.schemas import (
     ALL_RLS_COLUMNS,
     DEFAULT_RLS_COLUMNS,
@@ -92,11 +93,23 @@ async def list_rls_filters(
             logger=logger,
         )
 
+        # RLS 'roles' is valid filter data but lives in USER_DIRECTORY_FIELDS,
+        # so ModelListCore would raise ValueError for a column list that 
reduces
+        # to empty after privacy filtering (e.g. select_columns=["roles"]).
+        # Strip directory-field columns here; the bypass below restores them in
+        # the final serialized output from ALL_RLS_COLUMNS.
+        run_tool_columns = None
+        if request.select_columns:
+            non_directory = [
+                c for c in request.select_columns if c not in 
USER_DIRECTORY_FIELDS
+            ]
+            run_tool_columns = non_directory if non_directory else None
+
         with event_logger.log_context(action="mcp.list_rls_filters.query"):
             result = list_tool.run_tool(
                 filters=request.filters,
                 search=request.search,
-                select_columns=request.select_columns,
+                select_columns=run_tool_columns,
                 order_column=request.order_column,
                 order_direction=request.order_direction,
                 page=max(request.page - 1, 0),
diff --git a/tests/unit_tests/mcp_service/rls/tool/test_rls_tools.py 
b/tests/unit_tests/mcp_service/rls/tool/test_rls_tools.py
index ab45317fab4..1a05dddd37f 100644
--- a/tests/unit_tests/mcp_service/rls/tool/test_rls_tools.py
+++ b/tests/unit_tests/mcp_service/rls/tool/test_rls_tools.py
@@ -157,6 +157,28 @@ async def 
test_list_rls_filters_returns_tables_and_roles(mock_list, mcp_server):
         assert item["roles"][0]["name"] == "Alpha"
 
 
+@patch("superset.daos.security.RLSDAO.list")
[email protected]
+async def test_list_rls_filters_roles_only_select_columns(mock_list, 
mcp_server):
+    """Requesting only 'roles' must not raise ValueError from the privacy 
filter."""
+    rls_filter = create_mock_rls_filter()
+    mock_list.return_value = ([rls_filter], 1)
+
+    async with Client(mcp_server) as client:
+        request = ListRlsFiltersRequest(
+            page=1,
+            page_size=10,
+            select_columns=["roles"],
+        )
+        result = await client.call_tool(
+            "list_rls_filters", {"request": request.model_dump()}
+        )
+        data = json.loads(result.content[0].text)
+        item = data["rls_filters"][0]
+        assert "roles" in item
+        assert item["roles"][0]["name"] == "Alpha"
+
+
 @patch("superset.daos.security.RLSDAO.list")
 @pytest.mark.asyncio
 async def test_list_rls_filters_empty(mock_list, mcp_server):

Reply via email to