inboxassure-mcp-server
Version:
Comprehensive MCP server for InboxAssure email marketing platform with full API coverage including campaigns, replies, and email account management.
890 lines (835 loc) • 29.9 kB
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { BisonService } from "./bisonService.js";
// Create a MCP server without requiring an initial API key
export function createMcpServer(initialApiKey = null) {
const setupId = Math.random().toString(36).substring(2, 8);
console.error(`[DEBUG:SETUP:${setupId}] Starting MCP server creation at ${new Date().toISOString()}`);
console.error(`[DEBUG:SETUP:${setupId}] SDK version: @modelcontextprotocol/sdk version ${process.env.npm_package_dependencies_modelcontextprotocol_sdk || 'unknown'}`);
console.error(`[DEBUG:SETUP:${setupId}] Initial API key provided: ${initialApiKey ? 'yes' : 'no'}`);
try {
const server = new McpServer({
name: "InboxAssure MCP Server",
vendor: "InboxAssure",
version: "1.0.0"
});
console.error(`[DEBUG:SETUP:${setupId}] McpServer instance created successfully`);
let apiKey = initialApiKey;
let bisonService = null;
console.error(`[DEBUG:SETUP:${setupId}] Defining setApiKey method`);
// Add method to set/update API key
server.setApiKey = (newApiKey) => {
if (!newApiKey) {
console.error(`[DEBUG:SETUP:${setupId}] WARNING: Empty API key provided to setApiKey`);
return;
}
try {
console.error(`[DEBUG:SETUP:${setupId}] Setting API key: ${newApiKey.substring(0, 8)}...`);
apiKey = newApiKey;
// Create a new BisonService instance with the updated API key
console.error(`[DEBUG:SETUP:${setupId}] Creating new BisonService instance`);
bisonService = new BisonService(apiKey);
console.error(`[DEBUG:SETUP:${setupId}] BisonService initialized successfully`);
} catch (error) {
console.error(`[DEBUG:SETUP:${setupId}] Error in setApiKey: ${error.message}`);
console.error(error.stack);
}
};
// If an initial API key was provided, set up BisonService
if (initialApiKey) {
console.error(`[DEBUG:SETUP:${setupId}] Setting initial API key`);
server.setApiKey(initialApiKey);
} else {
console.error(`[DEBUG:SETUP:${setupId}] No initial API key provided, BisonService will be initialized on first request`);
}
// Helper function to create tool handler
const createToolHandler = (toolName) => {
return async (args) => {
const toolCallId = Math.random().toString(36).substring(2, 8);
console.error(`[DEBUG:TOOL:${toolCallId}] ${toolName} called with args: ${JSON.stringify(args)}`);
// Check if we have a BisonService (which means we have an API key)
if (!bisonService) {
console.error(`[DEBUG:TOOL:${toolCallId}] Error: API key has not been set. Cannot call BisonService.`);
throw new Error("API key has not been set. Cannot call BisonService.");
}
try {
// Directly call the service method with the correct parameters
let result;
switch (toolName) {
case "get_bison_hello":
result = await bisonService.getHello();
break;
// Campaigns API
case "list_campaigns":
result = await bisonService.listCampaigns(args.workspace_id, args.params || {});
break;
case "create_campaign":
result = await bisonService.createCampaign(args.workspace_id, {
name: args.name,
type: args.type
});
break;
case "get_campaign_details":
result = await bisonService.getCampaignDetails(args.workspace_id, args.campaign_id);
break;
case "pause_campaign":
result = await bisonService.pauseCampaign(args.workspace_id, args.campaign_id);
break;
case "resume_campaign":
result = await bisonService.resumeCampaign(args.workspace_id, args.campaign_id);
break;
case "get_campaign_stats":
result = await bisonService.getCampaignStats(args.workspace_id, args.campaign_id, {
start_date: args.start_date,
end_date: args.end_date
});
break;
case "get_campaign_leads":
result = await bisonService.getCampaignLeads(args.workspace_id, args.campaign_id, args.params || {});
break;
case "create_campaign_schedule":
result = await bisonService.createCampaignSchedule(args.workspace_id, args.campaign_id, {
monday: args.monday,
tuesday: args.tuesday,
wednesday: args.wednesday,
thursday: args.thursday,
friday: args.friday,
saturday: args.saturday,
sunday: args.sunday,
start_time: args.start_time,
end_time: args.end_time,
timezone: args.timezone
});
break;
case "get_schedule_templates":
result = await bisonService.getScheduleTemplates(args.workspace_id);
break;
case "create_sequence_steps":
result = await bisonService.createSequenceSteps(args.workspace_id, args.campaign_id, {
title: args.title,
sequence_steps: args.sequence_steps
});
break;
case "attach_lead_list":
result = await bisonService.attachLeadList(args.workspace_id, args.campaign_id, {
lead_list_id: args.lead_list_id
});
break;
// Replies API
case "list_replies":
result = await bisonService.listReplies(args.workspace_id, args.params || {});
break;
case "get_reply_details":
result = await bisonService.getReplyDetails(args.workspace_id, args.reply_id);
break;
case "compose_new_email":
result = await bisonService.composeNewEmail(args.workspace_id, {
sender_email_id: args.sender_email_id,
to_emails: args.to_emails,
message: args.message,
content_type: args.content_type || 'text/plain',
cc_emails: args.cc_emails || [],
bcc_emails: args.bcc_emails || []
});
break;
case "reply_to_email":
result = await bisonService.replyToEmail(args.workspace_id, args.reply_id, {
sender_email_id: args.sender_email_id,
to_emails: args.to_emails,
message: args.message,
content_type: args.content_type || 'text/plain'
});
break;
case "get_conversation_thread":
result = await bisonService.getConversationThread(args.workspace_id, args.reply_id);
break;
// Email Accounts API
case "list_email_accounts":
result = await bisonService.listEmailAccounts(args.workspace_id, args.params || {});
break;
case "get_email_account_details":
result = await bisonService.getEmailAccountDetails(args.workspace_id, args.email_id);
break;
case "update_email_account":
result = await bisonService.updateEmailAccount(args.workspace_id, args.email_id, {
daily_limit: args.daily_limit,
name: args.name,
email_signature: args.email_signature
});
break;
case "get_account_campaigns":
result = await bisonService.getAccountCampaigns(args.workspace_id, args.email_id);
break;
case "get_account_replies":
result = await bisonService.getAccountReplies(args.workspace_id, args.email_id);
break;
case "bulk_update_email_signatures":
result = await bisonService.bulkUpdateEmailSignatures(args.workspace_id, {
sender_email_ids: args.sender_email_ids,
email_signature: args.email_signature
});
break;
default:
throw new Error(`Tool not found: ${toolName}`);
}
console.error(`[DEBUG:TOOL:${toolCallId}] Tool ${toolName} succeeded`);
// Format the response as expected by the MCP protocol
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`[DEBUG:TOOL:${toolCallId}] Error in ${toolName} tool: ${errorMessage}`);
console.error(error.stack);
const errorResponse = {
isError: true,
content: [{ type: "text", text: `Error calling ${toolName}: ${errorMessage}` }]
};
console.error(`[DEBUG:TOOL:${toolCallId}] Returning error response: ${JSON.stringify(errorResponse)}`);
return errorResponse;
}
};
};
// ==================== REGISTER TOOLS ====================
console.error(`[DEBUG:SETUP:${setupId}] Registering tools`);
// Override the request handler to intercept tool calls directly
const originalHandler = server.requestHandler;
server.requestHandler = async (request, extra) => {
console.error(`[DEBUG:PROTOCOL] Raw request: ${JSON.stringify(request, null, 2)}`);
if (request.method === "tools/call") {
const toolName = request.params?.name;
const toolArguments = request.params?.arguments;
console.error(`[DEBUG:PROTOCOL] Tool call detected: ${toolName}`);
console.error(`[DEBUG:PROTOCOL] Tool arguments: ${JSON.stringify(toolArguments, null, 2)}`);
// Handle tool calls directly here
if (toolName === "list_campaigns" && toolArguments) {
try {
if (!bisonService) {
throw new Error("API key has not been set. Cannot call BisonService.");
}
const result = await bisonService.listCampaigns(toolArguments.workspace_id, toolArguments.params || {});
return {
jsonrpc: "2.0",
id: request.id,
result: {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
}
};
} catch (error) {
return {
jsonrpc: "2.0",
id: request.id,
result: {
isError: true,
content: [{ type: "text", text: `Error calling list_campaigns: ${error.message}` }]
}
};
}
}
}
// For other requests, use the original handler
return await originalHandler.call(server, request, extra);
};
// Original Hello tool
server.tool(
"get_bison_hello",
"Calls the GET https://bison.imnodev.com/api/hello endpoint.",
{
type: "object",
properties: {
random_string: {
type: "string",
description: "Dummy parameter for no-parameter tools"
}
},
required: []
},
createToolHandler("get_bison_hello")
);
// ==================== CAMPAIGNS API TOOLS ====================
server.tool(
"list_campaigns",
"List all campaigns in a workspace with optional filtering and pagination.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID to list campaigns for"
},
params: {
type: "object",
description: "Optional query parameters",
properties: {
search: { type: "string", description: "Search term for campaign names" },
status: { type: "string", description: "Filter by status (active, paused, draft)" },
page: { type: "number", description: "Page number for pagination" },
per_page: { type: "number", description: "Items per page (max 100)" }
}
}
},
required: ["workspace_id"]
},
createToolHandler("list_campaigns")
);
server.tool(
"create_campaign",
"Create a new email marketing campaign.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID to create the campaign in"
},
name: {
type: "string",
description: "Name of the campaign"
},
type: {
type: "string",
description: "Type of campaign (default: outbound)",
enum: ["outbound", "inbound"]
}
},
required: ["workspace_id", "name"]
},
createToolHandler("create_campaign")
);
server.tool(
"get_campaign_details",
"Get detailed information about a specific campaign.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
campaign_id: {
type: "string",
description: "The campaign ID to get details for"
}
},
required: ["workspace_id", "campaign_id"]
},
createToolHandler("get_campaign_details")
);
server.tool(
"pause_campaign",
"Pause a running campaign temporarily.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
campaign_id: {
type: "string",
description: "The campaign ID to pause"
}
},
required: ["workspace_id", "campaign_id"]
},
createToolHandler("pause_campaign")
);
server.tool(
"resume_campaign",
"Resume a paused campaign.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
campaign_id: {
type: "string",
description: "The campaign ID to resume"
}
},
required: ["workspace_id", "campaign_id"]
},
createToolHandler("resume_campaign")
);
server.tool(
"get_campaign_stats",
"Get detailed performance metrics for a campaign within a date range.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
campaign_id: {
type: "string",
description: "The campaign ID to get stats for"
},
start_date: {
type: "string",
description: "Start date in YYYY-MM-DD format"
},
end_date: {
type: "string",
description: "End date in YYYY-MM-DD format"
}
},
required: ["workspace_id", "campaign_id", "start_date", "end_date"]
},
createToolHandler("get_campaign_stats")
);
server.tool(
"get_campaign_leads",
"View all leads in a campaign with their status and progress.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
campaign_id: {
type: "string",
description: "The campaign ID to get leads for"
},
params: {
type: "object",
description: "Optional query parameters",
properties: {
page: { type: "number", description: "Page number for pagination" },
per_page: { type: "number", description: "Items per page" },
status: { type: "string", description: "Filter by lead status" }
}
}
},
required: ["workspace_id", "campaign_id"]
},
createToolHandler("get_campaign_leads")
);
server.tool(
"create_campaign_schedule",
"Set up when your campaign sends emails (days, times, timezone).",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
campaign_id: {
type: "string",
description: "The campaign ID to create schedule for"
},
monday: {
type: "boolean",
description: "Send emails on Monday"
},
tuesday: {
type: "boolean",
description: "Send emails on Tuesday"
},
wednesday: {
type: "boolean",
description: "Send emails on Wednesday"
},
thursday: {
type: "boolean",
description: "Send emails on Thursday"
},
friday: {
type: "boolean",
description: "Send emails on Friday"
},
saturday: {
type: "boolean",
description: "Send emails on Saturday"
},
sunday: {
type: "boolean",
description: "Send emails on Sunday"
},
start_time: {
type: "string",
description: "Start time in HH:MM format (e.g., 09:00)"
},
end_time: {
type: "string",
description: "End time in HH:MM format (e.g., 17:00)"
},
timezone: {
type: "string",
description: "Timezone (e.g., America/New_York)"
}
},
required: ["workspace_id", "campaign_id", "start_time", "end_time", "timezone"]
},
createToolHandler("create_campaign_schedule")
);
server.tool(
"get_schedule_templates",
"Retrieve predefined schedule templates for quick campaign setup.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
}
},
required: ["workspace_id"]
},
createToolHandler("get_schedule_templates")
);
server.tool(
"create_sequence_steps",
"Define the series of emails that will be sent to leads over time.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
campaign_id: {
type: "string",
description: "The campaign ID to create sequence for"
},
title: {
type: "string",
description: "Title for the email sequence"
},
sequence_steps: {
type: "array",
description: "Array of email steps in the sequence",
items: {
type: "object",
properties: {
email_subject: { type: "string", description: "Subject line for the email" },
email_body: { type: "string", description: "Body content of the email" },
wait_in_days: { type: "number", description: "Days to wait before sending this email" },
order: { type: "number", description: "Order of this step in the sequence" }
},
required: ["email_subject", "email_body", "wait_in_days", "order"]
}
}
},
required: ["workspace_id", "campaign_id", "title", "sequence_steps"]
},
createToolHandler("create_sequence_steps")
);
server.tool(
"attach_lead_list",
"Add an entire existing lead list to your campaign.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
campaign_id: {
type: "string",
description: "The campaign ID to attach leads to"
},
lead_list_id: {
type: "string",
description: "The ID of the lead list to attach"
}
},
required: ["workspace_id", "campaign_id", "lead_list_id"]
},
createToolHandler("attach_lead_list")
);
// ==================== REPLIES API TOOLS ====================
server.tool(
"list_replies",
"Retrieve all email replies across campaigns with filtering options.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
params: {
type: "object",
description: "Optional query parameters",
properties: {
search: { type: "string", description: "Search term for emails, subjects" },
status: { type: "string", description: "Filter by status (interested, not_interested, automated_reply)" },
folder: { type: "string", description: "Email folder (inbox, sent, spam, bounced, all)" },
read: { type: "boolean", description: "Filter by read status" },
campaign_id: { type: "string", description: "Filter by campaign ID" },
page: { type: "number", description: "Page number for pagination" },
per_page: { type: "number", description: "Items per page" }
}
}
},
required: ["workspace_id"]
},
createToolHandler("list_replies")
);
server.tool(
"get_reply_details",
"Get complete details of a specific reply including full message content.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
reply_id: {
type: "string",
description: "The reply ID to get details for"
}
},
required: ["workspace_id", "reply_id"]
},
createToolHandler("get_reply_details")
);
server.tool(
"compose_new_email",
"Send a standalone email outside of campaign sequences.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
sender_email_id: {
type: "string",
description: "ID of the sender email account"
},
to_emails: {
type: "array",
items: { type: "string" },
description: "Array of recipient email addresses"
},
message: {
type: "string",
description: "The email message content"
},
content_type: {
type: "string",
description: "Content type (text/plain or text/html)",
enum: ["text/plain", "text/html"]
},
cc_emails: {
type: "array",
items: { type: "string" },
description: "Array of CC email addresses"
},
bcc_emails: {
type: "array",
items: { type: "string" },
description: "Array of BCC email addresses"
}
},
required: ["workspace_id", "sender_email_id", "to_emails", "message"]
},
createToolHandler("compose_new_email")
);
server.tool(
"reply_to_email",
"Respond to an incoming email reply, maintaining conversation thread.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
reply_id: {
type: "string",
description: "The reply ID to respond to"
},
sender_email_id: {
type: "string",
description: "ID of the sender email account"
},
to_emails: {
type: "array",
items: { type: "string" },
description: "Array of recipient email addresses"
},
message: {
type: "string",
description: "The reply message content"
},
content_type: {
type: "string",
description: "Content type (text/plain or text/html)",
enum: ["text/plain", "text/html"]
}
},
required: ["workspace_id", "reply_id", "sender_email_id", "to_emails", "message"]
},
createToolHandler("reply_to_email")
);
server.tool(
"get_conversation_thread",
"Retrieve the complete email conversation history for context.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
reply_id: {
type: "string",
description: "The reply ID to get conversation thread for"
}
},
required: ["workspace_id", "reply_id"]
},
createToolHandler("get_conversation_thread")
);
// ==================== EMAIL ACCOUNTS API TOOLS ====================
server.tool(
"list_email_accounts",
"Get all email accounts configured in your workspace with their settings.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
params: {
type: "object",
description: "Optional query parameters",
properties: {
search: { type: "string", description: "Search term for email addresses, names" },
page: { type: "number", description: "Page number for pagination" },
per_page: { type: "number", description: "Items per page" }
}
}
},
required: ["workspace_id"]
},
createToolHandler("list_email_accounts")
);
server.tool(
"get_email_account_details",
"Retrieve detailed configuration and statistics for a specific email account.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
email_id: {
type: "string",
description: "The email account ID to get details for"
}
},
required: ["workspace_id", "email_id"]
},
createToolHandler("get_email_account_details")
);
server.tool(
"update_email_account",
"Modify email account configuration like daily limits and signatures.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
email_id: {
type: "string",
description: "The email account ID to update"
},
daily_limit: {
type: "number",
description: "New daily email sending limit"
},
name: {
type: "string",
description: "New display name for the email account"
},
email_signature: {
type: "string",
description: "New email signature"
}
},
required: ["workspace_id", "email_id"]
},
createToolHandler("update_email_account")
);
server.tool(
"get_account_campaigns",
"See which campaigns are using this email account for sending.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
email_id: {
type: "string",
description: "The email account ID to get campaigns for"
}
},
required: ["workspace_id", "email_id"]
},
createToolHandler("get_account_campaigns")
);
server.tool(
"get_account_replies",
"View all replies received by this specific email account.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
email_id: {
type: "string",
description: "The email account ID to get replies for"
}
},
required: ["workspace_id", "email_id"]
},
createToolHandler("get_account_replies")
);
server.tool(
"bulk_update_email_signatures",
"Update email signatures across multiple accounts simultaneously.",
{
type: "object",
properties: {
workspace_id: {
type: "string",
description: "The workspace ID"
},
sender_email_ids: {
type: "array",
items: { type: "string" },
description: "Array of email account IDs to update"
},
email_signature: {
type: "string",
description: "New email signature to apply to all selected accounts"
}
},
required: ["workspace_id", "sender_email_ids", "email_signature"]
},
createToolHandler("bulk_update_email_signatures")
);
console.error(`[DEBUG:SETUP:${setupId}] MCP server setup completed successfully with ${23} tools registered`);
return server;
} catch (error) {
console.error(`[DEBUG:SETUP:${setupId}] Error creating MCP server: ${error.message}`);
console.error(error.stack);
throw error;
}
}