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 identify coverage gaps and prioritize security monitoring.

Instructions

Count Sentinel analytics rules by MITRE technique.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
kwargsYes

Implementation Reference

  • Handler class implementing the tool logic. Inherits from MCPToolBase, defines name and the async run method that lists rules, extracts techniques using helper, and counts them by technique.
    class SentinelAnalyticsRulesCountByTechniqueTool(MCPToolBase): """ Count Sentinel analytics rules by MITRE technique. Extracts techniques from each rule and returns a mapping of technique to count and rule summaries. """ name = "sentinel_analytics_rules_count_by_technique" description = "Count Sentinel analytics rules by MITRE technique." 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)], }
  • Registration function that registers all analytics tools, including SentinelAnalyticsRulesCountByTechniqueTool at line 621.
    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)
  • Helper utility to extract tags, tactics, and techniques from rule dictionaries, crucially used in the tool's run method to parse techniques.
    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