mcp-servicenow
Version:
ServiceNow MCP server for Claude AI integration
368 lines (327 loc) • 10.3 kB
text/typescript
import axios from 'axios';
import { SendEmailParams, GetEmailsParams } from '../models/email';
import { log } from '../utils/logger';
// Tool definition for sending email
export const SendEmailTool = {
name: 'send_email',
description: 'Send an email through ServiceNow',
inputSchema: {
type: 'object',
properties: {
to: {
type: 'string',
description: 'Recipient email address (required)'
},
subject: {
type: 'string',
description: 'Email subject line (required)'
},
body: {
type: 'string',
description: 'Email body content (required)'
},
cc: {
type: 'string',
description: 'CC recipients (comma-separated email addresses)'
},
bcc: {
type: 'string',
description: 'BCC recipients (comma-separated email addresses)'
},
from: {
type: 'string',
description: 'Sender email address (optional, uses default if not provided)'
},
priority: {
type: 'string',
enum: ['1', '2', '3'],
description: 'Email priority (1=high, 2=medium, 3=low)'
},
content_type: {
type: 'string',
enum: ['text/plain', 'text/html'],
description: 'Content type of the email body (default: text/plain)'
}
},
required: ['to', 'subject', 'body']
}
};
// Tool definition for getting sent emails
export const GetEmailsTool = {
name: 'get_emails',
description: 'Get sent emails from ServiceNow',
inputSchema: {
type: 'object',
properties: {
recipient: {
type: 'string',
description: 'Filter by recipient email address'
},
sender: {
type: 'string',
description: 'Filter by sender email address'
},
subject: {
type: 'string',
description: 'Filter by subject containing this text'
},
created_since: {
type: 'string',
description: 'Filter emails created since this date (YYYY-MM-DD)'
},
state: {
type: 'string',
description: 'Filter by email state (e.g., "sent", "ready", "error")'
},
limit: {
type: 'number',
description: 'Maximum number of emails to return (default: 50)'
}
}
}
};
// Logic for sending email
export async function sendEmail(params: any = {}): Promise<any> {
try {
log(`sendEmail called with params: ${JSON.stringify(params)}`);
// Extract and validate required parameters
const { to, subject, body, cc, bcc, from, priority, content_type } = params;
// Validate required fields
if (!to) {
return {
content: [{
type: 'text',
text: 'Error: "to" parameter is required (recipient email address)'
}]
};
}
if (!subject) {
return {
content: [{
type: 'text',
text: 'Error: "subject" parameter is required'
}]
};
}
if (!body) {
return {
content: [{
type: 'text',
text: 'Error: "body" parameter is required'
}]
};
}
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(to)) {
return {
content: [{
type: 'text',
text: 'Error: "to" parameter must be a valid email address'
}]
};
}
const instanceUrl = process.env.SERVICENOW_INSTANCE_URL;
const username = process.env.SERVICENOW_USERNAME;
const password = process.env.SERVICENOW_PASSWORD;
const defaultSender = process.env.SERVICENOW_DEFAULT_SENDER_EMAIL;
if (!instanceUrl || !username || !password) {
return {
content: [{
type: 'text',
text: 'Error: Missing ServiceNow credentials. Please check environment variables.'
}]
};
}
log(`Sending email to: ${to}, subject: ${subject}`);
// Build the email payload
const emailPayload: any = {
recipient: to,
subject: subject,
body: body,
type: 'send-ready' // This tells ServiceNow to send the email
};
// Optional fields
if (cc) emailPayload.cc = cc;
if (bcc) emailPayload.bcc = bcc;
if (from) {
emailPayload.sender = from;
} else if (defaultSender) {
emailPayload.sender = defaultSender;
}
if (priority) emailPayload.priority = priority;
if (content_type) emailPayload.content_type = content_type;
log(`Email payload: ${JSON.stringify(emailPayload)}`);
const response = await axios.post(
`${instanceUrl}/api/now/table/sys_email`,
emailPayload,
{
auth: { username, password },
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
);
const email = response.data.result;
log(`Email created successfully: ${email.sys_id}`);
return {
content: [{
type: 'text',
text: `Email sent successfully:
- Email ID: ${email.sys_id}
- To: ${email.recipient}
- Subject: ${email.subject}
- From: ${email.sender || 'System Default'}
- Created: ${email.sys_created_on}
- State: ${email.state || 'Queued for sending'}
- Priority: ${email.priority || 'Normal'}`
}]
};
} catch (error: any) {
log(`Error sending email: ${error.message}`);
if (error.response) {
log(`Error response status: ${error.response.status}`);
log(`Error response data: ${JSON.stringify(error.response.data)}`);
}
let errorMessage = `Error sending email: ${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 has email sending permissions.';
} else if (error.response?.status === 400) {
errorMessage += '\nBad request. Please check email parameters and formats.';
if (error.response.data?.error?.message) {
errorMessage += `\nServiceNow error: ${error.response.data.error.message}`;
}
}
return {
content: [{
type: 'text',
text: errorMessage
}]
};
}
}
// Logic for getting sent emails
export async function getEmails(params: any = {}): Promise<any> {
try {
log(`getEmails 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: string[] = [];
if (params.recipient) {
queryParts.push(`recipient=${params.recipient}`);
}
if (params.sender) {
queryParts.push(`sender=${params.sender}`);
}
if (params.subject) {
queryParts.push(`subjectCONTAINS${params.subject}`);
}
if (params.created_since) {
queryParts.push(`sys_created_on>=${params.created_since}`);
}
if (params.state) {
queryParts.push(`state=${params.state}`);
}
const query = queryParts.join('^');
// Build request parameters
const requestParams: any = {
sysparm_fields: 'sys_id,recipient,sender,subject,body,state,sys_created_on,sent_on,type,priority',
sysparm_limit: params.limit || 50,
sysparm_order_by: '-sys_created_on'
};
if (query) {
requestParams.sysparm_query = query;
}
log(`Querying emails with: ${JSON.stringify(requestParams)}`);
const response = await axios.get(
`${instanceUrl}/api/now/table/sys_email`,
{
params: requestParams,
auth: { username, password },
headers: { 'Accept': 'application/json' }
}
);
log(`ServiceNow response status: ${response.status}`);
const emails = response.data.result;
if (!emails) {
return {
content: [{
type: 'text',
text: 'Error: Invalid response from ServiceNow.'
}]
};
}
log(`Found ${emails.length} emails`);
if (emails.length === 0) {
return {
content: [{
type: 'text',
text: 'No emails found matching the specified criteria.'
}]
};
}
// Format emails for display
const formattedEmails = emails.map((email: any) => {
const bodyPreview = email.body ?
(email.body.length > 100 ? email.body.substring(0, 100) + '...' : email.body) :
'No body';
return `• **Email ID:** ${email.sys_id}
**To:** ${email.recipient}
**From:** ${email.sender || 'System'}
**Subject:** ${email.subject}
**State:** ${email.state || 'Unknown'}
**Priority:** ${email.priority || 'Normal'}
**Created:** ${email.sys_created_on}
**Sent:** ${email.sent_on || 'Not sent yet'}
**Body Preview:** ${bodyPreview}`;
}).join('\n\n');
return {
content: [{
type: 'text',
text: `Found ${emails.length} email(s):\n\n${formattedEmails}`
}]
};
} catch (error: any) {
log(`Error getting emails: ${error.message}`);
if (error.response) {
log(`Error response status: ${error.response.status}`);
log(`Error response data: ${JSON.stringify(error.response.data)}`);
}
let errorMessage = `Error retrieving emails: ${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 for email access.';
}
return {
content: [{
type: 'text',
text: errorMessage
}]
};
}
}
// Collection of all email-related tools
export const emailTools = {
[SendEmailTool.name]: {
definition: SendEmailTool,
execute: sendEmail
},
[GetEmailsTool.name]: {
definition: GetEmailsTool,
execute: getEmails
}
};