mcp-servicenow
Version:
ServiceNow MCP server for Claude AI integration
368 lines (367 loc) • 14.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.catalogTools = exports.GetServiceRequestsTool = exports.RequestCatalogItemTool = exports.BrowseCatalogTool = void 0;
exports.browseCatalog = browseCatalog;
exports.requestCatalogItem = requestCatalogItem;
exports.getServiceRequests = getServiceRequests;
const axios_1 = __importDefault(require("axios"));
const logger_1 = require("../utils/logger");
// Tool definition for browsing catalog items
exports.BrowseCatalogTool = {
name: 'browse_catalog',
description: 'Browse available service catalog items',
inputSchema: {
type: 'object',
properties: {
category: {
type: 'string',
description: 'Filter by category (e.g., "Hardware", "Software", "Access")'
},
search: {
type: 'string',
description: 'Search term to find specific items'
},
active: {
type: 'boolean',
description: 'Filter by active status (default: true)'
},
limit: {
type: 'number',
description: 'Maximum number of items to return (default: 20)'
}
}
}
};
// Tool definition for requesting catalog items
exports.RequestCatalogItemTool = {
name: 'request_catalog_item',
description: 'Request a service catalog item',
inputSchema: {
type: 'object',
properties: {
catalog_item: {
type: 'string',
description: 'Name or sys_id of the catalog item to request'
},
requested_for: {
type: 'string',
description: 'User ID or email for whom the item is requested'
},
short_description: {
type: 'string',
description: 'Brief description of the request'
},
description: {
type: 'string',
description: 'Detailed description or justification'
},
urgency: {
type: 'string',
enum: ['1', '2', '3'],
description: 'Urgency level (1=high, 2=medium, 3=low)'
},
priority: {
type: 'string',
enum: ['1', '2', '3', '4', '5'],
description: 'Priority level (1=critical, 5=planning)'
},
variables: {
type: 'object',
description: 'Catalog item specific variables (e.g., laptop model, software version)'
}
},
required: ['catalog_item']
}
};
// Tool definition for checking request status
exports.GetServiceRequestsTool = {
name: 'get_service_requests',
description: 'Get service requests and their status',
inputSchema: {
type: 'object',
properties: {
requested_for: {
type: 'string',
description: 'Filter by user who the request is for'
},
state: {
type: 'string',
description: 'Filter by request state (e.g., "Open", "Work in Progress", "Closed")'
},
created_since: {
type: 'string',
description: 'Filter requests created since this date (YYYY-MM-DD)'
},
limit: {
type: 'number',
description: 'Maximum number of requests to return (default: 50)'
}
}
}
};
// Logic for browsing catalog items
async function browseCatalog(params = {}) {
try {
(0, logger_1.log)(`browseCatalog 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 query parameters
const queryParts = [];
if (params.active !== false) {
queryParts.push('active=true');
}
if (params.category) {
queryParts.push(`category.title=${params.category}`);
}
if (params.search) {
queryParts.push(`nameCONTAINS${params.search}^ORshort_descriptionCONTAINS${params.search}`);
}
const requestParams = {
sysparm_fields: 'sys_id,name,short_description,description,category.title,price,recurring_price,active',
sysparm_limit: params.limit || 20
};
if (queryParts.length > 0) {
requestParams.sysparm_query = queryParts.join('^');
}
(0, logger_1.log)(`Querying catalog items with: ${JSON.stringify(requestParams)}`);
const response = await axios_1.default.get(`${instanceUrl}/api/now/table/sc_cat_item`, {
params: requestParams,
auth: { username, password },
headers: { 'Accept': 'application/json' }
});
const items = response.data.result;
(0, logger_1.log)(`Found ${items.length} catalog items`);
if (items.length === 0) {
return {
content: [{
type: 'text',
text: 'No catalog items found matching the specified criteria.'
}]
};
}
// Format items for display
const formattedItems = items.map((item) => {
const price = item.price ? `$${item.price}` : 'Free';
const recurring = item.recurring_price ? ` (${item.recurring_price} recurring)` : '';
return `• **${item.name}**
Description: ${item.short_description}
Category: ${item.category?.display_value || 'N/A'}
Price: ${price}${recurring}
ID: ${item.sys_id}`;
}).join('\n\n');
return {
content: [{
type: 'text',
text: `Found ${items.length} catalog item(s):\n\n${formattedItems}`
}]
};
}
catch (error) {
(0, logger_1.log)(`Error browsing catalog: ${error.message}`);
return {
content: [{
type: 'text',
text: `Error browsing catalog: ${error.message}`
}]
};
}
}
// Logic for requesting catalog items
async function requestCatalogItem(params = {}) {
try {
(0, logger_1.log)(`requestCatalogItem 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;
const defaultUser = process.env.SERVICENOW_DEFAULT_SENDER_EMAIL;
if (!instanceUrl || !username || !password) {
return {
content: [{
type: 'text',
text: 'Error: Missing ServiceNow credentials.'
}]
};
}
const { catalog_item, requested_for, short_description, description, urgency, priority, variables } = params;
if (!catalog_item) {
return {
content: [{
type: 'text',
text: 'Error: catalog_item parameter is required'
}]
};
}
// First, find the catalog item by name or sys_id
let catalogItemId = catalog_item;
if (!catalog_item.startsWith('sys_id:') && catalog_item.length !== 32) {
// Search by name
const searchResponse = await axios_1.default.get(`${instanceUrl}/api/now/table/sc_cat_item`, {
params: {
sysparm_query: `name=${catalog_item}^ORshort_descriptionCONTAINS${catalog_item}`,
sysparm_limit: 1,
sysparm_fields: 'sys_id,name'
},
auth: { username, password },
headers: { 'Accept': 'application/json' }
});
if (searchResponse.data.result.length === 0) {
return {
content: [{
type: 'text',
text: `Error: Catalog item "${catalog_item}" not found. Please check the item name or use browse_catalog to find available items.`
}]
};
}
catalogItemId = searchResponse.data.result[0].sys_id;
(0, logger_1.log)(`Found catalog item: ${searchResponse.data.result[0].name} (${catalogItemId})`);
}
// Create the service request
const requestData = {
catalog_item: catalogItemId,
requested_for: requested_for || defaultUser,
short_description: short_description || `Request for ${catalog_item}`,
description: description || '',
urgency: urgency || '3',
priority: priority || '4'
};
// Add catalog item variables if provided
if (variables && typeof variables === 'object') {
requestData.variables = variables;
}
(0, logger_1.log)(`Creating service request: ${JSON.stringify(requestData)}`);
const response = await axios_1.default.post(`${instanceUrl}/api/now/table/sc_request`, requestData, {
auth: { username, password },
headers: { 'Accept': 'application/json' }
});
const request = response.data.result;
(0, logger_1.log)(`Service request created successfully: ${request.number}`);
return {
content: [{
type: 'text',
text: `Service request created successfully:
- Request Number: ${request.number}
- System ID: ${request.sys_id}
- State: ${request.state}
- Requested For: ${request.requested_for?.display_value || 'N/A'}
- Created: ${request.sys_created_on}
- Description: ${request.short_description}`
}]
};
}
catch (error) {
(0, logger_1.log)(`Error creating service request: ${error.message}`);
if (error.response) {
(0, logger_1.log)(`Error response: ${JSON.stringify(error.response.data)}`);
}
return {
content: [{
type: 'text',
text: `Error creating service request: ${error.message}`
}]
};
}
}
// Logic for getting service requests
async function getServiceRequests(params = {}) {
try {
(0, logger_1.log)(`getServiceRequests 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.'
}]
};
}
// Build query parameters
const queryParts = [];
if (params.requested_for) {
queryParts.push(`requested_for.email=${params.requested_for}^ORrequested_for.user_name=${params.requested_for}`);
}
if (params.state) {
queryParts.push(`state=${params.state}`);
}
if (params.created_since) {
queryParts.push(`sys_created_on>=${params.created_since}`);
}
const requestParams = {
sysparm_fields: 'number,sys_id,short_description,description,state,requested_for.display_value,requested_by.display_value,sys_created_on,catalog_item.display_value,approval',
sysparm_limit: params.limit || 50,
sysparm_order_by: '-sys_created_on'
};
if (queryParts.length > 0) {
requestParams.sysparm_query = queryParts.join('^');
}
(0, logger_1.log)(`Querying service requests with: ${JSON.stringify(requestParams)}`);
const response = await axios_1.default.get(`${instanceUrl}/api/now/table/sc_request`, {
params: requestParams,
auth: { username, password },
headers: { 'Accept': 'application/json' }
});
const requests = response.data.result;
(0, logger_1.log)(`Found ${requests.length} service requests`);
if (requests.length === 0) {
return {
content: [{
type: 'text',
text: 'No service requests found matching the specified criteria.'
}]
};
}
// Format requests for display
const formattedRequests = requests.map((request) => {
return `• **${request.number}** - ${request.short_description}
State: ${request.state}
Catalog Item: ${request.catalog_item?.display_value || 'N/A'}
Requested For: ${request.requested_for?.display_value || 'N/A'}
Requested By: ${request.requested_by?.display_value || 'N/A'}
Created: ${request.sys_created_on}
Approval: ${request.approval || 'Not Required'}`;
}).join('\n\n');
return {
content: [{
type: 'text',
text: `Found ${requests.length} service request(s):\n\n${formattedRequests}`
}]
};
}
catch (error) {
(0, logger_1.log)(`Error getting service requests: ${error.message}`);
return {
content: [{
type: 'text',
text: `Error getting service requests: ${error.message}`
}]
};
}
}
// Collection of all service catalog tools
exports.catalogTools = {
[exports.BrowseCatalogTool.name]: {
definition: exports.BrowseCatalogTool,
execute: browseCatalog
},
[exports.RequestCatalogItemTool.name]: {
definition: exports.RequestCatalogItemTool,
execute: requestCatalogItem
},
[exports.GetServiceRequestsTool.name]: {
definition: exports.GetServiceRequestsTool,
execute: getServiceRequests
}
};