UNPKG

opsgenie-mcp-server

Version:

A Model Context Protocol (MCP) server that provides comprehensive Opsgenie alert management capabilities

405 lines (404 loc) 16.2 kB
import { z } from 'zod'; import { acknowledgeAlert, addDetailsToAlert, addNoteToAlert, closeAlert, createAlert, listAlertLogs, listAlertNotes, listAlerts, OpsgenieAPIError, } from './api.js'; // Zod schemas for validation const RecipientSchema = z.object({ type: z.enum(['user', 'team', 'escalation', 'schedule']), id: z.string().optional(), name: z.string().optional(), }); const CreateAlertSchema = z.object({ apiKey: z .string() .optional() .describe('Opsgenie API key (optional if OPSGENIE_API_KEY env var is set)'), message: z.string().describe('Message of the alert'), alias: z .string() .optional() .describe('Client-defined identifier of the alert'), description: z.string().optional().describe('Description field of the alert'), responders: z .array(RecipientSchema) .optional() .describe('Responders that the alert will be routed to'), visibleTo: z .array(RecipientSchema) .optional() .describe('Teams and users that the alert will become visible to'), actions: z .array(z.string()) .optional() .describe('Custom actions that will be available for the alert'), tags: z.array(z.string()).optional().describe('Tags of the alert'), details: z .record(z.string()) .optional() .describe('Map of key-value pairs to use as custom properties'), entity: z.string().optional().describe('Entity field of the alert'), priority: z .enum(['P1', 'P2', 'P3', 'P4', 'P5']) .optional() .describe('Priority level of the alert'), user: z.string().optional().describe('Display name of the request owner'), note: z .string() .optional() .describe('Additional note that will be added while creating the alert'), source: z.string().optional().describe('Source field of the alert'), }); const ListAlertsSchema = z.object({ apiKey: z .string() .optional() .describe('Opsgenie API key (optional if OPSGENIE_API_KEY env var is set)'), query: z .string() .optional() .describe('Search query to apply while filtering the alerts'), searchIdentifier: z .string() .optional() .describe('Identifier of the saved search query'), searchIdentifierType: z .enum(['id', 'name']) .optional() .describe('Identifier type of the saved search query'), offset: z .number() .min(0) .optional() .describe('Start index of the result set (for pagination)'), limit: z .number() .min(1) .max(100) .optional() .describe('Maximum number of items to provide in the result'), sort: z .string() .optional() .describe('Name of the field that result set will be sorted by'), order: z .enum(['asc', 'desc']) .optional() .describe('Sorting order of the result set'), }); const AlertActionSchema = z.object({ apiKey: z .string() .optional() .describe('Opsgenie API key (optional if OPSGENIE_API_KEY env var is set)'), identifier: z.string().describe('Alert identifier (id, tiny id, or alias)'), identifierType: z .enum(['id', 'name', 'tiny']) .optional() .describe('Type of identifier'), user: z.string().optional().describe('Display name of the request owner'), note: z.string().optional().describe('Additional note'), source: z.string().optional().describe('Source field'), }); const AddNoteSchema = z.object({ apiKey: z .string() .optional() .describe('Opsgenie API key (optional if OPSGENIE_API_KEY env var is set)'), identifier: z.string().describe('Alert identifier (id, tiny id, or alias)'), identifierType: z .enum(['id', 'name', 'tiny']) .optional() .describe('Type of identifier'), note: z.string().describe('Note to add to the alert'), user: z.string().optional().describe('Display name of the request owner'), source: z.string().optional().describe('Source field'), }); const AddDetailsSchema = z.object({ apiKey: z .string() .optional() .describe('Opsgenie API key (optional if OPSGENIE_API_KEY env var is set)'), identifier: z.string().describe('Alert identifier (id, tiny id, or alias)'), identifierType: z .enum(['id', 'name', 'tiny']) .optional() .describe('Type of identifier'), details: z .record(z.string()) .describe('Key-value pairs to add as custom properties'), user: z.string().optional().describe('Display name of the request owner'), note: z.string().optional().describe('Additional note'), source: z.string().optional().describe('Source field'), }); const ListNotesLogsSchema = z.object({ apiKey: z .string() .optional() .describe('Opsgenie API key (optional if OPSGENIE_API_KEY env var is set)'), identifier: z.string().describe('Alert identifier (id, tiny id, or alias)'), identifierType: z .enum(['id', 'name', 'tiny']) .optional() .describe('Type of identifier'), offset: z .number() .min(0) .optional() .describe('Start index of the result set'), direction: z.enum(['next', 'prev']).optional().describe('Page direction'), limit: z .number() .min(1) .optional() .describe('Maximum number of items to provide'), order: z.enum(['asc', 'desc']).optional().describe('Sorting order'), }); // Helper function to format API errors function formatApiError(error) { if (error instanceof OpsgenieAPIError) { return `Opsgenie API Error (${error.status}): ${error.message}`; } return `Error: ${error instanceof Error ? error.message : 'Unknown error'}`; } export function registerOpsgenieTools(server) { // 1. List Alerts - GET /v2/alerts server.tool('opsgenie_list_alerts', 'List alerts from Opsgenie', ListAlertsSchema.shape, async (args) => { try { const { apiKey, ...params } = args; const effectiveApiKey = apiKey || process.env.OPSGENIE_API_KEY; if (!effectiveApiKey) { throw new Error('Opsgenie API key is required. Provide it via apiKey parameter or OPSGENIE_API_KEY environment variable.'); } const response = await listAlerts(effectiveApiKey, params); return { content: [ { type: 'text', text: `Found ${response.data.length} alerts:\n\n${response.data .map(alert => `• ${alert.message} (${alert.id})\n Status: ${alert.status} | Priority: ${alert.priority}\n Created: ${alert.createdAt}`) .join('\n\n')}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: formatApiError(error), }, ], }; } }); // 2. Create Alert - POST /v2/alerts server.tool('opsgenie_create_alert', 'Create a new alert in Opsgenie', CreateAlertSchema.shape, async (args) => { try { const { apiKey, ...payload } = args; const effectiveApiKey = apiKey || process.env.OPSGENIE_API_KEY; if (!effectiveApiKey) { throw new Error('Opsgenie API key is required. Provide it via apiKey parameter or OPSGENIE_API_KEY environment variable.'); } const response = await createAlert(effectiveApiKey, payload); return { content: [ { type: 'text', text: `Alert created successfully!\nRequest ID: ${response.requestId}\nResult: ${response.result}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: formatApiError(error), }, ], }; } }); // 3. Acknowledge Alert - POST /v2/alerts/{identifier}/acknowledge server.tool('opsgenie_acknowledge_alert', 'Acknowledge an alert in Opsgenie', AlertActionSchema.shape, async (args) => { try { const { apiKey, identifier, identifierType = 'id', ...payload } = args; const effectiveApiKey = apiKey || process.env.OPSGENIE_API_KEY; if (!effectiveApiKey) { throw new Error('Opsgenie API key is required. Provide it via apiKey parameter or OPSGENIE_API_KEY environment variable.'); } const response = await acknowledgeAlert(effectiveApiKey, identifier, payload, identifierType); return { content: [ { type: 'text', text: `Alert acknowledged successfully!\nRequest ID: ${response.requestId}\nResult: ${response.result}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: formatApiError(error), }, ], }; } }); // 4. Close Alert - POST /v2/alerts/{identifier}/close server.tool('opsgenie_close_alert', 'Close an alert in Opsgenie', AlertActionSchema.shape, async (args) => { try { const { apiKey, identifier, identifierType = 'id', ...payload } = args; const effectiveApiKey = apiKey || process.env.OPSGENIE_API_KEY; if (!effectiveApiKey) { throw new Error('Opsgenie API key is required. Provide it via apiKey parameter or OPSGENIE_API_KEY environment variable.'); } const response = await closeAlert(effectiveApiKey, identifier, payload, identifierType); return { content: [ { type: 'text', text: `Alert closed successfully!\nRequest ID: ${response.requestId}\nResult: ${response.result}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: formatApiError(error), }, ], }; } }); // 5. List Alert Notes - GET /v2/alerts/{identifier}/notes server.tool('opsgenie_list_alert_notes', 'List notes for an alert in Opsgenie', ListNotesLogsSchema.shape, async (args) => { try { const { apiKey, identifier, identifierType = 'id', ...params } = args; const effectiveApiKey = apiKey || process.env.OPSGENIE_API_KEY; if (!effectiveApiKey) { throw new Error('Opsgenie API key is required. Provide it via apiKey parameter or OPSGENIE_API_KEY environment variable.'); } const response = await listAlertNotes(effectiveApiKey, identifier, params, identifierType); return { content: [ { type: 'text', text: response.data.length > 0 ? `Found ${response.data.length} notes:\n\n${response.data .map(note => `• ${note.note}\n By: ${note.owner} at ${note.createdAt}`) .join('\n\n')}` : 'No notes found for this alert.', }, ], }; } catch (error) { return { content: [ { type: 'text', text: formatApiError(error), }, ], }; } }); // 6. Add Note - POST /v2/alerts/{identifier}/notes server.tool('opsgenie_add_note', 'Add a note to an alert in Opsgenie', AddNoteSchema.shape, async (args) => { try { const { apiKey, identifier, identifierType = 'id', ...payload } = args; const effectiveApiKey = apiKey || process.env.OPSGENIE_API_KEY; if (!effectiveApiKey) { throw new Error('Opsgenie API key is required. Provide it via apiKey parameter or OPSGENIE_API_KEY environment variable.'); } const response = await addNoteToAlert(effectiveApiKey, identifier, payload, identifierType); return { content: [ { type: 'text', text: `Note added successfully!\nRequest ID: ${response.requestId}\nResult: ${response.result}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: formatApiError(error), }, ], }; } }); // 7. List Alert Logs - GET /v2/alerts/{identifier}/logs server.tool('opsgenie_list_alert_logs', 'List logs for an alert in Opsgenie', ListNotesLogsSchema.shape, async (args) => { try { const { apiKey, identifier, identifierType = 'id', ...params } = args; const effectiveApiKey = apiKey || process.env.OPSGENIE_API_KEY; if (!effectiveApiKey) { throw new Error('Opsgenie API key is required. Provide it via apiKey parameter or OPSGENIE_API_KEY environment variable.'); } const response = await listAlertLogs(effectiveApiKey, identifier, params, identifierType); return { content: [ { type: 'text', text: response.data.length > 0 ? `Found ${response.data.length} log entries:\n\n${response.data .map(log => `• [${log.type}] ${log.log}\n By: ${log.owner} at ${log.createdAt}`) .join('\n\n')}` : 'No log entries found for this alert.', }, ], }; } catch (error) { return { content: [ { type: 'text', text: formatApiError(error), }, ], }; } }); // 8. Add Details - POST /v2/alerts/{identifier}/details server.tool('opsgenie_add_details', 'Add custom details to an alert in Opsgenie', AddDetailsSchema.shape, async (args) => { try { const { apiKey, identifier, identifierType = 'id', ...payload } = args; const effectiveApiKey = apiKey || process.env.OPSGENIE_API_KEY; if (!effectiveApiKey) { throw new Error('Opsgenie API key is required. Provide it via apiKey parameter or OPSGENIE_API_KEY environment variable.'); } const response = await addDetailsToAlert(effectiveApiKey, identifier, payload, identifierType); return { content: [ { type: 'text', text: `Details added successfully!\nRequest ID: ${response.requestId}\nResult: ${response.result}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: formatApiError(error), }, ], }; } }); }