sentinel_hunting_queries_list
List and filter Microsoft Sentinel hunting queries by tactics or techniques to identify security threats and investigate incidents.
Instructions
List all Sentinel hunting queries (saved searches) with optional tactic/technique filtering
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| kwargs | Yes |
Implementation Reference
- tools/hunting_tools.py:77-140 (handler)The main handler class SentinelHuntingQueriesListTool that implements the tool logic. It lists all Microsoft Sentinel hunting queries (saved searches) with optional filtering by tactics and techniques using the Azure Log Analytics client.class SentinelHuntingQueriesListTool(MCPToolBase): name = "sentinel_hunting_queries_list" description = ( "List all Sentinel hunting queries (saved searches) with " "optional tactic/technique filtering" ) async def run(self, ctx: Context, **kwargs): """ List all Sentinel hunting queries (saved searches) with optional tactic/technique filtering. Extracts tags, tactics, and techniques using shared utility. """ # Extract parameters using the centralized parameter extraction from MCPToolBase tactics = self._extract_param(kwargs, "tactics") techniques = self._extract_param(kwargs, "techniques") workspace_name, resource_group, subscription_id = self.get_azure_context(ctx) client = self.get_loganalytics_client(subscription_id) result = [] tactic_set = ( {t.strip().lower() for t in tactics.split(",")} if tactics else None ) technique_set = ( {t.strip().lower() for t in techniques.split(",")} if techniques else None ) try: searches = client.saved_searches.list_by_workspace( resource_group, workspace_name ) for s in getattr(searches, "value", []): tags, s_tactics, s_techniques = extract_tags_tactics_techniques(s) # Filtering if tactic_set and not any(t.lower() in tactic_set for t in s_tactics): continue if technique_set and not any( t.lower() in technique_set for t in s_techniques ): continue result.append( { "id": getattr(s, "id", None), "name": getattr(s, "name", None), "display_name": getattr( s, "display_name", getattr(s, "name", None) ), "category": getattr(s, "category", None), "query": getattr(s, "query", None), "tags": tags, "tactics": s_tactics, "techniques": s_techniques, "description": getattr(s, "description", None), "version": getattr(s, "version", None), } ) return {"valid": True, "error": None, "results": result, "errors": []} except Exception as e: self.logger.error("Error in %s: %s", self.__class__.__name__, str(e)) return { "valid": False, "error": str(e), "results": None, "errors": [str(e)], }
- tools/hunting_tools.py:283-287 (registration)The register_tools function where SentinelHuntingQueriesListTool is registered to the MCP server along with related tools.def register_tools(mcp: FastMCP): SentinelHuntingQueriesListTool.register(mcp) SentinelHuntingQueriesCountByTacticTool.register(mcp) SentinelHuntingQueryGetTool.register(mcp)
- tools/hunting_tools.py:12-74 (helper)Helper utility function extract_tags_tactics_techniques used by the tool to parse tags, tactics, and techniques from hunting query objects for filtering and output.def extract_tags_tactics_techniques(obj): """ Extracts tags, tactics, and techniques from a hunting query object. Returns: tags: List of {name, value} dicts. tactics: List of tactics (from tags or legacy fields). techniques: 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 = [] # Extract tags as list of {name, value} raw_tags = getattr(obj, "tags", None) if raw_tags: for tag in raw_tags: tag_name = None tag_value = None # Tag as dict if isinstance(tag, dict): tag_name = tag.get("name") or tag.get("Name") tag_value = tag.get("value") or tag.get("Value") # Tag as object with .name/.value elif hasattr(tag, "name") and hasattr(tag, "value"): tag_name = getattr(tag, "name", None) tag_value = getattr(tag, "value", None) # Tag as string: treat as name only elif isinstance(tag, str): tag_name = tag tag_value = None # Fallback: try string conversion else: try: tag_name = str(tag) except Exception: continue if tag_name is not None: tags.append({"name": tag_name, "value": tag_value}) # Tactics/techniques from tags (case-insensitive match) 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()] # Fallback: legacy fields legacy_tactics = getattr(obj, "tactics", None) if legacy_tactics: tactics += [ t.strip() for t in legacy_tactics if isinstance(t, str) and t.strip() ] legacy_techniques = getattr(obj, "techniques", None) if legacy_techniques: techniques += [ t.strip() for t in legacy_techniques if isinstance(t, str) and t.strip() ] # Deduplicate and preserve order 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