Skip to main content
Glama
Rootly-AI-Labs

Rootly MCP server

Official

createTeam

Define and establish new teams in the Rootly MCP server by specifying team details such as name, description, members, and integrations for streamlined incident management.

Instructions

Creates a new team from provided data

Responses:

  • 201 (Success): team created

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

    • Example:

{
  "key": "value"
}
  • 401: responds with unauthorized for invalid token

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

    • Example:

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

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

    • Example:

{
  "key": "value"
}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dataYes

Implementation Reference

  • DEFAULT_ALLOWED_PATHS includes "/teams" and "/teams/{team_id}", enabling the createTeam tool (generated from POST /v1/teams endpoint).
    DEFAULT_ALLOWED_PATHS = [
        "/incidents/{incident_id}/alerts",
        "/alerts",
        "/alerts/{alert_id}",
        "/severities",
        "/severities/{severity_id}",
        "/teams",
        "/teams/{team_id}",
        "/services",
        "/services/{service_id}",
        "/functionalities",
        "/functionalities/{functionality_id}",
        # Incident types
        "/incident_types",
        "/incident_types/{incident_type_id}",
        # Action items (all, by id, by incident)
        "/incident_action_items",
        "/incident_action_items/{incident_action_item_id}",
        "/incidents/{incident_id}/action_items",
        # Workflows
        "/workflows",
        "/workflows/{workflow_id}",
        # Workflow runs
        "/workflow_runs",
        "/workflow_runs/{workflow_run_id}",
        # Environments
        "/environments",
        "/environments/{environment_id}",
        # Users
        "/users",
        "/users/{user_id}",
        "/users/me",
        # Status pages
        "/status_pages",
        "/status_pages/{status_page_id}",
        # On-call schedules and shifts
        "/schedules",
        "/schedules/{schedule_id}",
        "/schedules/{schedule_id}/shifts",
        "/shifts",
        "/schedule_rotations/{schedule_rotation_id}",
        "/schedule_rotations/{schedule_rotation_id}/schedule_rotation_users",
        "/schedule_rotations/{schedule_rotation_id}/schedule_rotation_active_days",
        # On-call overrides
        "/schedules/{schedule_id}/override_shifts",
        "/override_shifts/{override_shift_id}",
        # On-call shadows and roles
        "/schedules/{schedule_id}/on_call_shadows",
        "/on_call_shadows/{on_call_shadow_id}",
        "/on_call_roles",
        "/on_call_roles/{on_call_role_id}",
    ]
  • FastMCP.from_openapi registers all filtered OpenAPI endpoints as MCP tools, including createTeam from the POST /v1/teams operationId in the Rootly OpenAPI spec.
    # Create the MCP server using OpenAPI integration
    # 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,
  • AuthenticatedHTTPXClient is the HTTP client passed to FastMCP.from_openapi, executing the actual API calls for all generated tools including createTeam (proxies requests to POST /v1/teams).
    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

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