Skip to main content
Glama

Rootly MCP Server for Cloudflare Workers

index.ts13.3 kB
export default { async fetch(request: Request, env: any): Promise<Response> { // CORS headers for all responses const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }; // Handle CORS preflight if (request.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); } const url = new URL(request.url); // Only handle /sse endpoint if (url.pathname !== '/sse') { return new Response('Not found', { status: 404, headers: corsHeaders }); } // Handle GET request for SSE connection if (request.method === 'GET') { return new Response('data: {"type":"connection","status":"connected"}\n\n', { headers: { ...corsHeaders, 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', } }); } // Handle POST request for MCP protocol if (request.method === 'POST') { try { const body = await request.json(); // Handle MCP initialize if (body.method === 'initialize') { return new Response(JSON.stringify({ jsonrpc: '2.0', id: body.id, result: { protocolVersion: '2024-11-05', capabilities: { tools: {} }, serverInfo: { name: 'rootly-simple-mcp', version: '1.0.0' } } }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } // Handle tools/list if (body.method === 'tools/list') { const tools = [ // Incidents { name: 'get_incidents', description: 'Get incidents from Rootly', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Number of incidents (default: 10)' } } } }, { name: 'get_incident_alerts', description: 'Get alerts for a specific incident', inputSchema: { type: 'object', properties: { incident_id: { type: 'string', description: 'Incident ID' } }, required: ['incident_id'] } }, // Alerts { name: 'get_alerts', description: 'Get alerts from Rootly', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Number of alerts (default: 10)' } } } }, { name: 'get_alert', description: 'Get specific alert by ID', inputSchema: { type: 'object', properties: { alert_id: { type: 'string', description: 'Alert ID' } }, required: ['alert_id'] } }, // Severities { name: 'get_severities', description: 'Get severity levels', inputSchema: { type: 'object', properties: {} } }, { name: 'get_severity', description: 'Get specific severity by ID', inputSchema: { type: 'object', properties: { severity_id: { type: 'string', description: 'Severity ID' } }, required: ['severity_id'] } }, // Teams { name: 'get_teams', description: 'Get teams', inputSchema: { type: 'object', properties: {} } }, { name: 'get_team', description: 'Get specific team by ID', inputSchema: { type: 'object', properties: { team_id: { type: 'string', description: 'Team ID' } }, required: ['team_id'] } }, // Services { name: 'get_services', description: 'Get services', inputSchema: { type: 'object', properties: {} } }, { name: 'get_service', description: 'Get specific service by ID', inputSchema: { type: 'object', properties: { service_id: { type: 'string', description: 'Service ID' } }, required: ['service_id'] } }, // Functionalities { name: 'get_functionalities', description: 'Get functionalities', inputSchema: { type: 'object', properties: {} } }, { name: 'get_functionality', description: 'Get specific functionality by ID', inputSchema: { type: 'object', properties: { functionality_id: { type: 'string', description: 'Functionality ID' } }, required: ['functionality_id'] } }, // Incident Types { name: 'get_incident_types', description: 'Get incident types', inputSchema: { type: 'object', properties: {} } }, { name: 'get_incident_type', description: 'Get specific incident type by ID', inputSchema: { type: 'object', properties: { incident_type_id: { type: 'string', description: 'Incident Type ID' } }, required: ['incident_type_id'] } }, // Action Items { name: 'get_incident_action_items', description: 'Get incident action items', inputSchema: { type: 'object', properties: {} } }, { name: 'get_incident_action_item', description: 'Get specific incident action item by ID', inputSchema: { type: 'object', properties: { incident_action_item_id: { type: 'string', description: 'Incident Action Item ID' } }, required: ['incident_action_item_id'] } }, { name: 'get_incident_action_items_for_incident', description: 'Get action items for specific incident', inputSchema: { type: 'object', properties: { incident_id: { type: 'string', description: 'Incident ID' } }, required: ['incident_id'] } }, // Workflows { name: 'get_workflows', description: 'Get workflows', inputSchema: { type: 'object', properties: {} } }, { name: 'get_workflow', description: 'Get specific workflow by ID', inputSchema: { type: 'object', properties: { workflow_id: { type: 'string', description: 'Workflow ID' } }, required: ['workflow_id'] } }, { name: 'get_workflow_runs', description: 'Get workflow runs', inputSchema: { type: 'object', properties: {} } }, { name: 'get_workflow_run', description: 'Get specific workflow run by ID', inputSchema: { type: 'object', properties: { workflow_run_id: { type: 'string', description: 'Workflow Run ID' } }, required: ['workflow_run_id'] } }, // Environments { name: 'get_environments', description: 'Get environments', inputSchema: { type: 'object', properties: {} } }, { name: 'get_environment', description: 'Get specific environment by ID', inputSchema: { type: 'object', properties: { environment_id: { type: 'string', description: 'Environment ID' } }, required: ['environment_id'] } }, // Users { name: 'get_users', description: 'Get users', inputSchema: { type: 'object', properties: {} } }, { name: 'get_user', description: 'Get specific user by ID', inputSchema: { type: 'object', properties: { user_id: { type: 'string', description: 'User ID' } }, required: ['user_id'] } }, { name: 'get_current_user', description: 'Get current user info', inputSchema: { type: 'object', properties: {} } }, // Status Pages { name: 'get_status_pages', description: 'Get status pages', inputSchema: { type: 'object', properties: {} } }, { name: 'get_status_page', description: 'Get specific status page by ID', inputSchema: { type: 'object', properties: { status_page_id: { type: 'string', description: 'Status Page ID' } }, required: ['status_page_id'] } } ]; return new Response(JSON.stringify({ jsonrpc: '2.0', id: body.id, result: { tools } }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } // Handle tools/call if (body.method === 'tools/call') { const toolName = body.params?.name; const args = body.params?.arguments || {}; // Get token from Authorization header or environment const authHeader = request.headers.get('Authorization'); const token = authHeader?.replace('Bearer ', '') || env.ROOTLY_API_TOKEN; if (!token) { return new Response(JSON.stringify({ jsonrpc: '2.0', id: body.id, error: { code: -32602, message: 'ROOTLY_API_TOKEN required. Use Authorization: Bearer <token> header.' } }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } // Map tool names to API endpoints const apiMap: Record<string, string> = { // Incidents 'get_incidents': '/v1/incidents', 'get_incident_alerts': `/v1/incidents/${args.incident_id}/alerts`, // Alerts 'get_alerts': '/v1/alerts', 'get_alert': `/v1/alerts/${args.alert_id}`, // Severities 'get_severities': '/v1/severities', 'get_severity': `/v1/severities/${args.severity_id}`, // Teams 'get_teams': '/v1/teams', 'get_team': `/v1/teams/${args.team_id}`, // Services 'get_services': '/v1/services', 'get_service': `/v1/services/${args.service_id}`, // Functionalities 'get_functionalities': '/v1/functionalities', 'get_functionality': `/v1/functionalities/${args.functionality_id}`, // Incident Types 'get_incident_types': '/v1/incident_types', 'get_incident_type': `/v1/incident_types/${args.incident_type_id}`, // Action Items 'get_incident_action_items': '/v1/incident_action_items', 'get_incident_action_item': `/v1/incident_action_items/${args.incident_action_item_id}`, 'get_incident_action_items_for_incident': `/v1/incidents/${args.incident_id}/action_items`, // Workflows 'get_workflows': '/v1/workflows', 'get_workflow': `/v1/workflows/${args.workflow_id}`, 'get_workflow_runs': '/v1/workflow_runs', 'get_workflow_run': `/v1/workflow_runs/${args.workflow_run_id}`, // Environments 'get_environments': '/v1/environments', 'get_environment': `/v1/environments/${args.environment_id}`, // Users 'get_users': '/v1/users', 'get_user': `/v1/users/${args.user_id}`, 'get_current_user': '/v1/users/me', // Status Pages 'get_status_pages': '/v1/status_pages', 'get_status_page': `/v1/status_pages/${args.status_page_id}` }; const apiPath = apiMap[toolName]; if (!apiPath) { return new Response(JSON.stringify({ jsonrpc: '2.0', id: body.id, error: { code: -32601, message: 'Unknown tool' } }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } // Add pagination for list endpoints let url = `https://api.rootly.com${apiPath}`; if (toolName.startsWith('get_') && !apiPath.includes('{') && args.limit) { url += `?page[size]=${args.limit}`; } else if (toolName === 'get_incidents' || toolName === 'get_alerts') { url += `?page[size]=${args.limit || 10}`; } // Call Rootly API const response = await fetch(url, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/vnd.api+json' } }); if (!response.ok) { return new Response(JSON.stringify({ jsonrpc: '2.0', id: body.id, error: { code: -32603, message: `Rootly API error: ${response.status} - ${response.statusText}` } }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } const data = await response.json(); return new Response(JSON.stringify({ jsonrpc: '2.0', id: body.id, result: { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] } }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } return new Response(JSON.stringify({ jsonrpc: '2.0', id: body.id, error: { code: -32601, message: 'Method not found' } }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ jsonrpc: '2.0', error: { code: -32700, message: 'Parse error' } }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } } return new Response('Method not allowed', { status: 405, headers: corsHeaders }); } };

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-cloudflare'

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