@iriseller/mcp-server
Version:
Model Context Protocol (MCP) server providing access to IRISeller's AI sales intelligence platform with 7 AI agents, multi-CRM integration, advanced sales workflows, email automation, Rosa demo functionality with action scoring, DNC compliance checking, G
766 lines (765 loc) • 31.8 kB
JavaScript
export class EmailToolHandlers {
apiService;
// Debug logging utilities that respect debug flag
debugLog = (message, ...args) => {
if (this.apiService.config?.debug) {
console.error(message, ...args);
}
};
errorLog = (message, ...args) => {
// Always log errors to stderr (for critical issues only)
console.error(message, ...args);
};
constructor(apiService) {
this.apiService = apiService;
}
/**
* Handle automatic email response generation
*/
async handleEmailAutoRespond(params) {
const { email, options = {} } = params;
try {
this.debugLog(`[MCP] Auto-responding to email from ${email.from}: "${email.subject}"`);
// Step 1: Analyze email intent
const intentAnalysis = await this.handleEmailIntentAnalysis({
emailContent: email.content,
subject: email.subject,
senderInfo: {
email: email.from,
name: email.fromName
}
});
// Step 2: Compile sales information using multiple agents
const salesInfo = await this.handleCompileSalesInfo({
prospect: {
email: email.from,
name: email.fromName,
company: await this.extractCompanyFromEmail(email.content)
},
inquiry: {
type: intentAnalysis.result?.inquiryType || 'general_inquiry',
details: email.content,
urgency: options.urgency || intentAnalysis.result?.urgency || 'medium'
},
agentWorkflows: intentAnalysis.result?.recommendedWorkflows || []
});
// Step 3: Generate personalized response
const response = await this.handleGenerateEmailResponse({
originalEmail: email,
salesInfo: salesInfo,
responseStyle: 'professional'
});
// Step 4: Schedule follow-up if needed
if (intentAnalysis.result?.needsFollowup) {
await this.handleScheduleEmailFollowup({
prospect: {
email: email.from,
name: email.fromName
},
followupType: intentAnalysis.result?.followupType || 'general',
delay: { value: 3, unit: 'days' },
conditions: { ifNoResponse: true }
});
}
return {
success: true,
response: {
content: response.content,
htmlContent: response.htmlContent,
agentsUsed: salesInfo.agentsUsed,
salesInfo: salesInfo.data,
confidence: intentAnalysis.result?.confidence || 0.5,
followupScheduled: intentAnalysis.result?.needsFollowup || false
}
};
}
catch (error) {
this.errorLog('[MCP] Error in email auto-respond:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Analyze email intent and classify inquiry type
*/
async handleEmailIntentAnalysis(params) {
const { emailContent, subject, senderInfo } = params;
try {
// Use AI to analyze email intent via agent execution
const analysisResult = await this.apiService.executeAgent({
agent_name: 'rosa_sdr',
input_data: {
task: 'analyze_email_intent',
content: emailContent,
subject: subject,
sender: senderInfo
},
options: {
timeout: 30,
priority: 'medium'
}
});
// Classify inquiry type based on content analysis
const inquiryType = this.classifyInquiryType(emailContent, subject);
const urgency = this.determineUrgency(emailContent, subject);
const recommendedWorkflows = this.getRecommendedWorkflows(inquiryType);
// Handle forecast requests specially
if (inquiryType === 'forecast_request') {
return {
success: true,
result: {
inquiryType: inquiryType,
urgency: urgency,
recommendedWorkflows: recommendedWorkflows,
confidence: 0.95,
needsFollowup: false,
followupType: 'none',
aiAnalysis: {
message: 'Forecast request detected. Route to analytics/reporting tools.',
recommended_tools: ['forecast_sales', 'query_crm'],
status: 'ready'
}
}
};
}
return {
success: true,
result: {
inquiryType: inquiryType,
urgency: urgency,
recommendedWorkflows: recommendedWorkflows,
confidence: 0.8, // Default confidence since API doesn't return this
needsFollowup: this.shouldScheduleFollowup(inquiryType),
followupType: this.getFollowupType(inquiryType),
aiAnalysis: analysisResult.results || analysisResult
}
};
}
catch (error) {
this.errorLog('[MCP] Error in email intent analysis:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Compile comprehensive sales information using multiple agents
*/
async handleCompileSalesInfo(params) {
const { prospect, inquiry, agentWorkflows = [] } = params;
try {
this.debugLog(`[MCP] Compiling sales info for ${prospect.email} with workflows: ${agentWorkflows.join(', ')}`);
const salesData = {
prospect: prospect,
company: {},
opportunities: [],
recommendations: [],
agentsUsed: []
};
// Execute agent workflows based on inquiry type
for (const workflow of agentWorkflows) {
try {
const agentResult = await this.executeAgentWorkflow(workflow, prospect, inquiry);
const agentResultAny = agentResult;
if (agentResultAny.status === 'success' || agentResultAny.success) {
salesData.agentsUsed.push(workflow);
// Merge agent-specific data from results
const results = agentResultAny.results || agentResultAny.data;
if (workflow === 'qualification') {
salesData.qualification = results?.qualification || {};
}
else if (workflow === 'research') {
salesData.research = results?.research || {};
}
else if (workflow === 'personalization') {
salesData.personalization = results?.personalization || {};
}
else if (workflow === 'objection_handling') {
salesData.objectionHandling = results?.objectionHandling || {};
}
}
}
catch (agentError) {
this.debugLog(`[MCP] Error in ${workflow} workflow:`, agentError);
}
}
// Research company information
if (prospect.company) {
try {
const companyResearch = await this.apiService.researchCompany({
company_name: prospect.company,
research_depth: 'basic',
include_competitors: false,
include_news: true,
include_financials: false
});
if (companyResearch.success) {
salesData.company = companyResearch.data;
salesData.agentsUsed.push('company_research');
}
}
catch (researchError) {
this.debugLog('[MCP] Company research not available:', researchError);
}
}
// Lead qualification using the built-in method
try {
const qualification = await this.apiService.qualifyLead({
contact_name: prospect.name,
company_name: prospect.company || 'Unknown',
email: prospect.email,
additional_context: `Email inquiry: ${inquiry.type}`
});
if (qualification.status === 'success') {
salesData.qualification = qualification.results;
salesData.agentsUsed.push('lead_qualification');
}
}
catch (qualError) {
this.debugLog('[MCP] Lead qualification not available:', qualError);
}
return {
success: true,
data: salesData,
agentsUsed: salesData.agentsUsed
};
}
catch (error) {
this.errorLog('[MCP] Error compiling sales info:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Generate personalized email response
*/
async handleGenerateEmailResponse(params) {
const { originalEmail, salesInfo, responseStyle = 'professional' } = params;
try {
// Use personalization agent to generate response
const responseResult = await this.apiService.personalizeOutreach({
prospect_data: {
name: originalEmail.fromName || 'Prospect',
company: originalEmail.from.split('@')[1] || 'Unknown Company',
email: originalEmail.from
},
message_type: 'email',
tone: responseStyle,
context: JSON.stringify({
original_email: originalEmail,
sales_info: salesInfo,
style: responseStyle
})
});
if (responseResult.success) {
return {
success: true,
content: responseResult.data?.message || responseResult.data?.response || 'Thank you for your email. We will get back to you soon.',
htmlContent: responseResult.data?.html_message || null,
agentsUsed: ['personalization']
};
}
else {
throw new Error('Personalization service failed');
}
}
catch (error) {
this.errorLog('[MCP] Error generating email response:', error);
// Fallback response
return {
success: true,
content: `Thank you for your email regarding "${originalEmail.subject}". We appreciate your interest and will get back to you within 24 hours.`,
htmlContent: null,
agentsUsed: ['fallback']
};
}
}
/**
* Schedule email follow-up
*/
async handleScheduleEmailFollowup(params) {
const { prospect, followupType, delay, conditions } = params;
try {
// Schedule follow-up - placeholder implementation
const scheduleResult = {
success: true,
data: {
followup_id: `followup_${Date.now()}`,
scheduled_date: new Date(Date.now() + delay.value * 24 * 60 * 60 * 1000).toISOString(),
prospect: prospect,
type: followupType
}
};
return {
success: true,
followupId: scheduleResult.data?.followup_id,
scheduledFor: this.calculateFollowupDate(delay),
conditions: conditions
};
}
catch (error) {
console.error('[MCP] Error scheduling email followup:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Track email analytics
*/
async handleEmailAnalytics(params) {
const { email, intentAnalysis, response } = params;
try {
// Analytics tracking - placeholder implementation
const analyticsResult = {
success: true,
data: {
event_id: `analytics_${Date.now()}`,
tracked: true
}
};
return {
success: true,
analyticsId: analyticsResult.data?.event_id,
tracked: true
};
}
catch (error) {
console.error('[MCP] Error tracking email analytics:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
// Private helper methods
async executeAgentWorkflow(workflow, prospect, inquiry) {
try {
// Map workflow names to valid agent names
const agentMap = {
'qualification': 'rosa_sdr',
'research': 'prospecting',
'personalization': 'personalization',
'objection_handling': 'objection_handling',
'nurturing': 'nurturing',
'sequence': 'sequence',
'social_selling': 'social_selling'
};
const agentName = agentMap[workflow] || 'rosa_sdr';
return await this.apiService.executeAgent({
agent_name: agentName,
input_data: {
prospect: prospect,
inquiry: inquiry,
context: `Email-based ${workflow} workflow`
},
options: {
timeout: 30,
priority: 'medium'
}
});
}
catch (error) {
console.error(`[MCP] Error executing ${workflow}:`, error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
data: { prospect, inquiry }
};
}
}
async extractCompanyFromEmail(content) {
// Simple company extraction - in production you'd use NLP
const emailDomainMatch = content.match(/@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/);
if (emailDomainMatch) {
return emailDomainMatch[1].replace(/^www\./, '');
}
return 'Unknown Company';
}
classifyInquiryType(content, subject) {
const text = (content + ' ' + subject).toLowerCase();
// Check for forecast/analytics requests FIRST
if (text.includes('forecast') || text.includes('projection') ||
text.includes('pipeline') || text.includes('revenue') ||
text.match(/\bq[1-4]\b/) || text.includes('quarterly') ||
text.includes('analytics') || text.includes('metrics'))
return 'forecast_request';
if (text.includes('demo') || text.includes('trial'))
return 'demo_request';
if (text.includes('price') || text.includes('cost') || text.includes('pricing'))
return 'pricing_question';
if (text.includes('meeting') || text.includes('call') || text.includes('schedule'))
return 'meeting_request';
if (text.includes('partner') || text.includes('integration'))
return 'partnership_inquiry';
if (text.includes('support') || text.includes('help') || text.includes('issue'))
return 'support_request';
if (text.includes('follow') || text.includes('checking in'))
return 'follow_up';
if (text.includes('buy') || text.includes('purchase') || text.includes('interested'))
return 'sales_inquiry';
return 'information_request';
}
determineUrgency(content, subject) {
const text = (content + ' ' + subject).toLowerCase();
if (text.includes('urgent') || text.includes('asap') || text.includes('immediately'))
return 'high';
if (text.includes('soon') || text.includes('quickly'))
return 'medium';
return 'low';
}
getRecommendedWorkflows(inquiryType) {
const workflowMap = {
'forecast_request': ['analytics', 'reporting'],
'sales_inquiry': ['qualification', 'research', 'personalization'],
'demo_request': ['qualification', 'personalization'],
'pricing_question': ['qualification', 'research'],
'meeting_request': ['qualification', 'personalization'],
'partnership_inquiry': ['research', 'personalization'],
'support_request': ['qualification'],
'follow_up': ['personalization'],
'information_request': ['research', 'personalization']
};
return workflowMap[inquiryType] || ['personalization'];
}
shouldScheduleFollowup(inquiryType) {
const followupTypes = ['sales_inquiry', 'demo_request', 'pricing_question', 'meeting_request'];
return followupTypes.includes(inquiryType);
}
getFollowupType(inquiryType) {
const followupMap = {
'sales_inquiry': 'gentle_reminder',
'demo_request': 'demo_followup',
'pricing_question': 'pricing_followup',
'meeting_request': 'meeting_followup'
};
return followupMap[inquiryType] || 'general';
}
calculateFollowupDate(delay) {
const multipliers = {
'hours': 60 * 60 * 1000,
'days': 24 * 60 * 60 * 1000,
'weeks': 7 * 24 * 60 * 60 * 1000
};
const multiplier = multipliers[delay.unit] || multipliers['days'];
return new Date(Date.now() + delay.value * multiplier).toISOString();
}
/**
* Handle email detection - Monitor for new incoming emails
*/
async handleEmailDetection(params) {
const { accountType = 'zoho', filters = {}, limit = 10, includeContent = true, markAsRead = false } = params;
try {
console.error(`[MCP] Detecting emails from ${accountType} account with filters:`, filters);
// Call the backend API to detect new emails
const response = await this.apiService.makeRequest('GET', '/api/email-automation/detect-emails', {
params: {
accountType,
unreadOnly: filters.unreadOnly ?? true,
fromDomain: filters.fromDomain,
keywords: filters.keywords ? filters.keywords.join(',') : undefined,
excludeAutomated: filters.excludeAutomated ?? true,
sinceTimestamp: filters.sinceTimestamp,
limit,
includeContent,
markAsRead
}
});
if (response.success) {
return {
success: true,
emails: response.data?.emails || [],
count: response.data?.count || 0,
hasMore: response.data?.hasMore || false,
nextTimestamp: response.data?.nextTimestamp,
accountType: accountType
};
}
else {
throw new Error(response.error || 'Email detection failed');
}
}
catch (error) {
console.error('[MCP] Error in email detection:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Handle email sending - Send emails via automation system
*/
async handleEmailSending(params) {
const { to, subject, content, template, options = {} } = params;
try {
console.error(`[MCP] Sending email to ${Array.isArray(to) ? to.join(', ') : to}: "${subject}"`);
let emailData = {
to,
subject,
options: {
replyTo: options.replyTo,
priority: options.priority || 'normal',
trackOpens: options.trackOpens ?? true,
trackClicks: options.trackClicks ?? true,
scheduleTime: options.scheduleTime
}
};
// Handle template-based emails
if (template) {
emailData.template = {
type: template.type,
data: template.data || {}
};
}
else if (content) {
// Handle direct content emails - support both string and object formats
if (typeof content === 'string') {
// If content is passed as a string, use it as both text and html
emailData.content = {
text: content,
html: content.replace(/\n/g, '<br>')
};
}
else {
// If content is an object, use the text and html properties
emailData.content = {
text: content.text,
html: content.html
};
}
}
else {
throw new Error('Either content or template must be provided');
}
// Call the backend API to send email using the existing custom email endpoint
let apiResponse;
if (template) {
// Use template-based sending (would need template processing)
apiResponse = await this.apiService.makeRequest('POST', '/api/email/custom', {
body: {
to: Array.isArray(to) ? to[0] : to, // Custom endpoint handles single recipient
subject: subject,
template: template.data?.html || '<html><body>{{content}}</body></html>',
data: {
content: template.data?.content || 'Template content',
...template.data
}
}
});
}
else {
// Use the send endpoint for direct content
apiResponse = await this.apiService.makeRequest('POST', '/api/email/send', {
body: {
emails: (Array.isArray(to) ? to : [to]).map(recipient => ({
recipient: recipient,
subject: subject,
body: content?.html || content?.text || 'Email content'
})),
campaignName: `MCP-${Date.now()}`,
scheduledTime: options.scheduleTime
}
});
}
const response = apiResponse;
if (response.success) {
return {
success: true,
messageId: response.data?.messageId,
status: response.data?.status || 'sent',
recipients: Array.isArray(to) ? to : [to],
scheduledFor: options.scheduleTime || null,
trackingEnabled: {
opens: options.trackOpens ?? true,
clicks: options.trackClicks ?? true
}
};
}
else {
throw new Error(response.error || 'Email sending failed');
}
}
catch (error) {
console.error('[MCP] Error in email sending:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Handle email status monitoring
*/
async handleEmailStatus(params) {
const { includeStats = true, includeQueue = false, timeRange } = params;
try {
console.error(`[MCP] Getting email automation status`);
const response = await this.apiService.makeRequest('GET', '/api/email-automation/status', {
params: {
includeStats,
includeQueue,
startTime: timeRange?.start,
endTime: timeRange?.end
}
});
if (response.success) {
return {
success: true,
status: response.data?.status || 'unknown',
services: response.data?.services || {},
stats: includeStats ? response.data?.stats || {} : undefined,
queue: includeQueue ? response.data?.queue || {} : undefined,
lastUpdated: response.data?.lastUpdated || new Date().toISOString()
};
}
else {
throw new Error(response.error || 'Failed to get email status');
}
}
catch (error) {
console.error('[MCP] Error getting email status:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Handle email template management
*/
async handleEmailTemplates(params) {
const { action, templateId, template } = params;
try {
console.error(`[MCP] Managing email templates - action: ${action}`);
let endpoint = '/api/email-automation/templates';
let method = 'GET';
let requestData = {};
switch (action) {
case 'list':
method = 'GET';
break;
case 'get':
if (!templateId)
throw new Error('templateId is required for get action');
endpoint = `${endpoint}/${templateId}`;
method = 'GET';
break;
case 'create':
if (!template)
throw new Error('template data is required for create action');
method = 'POST';
requestData.body = template;
break;
case 'update':
if (!templateId || !template)
throw new Error('templateId and template data are required for update action');
endpoint = `${endpoint}/${templateId}`;
method = 'PUT';
requestData.body = template;
break;
case 'delete':
if (!templateId)
throw new Error('templateId is required for delete action');
endpoint = `${endpoint}/${templateId}`;
method = 'DELETE';
break;
default:
throw new Error(`Invalid action: ${action}`);
}
const response = await this.apiService.makeRequest(method, endpoint, requestData);
if (response.success) {
return {
success: true,
action: action,
data: response.data,
templateId: templateId
};
}
else {
throw new Error(response.error || `Failed to ${action} email template`);
}
}
catch (error) {
console.error('[MCP] Error managing email templates:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Handle email campaign management
*/
async handleEmailCampaigns(params) {
const { action, campaignId, campaign } = params;
try {
console.error(`[MCP] Managing email campaigns - action: ${action}`);
let endpoint = '/api/email-automation/campaigns';
let method = 'GET';
let requestData = {};
switch (action) {
case 'list':
method = 'GET';
break;
case 'create':
if (!campaign)
throw new Error('campaign data is required for create action');
method = 'POST';
requestData.body = campaign;
break;
case 'start':
if (!campaignId)
throw new Error('campaignId is required for start action');
endpoint = `${endpoint}/${campaignId}/start`;
method = 'POST';
break;
case 'pause':
if (!campaignId)
throw new Error('campaignId is required for pause action');
endpoint = `${endpoint}/${campaignId}/pause`;
method = 'POST';
break;
case 'stop':
if (!campaignId)
throw new Error('campaignId is required for stop action');
endpoint = `${endpoint}/${campaignId}/stop`;
method = 'POST';
break;
case 'get_stats':
if (!campaignId)
throw new Error('campaignId is required for get_stats action');
endpoint = `${endpoint}/${campaignId}/stats`;
method = 'GET';
break;
default:
throw new Error(`Invalid action: ${action}`);
}
const response = await this.apiService.makeRequest(method, endpoint, requestData);
if (response.success) {
return {
success: true,
action: action,
data: response.data,
campaignId: campaignId
};
}
else {
throw new Error(response.error || `Failed to ${action} email campaign`);
}
}
catch (error) {
console.error('[MCP] Error managing email campaigns:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
}