This is page 2 of 2. Use http://codebase.md/jean-technologies/smartlead-mcp-server-local?page={x} to view the full context. # Directory Structure ``` ├── .env.example ├── .gitignore ├── DEVELOPER_ONBOARDING.md ├── Dockerfile ├── jest.config.js ├── llms-install.md ├── mcp_settings_example.json ├── package-lock.json ├── package.json ├── README.md ├── server │ └── license-server.js ├── smithery.yaml ├── src │ ├── cli.ts │ ├── config │ │ └── feature-config.ts │ ├── handlers │ │ ├── campaign.ts │ │ ├── clientManagement.ts │ │ ├── email.ts │ │ ├── lead.ts │ │ ├── smartDelivery.ts │ │ ├── smartSenders.ts │ │ ├── statistics.ts │ │ └── webhooks.ts │ ├── index.ts │ ├── licensing │ │ ├── index.ts │ │ └── stripe-integration.js │ ├── n8n │ │ └── index.ts │ ├── registry │ │ └── tool-registry.ts │ ├── supergateway-mock.ts │ ├── supergateway.ts │ ├── tools │ │ ├── campaign.ts │ │ ├── clientManagement.ts │ │ ├── email.d.ts │ │ ├── email.ts │ │ ├── lead.d.ts │ │ ├── lead.ts │ │ ├── smartDelivery.d.ts │ │ ├── smartDelivery.ts │ │ ├── smartSenders.ts │ │ ├── statistics.d.ts │ │ ├── statistics.ts │ │ └── webhooks.ts │ ├── types │ │ ├── campaign.ts │ │ ├── clientManagement.ts │ │ ├── common.ts │ │ ├── email.d.ts │ │ ├── email.ts │ │ ├── lead.ts │ │ ├── smartDelivery.ts │ │ ├── smartSenders.ts │ │ ├── statistics.d.ts │ │ ├── statistics.ts │ │ ├── supergateway.d.ts │ │ └── webhooks.ts │ └── utils │ └── download-tracker.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /src/handlers/campaign.ts: -------------------------------------------------------------------------------- ```typescript import { AxiosInstance } from 'axios'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { isCreateCampaignParams, isUpdateCampaignScheduleParams, isUpdateCampaignSettingsParams, isUpdateCampaignStatusParams, isGetCampaignParams, isListCampaignsParams, isSaveCampaignSequenceParams, isGetCampaignSequenceParams, isGetCampaignsByLeadParams, isExportCampaignLeadsParams, isDeleteCampaignParams, isGetCampaignAnalyticsByDateParams, isGetCampaignSequenceAnalyticsParams } from '../types/campaign.js'; // Handler for campaign-related tools export async function handleCampaignTool( toolName: string, args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { switch (toolName) { case 'smartlead_create_campaign': { return handleCreateCampaign(args, apiClient, withRetry); } case 'smartlead_update_campaign_schedule': { return handleUpdateCampaignSchedule(args, apiClient, withRetry); } case 'smartlead_update_campaign_settings': { return handleUpdateCampaignSettings(args, apiClient, withRetry); } case 'smartlead_update_campaign_status': { return handleUpdateCampaignStatus(args, apiClient, withRetry); } case 'smartlead_get_campaign': { return handleGetCampaign(args, apiClient, withRetry); } case 'smartlead_list_campaigns': { return handleListCampaigns(args, apiClient, withRetry); } case 'smartlead_save_campaign_sequence': { return handleSaveCampaignSequence(args, apiClient, withRetry); } case 'smartlead_get_campaign_sequence': { return handleGetCampaignSequence(args, apiClient, withRetry); } case 'smartlead_get_campaigns_by_lead': { return handleGetCampaignsByLead(args, apiClient, withRetry); } case 'smartlead_export_campaign_leads': { return handleExportCampaignLeads(args, apiClient, withRetry); } case 'smartlead_delete_campaign': { return handleDeleteCampaign(args, apiClient, withRetry); } case 'smartlead_get_campaign_analytics_by_date': { return handleGetCampaignAnalyticsByDate(args, apiClient, withRetry); } case 'smartlead_get_campaign_sequence_analytics': { return handleGetCampaignSequenceAnalytics(args, apiClient, withRetry); } default: throw new Error(`Unknown campaign tool: ${toolName}`); } } // Individual handlers for each tool async function handleCreateCampaign( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCreateCampaignParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_create_campaign' ); } try { const response = await withRetry( async () => apiClient.post('/campaigns/create', args), 'create campaign' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleUpdateCampaignSchedule( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isUpdateCampaignScheduleParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_update_campaign_schedule' ); } const { campaign_id, ...scheduleParams } = args; try { const response = await withRetry( async () => apiClient.post(`/campaigns/${campaign_id}/schedule`, scheduleParams), 'update campaign schedule' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleUpdateCampaignSettings( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isUpdateCampaignSettingsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_update_campaign_settings' ); } const { campaign_id, ...settingsParams } = args; try { const response = await withRetry( async () => apiClient.post(`/campaigns/${campaign_id}/settings`, settingsParams), 'update campaign settings' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleUpdateCampaignStatus( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isUpdateCampaignStatusParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_update_campaign_status' ); } const { campaign_id, status } = args; try { const response = await withRetry( async () => apiClient.post(`/campaigns/${campaign_id}/status`, { status }), 'update campaign status' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetCampaign( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetCampaignParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign' ); } try { const response = await withRetry( async () => apiClient.get(`/campaigns/${args.campaign_id}`), 'get campaign' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleListCampaigns( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isListCampaignsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_list_campaigns' ); } try { const response = await withRetry( async () => apiClient.get('/campaigns', { params: args }), 'list campaigns' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleSaveCampaignSequence( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isSaveCampaignSequenceParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_save_campaign_sequence' ); } const { campaign_id, sequence } = args; try { const response = await withRetry( async () => apiClient.post(`/campaigns/${campaign_id}/sequences`, { sequence }), 'save campaign sequence' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetCampaignSequence( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetCampaignSequenceParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_sequence' ); } try { const response = await withRetry( async () => apiClient.get(`/campaigns/${args.campaign_id}/sequences`), 'get campaign sequence' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } // New handler implementations for the remaining campaign management API endpoints async function handleGetCampaignsByLead( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetCampaignsByLeadParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaigns_by_lead' ); } try { const response = await withRetry( async () => apiClient.get(`/leads/${args.lead_id}/campaigns`), 'get campaigns by lead' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleExportCampaignLeads( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isExportCampaignLeadsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_export_campaign_leads' ); } try { const response = await withRetry( async () => apiClient.get(`/campaigns/${args.campaign_id}/leads-export`, { responseType: 'text' }), 'export campaign leads' ); return { content: [ { type: 'text', text: `CSV Data:\n${response.data}`, }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleDeleteCampaign( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isDeleteCampaignParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_delete_campaign' ); } try { const response = await withRetry( async () => apiClient.delete(`/campaigns/${args.campaign_id}`), 'delete campaign' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetCampaignAnalyticsByDate( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetCampaignAnalyticsByDateParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_analytics_by_date' ); } const { campaign_id, ...params } = args; try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/analytics-by-date`, { params }), 'get campaign analytics by date' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetCampaignSequenceAnalytics( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetCampaignSequenceAnalyticsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_sequence_analytics' ); } const { campaign_id, ...params } = args; try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/sequence-analytics`, { params }), 'get campaign sequence analytics' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } ``` -------------------------------------------------------------------------------- /src/handlers/email.ts: -------------------------------------------------------------------------------- ```typescript import { AxiosInstance } from 'axios'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { isListEmailAccountsParams, isAddEmailToCampaignParams, isRemoveEmailFromCampaignParams, isFetchEmailAccountsParams, isCreateEmailAccountParams, isUpdateEmailAccountParams, isFetchEmailAccountByIdParams, isUpdateEmailWarmupParams, isReconnectEmailAccountParams, isUpdateEmailAccountTagParams, ListEmailAccountsParams, AddEmailToCampaignParams, RemoveEmailFromCampaignParams, FetchEmailAccountsParams, UpdateEmailAccountParams, FetchEmailAccountByIdParams, UpdateEmailWarmupParams, UpdateEmailAccountTagParams, CreateEmailAccountParams } from '../types/email.js'; // Handler for email-related tools export async function handleEmailTool( toolName: string, args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { switch (toolName) { case 'smartlead_list_email_accounts_campaign': { return handleListEmailAccountsCampaign(args, apiClient, withRetry); } case 'smartlead_add_email_to_campaign': { return handleAddEmailToCampaign(args, apiClient, withRetry); } case 'smartlead_remove_email_from_campaign': { return handleRemoveEmailFromCampaign(args, apiClient, withRetry); } case 'smartlead_fetch_email_accounts': { return handleFetchEmailAccounts(args, apiClient, withRetry); } case 'smartlead_create_email_account': { return handleCreateEmailAccount(args, apiClient, withRetry); } case 'smartlead_update_email_account': { return handleUpdateEmailAccount(args, apiClient, withRetry); } case 'smartlead_fetch_email_account_by_id': { return handleFetchEmailAccountById(args, apiClient, withRetry); } case 'smartlead_update_email_warmup': { return handleUpdateEmailWarmup(args, apiClient, withRetry); } case 'smartlead_reconnect_email_account': { return handleReconnectEmailAccount(args, apiClient, withRetry); } case 'smartlead_update_email_account_tag': { return handleUpdateEmailAccountTag(args, apiClient, withRetry); } default: throw new Error(`Unknown email tool: ${toolName}`); } } // Individual handlers for each tool async function handleListEmailAccountsCampaign( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isListEmailAccountsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_list_email_accounts_campaign' ); } const params = args as ListEmailAccountsParams; const { campaign_id, status, limit, offset } = params; if (!campaign_id) { throw new McpError( ErrorCode.InvalidParams, 'campaign_id is required for smartlead_list_email_accounts_campaign' ); } // API endpoint: https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/email-accounts try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/email-accounts`), 'list email accounts for campaign' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } // Placeholder functions for the other handlers // These will be implemented once we have the API documentation for these endpoints async function handleAddEmailToCampaign( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isAddEmailToCampaignParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_add_email_to_campaign' ); } const { campaign_id, email_account_id } = args as AddEmailToCampaignParams; // API endpoint: https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/email-accounts try { const response = await withRetry( async () => apiClient.post(`/campaigns/${campaign_id}/email-accounts`, { email_account_ids: [email_account_id] }), 'add email to campaign' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleRemoveEmailFromCampaign( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isRemoveEmailFromCampaignParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_remove_email_from_campaign' ); } const { campaign_id, email_account_id } = args as RemoveEmailFromCampaignParams; // API endpoint: https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/email-accounts try { const response = await withRetry( async () => apiClient.delete(`/campaigns/${campaign_id}/email-accounts`, { data: { email_accounts_ids: [email_account_id] } }), 'remove email from campaign' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleFetchEmailAccounts( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isFetchEmailAccountsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_fetch_email_accounts' ); } const { status, limit, offset, username, client_id } = args as FetchEmailAccountsParams; // Build query parameters const queryParams: Record<string, string | number> = {}; if (limit !== undefined) queryParams.limit = limit; if (offset !== undefined) queryParams.offset = offset; if (username) queryParams.username = username; if (client_id) queryParams.client_id = client_id; // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/ try { const response = await withRetry( async () => apiClient.get('/email-accounts/', { params: queryParams }), 'fetch all email accounts' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCreateEmailAccount( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCreateEmailAccountParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_create_email_account' ); } const createParams = args as CreateEmailAccountParams; // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/save try { const response = await withRetry( async () => apiClient.post('/email-accounts/save', { id: null, // Set null to create new email account from_name: createParams.from_name, from_email: createParams.from_email, user_name: createParams.user_name, password: createParams.password, smtp_host: createParams.smtp_host, smtp_port: createParams.smtp_port, imap_host: createParams.imap_host, imap_port: createParams.imap_port, max_email_per_day: createParams.max_email_per_day, custom_tracking_url: createParams.custom_tracking_url, bcc: createParams.bcc, signature: createParams.signature, warmup_enabled: createParams.warmup_enabled, total_warmup_per_day: createParams.total_warmup_per_day, daily_rampup: createParams.daily_rampup, reply_rate_percentage: createParams.reply_rate_percentage, client_id: createParams.client_id }), 'create email account' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleUpdateEmailAccount( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isUpdateEmailAccountParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_update_email_account' ); } const { email_account_id, ...updateParams } = args as UpdateEmailAccountParams; // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/{email_account_id} try { const response = await withRetry( async () => apiClient.post(`/email-accounts/${email_account_id}`, updateParams), 'update email account' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleFetchEmailAccountById( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isFetchEmailAccountByIdParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_fetch_email_account_by_id' ); } const { email_account_id } = args as FetchEmailAccountByIdParams; // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/{account_id}/ try { const response = await withRetry( async () => apiClient.get(`/email-accounts/${email_account_id}/`), 'fetch email account by id' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleUpdateEmailWarmup( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isUpdateEmailWarmupParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_update_email_warmup' ); } const { email_account_id, ...warmupParams } = args as UpdateEmailWarmupParams; // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/{email_account_id}/warmup try { const response = await withRetry( async () => apiClient.post(`/email-accounts/${email_account_id}/warmup`, warmupParams), 'update email warmup' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleReconnectEmailAccount( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isReconnectEmailAccountParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_reconnect_email_account' ); } // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/reconnect-failed-email-accounts try { const response = await withRetry( async () => apiClient.post('/email-accounts/reconnect-failed-email-accounts', {}), 'reconnect failed email accounts' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleUpdateEmailAccountTag( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isUpdateEmailAccountTagParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_update_email_account_tag' ); } const { id, name, color } = args as UpdateEmailAccountTagParams; // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/tag-manager try { const response = await withRetry( async () => apiClient.post('/email-accounts/tag-manager', { id, name, color }), 'update email account tag' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } ``` -------------------------------------------------------------------------------- /src/handlers/statistics.ts: -------------------------------------------------------------------------------- ```typescript import { AxiosInstance } from 'axios'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { isCampaignStatisticsParams, isCampaignStatisticsByDateParams, isWarmupStatsByEmailParams, isCampaignTopLevelAnalyticsParams, isCampaignTopLevelAnalyticsByDateParams, isCampaignLeadStatisticsParams, isCampaignMailboxStatisticsParams, isDownloadCampaignDataParams, isViewDownloadStatisticsParams } from '../types/statistics.js'; import { trackDownload, getDownloadStats, getDownloadRecords } from '../utils/download-tracker.js'; // Handler for statistics-related tools export async function handleStatisticsTool( toolName: string, args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { switch (toolName) { case 'smartlead_get_campaign_statistics': { return handleCampaignStatistics(args, apiClient, withRetry); } case 'smartlead_get_campaign_statistics_by_date': { return handleCampaignStatisticsByDate(args, apiClient, withRetry); } case 'smartlead_get_warmup_stats_by_email': { return handleWarmupStatsByEmail(args, apiClient, withRetry); } case 'smartlead_get_campaign_top_level_analytics': { return handleCampaignTopLevelAnalytics(args, apiClient, withRetry); } case 'smartlead_get_campaign_top_level_analytics_by_date': { return handleCampaignTopLevelAnalyticsByDate(args, apiClient, withRetry); } case 'smartlead_get_campaign_lead_statistics': { return handleCampaignLeadStatistics(args, apiClient, withRetry); } case 'smartlead_get_campaign_mailbox_statistics': { return handleCampaignMailboxStatistics(args, apiClient, withRetry); } case 'smartlead_download_campaign_data': { return handleDownloadCampaignData(args, apiClient, withRetry); } case 'smartlead_view_download_statistics': { return handleViewDownloadStatistics(args); } default: throw new Error(`Unknown statistics tool: ${toolName}`); } } // Individual handlers for each tool async function handleCampaignStatistics( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCampaignStatisticsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_statistics' ); } const { campaign_id, ...queryParams } = args; try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/statistics`, { params: queryParams }), 'get campaign statistics' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCampaignStatisticsByDate( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCampaignStatisticsByDateParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_statistics_by_date' ); } const { campaign_id, start_date, end_date } = args; try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/analytics-by-date`, { params: { start_date, end_date } }), 'get campaign statistics by date' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleWarmupStatsByEmail( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isWarmupStatsByEmailParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_warmup_stats_by_email' ); } const { email_account_id } = args; try { const response = await withRetry( async () => apiClient.get(`/email-accounts/${email_account_id}/warmup-stats`), 'get warmup stats by email' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCampaignTopLevelAnalytics( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCampaignTopLevelAnalyticsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_top_level_analytics' ); } const { campaign_id } = args; try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/analytics`), 'get campaign top level analytics' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCampaignTopLevelAnalyticsByDate( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCampaignTopLevelAnalyticsByDateParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_top_level_analytics_by_date' ); } const { campaign_id, start_date, end_date } = args; try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/top-level-analytics-by-date`, { params: { start_date, end_date } }), 'get campaign top level analytics by date' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCampaignLeadStatistics( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCampaignLeadStatisticsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_lead_statistics' ); } const { campaign_id, ...queryParams } = args; try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/lead-statistics`, { params: queryParams }), 'get campaign lead statistics' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCampaignMailboxStatistics( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCampaignMailboxStatisticsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_campaign_mailbox_statistics' ); } const { campaign_id, ...queryParams } = args; try { const response = await withRetry( async () => apiClient.get(`/campaigns/${campaign_id}/mailbox-statistics`, { params: queryParams }), 'get campaign mailbox statistics' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } // New function to handle the download and tracking async function handleDownloadCampaignData( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isDownloadCampaignDataParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_download_campaign_data' ); } const { campaign_id, download_type, format, user_id } = args; try { let endpoint = ''; let transformFn = (data: any) => data; // Determine the API endpoint based on download type switch (download_type) { case 'analytics': endpoint = `/campaigns/${campaign_id}/analytics`; break; case 'leads': endpoint = `/campaigns/${campaign_id}/leads`; break; case 'sequence': endpoint = `/campaigns/${campaign_id}/sequence`; break; case 'full_export': endpoint = `/campaigns/${campaign_id}/export`; break; default: throw new McpError( ErrorCode.InvalidParams, `Invalid download_type: ${download_type}` ); } // Fetch the data const response = await withRetry( async () => apiClient.get(endpoint), `download campaign ${download_type}` ); let responseData = response.data; // If format is CSV, convert the JSON data to CSV if (format === 'csv') { responseData = convertToCSV(responseData); } // Track the download const downloadId = trackDownload( campaign_id, download_type, format, user_id ); // Add download metadata to the response const result = { data: responseData, download_metadata: { download_id: downloadId, timestamp: new Date().toISOString(), campaign_id, download_type, format } }; return { content: [ { type: 'text', text: format === 'json' ? JSON.stringify(result, null, 2) : result.data, // CSV data is already stringified }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } // Helper function to convert JSON to CSV function convertToCSV(data: any): string { if (!data || typeof data !== 'object') { return ''; } // Handle array of objects if (Array.isArray(data)) { if (data.length === 0) return ''; // Get all possible headers from all objects const headers = new Set<string>(); data.forEach(item => { if (item && typeof item === 'object') { Object.keys(item).forEach(key => headers.add(key)); } }); const headerRow = Array.from(headers).join(','); const rows = data.map(item => { if (!item || typeof item !== 'object') return ''; return Array.from(headers) .map(header => { const cell = item[header] ?? ''; // Escape strings with quotes if they contain commas return typeof cell === 'string' && cell.includes(',') ? `"${cell.replace(/"/g, '""')}"` : String(cell); }) .join(','); }); return [headerRow, ...rows].join('\n'); } // Handle single object const headers = Object.keys(data).join(','); const values = Object.values(data) .map(val => { if (typeof val === 'string' && val.includes(',')) { return `"${val.replace(/"/g, '""')}"`; } return String(val); }) .join(','); return [headers, values].join('\n'); } // New function to handle viewing download statistics async function handleViewDownloadStatistics(args: unknown) { if (!isViewDownloadStatisticsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_view_download_statistics' ); } const { time_period = 'all', group_by = 'type' } = args; try { // Get all download records const allRecords = getDownloadRecords(); // Filter records by time period const now = new Date(); const filteredRecords = allRecords.filter(record => { const recordDate = new Date(record.timestamp); switch (time_period) { case 'today': return recordDate.toDateString() === now.toDateString(); case 'week': { const oneWeekAgo = new Date(); oneWeekAgo.setDate(now.getDate() - 7); return recordDate >= oneWeekAgo; } case 'month': { const oneMonthAgo = new Date(); oneMonthAgo.setMonth(now.getMonth() - 1); return recordDate >= oneMonthAgo; } case 'all': default: return true; } }); // Get basic statistics const stats = { totalDownloads: filteredRecords.length, timePeriod: time_period, uniqueUsers: new Set(filteredRecords.map(r => r.userId || r.machineId)).size }; // Group by specified field let groupedData: Record<string, number> = {}; switch (group_by) { case 'type': groupedData = filteredRecords.reduce((acc, record) => { acc[record.downloadType] = (acc[record.downloadType] || 0) + 1; return acc; }, {} as Record<string, number>); break; case 'format': groupedData = filteredRecords.reduce((acc, record) => { acc[record.format] = (acc[record.format] || 0) + 1; return acc; }, {} as Record<string, number>); break; case 'campaign': groupedData = filteredRecords.reduce((acc, record) => { const campaignKey = `campaign_${record.campaignId}`; acc[campaignKey] = (acc[campaignKey] || 0) + 1; return acc; }, {} as Record<string, number>); break; case 'date': groupedData = filteredRecords.reduce((acc, record) => { const date = record.timestamp.split('T')[0]; acc[date] = (acc[date] || 0) + 1; return acc; }, {} as Record<string, number>); break; } // Compile the result const result = { ...stats, groupedBy: group_by, groups: groupedData, // Include recent downloads for reference recentDownloads: filteredRecords .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) .slice(0, 5) }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `Error retrieving download statistics: ${error.message}` }], isError: true, }; } } ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript #!/usr/bin/env node import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, InitializeRequestSchema, InitializedNotificationSchema, ServerCapabilities } from '@modelcontextprotocol/sdk/types.js'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import axios, { AxiosInstance } from 'axios'; import dotenv from 'dotenv'; import path from 'path'; // Import licensing system import { validateLicense, trackUsage, isFeatureEnabled, printLicenseStatus, getFeatureToken } from './licensing/index.js'; // Import Supergateway integration import { createSupergateway } from './supergateway.js'; // Import our modular components import { campaignTools } from './tools/campaign.js'; // import { emailTools } from './tools/email.js'; import { leadTools } from './tools/lead.js'; import { statisticsTools } from './tools/statistics.js'; import { smartDeliveryTools } from './tools/smartDelivery.js'; import { webhookTools } from './tools/webhooks.js'; import { clientManagementTools } from './tools/clientManagement.js'; import { smartSendersTools } from './tools/smartSenders.js'; import { handleCampaignTool } from './handlers/campaign.js'; // import { handleEmailTool } from './handlers/email.js'; import { handleLeadTool } from './handlers/lead.js'; import { handleStatisticsTool } from './handlers/statistics.js'; import { handleSmartDeliveryTool } from './handlers/smartDelivery.js'; import { handleWebhookTool } from './handlers/webhooks.js'; import { handleClientManagementTool } from './handlers/clientManagement.js'; import { handleSmartSendersTool } from './handlers/smartSenders.js'; import { enabledCategories, featureFlags } from './config/feature-config.js'; import { ToolCategory } from './types/common.js'; import { toolRegistry } from './registry/tool-registry.js'; console.log('Starting Smartlead MCP Server...'); // Load environment variables from .env file in the project root dotenv.config({ path: path.resolve(process.cwd(), '.env') }); // Check license on startup (async () => { // Print detailed license information await printLicenseStatus(); // Always enable n8n integration regardless of license featureFlags.n8nIntegration = true; })().catch(error => { console.error('License validation error:', error); // Still ensure n8n integration is enabled even if there's an error featureFlags.n8nIntegration = true; }); // Check if Supergateway integration is enabled const useSupergateway = process.env.USE_SUPERGATEWAY === 'true'; // Define server capabilities const serverCapabilities: ServerCapabilities = { tools: { callTool: true, listTools: true }, logging: { loggingMessage: true } }; // Server implementation const server = new Server( { name: 'smartlead-mcp', version: '1.0.0', }, { capabilities: serverCapabilities, instructions: 'Smartlead MCP Server for accessing Smartlead API functionality' } ); // Get API key and URL from environment variables const SMARTLEAD_API_KEY = process.env.SMARTLEAD_API_KEY; const SMARTLEAD_API_URL = process.env.SMARTLEAD_API_URL || 'https://server.smartlead.ai/api/v1'; // Check if API key is provided if (!SMARTLEAD_API_KEY) { console.error('Error: SMARTLEAD_API_KEY environment variable is required'); process.exit(1); } // Configuration for retries and monitoring const CONFIG = { retry: { maxAttempts: Number(process.env.SMARTLEAD_RETRY_MAX_ATTEMPTS) || 3, initialDelay: Number(process.env.SMARTLEAD_RETRY_INITIAL_DELAY) || 1000, maxDelay: Number(process.env.SMARTLEAD_RETRY_MAX_DELAY) || 10000, backoffFactor: Number(process.env.SMARTLEAD_RETRY_BACKOFF_FACTOR) || 2, }, }; // Initialize Axios instance for API requests const apiClient: AxiosInstance = axios.create({ baseURL: SMARTLEAD_API_URL, params: { api_key: SMARTLEAD_API_KEY, }, headers: { 'Content-Type': 'application/json', }, }); let isStdioTransport = true; function safeLog( level: | 'error' | 'debug' | 'info' | 'notice' | 'warning' | 'critical' | 'alert' | 'emergency', data: any ): void { try { // Always log to stderr for now to avoid protocol interference const logMessage = typeof data === 'object' ? JSON.stringify(data) : data; console.error(`[${level}] ${logMessage}`); // Try to send via proper logging mechanism, but don't throw if it fails try { server.sendLoggingMessage({ level, data }).catch(e => { console.error(`Failed to send log via protocol: ${e.message}`); }); } catch (e) { console.error(`Error in logging mechanism: ${e instanceof Error ? e.message : String(e)}`); } } catch (e) { // Last resort fallback if anything in the logging fails console.error(`[${level}] Failed to format log message: ${e instanceof Error ? e.message : String(e)}`); try { console.error(`Original data type: ${typeof data}`); } catch (_) { // Ignore any errors in the fallback logging } } } // Add utility function for delay function delay(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } // Add retry logic with exponential backoff async function withRetry<T>( operation: () => Promise<T>, context: string, attempt = 1 ): Promise<T> { try { return await operation(); } catch (error) { const isRateLimit = error instanceof Error && (error.message.includes('rate limit') || error.message.includes('429')); if (isRateLimit && attempt < CONFIG.retry.maxAttempts) { const delayMs = Math.min( CONFIG.retry.initialDelay * Math.pow(CONFIG.retry.backoffFactor, attempt - 1), CONFIG.retry.maxDelay ); safeLog( 'warning', `Rate limit hit for ${context}. Attempt ${attempt}/${CONFIG.retry.maxAttempts}. Retrying in ${delayMs}ms` ); await delay(delayMs); return withRetry(operation, context, attempt + 1); } throw error; } } // Register all available tools with the registry function registerTools() { // Register campaign tools if enabled if (enabledCategories.campaignManagement) { toolRegistry.registerMany(campaignTools); } // Register email account tools if enabled // if (enabledCategories.emailAccountManagement) { // toolRegistry.registerMany(emailTools); // } // Register lead management tools if enabled if (enabledCategories.leadManagement) { toolRegistry.registerMany(leadTools); } // Register campaign statistics tools if enabled if (enabledCategories.campaignStatistics) { toolRegistry.registerMany(statisticsTools); } // Register smart delivery tools if enabled if (enabledCategories.smartDelivery) { toolRegistry.registerMany(smartDeliveryTools); } // Register webhook tools if enabled if (enabledCategories.webhooks) { toolRegistry.registerMany(webhookTools); } // Register client management tools if enabled if (enabledCategories.clientManagement) { toolRegistry.registerMany(clientManagementTools); } // Register smart senders tools if enabled if (enabledCategories.smartSenders) { toolRegistry.registerMany(smartSendersTools); } // Add more categories here as they are implemented // For example: // if (enabledCategories.emailAccountManagement) { // toolRegistry.registerMany(emailAccountTools); // } } // Initialize the tool registry registerTools(); // Tool handlers server.setRequestHandler(ListToolsRequestSchema, async () => { safeLog('info', 'Handling listTools request'); try { // Get license-filtered tools const tools = await toolRegistry.getEnabledToolsAsync(); // Log license status and available tools count const license = await validateLicense(); safeLog('info', `Listing ${tools.length} tools available in ${license.level} license tier`); return { tools: tools, }; } catch (error) { // Fallback to sync method if async fails safeLog('warning', `License validation failed, using default tool list: ${error}`); return { tools: toolRegistry.getEnabledTools(), }; } }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const startTime = Date.now(); try { const { name, arguments: args } = request.params; // Log incoming request with timestamp safeLog( 'info', `[${new Date().toISOString()}] Received request for tool: ${name}` ); // Safe guard for undefined arguments const toolArgs = args || {}; // Check if the tool exists in the registry if (!toolRegistry.hasToolWithName(name)) { return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true, }; } // Get the tool details to determine which handler to use const tool = toolRegistry.getByName(name); if (!tool) { return { content: [{ type: "text", text: `Tool ${name} not found in registry` }], isError: true, }; } // Check license for tool access const licenseResult = await validateLicense(); // For premium features, we can require server-side validation tokens if (tool.requiresServerValidation && tool.category === 'premium') { const token = await getFeatureToken(); if (!token) { return { content: [{ type: "text", text: `Tool ${name} requires server-side validation. Please ensure you have a valid Premium license.` }], isError: true, }; } } // Track usage for analytics and quota tracking await trackUsage(process.env.JEAN_LICENSE_KEY, name); // Check if this tool category is allowed by the license if (!licenseResult.features.allowedCategories.includes(tool.category)) { return { content: [{ type: "text", text: `Tool ${name} is not available in your current license tier (${licenseResult.level}). Please upgrade to access this feature.` }], isError: true, }; } // Check for usage quota limits if (licenseResult.usageCount >= licenseResult.features.maxRequests && licenseResult.level !== 'premium') { return { content: [{ type: "text", text: `You have reached your monthly usage quota (${licenseResult.features.maxRequests} requests). Please upgrade your plan to continue using this service.` }], isError: true, }; } // Call the appropriate handler based on tool category switch (tool.category) { case ToolCategory.CAMPAIGN_MANAGEMENT: return await handleCampaignTool(name, toolArgs, apiClient, withRetry); // case ToolCategory.EMAIL_ACCOUNT_MANAGEMENT: // return await handleEmailTool(name, toolArgs, apiClient, withRetry); case ToolCategory.LEAD_MANAGEMENT: return await handleLeadTool(name, toolArgs, apiClient, withRetry); case ToolCategory.CAMPAIGN_STATISTICS: return await handleStatisticsTool(name, toolArgs, apiClient, withRetry); case ToolCategory.SMART_DELIVERY: return await handleSmartDeliveryTool(name, toolArgs, apiClient, withRetry); case ToolCategory.WEBHOOKS: return await handleWebhookTool(name, toolArgs, apiClient, withRetry); case ToolCategory.CLIENT_MANAGEMENT: return await handleClientManagementTool(name, toolArgs, apiClient, withRetry); case ToolCategory.SMART_SENDERS: return await handleSmartSendersTool(name, toolArgs, apiClient, withRetry); default: return { content: [{ type: "text", text: `Unsupported tool category: ${tool.category}` }], isError: true, }; } } catch (error) { // Log detailed error information safeLog('error', { message: `Request failed: ${ error instanceof Error ? error.message : String(error) }`, tool: request.params.name, arguments: request.params.arguments, timestamp: new Date().toISOString(), duration: Date.now() - startTime, }); return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } finally { // Log request completion with performance metrics safeLog('info', `Request completed in ${Date.now() - startTime}ms`); } }); // Initialize handler (part of the protocol) server.setRequestHandler(InitializeRequestSchema, async (request) => { safeLog('info', `Handling initialize request from ${request.params.clientInfo?.name || 'unknown client'}`); console.error(`[DEBUG] Initialize request received: ${JSON.stringify(request.params, null, 2)}`); // Respond with our server info and capabilities const response = { serverInfo: { name: 'smartlead-mcp', version: '1.0.0', }, capabilities: serverCapabilities, instructions: 'Smartlead MCP Server for accessing Smartlead API functionality', protocolVersion: request.params.protocolVersion || '2024-11-05' }; console.error(`[DEBUG] Sending initialize response: ${JSON.stringify(response, null, 2)}`); return response; }); // Initialized notification (part of the protocol) server.setNotificationHandler(InitializedNotificationSchema, () => { safeLog('info', 'Client initialized - ready to handle requests'); console.error('[DEBUG] Received initialized notification from client'); }); // Server startup async function runServer() { try { console.error('Initializing Smartlead MCP Server...'); // Check if we're trying to use n8n integration const usingN8nMode = process.env.USE_SUPERGATEWAY === 'true' || process.argv.includes('--sse'); if (usingN8nMode) { // Check license for n8n integration permission const licenseResult = await validateLicense(); if (!licenseResult.features.n8nIntegration) { console.error('============================================================='); console.error('ERROR: Your license does not include n8n integration features'); console.error('This feature requires a Basic or Premium license subscription.'); console.error('Visit https://yourservice.com/pricing to upgrade your plan.'); console.error('============================================================='); process.exit(1); } else { console.error('n8n integration enabled - ' + licenseResult.level.charAt(0).toUpperCase() + licenseResult.level.slice(1) + ' license detected'); } } // Use standard stdio transport directly const transport = new StdioServerTransport(); console.error('Running in stdio mode, logging will be directed to stderr'); // Set up error handling process.on('uncaughtException', (error) => { console.error(`[FATAL] Uncaught exception: ${error.message}`); console.error(error.stack); // Don't exit - just log the error }); process.on('unhandledRejection', (reason, promise) => { console.error(`[FATAL] Unhandled promise rejection: ${reason}`); // Don't exit - just log the error }); // Add transport error handler transport.onerror = (error) => { console.error(`[ERROR] Transport error: ${error.message}`); }; // Connect to the transport await server.connect(transport); // Set onclose handler transport.onclose = () => { console.error('[INFO] Transport was closed. This should only happen when the process is shutting down.'); }; // Now that we're connected, we can send logging messages safeLog('info', 'Smartlead MCP Server initialized successfully'); safeLog( 'info', `Configuration: API URL: ${SMARTLEAD_API_URL}` ); // Log license information const licenseInfo = await validateLicense(); safeLog('info', `License tier: ${licenseInfo.level} - ${licenseInfo.message}`); // Log which categories are enabled const enabledCats = licenseInfo.features.allowedCategories.join(', '); safeLog('info', `Enabled categories: ${enabledCats}`); // Log the number of enabled tools const enabledToolsCount = toolRegistry.getEnabledTools().length; safeLog('info', `Enabled tools: ${enabledToolsCount}`); console.error('Smartlead MCP Server running on stdio'); // Keep the process running process.stdin.resume(); } catch (error) { console.error('Fatal error running server:', error); process.exit(1); } } runServer().catch((error: any) => { console.error('Fatal error running server:', error); process.exit(1); }); ``` -------------------------------------------------------------------------------- /src/tools/smartDelivery.ts: -------------------------------------------------------------------------------- ```typescript import { CategoryTool, ToolCategory } from '../types/common.js'; // Smart Delivery Tools export const GET_REGION_WISE_PROVIDERS_TOOL: CategoryTool = { name: 'smartlead_get_region_wise_providers', description: 'Retrieve the list of all Email Providers for spam testing classified by region/country. These provider IDs are required to create manual or automated spam tests.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { // This endpoint doesn't require any specific parameters beyond the API key // which is handled at the API client level }, required: [], }, }; export const CREATE_MANUAL_PLACEMENT_TEST_TOOL: CategoryTool = { name: 'smartlead_create_manual_placement_test', description: 'Create a manual placement test using Smartlead mailboxes to test email deliverability across various email providers.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { test_name: { type: 'string', description: 'Name of your test', }, description: { type: 'string', description: 'Description for your test to reference later', }, spam_filters: { type: 'array', items: { type: 'string' }, description: 'Array of spam filters to test across, e.g. ["spam_assassin"]', }, link_checker: { type: 'boolean', description: 'Enable to check if domains for links in email body are blacklisted', }, campaign_id: { type: 'integer', description: 'Campaign ID for which you want to select the sequence to test', }, sequence_mapping_id: { type: 'integer', description: 'The ID of the sequence or variant you would like to test', }, provider_ids: { type: 'array', items: { type: 'integer' }, description: 'Array of provider IDs to send test emails to', }, sender_accounts: { type: 'array', items: { type: 'string' }, description: 'Array of email addresses to use as senders', }, all_email_sent_without_time_gap: { type: 'boolean', description: 'Set true to send all emails simultaneously', }, min_time_btwn_emails: { type: 'integer', description: 'Time gap between each email from each mailbox (if time gap enabled)', }, min_time_unit: { type: 'string', description: 'Time unit for the time gap (minutes, hours, days)', }, is_warmup: { type: 'boolean', description: 'Set true to receive positive intent responses and move emails from spam to inbox', }, }, required: ['test_name', 'spam_filters', 'link_checker', 'campaign_id', 'sequence_mapping_id', 'provider_ids', 'sender_accounts', 'all_email_sent_without_time_gap', 'min_time_btwn_emails', 'min_time_unit', 'is_warmup'], }, }; export const CREATE_AUTOMATED_PLACEMENT_TEST_TOOL: CategoryTool = { name: 'smartlead_create_automated_placement_test', description: 'Create an automated placement test that runs on a schedule using Smart Delivery.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { test_name: { type: 'string', description: 'Name of your test', }, description: { type: 'string', description: 'Description for your test to reference later', }, spam_filters: { type: 'array', items: { type: 'string' }, description: 'Array of spam filters to test across, e.g. ["spam_assassin"]', }, link_checker: { type: 'boolean', description: 'Enable to check if domains for links in email body are blacklisted', }, campaign_id: { type: 'integer', description: 'Campaign ID for which you want to select the sequence to test', }, sequence_mapping_id: { type: 'integer', description: 'The ID of the sequence or variant you would like to test', }, provider_ids: { type: 'array', items: { type: 'integer' }, description: 'Array of provider IDs to send test emails to', }, sender_accounts: { type: 'array', items: { type: 'string' }, description: 'Array of email addresses to use as senders', }, all_email_sent_without_time_gap: { type: 'boolean', description: 'Set true to send all emails simultaneously', }, min_time_btwn_emails: { type: 'integer', description: 'Time gap between each email from each mailbox (if time gap enabled)', }, min_time_unit: { type: 'string', description: 'Time unit for the time gap (minutes, hours, days)', }, is_warmup: { type: 'boolean', description: 'Set true to receive positive intent responses and move emails from spam to inbox', }, schedule_start_time: { type: 'string', description: 'Start date and time to schedule or run the test (ISO format)', }, test_end_date: { type: 'string', description: 'End date to stop running your test (YYYY-MM-DD format)', }, every_days: { type: 'integer', description: 'Frequency of how often to run a new test', }, tz: { type: 'string', description: 'Timezone for scheduling', }, days: { type: 'array', items: { type: 'integer' }, description: 'Days of the week to run the test (1-7, where 1 is Monday)', }, starHour: { type: 'string', description: 'Test start time', }, folder_id: { type: 'integer', description: 'Folder ID to assign the test to', }, }, required: ['test_name', 'spam_filters', 'link_checker', 'campaign_id', 'sequence_mapping_id', 'provider_ids', 'sender_accounts', 'all_email_sent_without_time_gap', 'min_time_btwn_emails', 'min_time_unit', 'is_warmup', 'schedule_start_time', 'test_end_date', 'every_days', 'tz', 'days'], }, }; export const GET_SPAM_TEST_DETAILS_TOOL: CategoryTool = { name: 'smartlead_get_spam_test_details', description: 'Retrieve details of a specific spam test by ID.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to retrieve details for', }, }, required: ['spam_test_id'], }, }; export const DELETE_SMART_DELIVERY_TESTS_TOOL: CategoryTool = { name: 'smartlead_delete_smart_delivery_tests', description: 'Delete multiple Smart Delivery tests in bulk.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spamTestIds: { type: 'array', items: { type: 'integer' }, description: 'Array of spam test IDs to delete', }, }, required: ['spamTestIds'], }, }; export const STOP_AUTOMATED_TEST_TOOL: CategoryTool = { name: 'smartlead_stop_automated_test', description: 'Stop an active automated test before its end date.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the automated test to stop', }, }, required: ['spam_test_id'], }, }; export const LIST_ALL_TESTS_TOOL: CategoryTool = { name: 'smartlead_list_all_tests', description: 'List all Smart Delivery tests, either manual or automated.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { testType: { type: 'string', enum: ['manual', 'auto'], description: 'Type of tests to list (manual or auto)', }, limit: { type: 'integer', description: 'Number of tests to retrieve (default: 10)', }, offset: { type: 'integer', description: 'Offset for pagination (default: 0)', }, }, required: ['testType'], }, }; export const GET_PROVIDER_WISE_REPORT_TOOL: CategoryTool = { name: 'smartlead_get_provider_wise_report', description: 'Get detailed report of a spam test sorted by email providers.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the provider-wise report for', }, }, required: ['spam_test_id'], }, }; export const GET_GROUP_WISE_REPORT_TOOL: CategoryTool = { name: 'smartlead_get_group_wise_report', description: 'Get detailed report of a spam test sorted by location (region/country).', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the group-wise report for', }, }, required: ['spam_test_id'], }, }; export const GET_SENDER_ACCOUNT_WISE_REPORT_TOOL: CategoryTool = { name: 'smartlead_get_sender_account_wise_report', description: 'Get detailed report of a spam test sorted by sender accounts with details of each email from each mailbox.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the sender account-wise report for', }, }, required: ['spam_test_id'], }, }; export const GET_SPAM_FILTER_DETAILS_TOOL: CategoryTool = { name: 'smartlead_get_spam_filter_details', description: 'Get spam filter report per sender mailbox showing each spam score with details leading to the score.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the spam filter details for', }, }, required: ['spam_test_id'], }, }; export const GET_DKIM_DETAILS_TOOL: CategoryTool = { name: 'smartlead_get_dkim_details', description: 'Check if DKIM authentication passed or failed for each sender mailbox and receiver account.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the DKIM details for', }, }, required: ['spam_test_id'], }, }; export const GET_SPF_DETAILS_TOOL: CategoryTool = { name: 'smartlead_get_spf_details', description: 'Check if SPF authentication passed or failed for the test.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the SPF details for', }, }, required: ['spam_test_id'], }, }; export const GET_RDNS_DETAILS_TOOL: CategoryTool = { name: 'smartlead_get_rdns_details', description: 'Check if rDNS was correct for an IP sending the email.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the rDNS details for', }, }, required: ['spam_test_id'], }, }; export const GET_SENDER_ACCOUNTS_TOOL: CategoryTool = { name: 'smartlead_get_sender_accounts', description: 'Get the list of all sender accounts selected for a specific spam test.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the sender accounts for', }, }, required: ['spam_test_id'], }, }; export const GET_BLACKLIST_TOOL: CategoryTool = { name: 'smartlead_get_blacklist', description: 'Get the list of all blacklists per IP per email sent.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the blacklist information for', }, }, required: ['spam_test_id'], }, }; export const GET_EMAIL_CONTENT_TOOL: CategoryTool = { name: 'smartlead_get_email_content', description: 'Get details for the email content (raw, HTML) along with campaign and sequence details.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the email content for', }, }, required: ['spam_test_id'], }, }; export const GET_IP_ANALYTICS_TOOL: CategoryTool = { name: 'smartlead_get_ip_analytics', description: 'Get total blacklist count identified in the test.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test to get the IP analytics for', }, }, required: ['spam_test_id'], }, }; export const GET_EMAIL_HEADERS_TOOL: CategoryTool = { name: 'smartlead_get_email_headers', description: 'Get details of the email headers for a specific email.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test', }, reply_id: { type: 'integer', description: 'ID of the email received by the seed account', }, }, required: ['spam_test_id', 'reply_id'], }, }; export const GET_SCHEDULE_HISTORY_TOOL: CategoryTool = { name: 'smartlead_get_schedule_history', description: 'Get the list and summary of all tests that ran for a particular automated test.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the automated spam test to get the schedule history for', }, }, required: ['spam_test_id'], }, }; export const GET_IP_DETAILS_TOOL: CategoryTool = { name: 'smartlead_get_ip_details', description: 'Get the list of all blacklists per IP for a specific email.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { spam_test_id: { type: 'integer', description: 'ID of the spam test', }, reply_id: { type: 'integer', description: 'ID of the email received by the seed account', }, }, required: ['spam_test_id', 'reply_id'], }, }; export const GET_MAILBOX_SUMMARY_TOOL: CategoryTool = { name: 'smartlead_get_mailbox_summary', description: 'Get the list of mailboxes used for any Smart Delivery test with overall performance across all tests.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { limit: { type: 'integer', description: 'Number of tests to retrieve (default: 10)', }, offset: { type: 'integer', description: 'Offset for pagination (default: 0)', }, }, }, }; export const GET_MAILBOX_COUNT_TOOL: CategoryTool = { name: 'smartlead_get_mailbox_count', description: 'Get the count of all mailboxes used for any spam test.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: {}, }, }; export const GET_ALL_FOLDERS_TOOL: CategoryTool = { name: 'smartlead_get_all_folders', description: 'Get the list and details of all folders created in Smart Delivery along with tests inside each folder.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { limit: { type: 'integer', description: 'Number of folders to retrieve (default: 10)', }, offset: { type: 'integer', description: 'Offset for pagination (default: 0)', }, name: { type: 'string', description: 'Filter folders by name', }, }, }, }; export const CREATE_FOLDER_TOOL: CategoryTool = { name: 'smartlead_create_folder', description: 'Create a folder in Smart Delivery to organize tests.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the folder to create', }, }, required: ['name'], }, }; export const GET_FOLDER_BY_ID_TOOL: CategoryTool = { name: 'smartlead_get_folder_by_id', description: 'Get details of a specific folder by ID.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { folder_id: { type: 'integer', description: 'ID of the folder to retrieve', }, }, required: ['folder_id'], }, }; export const DELETE_FOLDER_TOOL: CategoryTool = { name: 'smartlead_delete_folder', description: 'Delete a folder from Smart Delivery.', category: ToolCategory.SMART_DELIVERY, inputSchema: { type: 'object', properties: { folder_id: { type: 'integer', description: 'ID of the folder to delete', }, }, required: ['folder_id'], }, }; // Export all tools as an array for easy registration export const smartDeliveryTools = [ GET_REGION_WISE_PROVIDERS_TOOL, CREATE_MANUAL_PLACEMENT_TEST_TOOL, CREATE_AUTOMATED_PLACEMENT_TEST_TOOL, GET_SPAM_TEST_DETAILS_TOOL, DELETE_SMART_DELIVERY_TESTS_TOOL, STOP_AUTOMATED_TEST_TOOL, LIST_ALL_TESTS_TOOL, GET_PROVIDER_WISE_REPORT_TOOL, GET_GROUP_WISE_REPORT_TOOL, GET_SENDER_ACCOUNT_WISE_REPORT_TOOL, GET_SPAM_FILTER_DETAILS_TOOL, GET_DKIM_DETAILS_TOOL, GET_SPF_DETAILS_TOOL, GET_RDNS_DETAILS_TOOL, GET_SENDER_ACCOUNTS_TOOL, GET_BLACKLIST_TOOL, GET_EMAIL_CONTENT_TOOL, GET_IP_ANALYTICS_TOOL, GET_EMAIL_HEADERS_TOOL, GET_SCHEDULE_HISTORY_TOOL, GET_IP_DETAILS_TOOL, GET_MAILBOX_SUMMARY_TOOL, GET_MAILBOX_COUNT_TOOL, GET_ALL_FOLDERS_TOOL, CREATE_FOLDER_TOOL, GET_FOLDER_BY_ID_TOOL, DELETE_FOLDER_TOOL, ]; ``` -------------------------------------------------------------------------------- /src/handlers/smartDelivery.ts: -------------------------------------------------------------------------------- ```typescript import { AxiosInstance } from 'axios'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { isGetRegionWiseProvidersParams, isCreateManualPlacementTestParams, isCreateAutomatedPlacementTestParams, isGetSpamTestDetailsParams, isDeleteSmartDeliveryTestsParams, isStopAutomatedTestParams, isListAllTestsParams, isProviderWiseReportParams, isGroupWiseReportParams, isSenderAccountWiseReportParams, isSpamFilterDetailsParams, isDkimDetailsParams, isSpfDetailsParams, isRdnsDetailsParams, isSenderAccountsParams, isBlacklistParams, isEmailContentParams, isIpAnalyticsParams, isEmailHeadersParams, isScheduleHistoryParams, isIpDetailsParams, isMailboxSummaryParams, isMailboxCountParams, isGetAllFoldersParams, isCreateFolderParams, isGetFolderByIdParams, isDeleteFolderParams } from '../types/smartDelivery.js'; // SmartDelivery API base URL const SMART_DELIVERY_API_URL = 'https://smartdelivery.smartlead.ai/api/v1'; // Handler for SmartDelivery-related tools export async function handleSmartDeliveryTool( toolName: string, args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { switch (toolName) { case 'smartlead_get_region_wise_providers': { return handleGetRegionWiseProviders(args, apiClient, withRetry); } case 'smartlead_create_manual_placement_test': { return handleCreateManualPlacementTest(args, apiClient, withRetry); } case 'smartlead_create_automated_placement_test': { return handleCreateAutomatedPlacementTest(args, apiClient, withRetry); } case 'smartlead_get_spam_test_details': { return handleGetSpamTestDetails(args, apiClient, withRetry); } case 'smartlead_delete_smart_delivery_tests': { return handleDeleteSmartDeliveryTests(args, apiClient, withRetry); } case 'smartlead_stop_automated_test': { return handleStopAutomatedTest(args, apiClient, withRetry); } case 'smartlead_list_all_tests': { return handleListAllTests(args, apiClient, withRetry); } case 'smartlead_get_provider_wise_report': { return handleGetProviderWiseReport(args, apiClient, withRetry); } case 'smartlead_get_group_wise_report': { return handleGetGroupWiseReport(args, apiClient, withRetry); } case 'smartlead_get_sender_account_wise_report': { return handleGetSenderAccountWiseReport(args, apiClient, withRetry); } case 'smartlead_get_spam_filter_details': { return handleGetSpamFilterDetails(args, apiClient, withRetry); } case 'smartlead_get_dkim_details': { return handleGetDkimDetails(args, apiClient, withRetry); } case 'smartlead_get_spf_details': { return handleGetSpfDetails(args, apiClient, withRetry); } case 'smartlead_get_rdns_details': { return handleGetRdnsDetails(args, apiClient, withRetry); } case 'smartlead_get_sender_accounts': { return handleGetSenderAccounts(args, apiClient, withRetry); } case 'smartlead_get_blacklist': { return handleGetBlacklist(args, apiClient, withRetry); } case 'smartlead_get_email_content': { return handleGetEmailContent(args, apiClient, withRetry); } case 'smartlead_get_ip_analytics': { return handleGetIpAnalytics(args, apiClient, withRetry); } case 'smartlead_get_email_headers': { return handleGetEmailHeaders(args, apiClient, withRetry); } case 'smartlead_get_schedule_history': { return handleGetScheduleHistory(args, apiClient, withRetry); } case 'smartlead_get_ip_details': { return handleGetIpDetails(args, apiClient, withRetry); } case 'smartlead_get_mailbox_summary': { return handleGetMailboxSummary(args, apiClient, withRetry); } case 'smartlead_get_mailbox_count': { return handleGetMailboxCount(args, apiClient, withRetry); } case 'smartlead_get_all_folders': { return handleGetAllFolders(args, apiClient, withRetry); } case 'smartlead_create_folder': { return handleCreateFolder(args, apiClient, withRetry); } case 'smartlead_get_folder_by_id': { return handleGetFolderById(args, apiClient, withRetry); } case 'smartlead_delete_folder': { return handleDeleteFolder(args, apiClient, withRetry); } default: throw new Error(`Unknown SmartDelivery tool: ${toolName}`); } } // Create a modified client for SmartDelivery API with the correct base URL function createSmartDeliveryClient(apiClient: AxiosInstance) { return { get: (url: string, config?: any) => apiClient.get(`${SMART_DELIVERY_API_URL}${url}`, config), post: (url: string, data?: any, config?: any) => apiClient.post(`${SMART_DELIVERY_API_URL}${url}`, data, config), put: (url: string, data?: any, config?: any) => apiClient.put(`${SMART_DELIVERY_API_URL}${url}`, data, config), delete: (url: string, config?: any) => apiClient.delete(`${SMART_DELIVERY_API_URL}${url}`, config) }; } // Individual handlers for each tool async function handleGetRegionWiseProviders( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetRegionWiseProvidersParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_region_wise_providers' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const response = await withRetry( async () => smartDeliveryClient.get('/spam-test/seed/providers'), 'get region wise providers' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCreateManualPlacementTest( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCreateManualPlacementTestParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_create_manual_placement_test' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const response = await withRetry( async () => smartDeliveryClient.post('/spam-test/manual', args), 'create manual placement test' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCreateAutomatedPlacementTest( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCreateAutomatedPlacementTestParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_create_automated_placement_test' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const response = await withRetry( async () => smartDeliveryClient.post('/spam-test/schedule', args), 'create automated placement test' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetSpamTestDetails( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetSpamTestDetailsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_spam_test_details' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/${spam_test_id}`), 'get spam test details' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleDeleteSmartDeliveryTests( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isDeleteSmartDeliveryTestsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_delete_smart_delivery_tests' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const response = await withRetry( async () => smartDeliveryClient.post('/spam-test/delete', args), 'delete smart delivery tests' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleStopAutomatedTest( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isStopAutomatedTestParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_stop_automated_test' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.put(`/spam-test/${spam_test_id}/stop`), 'stop automated test' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleListAllTests( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isListAllTestsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_list_all_tests' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { testType, limit = 10, offset = 0 } = args; const response = await withRetry( async () => smartDeliveryClient.post(`/spam-test/report?testType=${testType}`, { limit, offset }), 'list all tests' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetProviderWiseReport( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isProviderWiseReportParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_provider_wise_report' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.post(`/spam-test/report/${spam_test_id}/providerwise`), 'get provider wise report' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetGroupWiseReport( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGroupWiseReportParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_group_wise_report' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.post(`/spam-test/report/${spam_test_id}/groupwise`), 'get group wise report' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetSenderAccountWiseReport( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isSenderAccountWiseReportParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_sender_account_wise_report' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/sender-account-wise`), 'get sender account wise report' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetSpamFilterDetails( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isSpamFilterDetailsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_spam_filter_details' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/spam-filter-details`), 'get spam filter details' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetDkimDetails( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isDkimDetailsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_dkim_details' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/dkim-details`), 'get DKIM details' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetSpfDetails( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isSpfDetailsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_spf_details' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/spf-details`), 'get SPF details' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetRdnsDetails( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isRdnsDetailsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_rdns_details' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/rdns-details`), 'get rDNS details' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetSenderAccounts( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isSenderAccountsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_sender_accounts' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/sender-accounts`), 'get sender accounts' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetBlacklist( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isBlacklistParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_blacklist' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/blacklist`), 'get blacklist' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetEmailContent( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isEmailContentParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_email_content' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/email-content`), 'get email content' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetIpAnalytics( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isIpAnalyticsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_ip_analytics' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/ip-analytics`), 'get IP analytics' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetEmailHeaders( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isEmailHeadersParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_email_headers' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id, reply_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/sender-account-wise/${reply_id}/email-headers`), 'get email headers' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetScheduleHistory( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isScheduleHistoryParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_schedule_history' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/schedule-history`), 'get schedule history' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetIpDetails( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isIpDetailsParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_ip_details' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { spam_test_id, reply_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/${spam_test_id}/sender-account-wise/${reply_id}/ip-details`), 'get IP details' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetMailboxSummary( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isMailboxSummaryParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_mailbox_summary' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { limit = 10, offset = 0 } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/report/mailboxes-summary?limit=${limit}&offset=${offset}`), 'get mailbox summary' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetMailboxCount( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isMailboxCountParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_mailbox_count' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const response = await withRetry( async () => smartDeliveryClient.get('/spam-test/report/mailboxes-count'), 'get mailbox count' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetAllFolders( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetAllFoldersParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_all_folders' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { limit = 10, offset = 0, name = '' } = args; const queryParams = new URLSearchParams(); queryParams.append('limit', limit.toString()); queryParams.append('offset', offset.toString()); if (name) { queryParams.append('name', name); } const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/folder?${queryParams.toString()}`), 'get all folders' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleCreateFolder( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isCreateFolderParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_create_folder' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { name } = args; const response = await withRetry( async () => smartDeliveryClient.post('/spam-test/folder', { name }), 'create folder' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleGetFolderById( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isGetFolderByIdParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_get_folder_by_id' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { folder_id } = args; const response = await withRetry( async () => smartDeliveryClient.get(`/spam-test/folder/${folder_id}`), 'get folder by ID' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } async function handleDeleteFolder( args: unknown, apiClient: AxiosInstance, withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T> ) { if (!isDeleteFolderParams(args)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid arguments for smartlead_delete_folder' ); } try { const smartDeliveryClient = createSmartDeliveryClient(apiClient); const { folder_id } = args; const response = await withRetry( async () => smartDeliveryClient.delete(`/spam-test/folder/${folder_id}`), 'delete folder' ); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], isError: false, }; } catch (error: any) { return { content: [{ type: 'text', text: `API Error: ${error.response?.data?.message || error.message}` }], isError: true, }; } } ```