Skip to main content
Glama
dstreefkerk

ms-sentinel-mcp-server

by dstreefkerk

sentinel_analytics_rule_templates_count_by_tactic

Count Microsoft Sentinel analytics rule templates by MITRE ATT&CK tactic to identify coverage gaps and prioritize security monitoring.

Instructions

Count Sentinel analytics rule templates by tactic.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
kwargsYes

Implementation Reference

  • MCPToolBase subclass that defines the tool name and implements the core logic in the `run` method: fetches Sentinel analytics rule templates, extracts tactics using the helper function, groups and counts them by tactic, returning a structured result dictionary.
    class SentinelAnalyticsRuleTemplatesCountByTacticTool(MCPToolBase):
        """
        Count Sentinel analytics rule templates by tactic.
        Extracts tactics from each template and returns a mapping of tactic to count
        and template summaries.
        """
    
        name = "sentinel_analytics_rule_templates_count_by_tactic"
        description = "Count Sentinel analytics rule templates by tactic."
    
        async def run(self, ctx: Context, **kwargs) -> Dict:
            """
            Count analytics rule templates by tactic.
            Returns a dict: {tactic: {count: int, templates: [{id, display_name}]}}
            """
            logger = self.logger
            # Extract Azure context
            workspace, resource_group, subscription_id = self.get_azure_context(ctx)
            client = self.get_securityinsight_client(subscription_id)
            tactic_map = {}
            try:
                templates = client.alert_rule_templates.list(resource_group, workspace)
                for template in templates:
                    template_dict = (
                        template.as_dict()
                        if hasattr(template, "as_dict")
                        else dict(template)
                    )
                    # pylint: disable=unused-variable
                    tags, tactics, _ = extract_tags_tactics_techniques_from_dict(
                        template_dict
                    )
                    display_name = (
                        template_dict.get("display_name")
                        or template_dict.get("displayName")
                        or template_dict.get("name")
                    )
                    for tactic in tactics or ["Unknown"]:
                        tkey = tactic.lower() or "unknown"
                        if tkey not in tactic_map:
                            tactic_map[tkey] = {"count": 0, "templates": []}
                        tactic_map[tkey]["count"] += 1
                        tactic_map[tkey]["templates"].append(
                            {
                                "id": template_dict.get("id"),
                                "display_name": display_name,
                            }
                        )
                return {
                    "valid": True,
                    "error": None,
                    "results": tactic_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)],
                }
  • Registers the SentinelAnalyticsRuleTemplatesCountByTacticTool class with the MCP server instance inside the `register_tools` function.
    SentinelAnalyticsRuleTemplatesCountByTacticTool.register(mcp)
  • Helper utility function called within the tool handler to parse tags, tactics, and techniques from rule template dictionaries, enabling the tactic-based grouping and counting.
    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