UNPKG

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
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; } }