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

arivero pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new db7665c0bc4 feat(mcp): add user roles to MCP response and 
permission-aware instructions (#38367)
db7665c0bc4 is described below

commit db7665c0bc40ad1878714c423927bef60c1ef55d
Author: Amin Ghadersohi <[email protected]>
AuthorDate: Fri Mar 6 02:16:51 2026 -0500

    feat(mcp): add user roles to MCP response and permission-aware instructions 
(#38367)
---
 superset/mcp_service/app.py                             | 17 +++++++++++++++++
 superset/mcp_service/system/schemas.py                  |  7 +++++++
 superset/mcp_service/system/tool/get_instance_info.py   | 11 +++++++++++
 .../mcp_service/system/tool/test_get_current_user.py    |  5 +++++
 4 files changed, 40 insertions(+)

diff --git a/superset/mcp_service/app.py b/superset/mcp_service/app.py
index 5f6f867c709..dd48c0a6766 100644
--- a/superset/mcp_service/app.py
+++ b/superset/mcp_service/app.py
@@ -159,6 +159,23 @@ Feature Availability:
 - Call get_instance_info to discover accessible menus for the current user.
 - Do NOT assume features exist; always check get_instance_info first.
 
+Permission Awareness:
+- get_instance_info returns current_user.roles (e.g., ["Admin"], ["Alpha"], 
["Viewer"]).
+- ALWAYS check the user's roles BEFORE suggesting write operations (creating 
datasets,
+  charts, dashboards, or running SQL).
+- Common roles and their typical capabilities:
+  - Admin: Full access to all features
+  - Alpha: Can create and modify charts, dashboards, datasets, and run SQL
+  - Gamma: Can view charts and dashboards they have been granted access to
+  - Viewer: Read-only access to shared dashboards and charts
+- If a user has a read-only role (Viewer, Gamma) and a listing tool returns 0 
results,
+  do NOT suggest they create resources. Instead:
+  1. Explain that they may not have access to the requested resources
+  2. Suggest they ask a workspace admin to grant them access or share content 
with them
+  3. Offer to help with what they CAN do (e.g., viewing dashboards they have 
access to)
+- If you are unsure about a user's capabilities, check their accessible_menus 
in
+  feature_availability from get_instance_info.
+
 If you are unsure which tool to use, start with get_instance_info
 or use the quickstart prompt for an interactive guide.
 
diff --git a/superset/mcp_service/system/schemas.py 
b/superset/mcp_service/system/schemas.py
index f59243f4e65..f1667471693 100644
--- a/superset/mcp_service/system/schemas.py
+++ b/superset/mcp_service/system/schemas.py
@@ -161,6 +161,13 @@ class UserInfo(BaseModel):
     last_name: str | None = None
     email: str | None = None
     active: bool | None = None
+    roles: List[str] = Field(
+        default_factory=list,
+        description=(
+            "Role names assigned to the user (e.g., Admin, Alpha, Gamma, 
Viewer). "
+            "Use this to determine what actions the user can perform."
+        ),
+    )
 
 
 class TagInfo(BaseModel):
diff --git a/superset/mcp_service/system/tool/get_instance_info.py 
b/superset/mcp_service/system/tool/get_instance_info.py
index 668b5e44845..4ffa7b8bfc7 100644
--- a/superset/mcp_service/system/tool/get_instance_info.py
+++ b/superset/mcp_service/system/tool/get_instance_info.py
@@ -110,12 +110,23 @@ def get_instance_info(
         # Attach the authenticated user's identity to the response
         user = getattr(g, "user", None)
         if user is not None:
+            raw_roles = getattr(user, "roles", None)
+            user_roles = []
+            if raw_roles is not None:
+                try:
+                    user_roles = [
+                        role.name for role in raw_roles if hasattr(role, 
"name")
+                    ]
+                except TypeError:
+                    logger.debug("Could not iterate user.roles: %s", 
type(raw_roles))
+                    user_roles = []
             result.current_user = UserInfo(
                 id=getattr(user, "id", None),
                 username=getattr(user, "username", None),
                 first_name=getattr(user, "first_name", None),
                 last_name=getattr(user, "last_name", None),
                 email=getattr(user, "email", None),
+                roles=user_roles,
             )
 
         return result
diff --git a/tests/unit_tests/mcp_service/system/tool/test_get_current_user.py 
b/tests/unit_tests/mcp_service/system/tool/test_get_current_user.py
index 74000ae8945..0a0dddfa5dc 100644
--- a/tests/unit_tests/mcp_service/system/tool/test_get_current_user.py
+++ b/tests/unit_tests/mcp_service/system/tool/test_get_current_user.py
@@ -213,12 +213,15 @@ class TestGetInstanceInfoCurrentUserViaMCP:
         # and breaks mock resolution on Python 3.10.
         from superset.mcp_service.mcp_core import InstanceInfoCore
 
+        mock_role = Mock()
+        mock_role.name = "Alpha"
         mock_g_user = Mock()
         mock_g_user.id = 5
         mock_g_user.username = "sophie"
         mock_g_user.first_name = "Sophie"
         mock_g_user.last_name = "Beaumont"
         mock_g_user.email = "[email protected]"
+        mock_g_user.roles = [mock_role]
 
         with (
             patch.object(
@@ -241,6 +244,7 @@ class TestGetInstanceInfoCurrentUserViaMCP:
         assert cu["first_name"] == "Sophie"
         assert cu["last_name"] == "Beaumont"
         assert cu["email"] == "[email protected]"
+        assert cu["roles"] == ["Alpha"]
 
     @pytest.mark.asyncio
     async def test_get_instance_info_no_user_returns_null(self, mcp_server):
@@ -295,6 +299,7 @@ class TestGetInstanceInfoCurrentUserViaMCP:
         assert cu["first_name"] is None
         assert cu["last_name"] is None
         assert cu["email"] is None
+        assert cu["roles"] == []
 
 
 # ---------------------------------------------------------------------------

Reply via email to