UNPKG

mcp-servicenow

Version:

ServiceNow MCP server for Claude AI integration

315 lines (314 loc) 12.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.incidentTools = exports.GetIncidentsTool = exports.CreateIncidentTool = void 0; exports.createIncident = createIncident; exports.getIncidents = getIncidents; const axios_1 = __importDefault(require("axios")); const logger_1 = require("../utils/logger"); // Tool definition for creating an incident exports.CreateIncidentTool = { name: 'create_incident', description: 'Create a new incident in ServiceNow', inputSchema: { type: 'object', properties: { short_description: { type: 'string', description: 'Short description of the incident' }, description: { type: 'string', description: 'Detailed description of the incident' }, urgency: { type: 'string', enum: ['1', '2', '3'], description: 'Urgency of the incident (1=high, 2=medium, 3=low)' }, impact: { type: 'string', enum: ['1', '2', '3'], description: 'Impact of the incident (1=high, 2=medium, 3=low)' }, priority: { type: 'string', enum: ['1', '2', '3', '4', '5'], description: 'Priority of the incident (1=critical, 5=planning)' }, state: { type: 'string', enum: ['New', 'In Progress', 'Resolved', 'On Hold', 'Closed', 'Cancelled'], description: 'State of the incident' } }, required: ['short_description'] } }; // Logic for creating an incident async function createIncident(params = {}) { try { (0, logger_1.log)(`createIncident called with params: ${JSON.stringify(params)}`); // Extract parameters const short_description = params.short_description; const description = params.description || ''; const urgency = params.urgency; const impact = params.impact; const priority = params.priority; const state = params.state; // Validate required field if (!short_description) { return { content: [{ type: 'text', text: 'Error: short_description parameter is required' }] }; } const instanceUrl = process.env.SERVICENOW_INSTANCE_URL; const username = process.env.SERVICENOW_USERNAME; const password = process.env.SERVICENOW_PASSWORD; const senderEmail = process.env.SERVICENOW_DEFAULT_SENDER_EMAIL; if (!instanceUrl || !username || !password) { return { content: [{ type: 'text', text: 'Error: Missing ServiceNow credentials. Please check environment variables.' }] }; } (0, logger_1.log)(`Creating incident: ${short_description}`); // Build the incident payload - only include fields that have values const incidentPayload = { short_description, description, caller_email: senderEmail }; // Only add optional fields if they have values if (urgency) incidentPayload.urgency = urgency; if (impact) incidentPayload.impact = impact; if (priority) incidentPayload.priority = priority; if (state) incidentPayload.state = state; (0, logger_1.log)(`Incident payload: ${JSON.stringify(incidentPayload)}`); const response = await axios_1.default.post(`${instanceUrl}/api/now/table/incident`, incidentPayload, { auth: { username, password }, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }); const incident = response.data.result; (0, logger_1.log)(`Incident created successfully: ${incident.number}`); return { content: [{ type: 'text', text: `Incident created successfully: - Number: ${incident.number} - System ID: ${incident.sys_id} - Created: ${incident.sys_created_on} - Short Description: ${incident.short_description} - State: ${incident.state || 'New'} - Priority: ${incident.priority || 'Not Set'} - Urgency: ${incident.urgency || 'Not Set'} - Impact: ${incident.impact || 'Not Set'}` }] }; } catch (error) { (0, logger_1.log)(`Error creating incident: ${error.message}`); if (error.response) { (0, logger_1.log)(`Error response status: ${error.response.status}`); (0, logger_1.log)(`Error response data: ${JSON.stringify(error.response.data)}`); } let errorMessage = `Error creating incident: ${error.message}`; if (error.response?.status === 401) { errorMessage += '\nAuthentication error. Please verify ServiceNow credentials.'; } else if (error.response?.status === 403) { errorMessage += '\nPermission error. Please verify ServiceNow user roles.'; } else if (error.response?.status === 400) { errorMessage += '\nBad request. Please check the field values and formats.'; if (error.response.data?.error?.message) { errorMessage += `\nServiceNow error: ${error.response.data.error.message}`; } } return { content: [{ type: 'text', text: errorMessage }] }; } } // Tool definition for getting incidents exports.GetIncidentsTool = { name: 'get_incidents', description: 'Get incidents from ServiceNow based on specified criteria', inputSchema: { type: 'object', properties: { assignment_group: { type: 'string', description: 'Name of the assignment group' }, active: { type: 'boolean', description: 'Filter by active status' }, description: { type: 'string', description: 'Keyword to search for in the description' }, number: { type: 'string', description: 'Incident number to search for' }, urgency: { type: 'string', description: 'Filter by urgency level (1=high, 2=medium, 3=low)' }, priority: { type: 'string', description: 'Filter by priority level (1=critical, 5=planning)' }, state: { type: 'string', description: 'State of the incident ("New", "In Progress", "Resolved", "On Hold", "Closed", "Cancelled")' }, limit: { type: 'number', description: 'Maximum number of incidents to return (default: 50)' } } } }; // Logic for getting incidents async function getIncidents(params = {}) { try { (0, logger_1.log)(`getIncidents called with params: ${JSON.stringify(params)}`); const instanceUrl = process.env.SERVICENOW_INSTANCE_URL; const username = process.env.SERVICENOW_USERNAME; const password = process.env.SERVICENOW_PASSWORD; if (!instanceUrl || !username || !password) { return { content: [{ type: 'text', text: 'Error: Missing ServiceNow credentials. Please check environment variables.' }] }; } // Build the query string dynamically const queryParts = []; if (params.number) { queryParts.push(`number=${params.number}`); } if (params.assignment_group) { queryParts.push(`assignment_group.name=${params.assignment_group}`); } if (typeof params.active === 'boolean') { queryParts.push(`active=${params.active}`); } if (params.description) { queryParts.push(`descriptionCONTAINS${params.description}`); } if (params.urgency) { queryParts.push(`urgency=${params.urgency}`); } if (params.priority) { queryParts.push(`priority=${params.priority}`); } if (params.state) { queryParts.push(`state=${params.state}`); } const query = queryParts.join('^'); // Build request params const requestParams = { sysparm_fields: 'number,sys_id,short_description,description,state,priority,urgency,assignment_group.display_value,caller_id.display_value,sys_created_on,active', sysparm_limit: params.limit || 50, sysparm_order_by: '-sys_created_on' }; if (query) { requestParams.sysparm_query = query; } (0, logger_1.log)(`Querying incidents with: ${JSON.stringify(requestParams)}`); const response = await axios_1.default.get(`${instanceUrl}/api/now/table/incident`, { params: requestParams, auth: { username, password }, headers: { 'Accept': 'application/json' } }); (0, logger_1.log)(`ServiceNow response status: ${response.status}`); const incidents = response.data.result; if (!incidents) { return { content: [{ type: 'text', text: 'Error: Invalid response from ServiceNow.' }] }; } (0, logger_1.log)(`Found ${incidents.length} incidents`); if (incidents.length === 0) { return { content: [{ type: 'text', text: 'No incidents found matching the specified criteria.' }] }; } // Format incidents for display const formattedIncidents = incidents.map((incident) => { return `• **${incident.number}** - ${incident.short_description} State: ${incident.state || 'Unknown'} Priority: ${incident.priority || 'Not Set'} Urgency: ${incident.urgency || 'Not Set'} Assignment Group: ${incident.assignment_group?.display_value || 'Unassigned'} Caller: ${incident.caller_id?.display_value || 'Unknown'} Created: ${incident.sys_created_on} Active: ${incident.active || 'Unknown'}`; }).join('\n\n'); return { content: [{ type: 'text', text: `Found ${incidents.length} incident(s):\n\n${formattedIncidents}` }] }; } catch (error) { (0, logger_1.log)(`Error getting incidents: ${error.message}`); if (error.response) { (0, logger_1.log)(`Error response status: ${error.response.status}`); (0, logger_1.log)(`Error response data: ${JSON.stringify(error.response.data)}`); } let errorMessage = `Error retrieving incidents: ${error.message}`; if (error.response?.status === 401) { errorMessage += '\nAuthentication error. Please verify ServiceNow credentials.'; } else if (error.response?.status === 403) { errorMessage += '\nPermission error. Please verify ServiceNow user roles.'; } else if (error.response?.status === 400) { errorMessage += '\nBad request. Please check the query parameters.'; } return { content: [{ type: 'text', text: errorMessage }] }; } } // Collection of all incident-related tools exports.incidentTools = { [exports.CreateIncidentTool.name]: { definition: exports.CreateIncidentTool, execute: createIncident }, [exports.GetIncidentsTool.name]: { definition: exports.GetIncidentsTool, execute: getIncidents } };