Skip to main content
Glama
Rootly-AI-Labs

Rootly MCP server

Official

createAlert

Generate and manage alerts in Rootly MCP server by defining source, summary, and additional attributes to streamline incident tracking and response.

Instructions

Creates a new alert from provided data

Responses:

  • 201 (Success): alert created

    • Content-Type: application/vnd.api+json

    • Example:

{
  "key": "value"
}
  • 401: resource not found

    • Content-Type: application/vnd.api+json

    • Example:

{
  "key": "value"
}
  • 422: invalid request

    • Content-Type: application/vnd.api+json

    • Example:

{
  "key": "value"
}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dataYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Registers all OpenAPI-derived tools including 'createAlert' using FastMCP.from_openapi with the filtered spec containing POST /v1/alerts endpoint.
    # By default, all routes become tools which is what we want
    mcp = FastMCP.from_openapi(
        openapi_spec=filtered_spec,
        client=http_client.client,
        name=name,
        timeout=30.0,
        tags={"rootly", "incident-management"},
    )
  • DEFAULT_ALLOWED_PATHS includes "/alerts" (line 123) which enables the POST /v1/alerts endpoint, generating the 'createAlert' tool.
    "/incidents/{incident_id}/alerts",
    "/alerts",
  • AuthenticatedHTTPXClient provides the HTTP request handling logic used by all generated tools, including createAlert which POSTs to /v1/alerts.
    class AuthenticatedHTTPXClient:
        """An HTTPX client wrapper that handles Rootly API authentication and parameter transformation."""
    
        def __init__(self, base_url: str = "https://api.rootly.com", hosted: bool = False, parameter_mapping: Optional[Dict[str, str]] = None):
            self._base_url = base_url
            self.hosted = hosted
            self._api_token = None
            self.parameter_mapping = parameter_mapping or {}
    
            if not self.hosted:
                self._api_token = self._get_api_token()
    
            # Create the HTTPX client  
            headers = {
                "Content-Type": "application/vnd.api+json", 
                "Accept": "application/vnd.api+json"
                # Let httpx handle Accept-Encoding automatically with all supported formats
            }
            if self._api_token:
                headers["Authorization"] = f"Bearer {self._api_token}"
    
            self.client = httpx.AsyncClient(
                base_url=base_url,
                headers=headers,
                timeout=30.0,
                follow_redirects=True,
                # Ensure proper handling of compressed responses
                limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
            )
    
        def _get_api_token(self) -> Optional[str]:
            """Get the API token from environment variables."""
            api_token = os.getenv("ROOTLY_API_TOKEN")
            if not api_token:
                logger.warning("ROOTLY_API_TOKEN environment variable is not set")
                return None
            return api_token
    
        def _transform_params(self, params: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
            """Transform sanitized parameter names back to original names."""
            if not params or not self.parameter_mapping:
                return params
    
            transformed = {}
            for key, value in params.items():
                # Use the original name if we have a mapping, otherwise keep the sanitized name
                original_key = self.parameter_mapping.get(key, key)
                transformed[original_key] = value
                if original_key != key:
                    logger.debug(f"Transformed parameter: '{key}' -> '{original_key}'")
            return transformed
    
        async def request(self, method: str, url: str, **kwargs):
            """Override request to transform parameters."""
            # Transform query parameters
            if 'params' in kwargs:
                kwargs['params'] = self._transform_params(kwargs['params'])
    
            # Call the underlying client's request method and let it handle everything
            return await self.client.request(method, url, **kwargs)
    
        async def get(self, url: str, **kwargs):
            """Proxy to request with GET method."""
            return await self.request('GET', url, **kwargs)
    
        async def post(self, url: str, **kwargs):
            """Proxy to request with POST method."""
            return await self.request('POST', url, **kwargs)
    
        async def put(self, url: str, **kwargs):
            """Proxy to request with PUT method."""
            return await self.request('PUT', url, **kwargs)
    
        async def patch(self, url: str, **kwargs):
            """Proxy to request with PATCH method."""
            return await self.request('PATCH', url, **kwargs)
    
        async def delete(self, url: str, **kwargs):
            """Proxy to request with DELETE method."""
            return await self.request('DELETE', url, **kwargs)
    
        async def __aenter__(self):
            return self
    
        async def __aexit__(self, exc_type, exc_val, exc_tb):
            pass
    
        def __getattr__(self, name):
            # Delegate all other attributes to the underlying client, except for request methods
            if name in ['request', 'get', 'post', 'put', 'patch', 'delete']:
                # Use our overridden methods instead
                return getattr(self, name)
            return getattr(self.client, name)
        
        @property 
        def base_url(self):
            return self._base_url
            
        @property
        def headers(self):
            return self.client.headers
  • Calls sanitize_parameters_in_spec to process the OpenAPI spec, ensuring input schema for createAlert (and other tools) uses MCP-compliant parameter names.
    parameter_mapping = sanitize_parameters_in_spec(filtered_spec)
    logger.info(f"Sanitized parameter names for MCP compatibility (mapped {len(parameter_mapping)} parameters)")
  • Utility function sanitizes OpenAPI parameter names (e.g., page[size] -> page_size) for MCP compliance, used for createAlert input schema.
    def sanitize_parameters_in_spec(spec: Dict[str, Any]) -> Dict[str, str]:
        """
        Sanitize all parameter names in an OpenAPI specification.
        
        This function modifies the spec in-place and builds a mapping 
        of sanitized names to original names.
        
        Args:
            spec: OpenAPI specification dictionary
            
        Returns:
            Dictionary mapping sanitized names to original names
        """
        parameter_mapping = {}
        
        # Sanitize parameters in paths
        if "paths" in spec:
            for path, path_item in spec["paths"].items():
                if not isinstance(path_item, dict):
                    continue
                    
                # Sanitize path-level parameters
                if "parameters" in path_item:
                    for param in path_item["parameters"]:
                        if "name" in param:
                            original_name = param["name"]
                            sanitized_name = sanitize_parameter_name(original_name)
                            if sanitized_name != original_name:
                                logger.debug(f"Sanitized path-level parameter: '{original_name}' -> '{sanitized_name}'")
                                param["name"] = sanitized_name
                                parameter_mapping[sanitized_name] = original_name
                
                # Sanitize operation-level parameters
                for method, operation in path_item.items():
                    if method.lower() not in ["get", "post", "put", "delete", "patch", "options", "head", "trace"]:
                        continue
                    if not isinstance(operation, dict):
                        continue
                        
                    if "parameters" in operation:
                        for param in operation["parameters"]:
                            if "name" in param:
                                original_name = param["name"]
                                sanitized_name = sanitize_parameter_name(original_name)
                                if sanitized_name != original_name:
                                    logger.debug(f"Sanitized operation parameter: '{original_name}' -> '{sanitized_name}'")
                                    param["name"] = sanitized_name
                                    parameter_mapping[sanitized_name] = original_name
        
        # Sanitize parameters in components (OpenAPI 3.0)
        if "components" in spec and "parameters" in spec["components"]:
            for param_name, param_def in spec["components"]["parameters"].items():
                if isinstance(param_def, dict) and "name" in param_def:
                    original_name = param_def["name"]
                    sanitized_name = sanitize_parameter_name(original_name)
                    if sanitized_name != original_name:
                        logger.debug(f"Sanitized component parameter: '{original_name}' -> '{sanitized_name}'")
                        param_def["name"] = sanitized_name
                        parameter_mapping[sanitized_name] = original_name
        
        return parameter_mapping
Behavior2/5

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

No annotations are provided, so the description must fully disclose behavioral traits. It states the tool creates an alert, implying a write operation, but fails to mention authentication requirements, rate limits, side effects, or what happens on failure beyond HTTP error codes. The inclusion of HTTP response examples (201, 401, 422) adds some value, but overall, the description is insufficient for a mutation tool with zero annotation coverage.

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

Conciseness3/5

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

The description is front-loaded with the core purpose but includes verbose HTTP response details that may not be essential for tool selection. While the response examples provide context, they add bulk without directly aiding in understanding when or how to use the tool. The structure could be more focused on usage rather than API response formatting.

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

Completeness2/5

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

Given the tool's complexity (a mutation with nested parameters, no annotations, and an output schema), the description is incomplete. It lacks details on parameter meanings, behavioral constraints, and differentiation from siblings. Although an output schema exists, the description does not adequately cover the input semantics or usage context, making it insufficient for effective agent decision-making.

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

Parameters1/5

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

The schema description coverage is 0%, meaning parameters are undocumented in the schema. The description does not compensate by explaining the 'data' parameter or its nested structure (e.g., attributes like 'summary', 'source', 'labels'), leaving the agent with no semantic understanding of required inputs. This is inadequate for a tool with one complex, required parameter.

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

Purpose3/5

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

The description states 'Creates a new alert from provided data', which clearly indicates the verb ('creates') and resource ('alert'), establishing the basic purpose. However, it lacks specificity about what constitutes an 'alert' in this context and does not differentiate from sibling tools like 'createIncident' or 'attachAlert', leaving ambiguity about when to use this versus other creation tools.

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 does not mention prerequisites, such as required permissions or dependencies, nor does it compare to sibling tools like 'createIncident' or 'listAlerts'. This absence of contextual usage information leaves the agent without clear direction for tool 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

Related 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/Rootly-AI-Labs/Rootly-MCP-server'

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