Skip to main content
Glama
panther-labs

Panther MCP Server

Official

get_detection

Read-only

Retrieve detailed information about security detections in Panther, including detection bodies and test cases, to analyze and understand threat monitoring rules.

Instructions

Get detailed information about a Panther detection, including the detection body and tests.

Permissions:{'all_of': ['View Rules', 'View Policies']}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
detection_idYesThe ID of the detection to fetch
detection_typeNoOne or more detection types - rules, scheduled_rules, simple_rules, or policies.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The main handler function implementing the 'get_detection' MCP tool. It fetches detailed information for a specific detection ID across specified detection types (rules, policies, etc.) using the Panther REST API, handles multiple types, 404s gracefully, and returns structured results with found/not found info.
    @mcp_tool(
        annotations={
            "permissions": all_perms(Permission.RULE_READ, Permission.POLICY_READ),
            "readOnlyHint": True,
        }
    )
    async def get_detection(
        detection_id: Annotated[
            str,
            Field(
                description="The ID of the detection to fetch",
                examples=["AWS.Suspicious.S3.Activity", "GCP.K8S.Privileged.Pod.Created"],
            ),
        ],
        detection_type: Annotated[
            list[str],
            Field(
                description="One or more detection types - rules, scheduled_rules, simple_rules, or policies.",
                examples=[
                    ["rules", "simple_rules", "scheduled_rules"],
                    ["policies"],
                ],
            ),
        ] = ["rules"],
    ) -> dict[str, Any]:
        """Get detailed information about a Panther detection, including the detection body and tests."""
        # Validate detection types
        validation_error = validate_detection_types(detection_type)
        if validation_error:
            return validation_error
    
        logger.info(f"Fetching details for ID {detection_id} in types: {detection_type}")
    
        # Use centralized field mapping
        field_map = SINGULAR_FIELD_MAP
    
        try:
            found_results = {}
            not_found_types = []
    
            async with get_rest_client() as client:
                for dt in detection_type:
                    # Allow 404 as a valid response to handle not found case
                    result, status = await client.get(
                        get_endpoint_for_detection(dt, detection_id),
                        expected_codes=[200, 404],
                    )
    
                    if status == 404:
                        not_found_types.append(dt)
                        # Use proper singular form from mapping instead of naive string manipulation
                        singular_form = SINGULAR_FIELD_MAP[dt].replace("_", " ")
                        logger.warning(f"No {singular_form} found with ID: {detection_id}")
                    else:
                        found_results[dt] = result
                        logger.info(
                            f"Successfully retrieved {dt} details for ID: {detection_id}"
                        )
    
            # If we found results in any detection type, return success
            if found_results:
                response = {"success": True}
    
                # Add results for each found type
                for dt, result in found_results.items():
                    response[field_map[dt]] = result
    
                # Add info about not found types if any
                if not_found_types:
                    response["not_found_in_types"] = not_found_types
                    response["found_in_types"] = list(found_results.keys())
    
                return response
            else:
                # Not found in any of the specified detection types
                return {
                    "success": False,
                    "message": f"No detection found with ID {detection_id} in any of the specified types: {detection_type}",
                }
        except Exception as e:
            logger.error(
                f"Failed to get detection details for types {detection_type}: {str(e)}"
            )
            return {
                "success": False,
                "message": f"Failed to get detection details for types {detection_type}: {str(e)}",
            }
  • The call to register_all_tools(mcp) which automatically registers all @mcp_tool decorated functions, including get_detection, with the FastMCP server instance.
    # Register all tools with MCP using the registry
    register_all_tools(mcp)
    # Register all prompts with MCP using the registry
    register_all_prompts(mcp)
    # Register all resources with MCP using the registry
    register_all_resources(mcp)
  • Helper function used by get_detection to construct the correct REST API endpoint based on detection type and ID.
    def get_endpoint_for_detection(
        detection_type: str, detection_id: str | None = None
    ) -> str:
        """Get the API endpoint for a detection type, optionally with an ID."""
        base_endpoint = DETECTION_TYPES[detection_type]
        return f"{base_endpoint}/{detection_id}" if detection_id else base_endpoint
  • Helper function used by get_detection to validate the provided detection_types parameter.
    def validate_detection_types(detection_types: list[str]) -> dict[str, Any] | None:
        """Validate detection types and return error dict if invalid, None if valid."""
        if not detection_types:
            return {
                "success": False,
                "message": "At least one detection type must be specified.",
            }
    
        invalid_types = [dt for dt in detection_types if dt not in DETECTION_TYPES]
        if invalid_types:
            valid_types = ", ".join(DETECTION_TYPES.keys())
            return {
                "success": False,
                "message": f"Invalid detection_types {invalid_types}. Valid values are: {valid_types}",
            }
        return None
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true, which the description doesn't contradict. The description adds value by specifying required permissions ('View Rules', 'View Policies'), which aren't covered by annotations. However, it lacks other behavioral details like rate limits, error handling, or output format, keeping it at a baseline level with some added context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded, with two sentences that directly state the purpose and permissions. There's no wasted text, though it could be slightly more structured (e.g., separating purpose from permissions).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (read operation with permissions), 100% schema coverage, annotations, and an output schema, the description is reasonably complete. It covers purpose and permissions, though it lacks usage guidelines. The output schema handles return values, so the description doesn't need to explain them.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents both parameters. The description adds no additional parameter semantics beyond what's in the schema (e.g., it doesn't explain 'detection body and tests' in relation to parameters). This meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Get detailed information about a Panther detection, including the detection body and tests.' It specifies the verb ('Get detailed information') and resource ('Panther detection'), making the function clear. However, it doesn't explicitly differentiate from siblings like 'list_detections' or 'get_alert', which would require a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It mentions permissions but doesn't specify use cases, prerequisites, or exclusions compared to sibling tools like 'list_detections' or 'get_alert'. This leaves the agent without context for selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/panther-labs/mcp-panther'

If you have feedback or need assistance with the MCP directory API, please join our Discord server