Skip to main content
Glama
dstreefkerk

ms-sentinel-mcp-server

by dstreefkerk

sentinel_analytics_rules_count_by_technique

Count Microsoft Sentinel analytics rules by MITRE ATT&CK technique to analyze security coverage and identify gaps in threat detection.

Instructions

Count Sentinel analytics rules by MITRE technique.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
kwargsYes

Implementation Reference

  • The async run method that implements the core logic of the tool: retrieves Sentinel analytics rules, extracts MITRE techniques using helper function, aggregates counts and rule summaries per technique, returns structured results or error.
    async def run(self, ctx: Context, **kwargs) -> Dict:
        """
        Count analytics rules by technique.
        Returns a dict: {technique: {count: int, rules: [{id, display_name}]}}
        """
        logger = self.logger
        workspace, resource_group, subscription_id = self.get_azure_context(ctx)
        client = self.get_securityinsight_client(subscription_id)
        technique_map = {}
        try:
            rules = client.alert_rules.list(
                resource_group_name=resource_group,
                workspace_name=workspace,
            )
            for rule in rules:
                rule_dict = rule.as_dict() if hasattr(rule, "as_dict") else dict(rule)
                _, _, techniques = extract_tags_tactics_techniques_from_dict(rule_dict)
                display_name = (
                    rule_dict.get("display_name")
                    or rule_dict.get("displayName")
                    or rule_dict.get("name")
                )
                for technique in techniques or ["Unknown"]:
                    tkey = technique.lower() or "unknown"
                    if tkey not in technique_map:
                        technique_map[tkey] = {"count": 0, "rules": []}
                    technique_map[tkey]["count"] += 1
                    technique_map[tkey]["rules"].append(
                        {
                            "id": rule_dict.get("id"),
                            "display_name": display_name,
                        }
                    )
            return {
                "valid": True,
                "error": None,
                "results": technique_map,
                "errors": [],
            }
        except Exception as e:
            logger.error("Error in %s: %s", self.__class__.__name__, str(e))
            return {
                "valid": False,
                "error": str(e),
                "results": None,
                "errors": [str(e)],
            }
  • The register_tools function that registers the SentinelAnalyticsRulesCountByTechniqueTool (line 621) along with other analytics tools to the MCP server.
    def register_tools(mcp):
        """
        Register all analytics tools with the given MCP server instance.
    
        Args:
            mcp: The MCP server instance to register tools with.
        """
        SentinelAnalyticsRuleListTool.register(mcp)
        SentinelAnalyticsRuleGetTool.register(mcp)
        SentinelAnalyticsRuleTemplatesListTool.register(mcp)
        SentinelAnalyticsRuleTemplateGetTool.register(mcp)
        SentinelAnalyticsRulesCountByTacticTool.register(mcp)
        SentinelAnalyticsRuleTemplatesCountByTacticTool.register(mcp)
        SentinelAnalyticsRulesCountByTechniqueTool.register(mcp)
        SentinelAnalyticsRuleTemplatesCountByTechniqueTool.register(mcp)
  • Tool name and description defining the schema/identity for the MCP tool.
    name = "sentinel_analytics_rules_count_by_technique"
    description = "Count Sentinel analytics rules by MITRE technique."
  • Helper function to extract tags, tactics, and techniques from rule dictionaries, used in the handler to parse MITRE ATT&CK mappings.
    def extract_tags_tactics_techniques_from_dict(obj):
        """
          Extract tags, tactics, and techniques from an analytics rule/template dict.
    
        Args:
            obj (dict): Analytics rule or template dictionary.
    
        Returns:
            tuple: (tags, tactics, techniques)
                tags (list[dict]): All tags as {name, value} pairs.
                tactics (list[str]): List of tactics (from tags or legacy fields).
                techniques (list[str]): List of techniques (from tags or legacy fields).
    
        Extraction precedence:
            - Tactics/techniques: Prefer tags with name 'tactics'/'techniques'
              (case-insensitive, split on comma). Fallback to legacy fields.
            - Tags: All tags as {name, value} pairs (robust to SDK object, dict, or string).
        """
        tags = []
        tactics = []
        techniques = []
        raw_tags = obj.get("tags")
        if raw_tags:
            for tag in raw_tags:
                tag_name = None
                tag_value = None
                if isinstance(tag, dict):
                    tag_name = tag.get("name") or tag.get("Name")
                    tag_value = tag.get("value") or tag.get("Value")
                elif hasattr(tag, "name") and hasattr(tag, "value"):
                    tag_name = getattr(tag, "name", None)
                    tag_value = getattr(tag, "value", None)
                elif isinstance(tag, str):
                    tag_name = tag
                    tag_value = None
                else:
                    try:
                        tag_name = str(tag)
                    except Exception:
                        continue
                if tag_name is not None:
                    tags.append({"name": tag_name, "value": tag_value})
        for tag in tags:
            if tag["name"] and isinstance(tag["name"], str):
                if tag["name"].lower() == "tactics" and tag["value"]:
                    tactics += [t.strip() for t in tag["value"].split(",") if t.strip()]
                elif tag["name"].lower() == "techniques" and tag["value"]:
                    techniques += [t.strip() for t in tag["value"].split(",") if t.strip()]
        legacy_tactics = obj.get("tactics")
        if legacy_tactics:
            tactics += [
                t.strip() for t in legacy_tactics if isinstance(t, str) and t.strip()
            ]
        legacy_techniques = obj.get("techniques")
        if legacy_techniques:
            techniques += [
                t.strip() for t in legacy_techniques if isinstance(t, str) and t.strip()
            ]
        tactics = list(dict.fromkeys([t for t in tactics if t]))
        techniques = list(dict.fromkeys([t for t in techniques if t]))
        return tags, tactics, techniques

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