Skip to main content
Glama
dstreefkerk

ms-sentinel-mcp-server

by dstreefkerk

sentinel_hunting_queries_list

Retrieve and filter Azure Sentinel hunting queries by tactic or technique to identify security threats.

Instructions

List all Sentinel hunting queries (saved searches) with optional tactic/technique filtering

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
kwargsYes

Implementation Reference

  • The SentinelHuntingQueriesListTool class implements the core handler logic for the 'sentinel_hunting_queries_list' tool. It defines the tool name, description, and the async run method that lists Sentinel hunting queries with optional filtering by tactics and techniques using 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)],
                }
  • Registration of the SentinelHuntingQueriesListTool with the MCP server in the register_tools function.
    SentinelHuntingQueriesListTool.register(mcp)
  • Helper function extract_tags_tactics_techniques used by the tool to extract 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

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