Skip to main content
Glama
dstreefkerk

ms-sentinel-mcp-server

by dstreefkerk

sentinel_authorization_summary

Summarize Azure RBAC role assignments to manage access permissions for Microsoft Sentinel and Log Analytics resources.

Instructions

Summarize Azure RBAC role assignments for Sentinel and Log Analytics access.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
kwargsYes

Implementation Reference

  • The `run` method implements the core tool logic: retrieves Azure context, lists role assignments at workspace/RG/subscription scopes, fetches role definitions, assesses read permissions for Sentinel and Log Analytics, and returns a structured summary of access.
    async def run(self, ctx: Context, **kwargs) -> Dict[str, Any]:
        """
        Execute the authorization summary tool.
    
        Retrieves Azure RBAC role assignments for the current identity and analyzes them
        to determine the level of access to Microsoft Sentinel and Log Analytics
        resources.
    
        Args:
            ctx: The MCP context object containing request context
            **kwargs: Additional parameters (unused but required for MCP compatibility)
    
        Returns:
            Dict[str, Any]: A dictionary containing:
                - workspace: Information about the Azure workspace
                - role_assignments: List of role assignments with details
                - permissions_assessment: Analysis of permissions
                - summary: Counts and statistics about the role assignments
        """
        logger = self.logger
    
        # Extract Azure context using MCPToolBase
        workspace_name, resource_group, subscription_id = self.get_azure_context(ctx)
        # For workspace_id, try to get from context, else None
        workspace_id = getattr(
            getattr(getattr(ctx, "request_context", None), "lifespan_context", None),
            "workspace_id",
            None,
        )
        if not (subscription_id and resource_group and workspace_name and workspace_id):
            logger.error("Missing Azure context for authorization summary.")
            return {"error": "Missing Azure context."}
    
        # Initialize AuthorizationManagementClient using MCPToolBase
        try:
            client = self.get_authorization_client(subscription_id)
            scope_used = (
                f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}"
                f"/providers/Microsoft.OperationalInsights/workspaces/{workspace_name}"
            )
            rg_scope = (
                f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}"
                if subscription_id and resource_group
                else None
            )
            sub_scope = f"/subscriptions/{subscription_id}" if subscription_id else None
            workspace_scope = (
                scope_used
                if subscription_id and resource_group and workspace_name
                else None
            )
            scopes_to_try = [
                ("workspace", workspace_scope),
                ("resource_group", rg_scope),
                ("subscription", sub_scope),
            ]
            scopes_to_try = [(label, s) for label, s in scopes_to_try if s]
            if not subscription_id or not workspace_scope:
                logger.error(
                    "Missing Azure context: subscription_id or workspace_scope unavailable."
                )
                return {
                    "error": (
                        "Missing Azure context: subscription_id or workspace_scope unavailable."
                    ),
                    "workspace": {
                        "subscription_id": subscription_id,
                        "resource_group": resource_group,
                        "workspace_name": workspace_name,
                        "workspace_id": workspace_id,
                        "scope_used": None,
                        "scopes_tried": [s for _, s in scopes_to_try],
                    },
                    "role_assignments": [],
                    "permissions_assessment": {},
                    "summary": {"errors": ["Missing Azure context"]},
                }
        except Exception as e:
            logger.error("Failed to extract Azure context: %s", e)
            return {
                "error": "Failed to extract Azure context: %s" % type(e).__name__,
                "workspace": {},
                "role_assignments": [],
                "permissions_assessment": {},
                "summary": {"errors": [str(e)]},
            }
    
        assignments = None
        scope_used = None
        errors = []
        for label, scope in scopes_to_try:
            try:
                logger.info(
                    "Attempting to fetch role assignments at %s scope: %s", label, scope
                )
                result = list(client.role_assignments.list_for_scope(scope))
                if result:
                    assignments = result
                    scope_used = scope
                    logger.info(
                        "Found %d role assignments at %s scope: %s",
                        len(assignments),
                        label,
                        scope,
                    )
                    break
                else:
                    logger.info(
                        "No role assignments found at %s scope: %s", label, scope
                    )
            except Exception as e:
                logger.warning("Error at %s scope (%s): %s", label, scope, e)
                errors.append(
                    "%s scope (%s): %s: %s" % (label, scope, type(e).__name__, e)
                )
        if assignments is None:
            logger.error(
                "Failed to fetch role assignments at any scope. Errors: %s", errors
            )
            return {
                "error": "Failed to fetch role assignments at any scope.",
                "scopes_tried": [s for _, s in scopes_to_try],
                "details": errors,
            }
    
        results = []
        sentinel_read_count = 0
        log_analytics_read_count = 0
        read_scopes = set()
    
        for assignment in assignments:
            role_def_id = assignment.role_definition_id.split("/")[-1]
            try:
                role_def = client.role_definitions.get(scope_used, role_def_id)
                role_name = getattr(role_def, "role_name", "")
                description = getattr(role_def, "description", "")
                category = getattr(role_def, "role_type", "")
                assignment_scope = getattr(assignment, "scope", "")
                is_read = _is_read_role(role_name, description)
                is_sentinel_read = role_name in SENTINEL_READ_ROLES
                is_log_analytics_read = role_name in LOG_ANALYTICS_READ_ROLES
                if is_read or is_sentinel_read or is_log_analytics_read:
                    read_scopes.add(assignment_scope)
                if is_sentinel_read:
                    sentinel_read_count += 1
                if is_log_analytics_read:
                    log_analytics_read_count += 1
                results.append(
                    {
                        "role_assignment_id": assignment.id,
                        "principal_id": assignment.principal_id,
                        "scope": assignment_scope,
                        "role_definition_id": role_def_id,
                        "role_name": role_name,
                        "description": description,
                        "category": category,
                        "is_read": is_read,
                        "is_sentinel_read": is_sentinel_read,
                        "is_log_analytics_read": is_log_analytics_read,
                    }
                )
            except (ClientAuthenticationError, HttpResponseError, AzureError) as e:
                logger.warning(
                    "Azure error fetching role definition %s: %s", role_def_id, e
                )
                errors.append(
                    "Azure error fetching role definition %s: %s"
                    % (role_def_id, type(e).__name__)
                )
                continue
            except Exception as e:
                logger.warning(
                    "Unexpected error for role definition %s: %s", role_def_id, e
                )
                errors.append(
                    "Unexpected error for role definition %s: %s"
                    % (role_def_id, type(e).__name__)
                )
                continue
    
        output = {
            "workspace": {
                "subscription_id": subscription_id,
                "resource_group": resource_group,
                "workspace_name": workspace_name,
                "workspace_id": workspace_id,
                "scope_used": scope_used,
                "scopes_tried": [s for _, s in scopes_to_try],
            },
            "role_assignments": results,
            "permissions_assessment": {
                "has_sentinel_read": sentinel_read_count > 0,
                "has_log_analytics_read": log_analytics_read_count > 0,
                "read_scopes": list(read_scopes),
            },
            "summary": {
                "sentinel_read_roles": sentinel_read_count,
                "log_analytics_read_roles": log_analytics_read_count,
                "total_roles": len(results),
                "scopes_with_read_access": len(read_scopes),
                "errors": errors,
            },
        }
        logger.debug("Authorization summary: %s", output["summary"])
        return output
  • The `register_tools` function registers the SentinelAuthorizationSummaryTool with the MCP server instance.
    def register_tools(mcp) -> None:
        """Register all authorization tools with the MCP server.
    
        Args:
            mcp: The MCP server instance
        """
        SentinelAuthorizationSummaryTool.register(mcp)
  • The `SentinelAuthorizationSummaryTool` class definition, including the tool `name` and `description`.
    class SentinelAuthorizationSummaryTool(MCPToolBase):
        """
        Tool for retrieving and summarizing Azure RBAC role assignments for Microsoft
        Sentinel.
    
        This tool queries the Azure Authorization API to retrieve role assignments at
        various scopes (workspace, resource group, subscription) and analyzes them to
        determine the level of access the current identity has to Microsoft Sentinel
        and Log Analytics resources.
        """
    
        name = "sentinel_authorization_summary"
        description = (
            "Summarize Azure RBAC role assignments for Sentinel and Log Analytics access."
        )
  • Helper function `_is_read_role` used to identify read-only roles based on name and description patterns.
    def _is_read_role(role_name: str, role_description: str) -> bool:
        """
        Detect if a role is a 'read' role by regex pattern matching.
    
        Args:
            role_name: The name of the Azure role
            role_description: The description of the Azure role
    
        Returns:
            bool: True if the role appears to be a read-only role based on pattern matching
        """
        for pattern in READ_PATTERNS:
            if re.search(pattern, role_name, re.IGNORECASE) or re.search(
                pattern, role_description, re.IGNORECASE
            ):
                return True
        return False
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool 'summarizes' role assignments, implying a read-only operation, but doesn't specify whether it requires specific permissions, what format the summary takes (e.g., structured data, report), or any limitations (e.g., rate limits, data freshness). For a tool with zero annotation coverage, this leaves significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's purpose without unnecessary words. It's front-loaded with the core functionality, making it easy to parse quickly. There's no wasted language or redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (summarizing RBAC role assignments), lack of annotations, no output schema, and poor parameter documentation (0% coverage with no description compensation), the description is incomplete. It doesn't provide enough context for an agent to understand how to use the tool effectively, what to expect in return, or handle potential errors. The conciseness comes at the cost of necessary details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has one parameter ('kwargs') with 0% schema description coverage, meaning the schema provides no details about what this parameter expects. The description adds no information about parameters—it doesn't mention 'kwargs' or explain what arguments should be passed. With a single undocumented parameter and no compensation in the description, this is inadequate for tool invocation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Summarize Azure RBAC role assignments for Sentinel and Log Analytics access.' It specifies the verb ('summarize'), resource ('Azure RBAC role assignments'), and scope ('for Sentinel and Log Analytics access'), making it easy to understand what the tool does. However, it doesn't explicitly differentiate from sibling tools like 'sentinel_metadata_get' or 'sentinel_workspace_get', which might also provide authorization-related information.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites, appropriate contexts, or exclusions. Given the sibling tools include various Sentinel-related tools (e.g., 'sentinel_metadata_get', 'sentinel_workspace_get'), there's no indication of how this tool differs or when it should be preferred over others for authorization-related queries.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dstreefkerk/ms-sentinel-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server