#
tokens: 45101/50000 11/54 files (page 2/3)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 2 of 3. Use http://codebase.md/jean-technologies/smartlead-mcp-server-local?lines=true&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/lead.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { AxiosInstance } from 'axios';
  2 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  3 | import {
  4 |   isListLeadsParams,
  5 |   isGetLeadParams,
  6 |   isAddLeadToCampaignParams,
  7 |   isUpdateLeadParams,
  8 |   isUpdateLeadStatusParams,
  9 |   isBulkImportLeadsParams,
 10 |   isDeleteLeadParams
 11 | } from '../types/lead.js';
 12 | 
 13 | // Handler for lead-related tools
 14 | export async function handleLeadTool(
 15 |   toolName: string,
 16 |   args: unknown,
 17 |   apiClient: AxiosInstance,
 18 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
 19 | ) {
 20 |   switch (toolName) {
 21 |     case 'smartlead_list_leads': {
 22 |       return handleListLeads(args, apiClient, withRetry);
 23 |     }
 24 |     case 'smartlead_get_lead': {
 25 |       return handleGetLead(args, apiClient, withRetry);
 26 |     }
 27 |     case 'smartlead_add_lead_to_campaign': {
 28 |       return handleAddLeadToCampaign(args, apiClient, withRetry);
 29 |     }
 30 |     case 'smartlead_update_lead': {
 31 |       return handleUpdateLead(args, apiClient, withRetry);
 32 |     }
 33 |     case 'smartlead_update_lead_status': {
 34 |       return handleUpdateLeadStatus(args, apiClient, withRetry);
 35 |     }
 36 |     case 'smartlead_bulk_import_leads': {
 37 |       return handleBulkImportLeads(args, apiClient, withRetry);
 38 |     }
 39 |     case 'smartlead_delete_lead': {
 40 |       return handleDeleteLead(args, apiClient, withRetry);
 41 |     }
 42 |     default:
 43 |       throw new Error(`Unknown lead tool: ${toolName}`);
 44 |   }
 45 | }
 46 | 
 47 | // Individual handlers for each tool
 48 | async function handleListLeads(
 49 |   args: unknown,
 50 |   apiClient: AxiosInstance,
 51 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
 52 | ) {
 53 |   if (!isListLeadsParams(args)) {
 54 |     throw new McpError(
 55 |       ErrorCode.InvalidParams,
 56 |       'Invalid arguments for smartlead_list_leads'
 57 |     );
 58 |   }
 59 | 
 60 |   try {
 61 |     // Build query parameters from args
 62 |     const params = new URLSearchParams();
 63 |     if (args.campaign_id !== undefined) {
 64 |       params.append('campaign_id', args.campaign_id.toString());
 65 |     }
 66 |     if (args.status !== undefined) {
 67 |       params.append('status', args.status);
 68 |     }
 69 |     if (args.limit !== undefined) {
 70 |       params.append('limit', args.limit.toString());
 71 |     }
 72 |     if (args.offset !== undefined) {
 73 |       params.append('offset', args.offset.toString());
 74 |     }
 75 |     if (args.search !== undefined) {
 76 |       params.append('search', args.search);
 77 |     }
 78 |     if (args.start_date !== undefined) {
 79 |       params.append('start_date', args.start_date);
 80 |     }
 81 |     if (args.end_date !== undefined) {
 82 |       params.append('end_date', args.end_date);
 83 |     }
 84 | 
 85 |     const response = await withRetry(
 86 |       async () => apiClient.get('/leads', { params }),
 87 |       'list leads'
 88 |     );
 89 | 
 90 |     return {
 91 |       content: [
 92 |         {
 93 |           type: 'text',
 94 |           text: JSON.stringify(response.data, null, 2),
 95 |         },
 96 |       ],
 97 |       isError: false,
 98 |     };
 99 |   } catch (error: any) {
100 |     return {
101 |       content: [{ 
102 |         type: 'text', 
103 |         text: `API Error: ${error.response?.data?.message || error.message}` 
104 |       }],
105 |       isError: true,
106 |     };
107 |   }
108 | }
109 | 
110 | async function handleGetLead(
111 |   args: unknown,
112 |   apiClient: AxiosInstance,
113 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
114 | ) {
115 |   if (!isGetLeadParams(args)) {
116 |     throw new McpError(
117 |       ErrorCode.InvalidParams,
118 |       'Invalid arguments for smartlead_get_lead'
119 |     );
120 |   }
121 | 
122 |   try {
123 |     const response = await withRetry(
124 |       async () => apiClient.get(`/leads/${args.lead_id}`),
125 |       'get lead'
126 |     );
127 | 
128 |     return {
129 |       content: [
130 |         {
131 |           type: 'text',
132 |           text: JSON.stringify(response.data, null, 2),
133 |         },
134 |       ],
135 |       isError: false,
136 |     };
137 |   } catch (error: any) {
138 |     return {
139 |       content: [{ 
140 |         type: 'text', 
141 |         text: `API Error: ${error.response?.data?.message || error.message}` 
142 |       }],
143 |       isError: true,
144 |     };
145 |   }
146 | }
147 | 
148 | async function handleAddLeadToCampaign(
149 |   args: unknown,
150 |   apiClient: AxiosInstance,
151 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
152 | ) {
153 |   if (!isAddLeadToCampaignParams(args)) {
154 |     throw new McpError(
155 |       ErrorCode.InvalidParams,
156 |       'Invalid arguments for smartlead_add_lead_to_campaign'
157 |     );
158 |   }
159 | 
160 |   try {
161 |     const response = await withRetry(
162 |       async () => apiClient.post(`/campaigns/${args.campaign_id}/leads`, args),
163 |       'add lead to campaign'
164 |     );
165 | 
166 |     return {
167 |       content: [
168 |         {
169 |           type: 'text',
170 |           text: JSON.stringify(response.data, null, 2),
171 |         },
172 |       ],
173 |       isError: false,
174 |     };
175 |   } catch (error: any) {
176 |     return {
177 |       content: [{ 
178 |         type: 'text', 
179 |         text: `API Error: ${error.response?.data?.message || error.message}` 
180 |       }],
181 |       isError: true,
182 |     };
183 |   }
184 | }
185 | 
186 | async function handleUpdateLead(
187 |   args: unknown,
188 |   apiClient: AxiosInstance,
189 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
190 | ) {
191 |   if (!isUpdateLeadParams(args)) {
192 |     throw new McpError(
193 |       ErrorCode.InvalidParams,
194 |       'Invalid arguments for smartlead_update_lead'
195 |     );
196 |   }
197 | 
198 |   const { lead_id, ...leadData } = args;
199 | 
200 |   try {
201 |     const response = await withRetry(
202 |       async () => apiClient.put(`/leads/${lead_id}`, leadData),
203 |       'update lead'
204 |     );
205 | 
206 |     return {
207 |       content: [
208 |         {
209 |           type: 'text',
210 |           text: JSON.stringify(response.data, null, 2),
211 |         },
212 |       ],
213 |       isError: false,
214 |     };
215 |   } catch (error: any) {
216 |     return {
217 |       content: [{ 
218 |         type: 'text', 
219 |         text: `API Error: ${error.response?.data?.message || error.message}` 
220 |       }],
221 |       isError: true,
222 |     };
223 |   }
224 | }
225 | 
226 | async function handleUpdateLeadStatus(
227 |   args: unknown,
228 |   apiClient: AxiosInstance,
229 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
230 | ) {
231 |   if (!isUpdateLeadStatusParams(args)) {
232 |     throw new McpError(
233 |       ErrorCode.InvalidParams,
234 |       'Invalid arguments for smartlead_update_lead_status'
235 |     );
236 |   }
237 | 
238 |   const { lead_id, status } = args;
239 | 
240 |   try {
241 |     const response = await withRetry(
242 |       async () => apiClient.put(`/leads/${lead_id}/status`, { status }),
243 |       'update lead status'
244 |     );
245 | 
246 |     return {
247 |       content: [
248 |         {
249 |           type: 'text',
250 |           text: JSON.stringify(response.data, null, 2),
251 |         },
252 |       ],
253 |       isError: false,
254 |     };
255 |   } catch (error: any) {
256 |     return {
257 |       content: [{ 
258 |         type: 'text', 
259 |         text: `API Error: ${error.response?.data?.message || error.message}` 
260 |       }],
261 |       isError: true,
262 |     };
263 |   }
264 | }
265 | 
266 | async function handleBulkImportLeads(
267 |   args: unknown,
268 |   apiClient: AxiosInstance,
269 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
270 | ) {
271 |   if (!isBulkImportLeadsParams(args)) {
272 |     throw new McpError(
273 |       ErrorCode.InvalidParams,
274 |       'Invalid arguments for smartlead_bulk_import_leads'
275 |     );
276 |   }
277 | 
278 |   try {
279 |     const response = await withRetry(
280 |       async () => apiClient.post(`/campaigns/${args.campaign_id}/leads/bulk`, {
281 |         leads: args.leads
282 |       }),
283 |       'bulk import leads'
284 |     );
285 | 
286 |     return {
287 |       content: [
288 |         {
289 |           type: 'text',
290 |           text: JSON.stringify(response.data, null, 2),
291 |         },
292 |       ],
293 |       isError: false,
294 |     };
295 |   } catch (error: any) {
296 |     return {
297 |       content: [{ 
298 |         type: 'text', 
299 |         text: `API Error: ${error.response?.data?.message || error.message}` 
300 |       }],
301 |       isError: true,
302 |     };
303 |   }
304 | }
305 | 
306 | async function handleDeleteLead(
307 |   args: unknown,
308 |   apiClient: AxiosInstance,
309 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
310 | ) {
311 |   if (!isDeleteLeadParams(args)) {
312 |     throw new McpError(
313 |       ErrorCode.InvalidParams,
314 |       'Invalid arguments for smartlead_delete_lead'
315 |     );
316 |   }
317 | 
318 |   try {
319 |     const response = await withRetry(
320 |       async () => apiClient.delete(`/leads/${args.lead_id}`),
321 |       'delete lead'
322 |     );
323 | 
324 |     return {
325 |       content: [
326 |         {
327 |           type: 'text',
328 |           text: JSON.stringify(response.data, null, 2),
329 |         },
330 |       ],
331 |       isError: false,
332 |     };
333 |   } catch (error: any) {
334 |     return {
335 |       content: [{ 
336 |         type: 'text', 
337 |         text: `API Error: ${error.response?.data?.message || error.message}` 
338 |       }],
339 |       isError: true,
340 |     };
341 |   }
342 | } 
```

--------------------------------------------------------------------------------
/src/licensing/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import axios from 'axios';
  2 | import * as dotenv from 'dotenv';
  3 | 
  4 | // Ensure .env file is loaded
  5 | dotenv.config();
  6 | 
  7 | // License levels
  8 | export enum LicenseLevel {
  9 |   FREE = 'free',
 10 |   BASIC = 'basic',
 11 |   PREMIUM = 'premium'
 12 | }
 13 | 
 14 | // Feature definitions
 15 | export interface LicenseFeatures {
 16 |   allowedCategories: string[];
 17 |   maxRequests: number;
 18 |   n8nIntegration: boolean;
 19 |   smartleadApiAccess: boolean;
 20 | }
 21 | 
 22 | // Licensing configuration - This is just a fallback when offline
 23 | const LICENSE_CONFIG: Record<LicenseLevel, LicenseFeatures> = {
 24 |   [LicenseLevel.FREE]: {
 25 |     allowedCategories: ['campaignManagement', 'leadManagement'],
 26 |     maxRequests: 100,
 27 |     n8nIntegration: false,
 28 |     smartleadApiAccess: true
 29 |   },
 30 |   [LicenseLevel.BASIC]: {
 31 |     allowedCategories: ['campaignManagement', 'leadManagement', 'campaignStatistics', 'smartDelivery', 'webhooks', 'clientManagement', 'smartSenders'],
 32 |     maxRequests: 1000,
 33 |     n8nIntegration: true,
 34 |     smartleadApiAccess: true
 35 |   },
 36 |   [LicenseLevel.PREMIUM]: {
 37 |     allowedCategories: ['campaignManagement', 'leadManagement', 'campaignStatistics', 'smartDelivery', 'webhooks', 'clientManagement', 'smartSenders'],
 38 |     maxRequests: 10000,
 39 |     n8nIntegration: true,
 40 |     smartleadApiAccess: true
 41 |   }
 42 | };
 43 | 
 44 | // License validation result
 45 | export interface LicenseValidationResult {
 46 |   valid: boolean;
 47 |   level: LicenseLevel;
 48 |   features: LicenseFeatures;
 49 |   message: string;
 50 |   usageCount: number;
 51 | }
 52 | 
 53 | // Generation info for server-side validation
 54 | export interface FeatureRequestToken {
 55 |   token: string;
 56 |   expires: number;
 57 | }
 58 | 
 59 | // Validation status cache
 60 | let cachedValidation: LicenseValidationResult | null = null;
 61 | let cachedFeatureToken: FeatureRequestToken | null = null;
 62 | const requestCounts: Record<string, number> = {};
 63 | 
 64 | // API configuration
 65 | // Use the environment variable for the license server URL
 66 | const LICENSE_SERVER_URL = process.env.LICENSE_SERVER_URL; 
 67 | const LICENSING_CACHE_TTL = 3600000; // 1 hour
 68 | let lastValidationTime = 0;
 69 | 
 70 | /**
 71 |  * Validate a license key
 72 |  * @param licenseKey Optional: license key to validate (primarily uses env var)
 73 |  * @returns License validation result
 74 |  */
 75 | export async function validateLicense(): Promise<LicenseValidationResult> {
 76 |   // Always return PREMIUM license regardless of key
 77 |   console.log('✅ License override: All features enabled in PREMIUM mode');
 78 |   return createValidationResult(
 79 |     LicenseLevel.PREMIUM,
 80 |     true,
 81 |     'License override enabled: All features available',
 82 |     0
 83 |   );
 84 |   
 85 |   // The following code will never be reached due to the early return above
 86 |   
 87 |   // Original code remains for reference but won't be executed:
 88 |   // Use the license key from the specific env var first
 89 |   // const apiKey = process.env.JEAN_LICENSE_KEY;
 90 |   // ... existing code ...
 91 | }
 92 | 
 93 | /**
 94 |  * Get a token for server-side feature validation
 95 |  * This adds security since critical operations will need server validation
 96 |  */
 97 | export async function getFeatureToken(): Promise<FeatureRequestToken | null> {
 98 |   // Ensure we have a valid license first
 99 |   const licenseResult = await validateLicense();
100 |   
101 |   // Check if we have a valid cached token
102 |   if (cachedFeatureToken && Date.now() < cachedFeatureToken.expires) {
103 |     return cachedFeatureToken;
104 |   }
105 |   
106 |   // If no license server URL or not in BASIC or PREMIUM tier, don't try to get a token
107 |   if (!LICENSE_SERVER_URL || 
108 |       (licenseResult.level !== LicenseLevel.BASIC && 
109 |        licenseResult.level !== LicenseLevel.PREMIUM)) {
110 |     return null;
111 |   }
112 |   
113 |   // Try to get a fresh token from the server
114 |   try {
115 |     const apiKey = process.env.JEAN_LICENSE_KEY;
116 |     if (!apiKey) return null;
117 |     
118 |     console.log(`Requesting feature token from ${LICENSE_SERVER_URL}/token`);
119 |     const response = await axios.post(`${LICENSE_SERVER_URL}/token`, {}, {
120 |       headers: {
121 |         'Authorization': `Bearer ${apiKey}`,
122 |         'Content-Type': 'application/json'
123 |       },
124 |       timeout: 5000
125 |     });
126 |     
127 |     if (response.data && response.data.token) {
128 |       cachedFeatureToken = {
129 |         token: response.data.token,
130 |         expires: response.data.expires || (Date.now() + 3600000) // Default to 1 hour if not specified
131 |       };
132 |       
133 |       console.log(`✅ Feature token acquired, valid until ${new Date(cachedFeatureToken.expires).toISOString()}`);
134 |       return cachedFeatureToken;
135 |     }
136 |     
137 |     return null;
138 |   } catch (error: any) {
139 |     console.error(`Failed to get feature token: ${error.message}`);
140 |     // Silent failure - just return null
141 |     return null;
142 |   }
143 | }
144 | 
145 | /**
146 |  * Track usage for a license
147 |  * @param licenseKey The license key
148 |  * @param toolName The tool being used
149 |  */
150 | export async function trackUsage(licenseKey?: string, toolName = 'unknown'): Promise<void> {
151 |   const apiKey = licenseKey || process.env.JEAN_LICENSE_KEY;
152 |   if (!apiKey || !LICENSE_SERVER_URL) return;
153 |   
154 |   // Get a unique identifier for tracking
155 |   const machineId = getMachineId();
156 |   
157 |   // Increment local counter
158 |   const key = apiKey.substring(0, 8); // Use part of the key as an identifier
159 |   requestCounts[key] = (requestCounts[key] || 0) + 1;
160 |   
161 |   // Only report every 10 requests to reduce API load
162 |   if (requestCounts[key] % 10 !== 0) return;
163 |   
164 |   try {
165 |     // Report usage asynchronously (don't await)
166 |     axios.post(`${LICENSE_SERVER_URL}/track`, {
167 |       key: apiKey,
168 |       tool: toolName,
169 |       count: 10, // Batch reporting
170 |       machineId
171 |     }, {
172 |       headers: {
173 |         'Authorization': `Bearer ${apiKey}`,
174 |         'Content-Type': 'application/json'
175 |       }
176 |     }).catch((error) => {
177 |       // Silently fail with more context - we don't want to impact performance
178 |       console.debug(`Usage tracking failed: ${error.message}`);
179 |     });
180 |   } catch (error) {
181 |     // Ignore errors - usage tracking is non-critical
182 |   }
183 | }
184 | 
185 | /**
186 |  * Create a validation result object
187 |  */
188 | function createValidationResult(
189 |   level: LicenseLevel, 
190 |   valid: boolean, 
191 |   message: string,
192 |   usageCount = 0
193 | ): LicenseValidationResult {
194 |   return {
195 |     valid,
196 |     level,
197 |     features: LICENSE_CONFIG[level],
198 |     message,
199 |     usageCount
200 |   };
201 | }
202 | 
203 | /**
204 |  * Check if a feature is available in the current license
205 |  * @param feature The feature to check
206 |  * @returns Whether the feature is available
207 |  */
208 | export async function isFeatureEnabled(feature: keyof LicenseFeatures): Promise<boolean> {
209 |   // Always return true for all features
210 |   return true;
211 | }
212 | 
213 | /**
214 |  * Check if a category is available in the current license
215 |  * @param category The category to check
216 |  * @returns Whether the category is available
217 |  */
218 | export async function isCategoryEnabled(category: string): Promise<boolean> {
219 |   // Always return true for all categories
220 |   return true;
221 | }
222 | 
223 | /**
224 |  * Get the current license information
225 |  * @returns Current license information
226 |  */
227 | export async function getLicenseInfo(): Promise<LicenseValidationResult> {
228 |   return validateLicense();
229 | }
230 | 
231 | /**
232 |  * Get a unique identifier for the machine
233 |  * This helps prevent sharing of license keys
234 |  */
235 | function getMachineId(): string {
236 |   try {
237 |     // Use environment variables to create a semi-unique ID
238 |     // This is not perfect but provides basic machine identification
239 |     const os = process.platform;
240 |     const cpus = process.env.NUMBER_OF_PROCESSORS || '';
241 |     const username = process.env.USER || process.env.USERNAME || '';
242 |     const hostname = process.env.HOSTNAME || '';
243 |     
244 |     // Create a simple hash of these values
245 |     const combinedString = `${os}-${cpus}-${username}-${hostname}`;
246 |     let hash = 0;
247 |     for (let i = 0; i < combinedString.length; i++) {
248 |       hash = ((hash << 5) - hash) + combinedString.charCodeAt(i);
249 |       hash |= 0; // Convert to 32bit integer
250 |     }
251 |     
252 |     return Math.abs(hash).toString(16);
253 |   } catch (e) {
254 |     // Fallback to a random ID if we can't get system info
255 |     return Math.random().toString(36).substring(2, 15);
256 |   }
257 | }
258 | 
259 | /**
260 |  * Print a summary of the current license status to the console
261 |  * This is useful for displaying on server startup
262 |  */
263 | export async function printLicenseStatus(): Promise<void> {
264 |   try {
265 |     const validation = await validateLicense();
266 |     
267 |     console.log('\n========== LICENSE STATUS ==========');
268 |     console.log(`License Tier: ${validation.level.toUpperCase()}`);
269 |     console.log(`Valid: ${validation.valid ? 'Yes' : 'No'}`);
270 |     console.log(`Message: ${validation.message}`);
271 |     console.log('Available Features:');
272 |     console.log(`- Categories: ${validation.features.allowedCategories.join(', ')}`);
273 |     console.log(`- Max Requests: ${validation.features.maxRequests}`);
274 |     console.log(`- n8n Integration: ${validation.features.n8nIntegration ? 'Enabled' : 'Disabled'}`);
275 |     console.log(`- Smartlead API Access: ${validation.features.smartleadApiAccess ? 'Enabled' : 'Disabled'}`);
276 |     
277 |     if (!validation.valid) {
278 |       console.log('\n⚠️  ATTENTION ⚠️');
279 |       console.log('Your license is not valid or is running in limited mode.');
280 |       if (!process.env.JEAN_LICENSE_KEY) {
281 |         console.log('You have not set a license key (JEAN_LICENSE_KEY) in your .env file.');
282 |       }
283 |       if (!process.env.LICENSE_SERVER_URL) {
284 |         console.log('The LICENSE_SERVER_URL is not configured in your .env file.');
285 |       }
286 |       console.log('To enable all features, please check your configuration.');
287 |     }
288 |     
289 |     if (process.env.LICENSE_LEVEL_OVERRIDE) {
290 |       console.log('\n⚠️  DEVELOPMENT MODE ⚠️');
291 |       console.log(`License level is overridden to: ${process.env.LICENSE_LEVEL_OVERRIDE.toUpperCase()}`);
292 |       console.log('This override should not be used in production environments.');
293 |     }
294 |     
295 |     console.log('=====================================\n');
296 |   } catch (error) {
297 |     console.error('Failed to print license status:', error);
298 |   }
299 | } 
```

--------------------------------------------------------------------------------
/server/license-server.js:
--------------------------------------------------------------------------------

```javascript
  1 | import express from 'express';
  2 | import crypto from 'crypto';
  3 | import { createClient } from '@supabase/supabase-js';
  4 | import Stripe from 'stripe';
  5 | import dotenv from 'dotenv';
  6 | 
  7 | dotenv.config();
  8 | 
  9 | // Initialize Express app
 10 | const app = express();
 11 | app.use(express.json());
 12 | 
 13 | // Initialize Stripe
 14 | const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
 15 | 
 16 | // Initialize Supabase client
 17 | const supabase = createClient(
 18 |   process.env.SUPABASE_URL,
 19 |   process.env.SUPABASE_SERVICE_KEY
 20 | );
 21 | 
 22 | // License levels
 23 | const LicenseLevel = {
 24 |   FREE: 'free',
 25 |   BASIC: 'basic',
 26 |   PREMIUM: 'premium'
 27 | };
 28 | 
 29 | // Feature mappings
 30 | const LicenseFeatures = {
 31 |   [LicenseLevel.FREE]: {
 32 |     allowedCategories: ['campaignManagement', 'leadManagement'],
 33 |     maxRequests: 100,
 34 |     n8nIntegration: false,
 35 |     smartleadApiAccess: true
 36 |   },
 37 |   [LicenseLevel.BASIC]: {
 38 |     allowedCategories: ['campaignManagement', 'leadManagement', 'campaignStatistics', 'smartDelivery'],
 39 |     maxRequests: 1000,
 40 |     n8nIntegration: false,
 41 |     smartleadApiAccess: true
 42 |   },
 43 |   [LicenseLevel.PREMIUM]: {
 44 |     allowedCategories: ['campaignManagement', 'leadManagement', 'campaignStatistics', 'smartDelivery', 'webhooks', 'clientManagement', 'smartSenders'],
 45 |     maxRequests: 10000,
 46 |     n8nIntegration: true,
 47 |     smartleadApiAccess: true
 48 |   }
 49 | };
 50 | 
 51 | // Generate a secure license key
 52 | function generateLicenseKey() {
 53 |   return crypto.randomBytes(24).toString('hex');
 54 | }
 55 | 
 56 | // Generate a temporary feature token
 57 | function generateFeatureToken() {
 58 |   return {
 59 |     token: crypto.randomBytes(32).toString('hex'),
 60 |     expires: Date.now() + 3600000 // 1 hour
 61 |   };
 62 | }
 63 | 
 64 | // API Routes
 65 | 
 66 | // Validate a license key
 67 | app.post('/validate', async (req, res) => {
 68 |   try {
 69 |     const { Authorization } = req.headers;
 70 |     const apiKey = Authorization ? Authorization.replace('Bearer ', '') : null;
 71 |     const machineId = req.headers['x-client-id'];
 72 |     
 73 |     if (!apiKey) {
 74 |       return res.json({
 75 |         valid: false,
 76 |         level: LicenseLevel.FREE,
 77 |         message: 'No API key provided'
 78 |       });
 79 |     }
 80 |     
 81 |     // Query license in Supabase
 82 |     const { data, error } = await supabase
 83 |       .from('licenses')
 84 |       .select('*')
 85 |       .eq('key', apiKey)
 86 |       .single();
 87 |     
 88 |     if (error || !data) {
 89 |       return res.json({
 90 |         valid: false,
 91 |         level: LicenseLevel.FREE,
 92 |         message: 'Invalid license key'
 93 |       });
 94 |     }
 95 |     
 96 |     // Check if license is expired
 97 |     if (data.expires && new Date(data.expires) < new Date()) {
 98 |       return res.json({
 99 |         valid: false,
100 |         level: LicenseLevel.FREE,
101 |         message: 'License expired'
102 |       });
103 |     }
104 |     
105 |     // Check if this machine is authorized
106 |     if (data.machine_ids && data.machine_ids.length > 0) {
107 |       if (!machineId || !data.machine_ids.includes(machineId)) {
108 |         // If this is a new machine, check if we've reached the limit
109 |         if (data.machine_ids.length >= data.max_machines) {
110 |           return res.json({
111 |             valid: false,
112 |             level: LicenseLevel.FREE,
113 |             message: 'Maximum number of machines reached'
114 |           });
115 |         }
116 |         
117 |         // Otherwise add this machine to the authorized list
118 |         const updatedMachines = [...data.machine_ids, machineId];
119 |         await supabase
120 |           .from('licenses')
121 |           .update({ machine_ids: updatedMachines })
122 |           .eq('id', data.id);
123 |       }
124 |     } else {
125 |       // Initialize the machine_ids array with this machine
126 |       await supabase
127 |         .from('licenses')
128 |         .update({ machine_ids: [machineId] })
129 |         .eq('id', data.id);
130 |     }
131 |     
132 |     // Generate a feature token for server-side validation
133 |     const featureToken = generateFeatureToken();
134 |     
135 |     // Track usage
136 |     await supabase
137 |       .from('license_usage')
138 |       .insert({
139 |         license_id: data.id,
140 |         machine_id: machineId,
141 |         timestamp: new Date().toISOString()
142 |       });
143 |     
144 |     // Increment usage count
145 |     const { count } = await supabase
146 |       .from('license_usage')
147 |       .select('count', { count: 'exact' })
148 |       .eq('license_id', data.id);
149 |     
150 |     return res.json({
151 |       valid: true,
152 |       level: data.level,
153 |       usage: count || 0,
154 |       featureToken: featureToken.token,
155 |       tokenExpires: 3600, // 1 hour in seconds
156 |       message: 'License validated successfully'
157 |     });
158 |   } catch (error) {
159 |     console.error('License validation error:', error);
160 |     return res.status(500).json({
161 |       valid: false,
162 |       level: LicenseLevel.FREE,
163 |       message: 'Server error during validation'
164 |     });
165 |   }
166 | });
167 | 
168 | // Validate an installation
169 | app.post('/validate-install', async (req, res) => {
170 |   try {
171 |     const { apiKey, machineId, version, installPath } = req.body;
172 |     
173 |     if (!apiKey) {
174 |       return res.json({
175 |         success: false,
176 |         message: 'No API key provided'
177 |       });
178 |     }
179 |     
180 |     // Query license in Supabase
181 |     const { data, error } = await supabase
182 |       .from('licenses')
183 |       .select('*')
184 |       .eq('key', apiKey)
185 |       .single();
186 |     
187 |     if (error || !data) {
188 |       return res.json({
189 |         success: false,
190 |         message: 'Invalid license key'
191 |       });
192 |     }
193 |     
194 |     // Check if license is expired
195 |     if (data.expires && new Date(data.expires) < new Date()) {
196 |       return res.json({
197 |         success: false,
198 |         message: 'License expired'
199 |       });
200 |     }
201 |     
202 |     // Record installation info
203 |     await supabase
204 |       .from('installations')
205 |       .insert({
206 |         license_id: data.id,
207 |         machine_id: machineId,
208 |         version,
209 |         install_path: installPath,
210 |         installed_at: new Date().toISOString()
211 |       });
212 |     
213 |     // Return license details
214 |     return res.json({
215 |       success: true,
216 |       level: data.level,
217 |       features: LicenseFeatures[data.level].allowedCategories,
218 |       expires: data.expires,
219 |       message: 'Installation validated successfully'
220 |     });
221 |   } catch (error) {
222 |     console.error('Installation validation error:', error);
223 |     return res.status(500).json({
224 |       success: false,
225 |       message: 'Server error during installation validation'
226 |     });
227 |   }
228 | });
229 | 
230 | // Generate a feature token (used for server-side validation)
231 | app.post('/token', async (req, res) => {
232 |   try {
233 |     const { Authorization } = req.headers;
234 |     const apiKey = Authorization ? Authorization.replace('Bearer ', '') : null;
235 |     
236 |     if (!apiKey) {
237 |       return res.status(401).json({ error: 'Unauthorized' });
238 |     }
239 |     
240 |     // Verify the license is valid
241 |     const { data, error } = await supabase
242 |       .from('licenses')
243 |       .select('level')
244 |       .eq('key', apiKey)
245 |       .single();
246 |     
247 |     if (error || !data) {
248 |       return res.status(401).json({ error: 'Invalid license' });
249 |     }
250 |     
251 |     // Generate a new token
252 |     const featureToken = generateFeatureToken();
253 |     
254 |     // Store the token in Supabase with expiration
255 |     await supabase
256 |       .from('feature_tokens')
257 |       .insert({
258 |         token: featureToken.token,
259 |         license_key: apiKey,
260 |         expires_at: new Date(featureToken.expires).toISOString()
261 |       });
262 |     
263 |     return res.json({
264 |       token: featureToken.token,
265 |       expires: featureToken.expires
266 |     });
267 |   } catch (error) {
268 |     console.error('Token generation error:', error);
269 |     return res.status(500).json({ error: 'Server error' });
270 |   }
271 | });
272 | 
273 | // Handle Stripe webhook
274 | app.post('/webhook', async (req, res) => {
275 |   const sig = req.headers['stripe-signature'];
276 |   
277 |   try {
278 |     const event = stripe.webhooks.constructEvent(
279 |       req.body,
280 |       sig,
281 |       process.env.STRIPE_WEBHOOK_SECRET
282 |     );
283 |     
284 |     switch (event.type) {
285 |       case 'customer.subscription.created':
286 |       case 'customer.subscription.updated': {
287 |         const subscription = event.data.object;
288 |         const customerId = subscription.customer;
289 |         
290 |         // Get the product details to determine license level
291 |         const product = await stripe.products.retrieve(
292 |           subscription.items.data[0].plan.product
293 |         );
294 |         
295 |         // Get license level from product metadata
296 |         const level = product.metadata.license_level || LicenseLevel.BASIC;
297 |         
298 |         // Generate a license key
299 |         const licenseKey = generateLicenseKey();
300 |         
301 |         // Get the customer email
302 |         const customer = await stripe.customers.retrieve(customerId);
303 |         
304 |         // Store the license in Supabase
305 |         await supabase
306 |           .from('licenses')
307 |           .upsert({
308 |             key: licenseKey,
309 |             customer_id: customerId,
310 |             customer_email: customer.email,
311 |             level,
312 |             created_at: new Date().toISOString(),
313 |             expires: subscription.current_period_end 
314 |               ? new Date(subscription.current_period_end * 1000).toISOString() 
315 |               : null,
316 |             max_machines: level === LicenseLevel.PREMIUM ? 5 : 2, // Limit based on tier
317 |             machine_ids: [],
318 |             active: true
319 |           }, { onConflict: 'customer_id' });
320 |         
321 |         // Update customer metadata with license key
322 |         await stripe.customers.update(customerId, {
323 |           metadata: { 
324 |             license_key: licenseKey,
325 |             license_level: level
326 |           }
327 |         });
328 |         
329 |         break;
330 |       }
331 |       
332 |       case 'customer.subscription.deleted': {
333 |         const subscription = event.data.object;
334 |         const customerId = subscription.customer;
335 |         
336 |         // Find the license by customer ID
337 |         const { data } = await supabase
338 |           .from('licenses')
339 |           .select('id')
340 |           .eq('customer_id', customerId)
341 |           .single();
342 |         
343 |         if (data) {
344 |           // Downgrade the license to free tier
345 |           await supabase
346 |             .from('licenses')
347 |             .update({ 
348 |               level: LicenseLevel.FREE,
349 |               expires: new Date().toISOString() // Expire now
350 |             })
351 |             .eq('id', data.id);
352 |         }
353 |         
354 |         break;
355 |       }
356 |     }
357 |     
358 |     res.json({ received: true });
359 |   } catch (error) {
360 |     console.error('Webhook error:', error);
361 |     res.status(400).json({ error: error.message });
362 |   }
363 | });
364 | 
365 | // Start server
366 | const PORT = process.env.PORT || 3000;
367 | app.listen(PORT, () => {
368 |   console.log(`License server running on port ${PORT}`);
369 | }); 
```

--------------------------------------------------------------------------------
/src/tools/email.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { CategoryTool, ToolCategory } from '../types/common.js';
  2 | 
  3 | // Email Account Management Tools
  4 | export const LIST_EMAIL_ACCOUNTS_CAMPAIGN_TOOL: CategoryTool = {
  5 |   name: 'smartlead_list_email_accounts_campaign',
  6 |   description: 'List all email accounts associated with a specific campaign.',
  7 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
  8 |   inputSchema: {
  9 |     type: 'object',
 10 |     properties: {
 11 |       campaign_id: {
 12 |         type: 'number',
 13 |         description: 'ID of the campaign to get email accounts for',
 14 |       },
 15 |       status: {
 16 |         type: 'string',
 17 |         enum: ['active', 'disconnected', 'pending'],
 18 |         description: 'Filter email accounts by status',
 19 |       },
 20 |       limit: {
 21 |         type: 'number',
 22 |         description: 'Maximum number of email accounts to return',
 23 |       },
 24 |       offset: {
 25 |         type: 'number',
 26 |         description: 'Offset for pagination',
 27 |       },
 28 |     },
 29 |     required: ['campaign_id'],
 30 |   },
 31 | };
 32 | 
 33 | export const ADD_EMAIL_TO_CAMPAIGN_TOOL: CategoryTool = {
 34 |   name: 'smartlead_add_email_to_campaign',
 35 |   description: 'Add an email account to a campaign.',
 36 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
 37 |   inputSchema: {
 38 |     type: 'object',
 39 |     properties: {
 40 |       campaign_id: {
 41 |         type: 'number',
 42 |         description: 'ID of the campaign to add the email account to',
 43 |       },
 44 |       email_account_id: {
 45 |         type: 'number',
 46 |         description: 'ID of the email account to add to the campaign',
 47 |       },
 48 |     },
 49 |     required: ['campaign_id', 'email_account_id'],
 50 |   },
 51 | };
 52 | 
 53 | export const REMOVE_EMAIL_FROM_CAMPAIGN_TOOL: CategoryTool = {
 54 |   name: 'smartlead_remove_email_from_campaign',
 55 |   description: 'Remove an email account from a campaign.',
 56 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
 57 |   inputSchema: {
 58 |     type: 'object',
 59 |     properties: {
 60 |       campaign_id: {
 61 |         type: 'number',
 62 |         description: 'ID of the campaign to remove the email account from',
 63 |       },
 64 |       email_account_id: {
 65 |         type: 'number',
 66 |         description: 'ID of the email account to remove from the campaign',
 67 |       },
 68 |     },
 69 |     required: ['campaign_id', 'email_account_id'],
 70 |   },
 71 | };
 72 | 
 73 | export const FETCH_EMAIL_ACCOUNTS_TOOL: CategoryTool = {
 74 |   name: 'smartlead_fetch_email_accounts',
 75 |   description: 'Fetch all email accounts associated with the user.',
 76 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
 77 |   inputSchema: {
 78 |     type: 'object',
 79 |     properties: {
 80 |       status: {
 81 |         type: 'string',
 82 |         enum: ['active', 'disconnected', 'pending'],
 83 |         description: 'Filter email accounts by status',
 84 |       },
 85 |       limit: {
 86 |         type: 'number',
 87 |         description: 'Maximum number of email accounts to return',
 88 |       },
 89 |       offset: {
 90 |         type: 'number',
 91 |         description: 'Offset for pagination',
 92 |       },
 93 |     },
 94 |   },
 95 | };
 96 | 
 97 | export const CREATE_EMAIL_ACCOUNT_TOOL: CategoryTool = {
 98 |   name: 'smartlead_create_email_account',
 99 |   description: 'Create a new email account.',
100 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
101 |   inputSchema: {
102 |     type: 'object',
103 |     properties: {
104 |       email: {
105 |         type: 'string',
106 |         description: 'Email address',
107 |       },
108 |       provider: {
109 |         type: 'string',
110 |         description: 'Email provider (e.g., "gmail", "outlook", "custom")',
111 |       },
112 |       name: {
113 |         type: 'string',
114 |         description: 'Display name for the email account',
115 |       },
116 |       smtp_host: {
117 |         type: 'string',
118 |         description: 'SMTP server hostname (for custom providers)',
119 |       },
120 |       smtp_port: {
121 |         type: 'number',
122 |         description: 'SMTP server port (for custom providers)',
123 |       },
124 |       smtp_username: {
125 |         type: 'string',
126 |         description: 'SMTP username (for custom providers)',
127 |       },
128 |       smtp_password: {
129 |         type: 'string',
130 |         description: 'SMTP password (for custom providers)',
131 |       },
132 |       imap_host: {
133 |         type: 'string',
134 |         description: 'IMAP server hostname (for custom providers)',
135 |       },
136 |       imap_port: {
137 |         type: 'number',
138 |         description: 'IMAP server port (for custom providers)',
139 |       },
140 |       imap_username: {
141 |         type: 'string',
142 |         description: 'IMAP username (for custom providers)',
143 |       },
144 |       imap_password: {
145 |         type: 'string',
146 |         description: 'IMAP password (for custom providers)',
147 |       },
148 |       oauth_token: {
149 |         type: 'string',
150 |         description: 'OAuth token (for OAuth-based providers)',
151 |       },
152 |       tags: {
153 |         type: 'array',
154 |         items: {
155 |           type: 'string',
156 |         },
157 |         description: 'Tags to assign to the email account',
158 |       },
159 |     },
160 |     required: ['email', 'provider'],
161 |   },
162 | };
163 | 
164 | export const UPDATE_EMAIL_ACCOUNT_TOOL: CategoryTool = {
165 |   name: 'smartlead_update_email_account',
166 |   description: 'Update an existing email account.',
167 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
168 |   inputSchema: {
169 |     type: 'object',
170 |     properties: {
171 |       email_account_id: {
172 |         type: 'number',
173 |         description: 'ID of the email account to update',
174 |       },
175 |       name: {
176 |         type: 'string',
177 |         description: 'Display name for the email account',
178 |       },
179 |       smtp_host: {
180 |         type: 'string',
181 |         description: 'SMTP server hostname',
182 |       },
183 |       smtp_port: {
184 |         type: 'number',
185 |         description: 'SMTP server port',
186 |       },
187 |       smtp_username: {
188 |         type: 'string',
189 |         description: 'SMTP username',
190 |       },
191 |       smtp_password: {
192 |         type: 'string',
193 |         description: 'SMTP password',
194 |       },
195 |       imap_host: {
196 |         type: 'string',
197 |         description: 'IMAP server hostname',
198 |       },
199 |       imap_port: {
200 |         type: 'number',
201 |         description: 'IMAP server port',
202 |       },
203 |       imap_username: {
204 |         type: 'string',
205 |         description: 'IMAP username',
206 |       },
207 |       imap_password: {
208 |         type: 'string',
209 |         description: 'IMAP password',
210 |       },
211 |       oauth_token: {
212 |         type: 'string',
213 |         description: 'OAuth token',
214 |       },
215 |       status: {
216 |         type: 'string',
217 |         enum: ['active', 'paused', 'disconnected'],
218 |         description: 'Status of the email account',
219 |       },
220 |     },
221 |     required: ['email_account_id'],
222 |   },
223 | };
224 | 
225 | export const FETCH_EMAIL_ACCOUNT_BY_ID_TOOL: CategoryTool = {
226 |   name: 'smartlead_fetch_email_account_by_id',
227 |   description: 'Fetch a specific email account by ID.',
228 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
229 |   inputSchema: {
230 |     type: 'object',
231 |     properties: {
232 |       email_account_id: {
233 |         type: 'number',
234 |         description: 'ID of the email account to fetch',
235 |       },
236 |     },
237 |     required: ['email_account_id'],
238 |   },
239 | };
240 | 
241 | export const UPDATE_EMAIL_WARMUP_TOOL: CategoryTool = {
242 |   name: 'smartlead_update_email_warmup',
243 |   description: 'Add or update warmup settings for an email account.',
244 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
245 |   inputSchema: {
246 |     type: 'object',
247 |     properties: {
248 |       email_account_id: {
249 |         type: 'number',
250 |         description: 'ID of the email account to update warmup settings for',
251 |       },
252 |       enabled: {
253 |         type: 'boolean',
254 |         description: 'Whether warmup is enabled for this email account',
255 |       },
256 |       daily_limit: {
257 |         type: 'number',
258 |         description: 'Daily limit for warmup emails',
259 |       },
260 |       warmup_settings: {
261 |         type: 'object',
262 |         properties: {
263 |           start_time: {
264 |             type: 'string',
265 |             description: 'Start time for warmup in HH:MM format',
266 |           },
267 |           end_time: {
268 |             type: 'string',
269 |             description: 'End time for warmup in HH:MM format',
270 |           },
271 |           days_of_week: {
272 |             type: 'array',
273 |             items: {
274 |               type: 'number',
275 |             },
276 |             description: 'Days of the week for warmup (1-7, where 1 is Monday)',
277 |           },
278 |         },
279 |         description: 'Additional warmup settings',
280 |       },
281 |     },
282 |     required: ['email_account_id', 'enabled'],
283 |   },
284 | };
285 | 
286 | export const RECONNECT_EMAIL_ACCOUNT_TOOL: CategoryTool = {
287 |   name: 'smartlead_reconnect_email_account',
288 |   description: 'Reconnect a failed email account.',
289 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
290 |   inputSchema: {
291 |     type: 'object',
292 |     properties: {
293 |       email_account_id: {
294 |         type: 'number',
295 |         description: 'ID of the email account to reconnect',
296 |       },
297 |       connection_details: {
298 |         type: 'object',
299 |         properties: {
300 |           smtp_host: {
301 |             type: 'string',
302 |             description: 'SMTP server hostname',
303 |           },
304 |           smtp_port: {
305 |             type: 'number',
306 |             description: 'SMTP server port',
307 |           },
308 |           smtp_username: {
309 |             type: 'string',
310 |             description: 'SMTP username',
311 |           },
312 |           smtp_password: {
313 |             type: 'string',
314 |             description: 'SMTP password',
315 |           },
316 |           imap_host: {
317 |             type: 'string',
318 |             description: 'IMAP server hostname',
319 |           },
320 |           imap_port: {
321 |             type: 'number',
322 |             description: 'IMAP server port',
323 |           },
324 |           imap_username: {
325 |             type: 'string',
326 |             description: 'IMAP username',
327 |           },
328 |           imap_password: {
329 |             type: 'string',
330 |             description: 'IMAP password',
331 |           },
332 |           oauth_token: {
333 |             type: 'string',
334 |             description: 'OAuth token',
335 |           },
336 |         },
337 |         description: 'Connection details for reconnecting the email account',
338 |       },
339 |     },
340 |     required: ['email_account_id'],
341 |   },
342 | };
343 | 
344 | export const UPDATE_EMAIL_ACCOUNT_TAG_TOOL: CategoryTool = {
345 |   name: 'smartlead_update_email_account_tag',
346 |   description: 'Update tags for an email account.',
347 |   category: ToolCategory.EMAIL_ACCOUNT_MANAGEMENT,
348 |   inputSchema: {
349 |     type: 'object',
350 |     properties: {
351 |       email_account_id: {
352 |         type: 'number',
353 |         description: 'ID of the email account to update tags for',
354 |       },
355 |       tags: {
356 |         type: 'array',
357 |         items: {
358 |           type: 'string',
359 |         },
360 |         description: 'Tags to assign to the email account',
361 |       },
362 |     },
363 |     required: ['email_account_id', 'tags'],
364 |   },
365 | };
366 | 
367 | // Export all email tools as an array
368 | export const emailTools = [
369 |   LIST_EMAIL_ACCOUNTS_CAMPAIGN_TOOL,
370 |   ADD_EMAIL_TO_CAMPAIGN_TOOL,
371 |   REMOVE_EMAIL_FROM_CAMPAIGN_TOOL,
372 |   FETCH_EMAIL_ACCOUNTS_TOOL,
373 |   CREATE_EMAIL_ACCOUNT_TOOL,
374 |   UPDATE_EMAIL_ACCOUNT_TOOL,
375 |   FETCH_EMAIL_ACCOUNT_BY_ID_TOOL,
376 |   UPDATE_EMAIL_WARMUP_TOOL,
377 |   RECONNECT_EMAIL_ACCOUNT_TOOL,
378 |   UPDATE_EMAIL_ACCOUNT_TAG_TOOL,
379 | ]; 
```

--------------------------------------------------------------------------------
/src/tools/campaign.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { CategoryTool, ToolCategory } from '../types/common.js';
  2 | 
  3 | // Campaign Management Tools
  4 | export const CREATE_CAMPAIGN_TOOL: CategoryTool = {
  5 |   name: 'smartlead_create_campaign',
  6 |   description: 'Create a new campaign in Smartlead.',
  7 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
  8 |   inputSchema: {
  9 |     type: 'object',
 10 |     properties: {
 11 |       name: {
 12 |         type: 'string',
 13 |         description: 'Name of the campaign',
 14 |       },
 15 |       client_id: {
 16 |         type: 'number',
 17 |         description: 'Client ID for the campaign',
 18 |       },
 19 |     },
 20 |     required: ['name'],
 21 |   },
 22 | };
 23 | 
 24 | export const UPDATE_CAMPAIGN_SCHEDULE_TOOL: CategoryTool = {
 25 |   name: 'smartlead_update_campaign_schedule',
 26 |   description: 'Update a campaign\'s schedule settings.',
 27 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
 28 |   inputSchema: {
 29 |     type: 'object',
 30 |     properties: {
 31 |       campaign_id: {
 32 |         type: 'number',
 33 |         description: 'ID of the campaign to update',
 34 |       },
 35 |       timezone: {
 36 |         type: 'string',
 37 |         description: 'Timezone for the campaign (e.g., "America/Los_Angeles")',
 38 |       },
 39 |       days_of_the_week: {
 40 |         type: 'array',
 41 |         items: { type: 'number' },
 42 |         description: 'Days of the week to send emails (1-7, where 1 is Monday)',
 43 |       },
 44 |       start_hour: {
 45 |         type: 'string',
 46 |         description: 'Start hour in 24-hour format (e.g., "09:00")',
 47 |       },
 48 |       end_hour: {
 49 |         type: 'string',
 50 |         description: 'End hour in 24-hour format (e.g., "17:00")',
 51 |       },
 52 |       min_time_btw_emails: {
 53 |         type: 'number',
 54 |         description: 'Minimum time between emails in minutes',
 55 |       },
 56 |       max_new_leads_per_day: {
 57 |         type: 'number',
 58 |         description: 'Maximum number of new leads per day',
 59 |       },
 60 |       schedule_start_time: {
 61 |         type: 'string',
 62 |         description: 'Schedule start time in ISO format',
 63 |       },
 64 |     },
 65 |     required: ['campaign_id'],
 66 |   },
 67 | };
 68 | 
 69 | export const UPDATE_CAMPAIGN_SETTINGS_TOOL: CategoryTool = {
 70 |   name: 'smartlead_update_campaign_settings',
 71 |   description: 'Update a campaign\'s general settings.',
 72 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
 73 |   inputSchema: {
 74 |     type: 'object',
 75 |     properties: {
 76 |       campaign_id: {
 77 |         type: 'number',
 78 |         description: 'ID of the campaign to update',
 79 |       },
 80 |       name: {
 81 |         type: 'string',
 82 |         description: 'New name for the campaign',
 83 |       },
 84 |       status: {
 85 |         type: 'string',
 86 |         enum: ['active', 'paused', 'completed'],
 87 |         description: 'Status of the campaign',
 88 |       },
 89 |       settings: {
 90 |         type: 'object',
 91 |         description: 'Additional campaign settings',
 92 |       },
 93 |     },
 94 |     required: ['campaign_id'],
 95 |   },
 96 | };
 97 | 
 98 | export const UPDATE_CAMPAIGN_STATUS_TOOL: CategoryTool = {
 99 |   name: 'smartlead_update_campaign_status',
100 |   description: 'Update the status of a campaign. Use this specifically for changing a campaign\'s status.',
101 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
102 |   inputSchema: {
103 |     type: 'object',
104 |     properties: {
105 |       campaign_id: {
106 |         type: 'number',
107 |         description: 'ID of the campaign to update the status for',
108 |       },
109 |       status: {
110 |         type: 'string',
111 |         enum: ['PAUSED', 'STOPPED', 'START'],
112 |         description: 'New status for the campaign (must be in uppercase)',
113 |       },
114 |     },
115 |     required: ['campaign_id', 'status'],
116 |   },
117 | };
118 | 
119 | export const GET_CAMPAIGN_TOOL: CategoryTool = {
120 |   name: 'smartlead_get_campaign',
121 |   description: 'Get details of a specific campaign by ID.',
122 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
123 |   inputSchema: {
124 |     type: 'object',
125 |     properties: {
126 |       campaign_id: {
127 |         type: 'number',
128 |         description: 'ID of the campaign to retrieve',
129 |       },
130 |     },
131 |     required: ['campaign_id'],
132 |   },
133 | };
134 | 
135 | export const LIST_CAMPAIGNS_TOOL: CategoryTool = {
136 |   name: 'smartlead_list_campaigns',
137 |   description: 'List all campaigns with optional filtering.',
138 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
139 |   inputSchema: {
140 |     type: 'object',
141 |     properties: {
142 |       status: {
143 |         type: 'string',
144 |         enum: ['active', 'paused', 'completed'],
145 |         description: 'Filter campaigns by status',
146 |       },
147 |       limit: {
148 |         type: 'number',
149 |         description: 'Maximum number of campaigns to return',
150 |       },
151 |       offset: {
152 |         type: 'number',
153 |         description: 'Offset for pagination',
154 |       },
155 |     },
156 |   },
157 | };
158 | 
159 | export const SAVE_CAMPAIGN_SEQUENCE_TOOL: CategoryTool = {
160 |   name: 'smartlead_save_campaign_sequence',
161 |   description: 'Save a sequence of emails for a campaign.',
162 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
163 |   inputSchema: {
164 |     type: 'object',
165 |     properties: {
166 |       campaign_id: {
167 |         type: 'number',
168 |         description: 'ID of the campaign',
169 |       },
170 |       sequence: {
171 |         type: 'array',
172 |         items: {
173 |           type: 'object',
174 |           properties: {
175 |             seq_number: {
176 |               type: 'number',
177 |               description: 'The sequence number (order) of this email',
178 |             },
179 |             seq_delay_details: {
180 |               type: 'object',
181 |               properties: {
182 |                 delay_in_days: {
183 |                   type: 'number',
184 |                   description: 'Days to wait before sending this email',
185 |                 }
186 |               },
187 |               description: 'Delay details for this sequence'
188 |             },
189 |             variant_distribution_type: {
190 |               type: 'string',
191 |               enum: ['MANUAL_EQUAL', 'MANUAL_PERCENTAGE', 'AI_EQUAL'],
192 |               description: 'How to distribute variants'
193 |             },
194 |             lead_distribution_percentage: {
195 |               type: 'number',
196 |               description: 'What sample % size of the lead pool to use to find the winner (for AI_EQUAL)'
197 |             },
198 |             winning_metric_property: {
199 |               type: 'string',
200 |               enum: ['OPEN_RATE', 'CLICK_RATE', 'REPLY_RATE', 'POSITIVE_REPLY_RATE'],
201 |               description: 'Metric to use for determining the winning variant (for AI_EQUAL)'
202 |             },
203 |             seq_variants: {
204 |               type: 'array',
205 |               items: {
206 |                 type: 'object',
207 |                 properties: {
208 |                   subject: {
209 |                     type: 'string',
210 |                     description: 'Email subject line',
211 |                   },
212 |                   email_body: {
213 |                     type: 'string',
214 |                     description: 'Email body content in HTML',
215 |                   },
216 |                   variant_label: {
217 |                     type: 'string',
218 |                     description: 'Label for this variant (A, B, C, etc.)',
219 |                   },
220 |                   variant_distribution_percentage: {
221 |                     type: 'number',
222 |                     description: 'Percentage of leads to receive this variant (for MANUAL_PERCENTAGE)'
223 |                   }
224 |                 },
225 |                 required: ['subject', 'email_body', 'variant_label'],
226 |               },
227 |               description: 'Variants of the email in this sequence'
228 |             }
229 |           },
230 |           required: ['seq_number', 'seq_delay_details', 'variant_distribution_type', 'seq_variants'],
231 |         },
232 |         description: 'Sequence of emails to send',
233 |       },
234 |     },
235 |     required: ['campaign_id', 'sequence'],
236 |   },
237 | };
238 | 
239 | export const GET_CAMPAIGN_SEQUENCE_TOOL: CategoryTool = {
240 |   name: 'smartlead_get_campaign_sequence',
241 |   description: 'Fetch a campaign\'s sequence data.',
242 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
243 |   inputSchema: {
244 |     type: 'object',
245 |     properties: {
246 |       campaign_id: {
247 |         type: 'number',
248 |         description: 'ID of the campaign to fetch sequences for',
249 |       },
250 |     },
251 |     required: ['campaign_id'],
252 |   },
253 | };
254 | 
255 | // New tool definitions for the remaining campaign management API endpoints
256 | 
257 | export const GET_CAMPAIGNS_BY_LEAD_TOOL: CategoryTool = {
258 |   name: 'smartlead_get_campaigns_by_lead',
259 |   description: 'Fetch all campaigns that a lead belongs to.',
260 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
261 |   inputSchema: {
262 |     type: 'object',
263 |     properties: {
264 |       lead_id: {
265 |         type: 'number',
266 |         description: 'ID of the lead to fetch campaigns for',
267 |       },
268 |     },
269 |     required: ['lead_id'],
270 |   },
271 | };
272 | 
273 | export const EXPORT_CAMPAIGN_LEADS_TOOL: CategoryTool = {
274 |   name: 'smartlead_export_campaign_leads',
275 |   description: 'Export all leads data from a campaign as CSV.',
276 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
277 |   inputSchema: {
278 |     type: 'object',
279 |     properties: {
280 |       campaign_id: {
281 |         type: 'number',
282 |         description: 'ID of the campaign to export leads from',
283 |       },
284 |     },
285 |     required: ['campaign_id'],
286 |   },
287 | };
288 | 
289 | export const DELETE_CAMPAIGN_TOOL: CategoryTool = {
290 |   name: 'smartlead_delete_campaign',
291 |   description: 'Delete a campaign permanently.',
292 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
293 |   inputSchema: {
294 |     type: 'object',
295 |     properties: {
296 |       campaign_id: {
297 |         type: 'number',
298 |         description: 'ID of the campaign to delete',
299 |       },
300 |     },
301 |     required: ['campaign_id'],
302 |   },
303 | };
304 | 
305 | export const GET_CAMPAIGN_ANALYTICS_BY_DATE_TOOL: CategoryTool = {
306 |   name: 'smartlead_get_campaign_analytics_by_date',
307 |   description: 'Fetch campaign analytics for a specific date range.',
308 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
309 |   inputSchema: {
310 |     type: 'object',
311 |     properties: {
312 |       campaign_id: {
313 |         type: 'number',
314 |         description: 'ID of the campaign to fetch analytics for',
315 |       },
316 |       start_date: {
317 |         type: 'string',
318 |         format: 'date',
319 |         description: 'Start date in YYYY-MM-DD format',
320 |       },
321 |       end_date: {
322 |         type: 'string',
323 |         format: 'date',
324 |         description: 'End date in YYYY-MM-DD format',
325 |       },
326 |     },
327 |     required: ['campaign_id', 'start_date', 'end_date'],
328 |   },
329 | };
330 | 
331 | export const GET_CAMPAIGN_SEQUENCE_ANALYTICS_TOOL: CategoryTool = {
332 |   name: 'smartlead_get_campaign_sequence_analytics',
333 |   description: 'Fetch analytics data for a specific email campaign sequence.',
334 |   category: ToolCategory.CAMPAIGN_MANAGEMENT,
335 |   inputSchema: {
336 |     type: 'object',
337 |     properties: {
338 |       campaign_id: {
339 |         type: 'number',
340 |         description: 'ID of the campaign to fetch sequence analytics for',
341 |       },
342 |       start_date: {
343 |         type: 'string',
344 |         description: 'Start date in YYYY-MM-DD HH:MM:SS format',
345 |       },
346 |       end_date: {
347 |         type: 'string',
348 |         description: 'End date in YYYY-MM-DD HH:MM:SS format',
349 |       },
350 |       time_zone: {
351 |         type: 'string',
352 |         description: 'Timezone for the analytics data (e.g., "Europe/London")',
353 |       },
354 |     },
355 |     required: ['campaign_id', 'start_date', 'end_date'],
356 |   },
357 | };
358 | 
359 | // Export an array of all campaign tools for registration
360 | export const campaignTools = [
361 |   CREATE_CAMPAIGN_TOOL,
362 |   UPDATE_CAMPAIGN_SCHEDULE_TOOL,
363 |   UPDATE_CAMPAIGN_SETTINGS_TOOL,
364 |   UPDATE_CAMPAIGN_STATUS_TOOL,
365 |   GET_CAMPAIGN_TOOL,
366 |   LIST_CAMPAIGNS_TOOL,
367 |   SAVE_CAMPAIGN_SEQUENCE_TOOL,
368 |   GET_CAMPAIGN_SEQUENCE_TOOL,
369 |   GET_CAMPAIGNS_BY_LEAD_TOOL,
370 |   EXPORT_CAMPAIGN_LEADS_TOOL,
371 |   DELETE_CAMPAIGN_TOOL,
372 |   GET_CAMPAIGN_ANALYTICS_BY_DATE_TOOL,
373 |   GET_CAMPAIGN_SEQUENCE_ANALYTICS_TOOL
374 | ]; 
```

--------------------------------------------------------------------------------
/src/types/smartDelivery.ts:
--------------------------------------------------------------------------------

```typescript
  1 | // Type definitions for SmartDelivery functionality
  2 | 
  3 | // Region wise Provider IDs response types
  4 | export interface SpamTestProvider {
  5 |   id: number;
  6 |   name: string;
  7 |   description?: string;
  8 |   region?: string;
  9 |   country?: string;
 10 |   is_active: boolean;
 11 | }
 12 | 
 13 | export interface RegionWiseProvidersResponse {
 14 |   success: boolean;
 15 |   data: {
 16 |     providers: SpamTestProvider[];
 17 |   };
 18 |   message?: string;
 19 | }
 20 | 
 21 | // Manual Placement Test types
 22 | export interface CreateManualPlacementTestParams {
 23 |   test_name: string;
 24 |   description?: string;
 25 |   spam_filters: string[];
 26 |   link_checker: boolean;
 27 |   campaign_id: number;
 28 |   sequence_mapping_id: number;
 29 |   provider_ids: number[];
 30 |   sender_accounts: string[];
 31 |   all_email_sent_without_time_gap: boolean;
 32 |   min_time_btwn_emails: number;
 33 |   min_time_unit: string;
 34 |   is_warmup: boolean;
 35 | }
 36 | 
 37 | // Automated Placement Test additional types
 38 | export interface CreateAutomatedPlacementTestParams extends CreateManualPlacementTestParams {
 39 |   schedule_start_time: string;
 40 |   test_end_date: string;
 41 |   every_days: number;
 42 |   tz: string;
 43 |   days: number[];
 44 |   starHour?: string;
 45 |   folder_id?: number;
 46 |   scheduler_cron_value?: {
 47 |     tz: string;
 48 |     days: number[];
 49 |   };
 50 | }
 51 | 
 52 | // Spam Test Details types
 53 | export interface GetSpamTestDetailsParams {
 54 |   spam_test_id: number;
 55 | }
 56 | 
 57 | // Delete Smart Delivery Tests types
 58 | export interface DeleteSmartDeliveryTestsParams {
 59 |   spamTestIds: number[];
 60 | }
 61 | 
 62 | // Stop Automated Test types
 63 | export interface StopAutomatedTestParams {
 64 |   spam_test_id: number;
 65 | }
 66 | 
 67 | // List all Tests types
 68 | export interface ListAllTestsParams {
 69 |   testType: 'manual' | 'auto';
 70 |   limit?: number;
 71 |   offset?: number;
 72 | }
 73 | 
 74 | // Provider wise report types
 75 | export interface ProviderWiseReportParams {
 76 |   spam_test_id: number;
 77 | }
 78 | 
 79 | // Geo wise report types
 80 | export interface GroupWiseReportParams {
 81 |   spam_test_id: number;
 82 | }
 83 | 
 84 | // Sender Account wise report types
 85 | export interface SenderAccountWiseReportParams {
 86 |   spam_test_id: number;
 87 | }
 88 | 
 89 | // Spam filter report types
 90 | export interface SpamFilterDetailsParams {
 91 |   spam_test_id: number;
 92 | }
 93 | 
 94 | // DKIM details types
 95 | export interface DkimDetailsParams {
 96 |   spam_test_id: number;
 97 | }
 98 | 
 99 | // SPF details types
100 | export interface SpfDetailsParams {
101 |   spam_test_id: number;
102 | }
103 | 
104 | // rDNS report types
105 | export interface RdnsDetailsParams {
106 |   spam_test_id: number;
107 | }
108 | 
109 | // Sender Account list types
110 | export interface SenderAccountsParams {
111 |   spam_test_id: number;
112 | }
113 | 
114 | // Blacklists types
115 | export interface BlacklistParams {
116 |   spam_test_id: number;
117 | }
118 | 
119 | // Spam test email content types
120 | export interface EmailContentParams {
121 |   spam_test_id: number;
122 | }
123 | 
124 | // Spam test IP blacklist count types
125 | export interface IpAnalyticsParams {
126 |   spam_test_id: number;
127 | }
128 | 
129 | // Email reply headers types
130 | export interface EmailHeadersParams {
131 |   spam_test_id: number;
132 |   reply_id: number;
133 | }
134 | 
135 | // Schedule history for automated tests types
136 | export interface ScheduleHistoryParams {
137 |   spam_test_id: number;
138 | }
139 | 
140 | // IP details types
141 | export interface IpDetailsParams {
142 |   spam_test_id: number;
143 |   reply_id: number;
144 | }
145 | 
146 | // Mailbox summary types
147 | export interface MailboxSummaryParams {
148 |   limit?: number;
149 |   offset?: number;
150 | }
151 | 
152 | // Mailbox count API types
153 | export interface MailboxCountParams {
154 |   // This endpoint doesn't require any specific parameters
155 | }
156 | 
157 | // Get all folders types
158 | export interface GetAllFoldersParams {
159 |   limit?: number;
160 |   offset?: number;
161 |   name?: string;
162 | }
163 | 
164 | // Create folders types
165 | export interface CreateFolderParams {
166 |   name: string;
167 | }
168 | 
169 | // Get folder by ID types
170 | export interface GetFolderByIdParams {
171 |   folder_id: number;
172 | }
173 | 
174 | // Delete folder types
175 | export interface DeleteFolderParams {
176 |   folder_id: number;
177 | }
178 | 
179 | // Tool parameter interfaces
180 | export interface GetRegionWiseProvidersParams {
181 |   // This endpoint doesn't require any specific parameters beyond the API key
182 |   // which is handled at the API client level
183 | }
184 | 
185 | // Type guards
186 | export function isGetRegionWiseProvidersParams(args: unknown): args is GetRegionWiseProvidersParams {
187 |   // Since this tool doesn't require specific parameters, any object is valid
188 |   return typeof args === 'object' && args !== null;
189 | }
190 | 
191 | export function isCreateManualPlacementTestParams(args: unknown): args is CreateManualPlacementTestParams {
192 |   if (typeof args !== 'object' || args === null) return false;
193 |   
194 |   const params = args as Partial<CreateManualPlacementTestParams>;
195 |   
196 |   return (
197 |     typeof params.test_name === 'string' &&
198 |     Array.isArray(params.spam_filters) &&
199 |     typeof params.link_checker === 'boolean' &&
200 |     typeof params.campaign_id === 'number' &&
201 |     typeof params.sequence_mapping_id === 'number' &&
202 |     Array.isArray(params.provider_ids) &&
203 |     Array.isArray(params.sender_accounts) &&
204 |     typeof params.all_email_sent_without_time_gap === 'boolean' &&
205 |     typeof params.min_time_btwn_emails === 'number' &&
206 |     typeof params.min_time_unit === 'string' &&
207 |     typeof params.is_warmup === 'boolean'
208 |   );
209 | }
210 | 
211 | export function isCreateAutomatedPlacementTestParams(args: unknown): args is CreateAutomatedPlacementTestParams {
212 |   if (!isCreateManualPlacementTestParams(args)) return false;
213 |   
214 |   const params = args as Partial<CreateAutomatedPlacementTestParams>;
215 |   
216 |   return (
217 |     typeof params.schedule_start_time === 'string' &&
218 |     typeof params.test_end_date === 'string' &&
219 |     typeof params.every_days === 'number' &&
220 |     typeof params.tz === 'string' &&
221 |     Array.isArray(params.days)
222 |   );
223 | }
224 | 
225 | export function isGetSpamTestDetailsParams(args: unknown): args is GetSpamTestDetailsParams {
226 |   return (
227 |     typeof args === 'object' &&
228 |     args !== null &&
229 |     'spam_test_id' in args &&
230 |     typeof (args as GetSpamTestDetailsParams).spam_test_id === 'number'
231 |   );
232 | }
233 | 
234 | export function isDeleteSmartDeliveryTestsParams(args: unknown): args is DeleteSmartDeliveryTestsParams {
235 |   return (
236 |     typeof args === 'object' &&
237 |     args !== null &&
238 |     'spamTestIds' in args &&
239 |     Array.isArray((args as DeleteSmartDeliveryTestsParams).spamTestIds) &&
240 |     (args as DeleteSmartDeliveryTestsParams).spamTestIds.every(id => typeof id === 'number')
241 |   );
242 | }
243 | 
244 | export function isStopAutomatedTestParams(args: unknown): args is StopAutomatedTestParams {
245 |   return (
246 |     typeof args === 'object' &&
247 |     args !== null &&
248 |     'spam_test_id' in args &&
249 |     typeof (args as StopAutomatedTestParams).spam_test_id === 'number'
250 |   );
251 | }
252 | 
253 | export function isListAllTestsParams(args: unknown): args is ListAllTestsParams {
254 |   if (typeof args !== 'object' || args === null) return false;
255 |   
256 |   const params = args as Partial<ListAllTestsParams>;
257 |   
258 |   return (
259 |     params.testType === 'manual' || params.testType === 'auto'
260 |   );
261 | }
262 | 
263 | export function isProviderWiseReportParams(args: unknown): args is ProviderWiseReportParams {
264 |   return (
265 |     typeof args === 'object' &&
266 |     args !== null &&
267 |     'spam_test_id' in args &&
268 |     typeof (args as ProviderWiseReportParams).spam_test_id === 'number'
269 |   );
270 | }
271 | 
272 | export function isGroupWiseReportParams(args: unknown): args is GroupWiseReportParams {
273 |   return (
274 |     typeof args === 'object' &&
275 |     args !== null &&
276 |     'spam_test_id' in args &&
277 |     typeof (args as GroupWiseReportParams).spam_test_id === 'number'
278 |   );
279 | }
280 | 
281 | export function isSenderAccountWiseReportParams(args: unknown): args is SenderAccountWiseReportParams {
282 |   return (
283 |     typeof args === 'object' &&
284 |     args !== null &&
285 |     'spam_test_id' in args &&
286 |     typeof (args as SenderAccountWiseReportParams).spam_test_id === 'number'
287 |   );
288 | }
289 | 
290 | export function isSpamFilterDetailsParams(args: unknown): args is SpamFilterDetailsParams {
291 |   return (
292 |     typeof args === 'object' &&
293 |     args !== null &&
294 |     'spam_test_id' in args &&
295 |     typeof (args as SpamFilterDetailsParams).spam_test_id === 'number'
296 |   );
297 | }
298 | 
299 | export function isDkimDetailsParams(args: unknown): args is DkimDetailsParams {
300 |   return (
301 |     typeof args === 'object' &&
302 |     args !== null &&
303 |     'spam_test_id' in args &&
304 |     typeof (args as DkimDetailsParams).spam_test_id === 'number'
305 |   );
306 | }
307 | 
308 | export function isSpfDetailsParams(args: unknown): args is SpfDetailsParams {
309 |   return (
310 |     typeof args === 'object' &&
311 |     args !== null &&
312 |     'spam_test_id' in args &&
313 |     typeof (args as SpfDetailsParams).spam_test_id === 'number'
314 |   );
315 | }
316 | 
317 | export function isRdnsDetailsParams(args: unknown): args is RdnsDetailsParams {
318 |   return (
319 |     typeof args === 'object' &&
320 |     args !== null &&
321 |     'spam_test_id' in args &&
322 |     typeof (args as RdnsDetailsParams).spam_test_id === 'number'
323 |   );
324 | }
325 | 
326 | export function isSenderAccountsParams(args: unknown): args is SenderAccountsParams {
327 |   return (
328 |     typeof args === 'object' &&
329 |     args !== null &&
330 |     'spam_test_id' in args &&
331 |     typeof (args as SenderAccountsParams).spam_test_id === 'number'
332 |   );
333 | }
334 | 
335 | export function isBlacklistParams(args: unknown): args is BlacklistParams {
336 |   return (
337 |     typeof args === 'object' &&
338 |     args !== null &&
339 |     'spam_test_id' in args &&
340 |     typeof (args as BlacklistParams).spam_test_id === 'number'
341 |   );
342 | }
343 | 
344 | export function isEmailContentParams(args: unknown): args is EmailContentParams {
345 |   return (
346 |     typeof args === 'object' &&
347 |     args !== null &&
348 |     'spam_test_id' in args &&
349 |     typeof (args as EmailContentParams).spam_test_id === 'number'
350 |   );
351 | }
352 | 
353 | export function isIpAnalyticsParams(args: unknown): args is IpAnalyticsParams {
354 |   return (
355 |     typeof args === 'object' &&
356 |     args !== null &&
357 |     'spam_test_id' in args &&
358 |     typeof (args as IpAnalyticsParams).spam_test_id === 'number'
359 |   );
360 | }
361 | 
362 | export function isEmailHeadersParams(args: unknown): args is EmailHeadersParams {
363 |   return (
364 |     typeof args === 'object' &&
365 |     args !== null &&
366 |     'spam_test_id' in args &&
367 |     typeof (args as EmailHeadersParams).spam_test_id === 'number' &&
368 |     'reply_id' in args &&
369 |     typeof (args as EmailHeadersParams).reply_id === 'number'
370 |   );
371 | }
372 | 
373 | export function isScheduleHistoryParams(args: unknown): args is ScheduleHistoryParams {
374 |   return (
375 |     typeof args === 'object' &&
376 |     args !== null &&
377 |     'spam_test_id' in args &&
378 |     typeof (args as ScheduleHistoryParams).spam_test_id === 'number'
379 |   );
380 | }
381 | 
382 | export function isIpDetailsParams(args: unknown): args is IpDetailsParams {
383 |   return (
384 |     typeof args === 'object' &&
385 |     args !== null &&
386 |     'spam_test_id' in args &&
387 |     typeof (args as IpDetailsParams).spam_test_id === 'number' &&
388 |     'reply_id' in args &&
389 |     typeof (args as IpDetailsParams).reply_id === 'number'
390 |   );
391 | }
392 | 
393 | export function isMailboxSummaryParams(args: unknown): args is MailboxSummaryParams {
394 |   return typeof args === 'object' && args !== null;
395 | }
396 | 
397 | export function isMailboxCountParams(args: unknown): args is MailboxCountParams {
398 |   return typeof args === 'object' && args !== null;
399 | }
400 | 
401 | export function isGetAllFoldersParams(args: unknown): args is GetAllFoldersParams {
402 |   return typeof args === 'object' && args !== null;
403 | }
404 | 
405 | export function isCreateFolderParams(args: unknown): args is CreateFolderParams {
406 |   return (
407 |     typeof args === 'object' &&
408 |     args !== null &&
409 |     'name' in args &&
410 |     typeof (args as CreateFolderParams).name === 'string'
411 |   );
412 | }
413 | 
414 | export function isGetFolderByIdParams(args: unknown): args is GetFolderByIdParams {
415 |   return (
416 |     typeof args === 'object' &&
417 |     args !== null &&
418 |     'folder_id' in args &&
419 |     typeof (args as GetFolderByIdParams).folder_id === 'number'
420 |   );
421 | }
422 | 
423 | export function isDeleteFolderParams(args: unknown): args is DeleteFolderParams {
424 |   return (
425 |     typeof args === 'object' &&
426 |     args !== null &&
427 |     'folder_id' in args &&
428 |     typeof (args as DeleteFolderParams).folder_id === 'number'
429 |   );
430 | }
431 | 
```

--------------------------------------------------------------------------------
/src/handlers/campaign.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { AxiosInstance } from 'axios';
  2 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  3 | import { 
  4 |   isCreateCampaignParams, 
  5 |   isUpdateCampaignScheduleParams, 
  6 |   isUpdateCampaignSettingsParams,
  7 |   isUpdateCampaignStatusParams,
  8 |   isGetCampaignParams,
  9 |   isListCampaignsParams,
 10 |   isSaveCampaignSequenceParams,
 11 |   isGetCampaignSequenceParams,
 12 |   isGetCampaignsByLeadParams,
 13 |   isExportCampaignLeadsParams,
 14 |   isDeleteCampaignParams,
 15 |   isGetCampaignAnalyticsByDateParams,
 16 |   isGetCampaignSequenceAnalyticsParams
 17 | } from '../types/campaign.js';
 18 | 
 19 | // Handler for campaign-related tools
 20 | export async function handleCampaignTool(
 21 |   toolName: string, 
 22 |   args: unknown, 
 23 |   apiClient: AxiosInstance,
 24 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
 25 | ) {
 26 |   switch (toolName) {
 27 |     case 'smartlead_create_campaign': {
 28 |       return handleCreateCampaign(args, apiClient, withRetry);
 29 |     }
 30 |     case 'smartlead_update_campaign_schedule': {
 31 |       return handleUpdateCampaignSchedule(args, apiClient, withRetry);
 32 |     }
 33 |     case 'smartlead_update_campaign_settings': {
 34 |       return handleUpdateCampaignSettings(args, apiClient, withRetry);
 35 |     }
 36 |     case 'smartlead_update_campaign_status': {
 37 |       return handleUpdateCampaignStatus(args, apiClient, withRetry);
 38 |     }
 39 |     case 'smartlead_get_campaign': {
 40 |       return handleGetCampaign(args, apiClient, withRetry);
 41 |     }
 42 |     case 'smartlead_list_campaigns': {
 43 |       return handleListCampaigns(args, apiClient, withRetry);
 44 |     }
 45 |     case 'smartlead_save_campaign_sequence': {
 46 |       return handleSaveCampaignSequence(args, apiClient, withRetry);
 47 |     }
 48 |     case 'smartlead_get_campaign_sequence': {
 49 |       return handleGetCampaignSequence(args, apiClient, withRetry);
 50 |     }
 51 |     case 'smartlead_get_campaigns_by_lead': {
 52 |       return handleGetCampaignsByLead(args, apiClient, withRetry);
 53 |     }
 54 |     case 'smartlead_export_campaign_leads': {
 55 |       return handleExportCampaignLeads(args, apiClient, withRetry);
 56 |     }
 57 |     case 'smartlead_delete_campaign': {
 58 |       return handleDeleteCampaign(args, apiClient, withRetry);
 59 |     }
 60 |     case 'smartlead_get_campaign_analytics_by_date': {
 61 |       return handleGetCampaignAnalyticsByDate(args, apiClient, withRetry);
 62 |     }
 63 |     case 'smartlead_get_campaign_sequence_analytics': {
 64 |       return handleGetCampaignSequenceAnalytics(args, apiClient, withRetry);
 65 |     }
 66 |     default:
 67 |       throw new Error(`Unknown campaign tool: ${toolName}`);
 68 |   }
 69 | }
 70 | 
 71 | // Individual handlers for each tool
 72 | async function handleCreateCampaign(
 73 |   args: unknown, 
 74 |   apiClient: AxiosInstance,
 75 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
 76 | ) {
 77 |   if (!isCreateCampaignParams(args)) {
 78 |     throw new McpError(
 79 |       ErrorCode.InvalidParams,
 80 |       'Invalid arguments for smartlead_create_campaign'
 81 |     );
 82 |   }
 83 | 
 84 |   try {
 85 |     const response = await withRetry(
 86 |       async () => apiClient.post('/campaigns/create', args),
 87 |       'create campaign'
 88 |     );
 89 | 
 90 |     return {
 91 |       content: [
 92 |         {
 93 |           type: 'text',
 94 |           text: JSON.stringify(response.data, null, 2),
 95 |         },
 96 |       ],
 97 |       isError: false,
 98 |     };
 99 |   } catch (error: any) {
100 |     return {
101 |       content: [{ 
102 |         type: 'text', 
103 |         text: `API Error: ${error.response?.data?.message || error.message}` 
104 |       }],
105 |       isError: true,
106 |     };
107 |   }
108 | }
109 | 
110 | async function handleUpdateCampaignSchedule(
111 |   args: unknown, 
112 |   apiClient: AxiosInstance,
113 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
114 | ) {
115 |   if (!isUpdateCampaignScheduleParams(args)) {
116 |     throw new McpError(
117 |       ErrorCode.InvalidParams,
118 |       'Invalid arguments for smartlead_update_campaign_schedule'
119 |     );
120 |   }
121 | 
122 |   const { campaign_id, ...scheduleParams } = args;
123 | 
124 |   try {
125 |     const response = await withRetry(
126 |       async () => apiClient.post(`/campaigns/${campaign_id}/schedule`, scheduleParams),
127 |       'update campaign schedule'
128 |     );
129 | 
130 |     return {
131 |       content: [
132 |         {
133 |           type: 'text',
134 |           text: JSON.stringify(response.data, null, 2),
135 |         },
136 |       ],
137 |       isError: false,
138 |     };
139 |   } catch (error: any) {
140 |     return {
141 |       content: [{ 
142 |         type: 'text', 
143 |         text: `API Error: ${error.response?.data?.message || error.message}` 
144 |       }],
145 |       isError: true,
146 |     };
147 |   }
148 | }
149 | 
150 | async function handleUpdateCampaignSettings(
151 |   args: unknown, 
152 |   apiClient: AxiosInstance,
153 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
154 | ) {
155 |   if (!isUpdateCampaignSettingsParams(args)) {
156 |     throw new McpError(
157 |       ErrorCode.InvalidParams,
158 |       'Invalid arguments for smartlead_update_campaign_settings'
159 |     );
160 |   }
161 | 
162 |   const { campaign_id, ...settingsParams } = args;
163 | 
164 |   try {
165 |     const response = await withRetry(
166 |       async () => apiClient.post(`/campaigns/${campaign_id}/settings`, settingsParams),
167 |       'update campaign settings'
168 |     );
169 | 
170 |     return {
171 |       content: [
172 |         {
173 |           type: 'text',
174 |           text: JSON.stringify(response.data, null, 2),
175 |         },
176 |       ],
177 |       isError: false,
178 |     };
179 |   } catch (error: any) {
180 |     return {
181 |       content: [{ 
182 |         type: 'text', 
183 |         text: `API Error: ${error.response?.data?.message || error.message}` 
184 |       }],
185 |       isError: true,
186 |     };
187 |   }
188 | }
189 | 
190 | async function handleUpdateCampaignStatus(
191 |   args: unknown, 
192 |   apiClient: AxiosInstance,
193 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
194 | ) {
195 |   if (!isUpdateCampaignStatusParams(args)) {
196 |     throw new McpError(
197 |       ErrorCode.InvalidParams,
198 |       'Invalid arguments for smartlead_update_campaign_status'
199 |     );
200 |   }
201 | 
202 |   const { campaign_id, status } = args;
203 | 
204 |   try {
205 |     const response = await withRetry(
206 |       async () => apiClient.post(`/campaigns/${campaign_id}/status`, { status }),
207 |       'update campaign status'
208 |     );
209 | 
210 |     return {
211 |       content: [
212 |         {
213 |           type: 'text',
214 |           text: JSON.stringify(response.data, null, 2),
215 |         },
216 |       ],
217 |       isError: false,
218 |     };
219 |   } catch (error: any) {
220 |     return {
221 |       content: [{ 
222 |         type: 'text', 
223 |         text: `API Error: ${error.response?.data?.message || error.message}` 
224 |       }],
225 |       isError: true,
226 |     };
227 |   }
228 | }
229 | 
230 | async function handleGetCampaign(
231 |   args: unknown, 
232 |   apiClient: AxiosInstance,
233 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
234 | ) {
235 |   if (!isGetCampaignParams(args)) {
236 |     throw new McpError(
237 |       ErrorCode.InvalidParams,
238 |       'Invalid arguments for smartlead_get_campaign'
239 |     );
240 |   }
241 | 
242 |   try {
243 |     const response = await withRetry(
244 |       async () => apiClient.get(`/campaigns/${args.campaign_id}`),
245 |       'get campaign'
246 |     );
247 | 
248 |     return {
249 |       content: [
250 |         {
251 |           type: 'text',
252 |           text: JSON.stringify(response.data, null, 2),
253 |         },
254 |       ],
255 |       isError: false,
256 |     };
257 |   } catch (error: any) {
258 |     return {
259 |       content: [{ 
260 |         type: 'text', 
261 |         text: `API Error: ${error.response?.data?.message || error.message}` 
262 |       }],
263 |       isError: true,
264 |     };
265 |   }
266 | }
267 | 
268 | async function handleListCampaigns(
269 |   args: unknown, 
270 |   apiClient: AxiosInstance,
271 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
272 | ) {
273 |   if (!isListCampaignsParams(args)) {
274 |     throw new McpError(
275 |       ErrorCode.InvalidParams,
276 |       'Invalid arguments for smartlead_list_campaigns'
277 |     );
278 |   }
279 | 
280 |   try {
281 |     const response = await withRetry(
282 |       async () => apiClient.get('/campaigns', { params: args }),
283 |       'list campaigns'
284 |     );
285 | 
286 |     return {
287 |       content: [
288 |         {
289 |           type: 'text',
290 |           text: JSON.stringify(response.data, null, 2),
291 |         },
292 |       ],
293 |       isError: false,
294 |     };
295 |   } catch (error: any) {
296 |     return {
297 |       content: [{ 
298 |         type: 'text', 
299 |         text: `API Error: ${error.response?.data?.message || error.message}` 
300 |       }],
301 |       isError: true,
302 |     };
303 |   }
304 | }
305 | 
306 | async function handleSaveCampaignSequence(
307 |   args: unknown, 
308 |   apiClient: AxiosInstance,
309 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
310 | ) {
311 |   if (!isSaveCampaignSequenceParams(args)) {
312 |     throw new McpError(
313 |       ErrorCode.InvalidParams,
314 |       'Invalid arguments for smartlead_save_campaign_sequence'
315 |     );
316 |   }
317 | 
318 |   const { campaign_id, sequence } = args;
319 | 
320 |   try {
321 |     const response = await withRetry(
322 |       async () => apiClient.post(`/campaigns/${campaign_id}/sequences`, { sequence }),
323 |       'save campaign sequence'
324 |     );
325 | 
326 |     return {
327 |       content: [
328 |         {
329 |           type: 'text',
330 |           text: JSON.stringify(response.data, null, 2),
331 |         },
332 |       ],
333 |       isError: false,
334 |     };
335 |   } catch (error: any) {
336 |     return {
337 |       content: [{ 
338 |         type: 'text', 
339 |         text: `API Error: ${error.response?.data?.message || error.message}` 
340 |       }],
341 |       isError: true,
342 |     };
343 |   }
344 | }
345 | 
346 | async function handleGetCampaignSequence(
347 |   args: unknown, 
348 |   apiClient: AxiosInstance,
349 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
350 | ) {
351 |   if (!isGetCampaignSequenceParams(args)) {
352 |     throw new McpError(
353 |       ErrorCode.InvalidParams,
354 |       'Invalid arguments for smartlead_get_campaign_sequence'
355 |     );
356 |   }
357 | 
358 |   try {
359 |     const response = await withRetry(
360 |       async () => apiClient.get(`/campaigns/${args.campaign_id}/sequences`),
361 |       'get campaign sequence'
362 |     );
363 | 
364 |     return {
365 |       content: [
366 |         {
367 |           type: 'text',
368 |           text: JSON.stringify(response.data, null, 2),
369 |         },
370 |       ],
371 |       isError: false,
372 |     };
373 |   } catch (error: any) {
374 |     return {
375 |       content: [{ 
376 |         type: 'text', 
377 |         text: `API Error: ${error.response?.data?.message || error.message}` 
378 |       }],
379 |       isError: true,
380 |     };
381 |   }
382 | }
383 | 
384 | // New handler implementations for the remaining campaign management API endpoints
385 | async function handleGetCampaignsByLead(
386 |   args: unknown, 
387 |   apiClient: AxiosInstance,
388 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
389 | ) {
390 |   if (!isGetCampaignsByLeadParams(args)) {
391 |     throw new McpError(
392 |       ErrorCode.InvalidParams,
393 |       'Invalid arguments for smartlead_get_campaigns_by_lead'
394 |     );
395 |   }
396 | 
397 |   try {
398 |     const response = await withRetry(
399 |       async () => apiClient.get(`/leads/${args.lead_id}/campaigns`),
400 |       'get campaigns by lead'
401 |     );
402 | 
403 |     return {
404 |       content: [
405 |         {
406 |           type: 'text',
407 |           text: JSON.stringify(response.data, null, 2),
408 |         },
409 |       ],
410 |       isError: false,
411 |     };
412 |   } catch (error: any) {
413 |     return {
414 |       content: [{ 
415 |         type: 'text', 
416 |         text: `API Error: ${error.response?.data?.message || error.message}` 
417 |       }],
418 |       isError: true,
419 |     };
420 |   }
421 | }
422 | 
423 | async function handleExportCampaignLeads(
424 |   args: unknown, 
425 |   apiClient: AxiosInstance,
426 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
427 | ) {
428 |   if (!isExportCampaignLeadsParams(args)) {
429 |     throw new McpError(
430 |       ErrorCode.InvalidParams,
431 |       'Invalid arguments for smartlead_export_campaign_leads'
432 |     );
433 |   }
434 | 
435 |   try {
436 |     const response = await withRetry(
437 |       async () => apiClient.get(`/campaigns/${args.campaign_id}/leads-export`, {
438 |         responseType: 'text'
439 |       }),
440 |       'export campaign leads'
441 |     );
442 | 
443 |     return {
444 |       content: [
445 |         {
446 |           type: 'text',
447 |           text: `CSV Data:\n${response.data}`,
448 |         },
449 |       ],
450 |       isError: false,
451 |     };
452 |   } catch (error: any) {
453 |     return {
454 |       content: [{ 
455 |         type: 'text', 
456 |         text: `API Error: ${error.response?.data?.message || error.message}` 
457 |       }],
458 |       isError: true,
459 |     };
460 |   }
461 | }
462 | 
463 | async function handleDeleteCampaign(
464 |   args: unknown, 
465 |   apiClient: AxiosInstance,
466 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
467 | ) {
468 |   if (!isDeleteCampaignParams(args)) {
469 |     throw new McpError(
470 |       ErrorCode.InvalidParams,
471 |       'Invalid arguments for smartlead_delete_campaign'
472 |     );
473 |   }
474 | 
475 |   try {
476 |     const response = await withRetry(
477 |       async () => apiClient.delete(`/campaigns/${args.campaign_id}`),
478 |       'delete campaign'
479 |     );
480 | 
481 |     return {
482 |       content: [
483 |         {
484 |           type: 'text',
485 |           text: JSON.stringify(response.data, null, 2),
486 |         },
487 |       ],
488 |       isError: false,
489 |     };
490 |   } catch (error: any) {
491 |     return {
492 |       content: [{ 
493 |         type: 'text', 
494 |         text: `API Error: ${error.response?.data?.message || error.message}` 
495 |       }],
496 |       isError: true,
497 |     };
498 |   }
499 | }
500 | 
501 | async function handleGetCampaignAnalyticsByDate(
502 |   args: unknown, 
503 |   apiClient: AxiosInstance,
504 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
505 | ) {
506 |   if (!isGetCampaignAnalyticsByDateParams(args)) {
507 |     throw new McpError(
508 |       ErrorCode.InvalidParams,
509 |       'Invalid arguments for smartlead_get_campaign_analytics_by_date'
510 |     );
511 |   }
512 | 
513 |   const { campaign_id, ...params } = args;
514 | 
515 |   try {
516 |     const response = await withRetry(
517 |       async () => apiClient.get(`/campaigns/${campaign_id}/analytics-by-date`, { 
518 |         params 
519 |       }),
520 |       'get campaign analytics by date'
521 |     );
522 | 
523 |     return {
524 |       content: [
525 |         {
526 |           type: 'text',
527 |           text: JSON.stringify(response.data, null, 2),
528 |         },
529 |       ],
530 |       isError: false,
531 |     };
532 |   } catch (error: any) {
533 |     return {
534 |       content: [{ 
535 |         type: 'text', 
536 |         text: `API Error: ${error.response?.data?.message || error.message}` 
537 |       }],
538 |       isError: true,
539 |     };
540 |   }
541 | }
542 | 
543 | async function handleGetCampaignSequenceAnalytics(
544 |   args: unknown, 
545 |   apiClient: AxiosInstance,
546 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
547 | ) {
548 |   if (!isGetCampaignSequenceAnalyticsParams(args)) {
549 |     throw new McpError(
550 |       ErrorCode.InvalidParams,
551 |       'Invalid arguments for smartlead_get_campaign_sequence_analytics'
552 |     );
553 |   }
554 | 
555 |   const { campaign_id, ...params } = args;
556 | 
557 |   try {
558 |     const response = await withRetry(
559 |       async () => apiClient.get(`/campaigns/${campaign_id}/sequence-analytics`, { 
560 |         params 
561 |       }),
562 |       'get campaign sequence analytics'
563 |     );
564 | 
565 |     return {
566 |       content: [
567 |         {
568 |           type: 'text',
569 |           text: JSON.stringify(response.data, null, 2),
570 |         },
571 |       ],
572 |       isError: false,
573 |     };
574 |   } catch (error: any) {
575 |     return {
576 |       content: [{ 
577 |         type: 'text', 
578 |         text: `API Error: ${error.response?.data?.message || error.message}` 
579 |       }],
580 |       isError: true,
581 |     };
582 |   }
583 | } 
```

--------------------------------------------------------------------------------
/src/handlers/email.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { AxiosInstance } from 'axios';
  2 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  3 | import {
  4 |   isListEmailAccountsParams,
  5 |   isAddEmailToCampaignParams,
  6 |   isRemoveEmailFromCampaignParams,
  7 |   isFetchEmailAccountsParams,
  8 |   isCreateEmailAccountParams,
  9 |   isUpdateEmailAccountParams,
 10 |   isFetchEmailAccountByIdParams,
 11 |   isUpdateEmailWarmupParams,
 12 |   isReconnectEmailAccountParams,
 13 |   isUpdateEmailAccountTagParams,
 14 |   ListEmailAccountsParams,
 15 |   AddEmailToCampaignParams,
 16 |   RemoveEmailFromCampaignParams,
 17 |   FetchEmailAccountsParams,
 18 |   UpdateEmailAccountParams,
 19 |   FetchEmailAccountByIdParams,
 20 |   UpdateEmailWarmupParams,
 21 |   UpdateEmailAccountTagParams,
 22 |   CreateEmailAccountParams
 23 | } from '../types/email.js';
 24 | 
 25 | // Handler for email-related tools
 26 | export async function handleEmailTool(
 27 |   toolName: string,
 28 |   args: unknown,
 29 |   apiClient: AxiosInstance,
 30 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
 31 | ) {
 32 |   switch (toolName) {
 33 |     case 'smartlead_list_email_accounts_campaign': {
 34 |       return handleListEmailAccountsCampaign(args, apiClient, withRetry);
 35 |     }
 36 |     case 'smartlead_add_email_to_campaign': {
 37 |       return handleAddEmailToCampaign(args, apiClient, withRetry);
 38 |     }
 39 |     case 'smartlead_remove_email_from_campaign': {
 40 |       return handleRemoveEmailFromCampaign(args, apiClient, withRetry);
 41 |     }
 42 |     case 'smartlead_fetch_email_accounts': {
 43 |       return handleFetchEmailAccounts(args, apiClient, withRetry);
 44 |     }
 45 |     case 'smartlead_create_email_account': {
 46 |       return handleCreateEmailAccount(args, apiClient, withRetry);
 47 |     }
 48 |     case 'smartlead_update_email_account': {
 49 |       return handleUpdateEmailAccount(args, apiClient, withRetry);
 50 |     }
 51 |     case 'smartlead_fetch_email_account_by_id': {
 52 |       return handleFetchEmailAccountById(args, apiClient, withRetry);
 53 |     }
 54 |     case 'smartlead_update_email_warmup': {
 55 |       return handleUpdateEmailWarmup(args, apiClient, withRetry);
 56 |     }
 57 |     case 'smartlead_reconnect_email_account': {
 58 |       return handleReconnectEmailAccount(args, apiClient, withRetry);
 59 |     }
 60 |     case 'smartlead_update_email_account_tag': {
 61 |       return handleUpdateEmailAccountTag(args, apiClient, withRetry);
 62 |     }
 63 |     default:
 64 |       throw new Error(`Unknown email tool: ${toolName}`);
 65 |   }
 66 | }
 67 | 
 68 | // Individual handlers for each tool
 69 | async function handleListEmailAccountsCampaign(
 70 |   args: unknown,
 71 |   apiClient: AxiosInstance,
 72 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
 73 | ) {
 74 |   if (!isListEmailAccountsParams(args)) {
 75 |     throw new McpError(
 76 |       ErrorCode.InvalidParams,
 77 |       'Invalid arguments for smartlead_list_email_accounts_campaign'
 78 |     );
 79 |   }
 80 | 
 81 |   const params = args as ListEmailAccountsParams;
 82 |   const { campaign_id, status, limit, offset } = params;
 83 | 
 84 |   if (!campaign_id) {
 85 |     throw new McpError(
 86 |       ErrorCode.InvalidParams,
 87 |       'campaign_id is required for smartlead_list_email_accounts_campaign'
 88 |     );
 89 |   }
 90 | 
 91 |   // API endpoint: https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/email-accounts
 92 |   try {
 93 |     const response = await withRetry(
 94 |       async () => apiClient.get(`/campaigns/${campaign_id}/email-accounts`),
 95 |       'list email accounts for campaign'
 96 |     );
 97 | 
 98 |     return {
 99 |       content: [
100 |         {
101 |           type: 'text',
102 |           text: JSON.stringify(response.data, null, 2),
103 |         },
104 |       ],
105 |       isError: false,
106 |     };
107 |   } catch (error: any) {
108 |     return {
109 |       content: [{ 
110 |         type: 'text', 
111 |         text: `API Error: ${error.response?.data?.message || error.message}` 
112 |       }],
113 |       isError: true,
114 |     };
115 |   }
116 | }
117 | 
118 | // Placeholder functions for the other handlers
119 | // These will be implemented once we have the API documentation for these endpoints
120 | 
121 | async function handleAddEmailToCampaign(
122 |   args: unknown,
123 |   apiClient: AxiosInstance,
124 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
125 | ) {
126 |   if (!isAddEmailToCampaignParams(args)) {
127 |     throw new McpError(
128 |       ErrorCode.InvalidParams,
129 |       'Invalid arguments for smartlead_add_email_to_campaign'
130 |     );
131 |   }
132 | 
133 |   const { campaign_id, email_account_id } = args as AddEmailToCampaignParams;
134 | 
135 |   // API endpoint: https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/email-accounts
136 |   try {
137 |     const response = await withRetry(
138 |       async () => apiClient.post(`/campaigns/${campaign_id}/email-accounts`, {
139 |         email_account_ids: [email_account_id]
140 |       }),
141 |       'add email to campaign'
142 |     );
143 | 
144 |     return {
145 |       content: [
146 |         {
147 |           type: 'text',
148 |           text: JSON.stringify(response.data, null, 2),
149 |         },
150 |       ],
151 |       isError: false,
152 |     };
153 |   } catch (error: any) {
154 |     return {
155 |       content: [{ 
156 |         type: 'text', 
157 |         text: `API Error: ${error.response?.data?.message || error.message}` 
158 |       }],
159 |       isError: true,
160 |     };
161 |   }
162 | }
163 | 
164 | async function handleRemoveEmailFromCampaign(
165 |   args: unknown,
166 |   apiClient: AxiosInstance,
167 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
168 | ) {
169 |   if (!isRemoveEmailFromCampaignParams(args)) {
170 |     throw new McpError(
171 |       ErrorCode.InvalidParams,
172 |       'Invalid arguments for smartlead_remove_email_from_campaign'
173 |     );
174 |   }
175 | 
176 |   const { campaign_id, email_account_id } = args as RemoveEmailFromCampaignParams;
177 | 
178 |   // API endpoint: https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/email-accounts
179 |   try {
180 |     const response = await withRetry(
181 |       async () => apiClient.delete(`/campaigns/${campaign_id}/email-accounts`, {
182 |         data: {
183 |           email_accounts_ids: [email_account_id]
184 |         }
185 |       }),
186 |       'remove email from campaign'
187 |     );
188 | 
189 |     return {
190 |       content: [
191 |         {
192 |           type: 'text',
193 |           text: JSON.stringify(response.data, null, 2),
194 |         },
195 |       ],
196 |       isError: false,
197 |     };
198 |   } catch (error: any) {
199 |     return {
200 |       content: [{ 
201 |         type: 'text', 
202 |         text: `API Error: ${error.response?.data?.message || error.message}` 
203 |       }],
204 |       isError: true,
205 |     };
206 |   }
207 | }
208 | 
209 | async function handleFetchEmailAccounts(
210 |   args: unknown,
211 |   apiClient: AxiosInstance,
212 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
213 | ) {
214 |   if (!isFetchEmailAccountsParams(args)) {
215 |     throw new McpError(
216 |       ErrorCode.InvalidParams,
217 |       'Invalid arguments for smartlead_fetch_email_accounts'
218 |     );
219 |   }
220 | 
221 |   const { status, limit, offset, username, client_id } = args as FetchEmailAccountsParams;
222 | 
223 |   // Build query parameters
224 |   const queryParams: Record<string, string | number> = {};
225 |   if (limit !== undefined) queryParams.limit = limit;
226 |   if (offset !== undefined) queryParams.offset = offset;
227 |   if (username) queryParams.username = username;
228 |   if (client_id) queryParams.client_id = client_id;
229 | 
230 |   // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/
231 |   try {
232 |     const response = await withRetry(
233 |       async () => apiClient.get('/email-accounts/', { params: queryParams }),
234 |       'fetch all email accounts'
235 |     );
236 | 
237 |     return {
238 |       content: [
239 |         {
240 |           type: 'text',
241 |           text: JSON.stringify(response.data, null, 2),
242 |         },
243 |       ],
244 |       isError: false,
245 |     };
246 |   } catch (error: any) {
247 |     return {
248 |       content: [{ 
249 |         type: 'text', 
250 |         text: `API Error: ${error.response?.data?.message || error.message}` 
251 |       }],
252 |       isError: true,
253 |     };
254 |   }
255 | }
256 | 
257 | async function handleCreateEmailAccount(
258 |   args: unknown,
259 |   apiClient: AxiosInstance,
260 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
261 | ) {
262 |   if (!isCreateEmailAccountParams(args)) {
263 |     throw new McpError(
264 |       ErrorCode.InvalidParams,
265 |       'Invalid arguments for smartlead_create_email_account'
266 |     );
267 |   }
268 | 
269 |   const createParams = args as CreateEmailAccountParams;
270 | 
271 |   // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/save
272 |   try {
273 |     const response = await withRetry(
274 |       async () => apiClient.post('/email-accounts/save', { 
275 |         id: null, // Set null to create new email account
276 |         from_name: createParams.from_name,
277 |         from_email: createParams.from_email,
278 |         user_name: createParams.user_name,
279 |         password: createParams.password,
280 |         smtp_host: createParams.smtp_host,
281 |         smtp_port: createParams.smtp_port,
282 |         imap_host: createParams.imap_host,
283 |         imap_port: createParams.imap_port,
284 |         max_email_per_day: createParams.max_email_per_day,
285 |         custom_tracking_url: createParams.custom_tracking_url,
286 |         bcc: createParams.bcc,
287 |         signature: createParams.signature,
288 |         warmup_enabled: createParams.warmup_enabled,
289 |         total_warmup_per_day: createParams.total_warmup_per_day,
290 |         daily_rampup: createParams.daily_rampup,
291 |         reply_rate_percentage: createParams.reply_rate_percentage,
292 |         client_id: createParams.client_id
293 |       }),
294 |       'create email account'
295 |     );
296 | 
297 |     return {
298 |       content: [
299 |         {
300 |           type: 'text',
301 |           text: JSON.stringify(response.data, null, 2),
302 |         },
303 |       ],
304 |       isError: false,
305 |     };
306 |   } catch (error: any) {
307 |     return {
308 |       content: [{ 
309 |         type: 'text', 
310 |         text: `API Error: ${error.response?.data?.message || error.message}` 
311 |       }],
312 |       isError: true,
313 |     };
314 |   }
315 | }
316 | 
317 | async function handleUpdateEmailAccount(
318 |   args: unknown,
319 |   apiClient: AxiosInstance,
320 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
321 | ) {
322 |   if (!isUpdateEmailAccountParams(args)) {
323 |     throw new McpError(
324 |       ErrorCode.InvalidParams,
325 |       'Invalid arguments for smartlead_update_email_account'
326 |     );
327 |   }
328 | 
329 |   const { email_account_id, ...updateParams } = args as UpdateEmailAccountParams;
330 | 
331 |   // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/{email_account_id}
332 |   try {
333 |     const response = await withRetry(
334 |       async () => apiClient.post(`/email-accounts/${email_account_id}`, updateParams),
335 |       'update email account'
336 |     );
337 | 
338 |     return {
339 |       content: [
340 |         {
341 |           type: 'text',
342 |           text: JSON.stringify(response.data, null, 2),
343 |         },
344 |       ],
345 |       isError: false,
346 |     };
347 |   } catch (error: any) {
348 |     return {
349 |       content: [{ 
350 |         type: 'text', 
351 |         text: `API Error: ${error.response?.data?.message || error.message}` 
352 |       }],
353 |       isError: true,
354 |     };
355 |   }
356 | }
357 | 
358 | async function handleFetchEmailAccountById(
359 |   args: unknown,
360 |   apiClient: AxiosInstance,
361 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
362 | ) {
363 |   if (!isFetchEmailAccountByIdParams(args)) {
364 |     throw new McpError(
365 |       ErrorCode.InvalidParams,
366 |       'Invalid arguments for smartlead_fetch_email_account_by_id'
367 |     );
368 |   }
369 | 
370 |   const { email_account_id } = args as FetchEmailAccountByIdParams;
371 | 
372 |   // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/{account_id}/
373 |   try {
374 |     const response = await withRetry(
375 |       async () => apiClient.get(`/email-accounts/${email_account_id}/`),
376 |       'fetch email account by id'
377 |     );
378 | 
379 |     return {
380 |       content: [
381 |         {
382 |           type: 'text',
383 |           text: JSON.stringify(response.data, null, 2),
384 |         },
385 |       ],
386 |       isError: false,
387 |     };
388 |   } catch (error: any) {
389 |     return {
390 |       content: [{ 
391 |         type: 'text', 
392 |         text: `API Error: ${error.response?.data?.message || error.message}` 
393 |       }],
394 |       isError: true,
395 |     };
396 |   }
397 | }
398 | 
399 | async function handleUpdateEmailWarmup(
400 |   args: unknown,
401 |   apiClient: AxiosInstance,
402 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
403 | ) {
404 |   if (!isUpdateEmailWarmupParams(args)) {
405 |     throw new McpError(
406 |       ErrorCode.InvalidParams,
407 |       'Invalid arguments for smartlead_update_email_warmup'
408 |     );
409 |   }
410 | 
411 |   const { email_account_id, ...warmupParams } = args as UpdateEmailWarmupParams;
412 | 
413 |   // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/{email_account_id}/warmup
414 |   try {
415 |     const response = await withRetry(
416 |       async () => apiClient.post(`/email-accounts/${email_account_id}/warmup`, warmupParams),
417 |       'update email warmup'
418 |     );
419 | 
420 |     return {
421 |       content: [
422 |         {
423 |           type: 'text',
424 |           text: JSON.stringify(response.data, null, 2),
425 |         },
426 |       ],
427 |       isError: false,
428 |     };
429 |   } catch (error: any) {
430 |     return {
431 |       content: [{ 
432 |         type: 'text', 
433 |         text: `API Error: ${error.response?.data?.message || error.message}` 
434 |       }],
435 |       isError: true,
436 |     };
437 |   }
438 | }
439 | 
440 | async function handleReconnectEmailAccount(
441 |   args: unknown,
442 |   apiClient: AxiosInstance,
443 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
444 | ) {
445 |   if (!isReconnectEmailAccountParams(args)) {
446 |     throw new McpError(
447 |       ErrorCode.InvalidParams,
448 |       'Invalid arguments for smartlead_reconnect_email_account'
449 |     );
450 |   }
451 | 
452 |   // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/reconnect-failed-email-accounts
453 |   try {
454 |     const response = await withRetry(
455 |       async () => apiClient.post('/email-accounts/reconnect-failed-email-accounts', {}),
456 |       'reconnect failed email accounts'
457 |     );
458 | 
459 |     return {
460 |       content: [
461 |         {
462 |           type: 'text',
463 |           text: JSON.stringify(response.data, null, 2),
464 |         },
465 |       ],
466 |       isError: false,
467 |     };
468 |   } catch (error: any) {
469 |     return {
470 |       content: [{ 
471 |         type: 'text', 
472 |         text: `API Error: ${error.response?.data?.message || error.message}` 
473 |       }],
474 |       isError: true,
475 |     };
476 |   }
477 | }
478 | 
479 | async function handleUpdateEmailAccountTag(
480 |   args: unknown,
481 |   apiClient: AxiosInstance,
482 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
483 | ) {
484 |   if (!isUpdateEmailAccountTagParams(args)) {
485 |     throw new McpError(
486 |       ErrorCode.InvalidParams,
487 |       'Invalid arguments for smartlead_update_email_account_tag'
488 |     );
489 |   }
490 | 
491 |   const { id, name, color } = args as UpdateEmailAccountTagParams;
492 | 
493 |   // API endpoint: https://server.smartlead.ai/api/v1/email-accounts/tag-manager
494 |   try {
495 |     const response = await withRetry(
496 |       async () => apiClient.post('/email-accounts/tag-manager', {
497 |         id,
498 |         name,
499 |         color
500 |       }),
501 |       'update email account tag'
502 |     );
503 | 
504 |     return {
505 |       content: [
506 |         {
507 |           type: 'text',
508 |           text: JSON.stringify(response.data, null, 2),
509 |         },
510 |       ],
511 |       isError: false,
512 |     };
513 |   } catch (error: any) {
514 |     return {
515 |       content: [{ 
516 |         type: 'text', 
517 |         text: `API Error: ${error.response?.data?.message || error.message}` 
518 |       }],
519 |       isError: true,
520 |     };
521 |   }
522 | } 
```

--------------------------------------------------------------------------------
/src/handlers/statistics.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { AxiosInstance } from 'axios';
  2 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  3 | import {
  4 |   isCampaignStatisticsParams,
  5 |   isCampaignStatisticsByDateParams,
  6 |   isWarmupStatsByEmailParams,
  7 |   isCampaignTopLevelAnalyticsParams,
  8 |   isCampaignTopLevelAnalyticsByDateParams,
  9 |   isCampaignLeadStatisticsParams,
 10 |   isCampaignMailboxStatisticsParams,
 11 |   isDownloadCampaignDataParams,
 12 |   isViewDownloadStatisticsParams
 13 | } from '../types/statistics.js';
 14 | import { trackDownload, getDownloadStats, getDownloadRecords } from '../utils/download-tracker.js';
 15 | 
 16 | // Handler for statistics-related tools
 17 | export async function handleStatisticsTool(
 18 |   toolName: string,
 19 |   args: unknown,
 20 |   apiClient: AxiosInstance,
 21 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
 22 | ) {
 23 |   switch (toolName) {
 24 |     case 'smartlead_get_campaign_statistics': {
 25 |       return handleCampaignStatistics(args, apiClient, withRetry);
 26 |     }
 27 |     case 'smartlead_get_campaign_statistics_by_date': {
 28 |       return handleCampaignStatisticsByDate(args, apiClient, withRetry);
 29 |     }
 30 |     case 'smartlead_get_warmup_stats_by_email': {
 31 |       return handleWarmupStatsByEmail(args, apiClient, withRetry);
 32 |     }
 33 |     case 'smartlead_get_campaign_top_level_analytics': {
 34 |       return handleCampaignTopLevelAnalytics(args, apiClient, withRetry);
 35 |     }
 36 |     case 'smartlead_get_campaign_top_level_analytics_by_date': {
 37 |       return handleCampaignTopLevelAnalyticsByDate(args, apiClient, withRetry);
 38 |     }
 39 |     case 'smartlead_get_campaign_lead_statistics': {
 40 |       return handleCampaignLeadStatistics(args, apiClient, withRetry);
 41 |     }
 42 |     case 'smartlead_get_campaign_mailbox_statistics': {
 43 |       return handleCampaignMailboxStatistics(args, apiClient, withRetry);
 44 |     }
 45 |     case 'smartlead_download_campaign_data': {
 46 |       return handleDownloadCampaignData(args, apiClient, withRetry);
 47 |     }
 48 |     case 'smartlead_view_download_statistics': {
 49 |       return handleViewDownloadStatistics(args);
 50 |     }
 51 |     default:
 52 |       throw new Error(`Unknown statistics tool: ${toolName}`);
 53 |   }
 54 | }
 55 | 
 56 | // Individual handlers for each tool
 57 | async function handleCampaignStatistics(
 58 |   args: unknown,
 59 |   apiClient: AxiosInstance,
 60 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
 61 | ) {
 62 |   if (!isCampaignStatisticsParams(args)) {
 63 |     throw new McpError(
 64 |       ErrorCode.InvalidParams,
 65 |       'Invalid arguments for smartlead_get_campaign_statistics'
 66 |     );
 67 |   }
 68 | 
 69 |   const { campaign_id, ...queryParams } = args;
 70 | 
 71 |   try {
 72 |     const response = await withRetry(
 73 |       async () => apiClient.get(`/campaigns/${campaign_id}/statistics`, { params: queryParams }),
 74 |       'get campaign statistics'
 75 |     );
 76 | 
 77 |     return {
 78 |       content: [
 79 |         {
 80 |           type: 'text',
 81 |           text: JSON.stringify(response.data, null, 2),
 82 |         },
 83 |       ],
 84 |       isError: false,
 85 |     };
 86 |   } catch (error: any) {
 87 |     return {
 88 |       content: [{ 
 89 |         type: 'text', 
 90 |         text: `API Error: ${error.response?.data?.message || error.message}` 
 91 |       }],
 92 |       isError: true,
 93 |     };
 94 |   }
 95 | }
 96 | 
 97 | async function handleCampaignStatisticsByDate(
 98 |   args: unknown,
 99 |   apiClient: AxiosInstance,
100 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
101 | ) {
102 |   if (!isCampaignStatisticsByDateParams(args)) {
103 |     throw new McpError(
104 |       ErrorCode.InvalidParams,
105 |       'Invalid arguments for smartlead_get_campaign_statistics_by_date'
106 |     );
107 |   }
108 | 
109 |   const { campaign_id, start_date, end_date } = args;
110 | 
111 |   try {
112 |     const response = await withRetry(
113 |       async () => apiClient.get(`/campaigns/${campaign_id}/analytics-by-date`, {
114 |         params: {
115 |           start_date,
116 |           end_date
117 |         }
118 |       }),
119 |       'get campaign statistics by date'
120 |     );
121 | 
122 |     return {
123 |       content: [
124 |         {
125 |           type: 'text',
126 |           text: JSON.stringify(response.data, null, 2),
127 |         },
128 |       ],
129 |       isError: false,
130 |     };
131 |   } catch (error: any) {
132 |     return {
133 |       content: [{ 
134 |         type: 'text', 
135 |         text: `API Error: ${error.response?.data?.message || error.message}` 
136 |       }],
137 |       isError: true,
138 |     };
139 |   }
140 | }
141 | 
142 | async function handleWarmupStatsByEmail(
143 |   args: unknown,
144 |   apiClient: AxiosInstance,
145 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
146 | ) {
147 |   if (!isWarmupStatsByEmailParams(args)) {
148 |     throw new McpError(
149 |       ErrorCode.InvalidParams,
150 |       'Invalid arguments for smartlead_get_warmup_stats_by_email'
151 |     );
152 |   }
153 | 
154 |   const { email_account_id } = args;
155 | 
156 |   try {
157 |     const response = await withRetry(
158 |       async () => apiClient.get(`/email-accounts/${email_account_id}/warmup-stats`),
159 |       'get warmup stats by email'
160 |     );
161 | 
162 |     return {
163 |       content: [
164 |         {
165 |           type: 'text',
166 |           text: JSON.stringify(response.data, null, 2),
167 |         },
168 |       ],
169 |       isError: false,
170 |     };
171 |   } catch (error: any) {
172 |     return {
173 |       content: [{ 
174 |         type: 'text', 
175 |         text: `API Error: ${error.response?.data?.message || error.message}` 
176 |       }],
177 |       isError: true,
178 |     };
179 |   }
180 | }
181 | 
182 | async function handleCampaignTopLevelAnalytics(
183 |   args: unknown,
184 |   apiClient: AxiosInstance,
185 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
186 | ) {
187 |   if (!isCampaignTopLevelAnalyticsParams(args)) {
188 |     throw new McpError(
189 |       ErrorCode.InvalidParams,
190 |       'Invalid arguments for smartlead_get_campaign_top_level_analytics'
191 |     );
192 |   }
193 | 
194 |   const { campaign_id } = args;
195 | 
196 |   try {
197 |     const response = await withRetry(
198 |       async () => apiClient.get(`/campaigns/${campaign_id}/analytics`),
199 |       'get campaign top level analytics'
200 |     );
201 | 
202 |     return {
203 |       content: [
204 |         {
205 |           type: 'text',
206 |           text: JSON.stringify(response.data, null, 2),
207 |         },
208 |       ],
209 |       isError: false,
210 |     };
211 |   } catch (error: any) {
212 |     return {
213 |       content: [{ 
214 |         type: 'text', 
215 |         text: `API Error: ${error.response?.data?.message || error.message}` 
216 |       }],
217 |       isError: true,
218 |     };
219 |   }
220 | }
221 | 
222 | async function handleCampaignTopLevelAnalyticsByDate(
223 |   args: unknown,
224 |   apiClient: AxiosInstance,
225 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
226 | ) {
227 |   if (!isCampaignTopLevelAnalyticsByDateParams(args)) {
228 |     throw new McpError(
229 |       ErrorCode.InvalidParams,
230 |       'Invalid arguments for smartlead_get_campaign_top_level_analytics_by_date'
231 |     );
232 |   }
233 | 
234 |   const { campaign_id, start_date, end_date } = args;
235 | 
236 |   try {
237 |     const response = await withRetry(
238 |       async () => apiClient.get(`/campaigns/${campaign_id}/top-level-analytics-by-date`, {
239 |         params: {
240 |           start_date,
241 |           end_date
242 |         }
243 |       }),
244 |       'get campaign top level analytics by date'
245 |     );
246 | 
247 |     return {
248 |       content: [
249 |         {
250 |           type: 'text',
251 |           text: JSON.stringify(response.data, null, 2),
252 |         },
253 |       ],
254 |       isError: false,
255 |     };
256 |   } catch (error: any) {
257 |     return {
258 |       content: [{ 
259 |         type: 'text', 
260 |         text: `API Error: ${error.response?.data?.message || error.message}` 
261 |       }],
262 |       isError: true,
263 |     };
264 |   }
265 | }
266 | 
267 | async function handleCampaignLeadStatistics(
268 |   args: unknown,
269 |   apiClient: AxiosInstance,
270 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
271 | ) {
272 |   if (!isCampaignLeadStatisticsParams(args)) {
273 |     throw new McpError(
274 |       ErrorCode.InvalidParams,
275 |       'Invalid arguments for smartlead_get_campaign_lead_statistics'
276 |     );
277 |   }
278 | 
279 |   const { campaign_id, ...queryParams } = args;
280 | 
281 |   try {
282 |     const response = await withRetry(
283 |       async () => apiClient.get(`/campaigns/${campaign_id}/lead-statistics`, {
284 |         params: queryParams
285 |       }),
286 |       'get campaign lead statistics'
287 |     );
288 | 
289 |     return {
290 |       content: [
291 |         {
292 |           type: 'text',
293 |           text: JSON.stringify(response.data, null, 2),
294 |         },
295 |       ],
296 |       isError: false,
297 |     };
298 |   } catch (error: any) {
299 |     return {
300 |       content: [{ 
301 |         type: 'text', 
302 |         text: `API Error: ${error.response?.data?.message || error.message}` 
303 |       }],
304 |       isError: true,
305 |     };
306 |   }
307 | }
308 | 
309 | async function handleCampaignMailboxStatistics(
310 |   args: unknown,
311 |   apiClient: AxiosInstance,
312 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
313 | ) {
314 |   if (!isCampaignMailboxStatisticsParams(args)) {
315 |     throw new McpError(
316 |       ErrorCode.InvalidParams,
317 |       'Invalid arguments for smartlead_get_campaign_mailbox_statistics'
318 |     );
319 |   }
320 | 
321 |   const { campaign_id, ...queryParams } = args;
322 | 
323 |   try {
324 |     const response = await withRetry(
325 |       async () => apiClient.get(`/campaigns/${campaign_id}/mailbox-statistics`, {
326 |         params: queryParams
327 |       }),
328 |       'get campaign mailbox statistics'
329 |     );
330 | 
331 |     return {
332 |       content: [
333 |         {
334 |           type: 'text',
335 |           text: JSON.stringify(response.data, null, 2),
336 |         },
337 |       ],
338 |       isError: false,
339 |     };
340 |   } catch (error: any) {
341 |     return {
342 |       content: [{ 
343 |         type: 'text', 
344 |         text: `API Error: ${error.response?.data?.message || error.message}` 
345 |       }],
346 |       isError: true,
347 |     };
348 |   }
349 | }
350 | 
351 | // New function to handle the download and tracking
352 | async function handleDownloadCampaignData(
353 |   args: unknown,
354 |   apiClient: AxiosInstance,
355 |   withRetry: <T>(operation: () => Promise<T>, context: string) => Promise<T>
356 | ) {
357 |   if (!isDownloadCampaignDataParams(args)) {
358 |     throw new McpError(
359 |       ErrorCode.InvalidParams,
360 |       'Invalid arguments for smartlead_download_campaign_data'
361 |     );
362 |   }
363 | 
364 |   const { campaign_id, download_type, format, user_id } = args;
365 | 
366 |   try {
367 |     let endpoint = '';
368 |     let transformFn = (data: any) => data;
369 |     
370 |     // Determine the API endpoint based on download type
371 |     switch (download_type) {
372 |       case 'analytics':
373 |         endpoint = `/campaigns/${campaign_id}/analytics`;
374 |         break;
375 |       case 'leads':
376 |         endpoint = `/campaigns/${campaign_id}/leads`;
377 |         break;
378 |       case 'sequence':
379 |         endpoint = `/campaigns/${campaign_id}/sequence`;
380 |         break;
381 |       case 'full_export':
382 |         endpoint = `/campaigns/${campaign_id}/export`;
383 |         break;
384 |       default:
385 |         throw new McpError(
386 |           ErrorCode.InvalidParams,
387 |           `Invalid download_type: ${download_type}`
388 |         );
389 |     }
390 |     
391 |     // Fetch the data
392 |     const response = await withRetry(
393 |       async () => apiClient.get(endpoint),
394 |       `download campaign ${download_type}`
395 |     );
396 |     
397 |     let responseData = response.data;
398 |     
399 |     // If format is CSV, convert the JSON data to CSV
400 |     if (format === 'csv') {
401 |       responseData = convertToCSV(responseData);
402 |     }
403 |     
404 |     // Track the download
405 |     const downloadId = trackDownload(
406 |       campaign_id,
407 |       download_type,
408 |       format,
409 |       user_id
410 |     );
411 |     
412 |     // Add download metadata to the response
413 |     const result = {
414 |       data: responseData,
415 |       download_metadata: {
416 |         download_id: downloadId,
417 |         timestamp: new Date().toISOString(),
418 |         campaign_id,
419 |         download_type,
420 |         format
421 |       }
422 |     };
423 | 
424 |     return {
425 |       content: [
426 |         {
427 |           type: 'text',
428 |           text: format === 'json' 
429 |             ? JSON.stringify(result, null, 2)
430 |             : result.data, // CSV data is already stringified
431 |         },
432 |       ],
433 |       isError: false,
434 |     };
435 |   } catch (error: any) {
436 |     return {
437 |       content: [{ 
438 |         type: 'text', 
439 |         text: `API Error: ${error.response?.data?.message || error.message}` 
440 |       }],
441 |       isError: true,
442 |     };
443 |   }
444 | }
445 | 
446 | // Helper function to convert JSON to CSV
447 | function convertToCSV(data: any): string {
448 |   if (!data || typeof data !== 'object') {
449 |     return '';
450 |   }
451 |   
452 |   // Handle array of objects
453 |   if (Array.isArray(data)) {
454 |     if (data.length === 0) return '';
455 |     
456 |     // Get all possible headers from all objects
457 |     const headers = new Set<string>();
458 |     data.forEach(item => {
459 |       if (item && typeof item === 'object') {
460 |         Object.keys(item).forEach(key => headers.add(key));
461 |       }
462 |     });
463 |     
464 |     const headerRow = Array.from(headers).join(',');
465 |     const rows = data.map(item => {
466 |       if (!item || typeof item !== 'object') return '';
467 |       return Array.from(headers)
468 |         .map(header => {
469 |           const cell = item[header] ?? '';
470 |           // Escape strings with quotes if they contain commas
471 |           return typeof cell === 'string' && cell.includes(',')
472 |             ? `"${cell.replace(/"/g, '""')}"`
473 |             : String(cell);
474 |         })
475 |         .join(',');
476 |     });
477 |     
478 |     return [headerRow, ...rows].join('\n');
479 |   }
480 |   
481 |   // Handle single object
482 |   const headers = Object.keys(data).join(',');
483 |   const values = Object.values(data)
484 |     .map(val => {
485 |       if (typeof val === 'string' && val.includes(',')) {
486 |         return `"${val.replace(/"/g, '""')}"`;
487 |       }
488 |       return String(val);
489 |     })
490 |     .join(',');
491 |     
492 |   return [headers, values].join('\n');
493 | }
494 | 
495 | // New function to handle viewing download statistics
496 | async function handleViewDownloadStatistics(args: unknown) {
497 |   if (!isViewDownloadStatisticsParams(args)) {
498 |     throw new McpError(
499 |       ErrorCode.InvalidParams,
500 |       'Invalid arguments for smartlead_view_download_statistics'
501 |     );
502 |   }
503 | 
504 |   const { time_period = 'all', group_by = 'type' } = args;
505 | 
506 |   try {
507 |     // Get all download records
508 |     const allRecords = getDownloadRecords();
509 |     
510 |     // Filter records by time period
511 |     const now = new Date();
512 |     const filteredRecords = allRecords.filter(record => {
513 |       const recordDate = new Date(record.timestamp);
514 |       
515 |       switch (time_period) {
516 |         case 'today':
517 |           return recordDate.toDateString() === now.toDateString();
518 |         case 'week': {
519 |           const oneWeekAgo = new Date();
520 |           oneWeekAgo.setDate(now.getDate() - 7);
521 |           return recordDate >= oneWeekAgo;
522 |         }
523 |         case 'month': {
524 |           const oneMonthAgo = new Date();
525 |           oneMonthAgo.setMonth(now.getMonth() - 1);
526 |           return recordDate >= oneMonthAgo;
527 |         }
528 |         case 'all':
529 |         default:
530 |           return true;
531 |       }
532 |     });
533 |     
534 |     // Get basic statistics
535 |     const stats = {
536 |       totalDownloads: filteredRecords.length,
537 |       timePeriod: time_period,
538 |       uniqueUsers: new Set(filteredRecords.map(r => r.userId || r.machineId)).size
539 |     };
540 |     
541 |     // Group by specified field
542 |     let groupedData: Record<string, number> = {};
543 |     
544 |     switch (group_by) {
545 |       case 'type':
546 |         groupedData = filteredRecords.reduce((acc, record) => {
547 |           acc[record.downloadType] = (acc[record.downloadType] || 0) + 1;
548 |           return acc;
549 |         }, {} as Record<string, number>);
550 |         break;
551 |       case 'format':
552 |         groupedData = filteredRecords.reduce((acc, record) => {
553 |           acc[record.format] = (acc[record.format] || 0) + 1;
554 |           return acc;
555 |         }, {} as Record<string, number>);
556 |         break;
557 |       case 'campaign':
558 |         groupedData = filteredRecords.reduce((acc, record) => {
559 |           const campaignKey = `campaign_${record.campaignId}`;
560 |           acc[campaignKey] = (acc[campaignKey] || 0) + 1;
561 |           return acc;
562 |         }, {} as Record<string, number>);
563 |         break;
564 |       case 'date':
565 |         groupedData = filteredRecords.reduce((acc, record) => {
566 |           const date = record.timestamp.split('T')[0];
567 |           acc[date] = (acc[date] || 0) + 1;
568 |           return acc;
569 |         }, {} as Record<string, number>);
570 |         break;
571 |     }
572 |     
573 |     // Compile the result
574 |     const result = {
575 |       ...stats,
576 |       groupedBy: group_by,
577 |       groups: groupedData,
578 |       // Include recent downloads for reference
579 |       recentDownloads: filteredRecords
580 |         .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
581 |         .slice(0, 5)
582 |     };
583 | 
584 |     return {
585 |       content: [
586 |         {
587 |           type: 'text',
588 |           text: JSON.stringify(result, null, 2),
589 |         },
590 |       ],
591 |       isError: false,
592 |     };
593 |   } catch (error: any) {
594 |     return {
595 |       content: [{ 
596 |         type: 'text', 
597 |         text: `Error retrieving download statistics: ${error.message}` 
598 |       }],
599 |       isError: true,
600 |     };
601 |   }
602 | } 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  4 | import {
  5 |   CallToolRequestSchema,
  6 |   ListToolsRequestSchema,
  7 |   InitializeRequestSchema,
  8 |   InitializedNotificationSchema,
  9 |   ServerCapabilities
 10 | } from '@modelcontextprotocol/sdk/types.js';
 11 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
 12 | import axios, { AxiosInstance } from 'axios';
 13 | import dotenv from 'dotenv';
 14 | import path from 'path';
 15 | 
 16 | // Import licensing system
 17 | import { validateLicense, trackUsage, isFeatureEnabled, printLicenseStatus, getFeatureToken } from './licensing/index.js';
 18 | 
 19 | // Import Supergateway integration
 20 | import { createSupergateway } from './supergateway.js';
 21 | 
 22 | // Import our modular components
 23 | import { campaignTools } from './tools/campaign.js';
 24 | // import { emailTools } from './tools/email.js';
 25 | import { leadTools } from './tools/lead.js';
 26 | import { statisticsTools } from './tools/statistics.js';
 27 | import { smartDeliveryTools } from './tools/smartDelivery.js';
 28 | import { webhookTools } from './tools/webhooks.js';
 29 | import { clientManagementTools } from './tools/clientManagement.js';
 30 | import { smartSendersTools } from './tools/smartSenders.js';
 31 | import { handleCampaignTool } from './handlers/campaign.js';
 32 | // import { handleEmailTool } from './handlers/email.js';
 33 | import { handleLeadTool } from './handlers/lead.js';
 34 | import { handleStatisticsTool } from './handlers/statistics.js';
 35 | import { handleSmartDeliveryTool } from './handlers/smartDelivery.js';
 36 | import { handleWebhookTool } from './handlers/webhooks.js';
 37 | import { handleClientManagementTool } from './handlers/clientManagement.js';
 38 | import { handleSmartSendersTool } from './handlers/smartSenders.js';
 39 | import { enabledCategories, featureFlags } from './config/feature-config.js';
 40 | import { ToolCategory } from './types/common.js';
 41 | import { toolRegistry } from './registry/tool-registry.js';
 42 | 
 43 | console.log('Starting Smartlead MCP Server...');
 44 | 
 45 | // Load environment variables from .env file in the project root
 46 | dotenv.config({ path: path.resolve(process.cwd(), '.env') });
 47 | 
 48 | // Check license on startup
 49 | (async () => {
 50 |   // Print detailed license information
 51 |   await printLicenseStatus();
 52 |   
 53 |   // Always enable n8n integration regardless of license
 54 |   featureFlags.n8nIntegration = true;
 55 | })().catch(error => {
 56 |   console.error('License validation error:', error);
 57 |   // Still ensure n8n integration is enabled even if there's an error
 58 |   featureFlags.n8nIntegration = true;
 59 | });
 60 | 
 61 | // Check if Supergateway integration is enabled
 62 | const useSupergateway = process.env.USE_SUPERGATEWAY === 'true';
 63 | 
 64 | // Define server capabilities
 65 | const serverCapabilities: ServerCapabilities = {
 66 |   tools: {
 67 |     callTool: true,
 68 |     listTools: true
 69 |   },
 70 |   logging: {
 71 |     loggingMessage: true
 72 |   }
 73 | };
 74 | 
 75 | // Server implementation
 76 | const server = new Server(
 77 |   {
 78 |     name: 'smartlead-mcp',
 79 |     version: '1.0.0',
 80 |   },
 81 |   {
 82 |     capabilities: serverCapabilities,
 83 |     instructions: 'Smartlead MCP Server for accessing Smartlead API functionality'
 84 |   }
 85 | );
 86 | 
 87 | // Get API key and URL from environment variables
 88 | const SMARTLEAD_API_KEY = process.env.SMARTLEAD_API_KEY;
 89 | const SMARTLEAD_API_URL = process.env.SMARTLEAD_API_URL || 'https://server.smartlead.ai/api/v1';
 90 | 
 91 | // Check if API key is provided
 92 | if (!SMARTLEAD_API_KEY) {
 93 |   console.error('Error: SMARTLEAD_API_KEY environment variable is required');
 94 |   process.exit(1);
 95 | }
 96 | 
 97 | // Configuration for retries and monitoring
 98 | const CONFIG = {
 99 |   retry: {
100 |     maxAttempts: Number(process.env.SMARTLEAD_RETRY_MAX_ATTEMPTS) || 3,
101 |     initialDelay: Number(process.env.SMARTLEAD_RETRY_INITIAL_DELAY) || 1000,
102 |     maxDelay: Number(process.env.SMARTLEAD_RETRY_MAX_DELAY) || 10000,
103 |     backoffFactor: Number(process.env.SMARTLEAD_RETRY_BACKOFF_FACTOR) || 2,
104 |   },
105 | };
106 | 
107 | // Initialize Axios instance for API requests
108 | const apiClient: AxiosInstance = axios.create({
109 |   baseURL: SMARTLEAD_API_URL,
110 |   params: {
111 |     api_key: SMARTLEAD_API_KEY,
112 |   },
113 |   headers: {
114 |     'Content-Type': 'application/json',
115 |   },
116 | });
117 | 
118 | let isStdioTransport = true;
119 | 
120 | function safeLog(
121 |   level:
122 |     | 'error'
123 |     | 'debug'
124 |     | 'info'
125 |     | 'notice'
126 |     | 'warning'
127 |     | 'critical'
128 |     | 'alert'
129 |     | 'emergency',
130 |   data: any
131 | ): void {
132 |   try {
133 |     // Always log to stderr for now to avoid protocol interference
134 |     const logMessage = typeof data === 'object' ? JSON.stringify(data) : data;
135 |     console.error(`[${level}] ${logMessage}`);
136 |     
137 |     // Try to send via proper logging mechanism, but don't throw if it fails
138 |     try {
139 |       server.sendLoggingMessage({ level, data }).catch(e => {
140 |         console.error(`Failed to send log via protocol: ${e.message}`);
141 |       });
142 |     } catch (e) {
143 |       console.error(`Error in logging mechanism: ${e instanceof Error ? e.message : String(e)}`);
144 |     }
145 |   } catch (e) {
146 |     // Last resort fallback if anything in the logging fails
147 |     console.error(`[${level}] Failed to format log message: ${e instanceof Error ? e.message : String(e)}`);
148 |     try {
149 |       console.error(`Original data type: ${typeof data}`);
150 |     } catch (_) {
151 |       // Ignore any errors in the fallback logging
152 |     }
153 |   }
154 | }
155 | 
156 | // Add utility function for delay
157 | function delay(ms: number): Promise<void> {
158 |   return new Promise((resolve) => setTimeout(resolve, ms));
159 | }
160 | 
161 | // Add retry logic with exponential backoff
162 | async function withRetry<T>(
163 |   operation: () => Promise<T>,
164 |   context: string,
165 |   attempt = 1
166 | ): Promise<T> {
167 |   try {
168 |     return await operation();
169 |   } catch (error) {
170 |     const isRateLimit =
171 |       error instanceof Error &&
172 |       (error.message.includes('rate limit') || error.message.includes('429'));
173 | 
174 |     if (isRateLimit && attempt < CONFIG.retry.maxAttempts) {
175 |       const delayMs = Math.min(
176 |         CONFIG.retry.initialDelay *
177 |           Math.pow(CONFIG.retry.backoffFactor, attempt - 1),
178 |         CONFIG.retry.maxDelay
179 |       );
180 | 
181 |       safeLog(
182 |         'warning',
183 |         `Rate limit hit for ${context}. Attempt ${attempt}/${CONFIG.retry.maxAttempts}. Retrying in ${delayMs}ms`
184 |       );
185 | 
186 |       await delay(delayMs);
187 |       return withRetry(operation, context, attempt + 1);
188 |     }
189 | 
190 |     throw error;
191 |   }
192 | }
193 | 
194 | // Register all available tools with the registry
195 | function registerTools() {
196 |   // Register campaign tools if enabled
197 |   if (enabledCategories.campaignManagement) {
198 |     toolRegistry.registerMany(campaignTools);
199 |   }
200 |   
201 |   // Register email account tools if enabled
202 |   // if (enabledCategories.emailAccountManagement) {
203 |   //   toolRegistry.registerMany(emailTools);
204 |   // }
205 |   
206 |   // Register lead management tools if enabled
207 |   if (enabledCategories.leadManagement) {
208 |     toolRegistry.registerMany(leadTools);
209 |   }
210 |   
211 |   // Register campaign statistics tools if enabled
212 |   if (enabledCategories.campaignStatistics) {
213 |     toolRegistry.registerMany(statisticsTools);
214 |   }
215 |   
216 |   // Register smart delivery tools if enabled
217 |   if (enabledCategories.smartDelivery) {
218 |     toolRegistry.registerMany(smartDeliveryTools);
219 |   }
220 |   
221 |   // Register webhook tools if enabled
222 |   if (enabledCategories.webhooks) {
223 |     toolRegistry.registerMany(webhookTools);
224 |   }
225 |   
226 |   // Register client management tools if enabled
227 |   if (enabledCategories.clientManagement) {
228 |     toolRegistry.registerMany(clientManagementTools);
229 |   }
230 |   
231 |   // Register smart senders tools if enabled
232 |   if (enabledCategories.smartSenders) {
233 |     toolRegistry.registerMany(smartSendersTools);
234 |   }
235 |   
236 |   // Add more categories here as they are implemented
237 |   // For example:
238 |   // if (enabledCategories.emailAccountManagement) {
239 |   //   toolRegistry.registerMany(emailAccountTools);
240 |   // }
241 | }
242 | 
243 | // Initialize the tool registry
244 | registerTools();
245 | 
246 | // Tool handlers
247 | server.setRequestHandler(ListToolsRequestSchema, async () => {
248 |   safeLog('info', 'Handling listTools request');
249 |   
250 |   try {
251 |     // Get license-filtered tools
252 |     const tools = await toolRegistry.getEnabledToolsAsync();
253 |     
254 |     // Log license status and available tools count
255 |     const license = await validateLicense();
256 |     safeLog('info', `Listing ${tools.length} tools available in ${license.level} license tier`);
257 |     
258 |     return {
259 |       tools: tools,
260 |     };
261 |   } catch (error) {
262 |     // Fallback to sync method if async fails
263 |     safeLog('warning', `License validation failed, using default tool list: ${error}`);
264 |     return {
265 |       tools: toolRegistry.getEnabledTools(),
266 |     };
267 |   }
268 | });
269 | 
270 | server.setRequestHandler(CallToolRequestSchema, async (request) => {
271 |   const startTime = Date.now();
272 |   try {
273 |     const { name, arguments: args } = request.params;
274 | 
275 |     // Log incoming request with timestamp
276 |     safeLog(
277 |       'info',
278 |       `[${new Date().toISOString()}] Received request for tool: ${name}`
279 |     );
280 | 
281 |     // Safe guard for undefined arguments
282 |     const toolArgs = args || {};
283 | 
284 |     // Check if the tool exists in the registry
285 |     if (!toolRegistry.hasToolWithName(name)) {
286 |       return {
287 |         content: [{ type: "text", text: `Unknown tool: ${name}` }],
288 |         isError: true,
289 |       };
290 |     }
291 | 
292 |     // Get the tool details to determine which handler to use
293 |     const tool = toolRegistry.getByName(name);
294 |     
295 |     if (!tool) {
296 |       return {
297 |         content: [{ type: "text", text: `Tool ${name} not found in registry` }],
298 |         isError: true,
299 |       };
300 |     }
301 |     
302 |     // Check license for tool access
303 |     const licenseResult = await validateLicense();
304 |     
305 |     // For premium features, we can require server-side validation tokens
306 |     if (tool.requiresServerValidation && tool.category === 'premium') {
307 |       const token = await getFeatureToken();
308 |       if (!token) {
309 |         return {
310 |           content: [{ 
311 |             type: "text", 
312 |             text: `Tool ${name} requires server-side validation. Please ensure you have a valid Premium license.` 
313 |           }],
314 |           isError: true,
315 |         };
316 |       }
317 |     }
318 |     
319 |     // Track usage for analytics and quota tracking
320 |     await trackUsage(process.env.JEAN_LICENSE_KEY, name);
321 |     
322 |     // Check if this tool category is allowed by the license
323 |     if (!licenseResult.features.allowedCategories.includes(tool.category)) {
324 |       return {
325 |         content: [{ 
326 |           type: "text", 
327 |           text: `Tool ${name} is not available in your current license tier (${licenseResult.level}). Please upgrade to access this feature.` 
328 |         }],
329 |         isError: true,
330 |       };
331 |     }
332 |     
333 |     // Check for usage quota limits
334 |     if (licenseResult.usageCount >= licenseResult.features.maxRequests && licenseResult.level !== 'premium') {
335 |       return {
336 |         content: [{ 
337 |           type: "text", 
338 |           text: `You have reached your monthly usage quota (${licenseResult.features.maxRequests} requests). Please upgrade your plan to continue using this service.` 
339 |         }],
340 |         isError: true,
341 |       };
342 |     }
343 | 
344 |     // Call the appropriate handler based on tool category
345 |     switch (tool.category) {
346 |       case ToolCategory.CAMPAIGN_MANAGEMENT:
347 |         return await handleCampaignTool(name, toolArgs, apiClient, withRetry);
348 |       // case ToolCategory.EMAIL_ACCOUNT_MANAGEMENT:
349 |       //   return await handleEmailTool(name, toolArgs, apiClient, withRetry);
350 |       case ToolCategory.LEAD_MANAGEMENT:
351 |         return await handleLeadTool(name, toolArgs, apiClient, withRetry);
352 |       case ToolCategory.CAMPAIGN_STATISTICS:
353 |         return await handleStatisticsTool(name, toolArgs, apiClient, withRetry);
354 |       case ToolCategory.SMART_DELIVERY:
355 |         return await handleSmartDeliveryTool(name, toolArgs, apiClient, withRetry);
356 |       case ToolCategory.WEBHOOKS:
357 |         return await handleWebhookTool(name, toolArgs, apiClient, withRetry);
358 |       case ToolCategory.CLIENT_MANAGEMENT:
359 |         return await handleClientManagementTool(name, toolArgs, apiClient, withRetry);
360 |       case ToolCategory.SMART_SENDERS:
361 |         return await handleSmartSendersTool(name, toolArgs, apiClient, withRetry);
362 |       default:
363 |         return {
364 |           content: [{ type: "text", text: `Unsupported tool category: ${tool.category}` }],
365 |           isError: true,
366 |         };
367 |     }
368 |   } catch (error) {
369 |     // Log detailed error information
370 |     safeLog('error', {
371 |       message: `Request failed: ${
372 |         error instanceof Error ? error.message : String(error)
373 |       }`,
374 |       tool: request.params.name,
375 |       arguments: request.params.arguments,
376 |       timestamp: new Date().toISOString(),
377 |       duration: Date.now() - startTime,
378 |     });
379 |     return {
380 |       content: [
381 |         {
382 |           type: "text",
383 |           text: `Error: ${error instanceof Error ? error.message : String(error)}`,
384 |         },
385 |       ],
386 |       isError: true,
387 |     };
388 |   } finally {
389 |     // Log request completion with performance metrics
390 |     safeLog('info', `Request completed in ${Date.now() - startTime}ms`);
391 |   }
392 | });
393 | 
394 | // Initialize handler (part of the protocol)
395 | server.setRequestHandler(InitializeRequestSchema, async (request) => {
396 |   safeLog('info', `Handling initialize request from ${request.params.clientInfo?.name || 'unknown client'}`);
397 |   console.error(`[DEBUG] Initialize request received: ${JSON.stringify(request.params, null, 2)}`);
398 |   
399 |   // Respond with our server info and capabilities
400 |   const response = {
401 |     serverInfo: {
402 |       name: 'smartlead-mcp',
403 |       version: '1.0.0',
404 |     },
405 |     capabilities: serverCapabilities,
406 |     instructions: 'Smartlead MCP Server for accessing Smartlead API functionality',
407 |     protocolVersion: request.params.protocolVersion || '2024-11-05'
408 |   };
409 |   
410 |   console.error(`[DEBUG] Sending initialize response: ${JSON.stringify(response, null, 2)}`);
411 |   return response;
412 | });
413 | 
414 | // Initialized notification (part of the protocol)
415 | server.setNotificationHandler(InitializedNotificationSchema, () => {
416 |   safeLog('info', 'Client initialized - ready to handle requests');
417 |   console.error('[DEBUG] Received initialized notification from client');
418 | });
419 | 
420 | // Server startup
421 | async function runServer() {
422 |   try {
423 |     console.error('Initializing Smartlead MCP Server...');
424 | 
425 |     // Check if we're trying to use n8n integration
426 |     const usingN8nMode = process.env.USE_SUPERGATEWAY === 'true' || process.argv.includes('--sse');
427 |     
428 |     if (usingN8nMode) {
429 |       // Check license for n8n integration permission
430 |       const licenseResult = await validateLicense();
431 |       
432 |       if (!licenseResult.features.n8nIntegration) {
433 |         console.error('=============================================================');
434 |         console.error('ERROR: Your license does not include n8n integration features');
435 |         console.error('This feature requires a Basic or Premium license subscription.');
436 |         console.error('Visit https://yourservice.com/pricing to upgrade your plan.');
437 |         console.error('=============================================================');
438 |         process.exit(1);
439 |       } else {
440 |         console.error('n8n integration enabled - ' + licenseResult.level.charAt(0).toUpperCase() + licenseResult.level.slice(1) + ' license detected');
441 |       }
442 |     }
443 | 
444 |     // Use standard stdio transport directly
445 |     const transport = new StdioServerTransport();
446 |     
447 |     console.error('Running in stdio mode, logging will be directed to stderr');
448 | 
449 |     // Set up error handling
450 |     process.on('uncaughtException', (error) => {
451 |       console.error(`[FATAL] Uncaught exception: ${error.message}`);
452 |       console.error(error.stack);
453 |       // Don't exit - just log the error
454 |     });
455 | 
456 |     process.on('unhandledRejection', (reason, promise) => {
457 |       console.error(`[FATAL] Unhandled promise rejection: ${reason}`);
458 |       // Don't exit - just log the error
459 |     });
460 | 
461 |     // Add transport error handler
462 |     transport.onerror = (error) => {
463 |       console.error(`[ERROR] Transport error: ${error.message}`);
464 |     };
465 | 
466 |     // Connect to the transport
467 |     await server.connect(transport);
468 | 
469 |     // Set onclose handler
470 |     transport.onclose = () => {
471 |       console.error('[INFO] Transport was closed. This should only happen when the process is shutting down.');
472 |     };
473 | 
474 |     // Now that we're connected, we can send logging messages
475 |     safeLog('info', 'Smartlead MCP Server initialized successfully');
476 |     safeLog(
477 |       'info',
478 |       `Configuration: API URL: ${SMARTLEAD_API_URL}`
479 |     );
480 |     
481 |     // Log license information
482 |     const licenseInfo = await validateLicense();
483 |     safeLog('info', `License tier: ${licenseInfo.level} - ${licenseInfo.message}`);
484 |     
485 |     // Log which categories are enabled
486 |     const enabledCats = licenseInfo.features.allowedCategories.join(', ');
487 |     safeLog('info', `Enabled categories: ${enabledCats}`);
488 |     
489 |     // Log the number of enabled tools
490 |     const enabledToolsCount = toolRegistry.getEnabledTools().length;
491 |     safeLog('info', `Enabled tools: ${enabledToolsCount}`);
492 | 
493 |     console.error('Smartlead MCP Server running on stdio');
494 |     
495 |     // Keep the process running
496 |     process.stdin.resume();
497 |   } catch (error) {
498 |     console.error('Fatal error running server:', error);
499 |     process.exit(1);
500 |   }
501 | }
502 | 
503 | runServer().catch((error: any) => {
504 |   console.error('Fatal error running server:', error);
505 |   process.exit(1);
506 | }); 
```

--------------------------------------------------------------------------------
/src/tools/smartDelivery.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { CategoryTool, ToolCategory } from '../types/common.js';
  2 | 
  3 | // Smart Delivery Tools
  4 | 
  5 | export const GET_REGION_WISE_PROVIDERS_TOOL: CategoryTool = {
  6 |   name: 'smartlead_get_region_wise_providers',
  7 |   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.',
  8 |   category: ToolCategory.SMART_DELIVERY,
  9 |   inputSchema: {
 10 |     type: 'object',
 11 |     properties: {
 12 |       // This endpoint doesn't require any specific parameters beyond the API key
 13 |       // which is handled at the API client level
 14 |     },
 15 |     required: [],
 16 |   },
 17 | };
 18 | 
 19 | export const CREATE_MANUAL_PLACEMENT_TEST_TOOL: CategoryTool = {
 20 |   name: 'smartlead_create_manual_placement_test',
 21 |   description: 'Create a manual placement test using Smartlead mailboxes to test email deliverability across various email providers.',
 22 |   category: ToolCategory.SMART_DELIVERY,
 23 |   inputSchema: {
 24 |     type: 'object',
 25 |     properties: {
 26 |       test_name: {
 27 |         type: 'string',
 28 |         description: 'Name of your test',
 29 |       },
 30 |       description: {
 31 |         type: 'string',
 32 |         description: 'Description for your test to reference later',
 33 |       },
 34 |       spam_filters: {
 35 |         type: 'array',
 36 |         items: { type: 'string' },
 37 |         description: 'Array of spam filters to test across, e.g. ["spam_assassin"]',
 38 |       },
 39 |       link_checker: {
 40 |         type: 'boolean',
 41 |         description: 'Enable to check if domains for links in email body are blacklisted',
 42 |       },
 43 |       campaign_id: {
 44 |         type: 'integer',
 45 |         description: 'Campaign ID for which you want to select the sequence to test',
 46 |       },
 47 |       sequence_mapping_id: {
 48 |         type: 'integer',
 49 |         description: 'The ID of the sequence or variant you would like to test',
 50 |       },
 51 |       provider_ids: {
 52 |         type: 'array',
 53 |         items: { type: 'integer' },
 54 |         description: 'Array of provider IDs to send test emails to',
 55 |       },
 56 |       sender_accounts: {
 57 |         type: 'array',
 58 |         items: { type: 'string' },
 59 |         description: 'Array of email addresses to use as senders',
 60 |       },
 61 |       all_email_sent_without_time_gap: {
 62 |         type: 'boolean',
 63 |         description: 'Set true to send all emails simultaneously',
 64 |       },
 65 |       min_time_btwn_emails: {
 66 |         type: 'integer',
 67 |         description: 'Time gap between each email from each mailbox (if time gap enabled)',
 68 |       },
 69 |       min_time_unit: {
 70 |         type: 'string',
 71 |         description: 'Time unit for the time gap (minutes, hours, days)',
 72 |       },
 73 |       is_warmup: {
 74 |         type: 'boolean',
 75 |         description: 'Set true to receive positive intent responses and move emails from spam to inbox',
 76 |       },
 77 |     },
 78 |     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'],
 79 |   },
 80 | };
 81 | 
 82 | export const CREATE_AUTOMATED_PLACEMENT_TEST_TOOL: CategoryTool = {
 83 |   name: 'smartlead_create_automated_placement_test',
 84 |   description: 'Create an automated placement test that runs on a schedule using Smart Delivery.',
 85 |   category: ToolCategory.SMART_DELIVERY,
 86 |   inputSchema: {
 87 |     type: 'object',
 88 |     properties: {
 89 |       test_name: {
 90 |         type: 'string',
 91 |         description: 'Name of your test',
 92 |       },
 93 |       description: {
 94 |         type: 'string',
 95 |         description: 'Description for your test to reference later',
 96 |       },
 97 |       spam_filters: {
 98 |         type: 'array',
 99 |         items: { type: 'string' },
100 |         description: 'Array of spam filters to test across, e.g. ["spam_assassin"]',
101 |       },
102 |       link_checker: {
103 |         type: 'boolean',
104 |         description: 'Enable to check if domains for links in email body are blacklisted',
105 |       },
106 |       campaign_id: {
107 |         type: 'integer',
108 |         description: 'Campaign ID for which you want to select the sequence to test',
109 |       },
110 |       sequence_mapping_id: {
111 |         type: 'integer',
112 |         description: 'The ID of the sequence or variant you would like to test',
113 |       },
114 |       provider_ids: {
115 |         type: 'array',
116 |         items: { type: 'integer' },
117 |         description: 'Array of provider IDs to send test emails to',
118 |       },
119 |       sender_accounts: {
120 |         type: 'array',
121 |         items: { type: 'string' },
122 |         description: 'Array of email addresses to use as senders',
123 |       },
124 |       all_email_sent_without_time_gap: {
125 |         type: 'boolean',
126 |         description: 'Set true to send all emails simultaneously',
127 |       },
128 |       min_time_btwn_emails: {
129 |         type: 'integer',
130 |         description: 'Time gap between each email from each mailbox (if time gap enabled)',
131 |       },
132 |       min_time_unit: {
133 |         type: 'string',
134 |         description: 'Time unit for the time gap (minutes, hours, days)',
135 |       },
136 |       is_warmup: {
137 |         type: 'boolean',
138 |         description: 'Set true to receive positive intent responses and move emails from spam to inbox',
139 |       },
140 |       schedule_start_time: {
141 |         type: 'string',
142 |         description: 'Start date and time to schedule or run the test (ISO format)',
143 |       },
144 |       test_end_date: {
145 |         type: 'string',
146 |         description: 'End date to stop running your test (YYYY-MM-DD format)',
147 |       },
148 |       every_days: {
149 |         type: 'integer',
150 |         description: 'Frequency of how often to run a new test',
151 |       },
152 |       tz: {
153 |         type: 'string',
154 |         description: 'Timezone for scheduling',
155 |       },
156 |       days: {
157 |         type: 'array',
158 |         items: { type: 'integer' },
159 |         description: 'Days of the week to run the test (1-7, where 1 is Monday)',
160 |       },
161 |       starHour: {
162 |         type: 'string',
163 |         description: 'Test start time',
164 |       },
165 |       folder_id: {
166 |         type: 'integer',
167 |         description: 'Folder ID to assign the test to',
168 |       },
169 |     },
170 |     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'],
171 |   },
172 | };
173 | 
174 | export const GET_SPAM_TEST_DETAILS_TOOL: CategoryTool = {
175 |   name: 'smartlead_get_spam_test_details',
176 |   description: 'Retrieve details of a specific spam test by ID.',
177 |   category: ToolCategory.SMART_DELIVERY,
178 |   inputSchema: {
179 |     type: 'object',
180 |     properties: {
181 |       spam_test_id: {
182 |         type: 'integer',
183 |         description: 'ID of the spam test to retrieve details for',
184 |       },
185 |     },
186 |     required: ['spam_test_id'],
187 |   },
188 | };
189 | 
190 | export const DELETE_SMART_DELIVERY_TESTS_TOOL: CategoryTool = {
191 |   name: 'smartlead_delete_smart_delivery_tests',
192 |   description: 'Delete multiple Smart Delivery tests in bulk.',
193 |   category: ToolCategory.SMART_DELIVERY,
194 |   inputSchema: {
195 |     type: 'object',
196 |     properties: {
197 |       spamTestIds: {
198 |         type: 'array',
199 |         items: { type: 'integer' },
200 |         description: 'Array of spam test IDs to delete',
201 |       },
202 |     },
203 |     required: ['spamTestIds'],
204 |   },
205 | };
206 | 
207 | export const STOP_AUTOMATED_TEST_TOOL: CategoryTool = {
208 |   name: 'smartlead_stop_automated_test',
209 |   description: 'Stop an active automated test before its end date.',
210 |   category: ToolCategory.SMART_DELIVERY,
211 |   inputSchema: {
212 |     type: 'object',
213 |     properties: {
214 |       spam_test_id: {
215 |         type: 'integer',
216 |         description: 'ID of the automated test to stop',
217 |       },
218 |     },
219 |     required: ['spam_test_id'],
220 |   },
221 | };
222 | 
223 | export const LIST_ALL_TESTS_TOOL: CategoryTool = {
224 |   name: 'smartlead_list_all_tests',
225 |   description: 'List all Smart Delivery tests, either manual or automated.',
226 |   category: ToolCategory.SMART_DELIVERY,
227 |   inputSchema: {
228 |     type: 'object',
229 |     properties: {
230 |       testType: {
231 |         type: 'string',
232 |         enum: ['manual', 'auto'],
233 |         description: 'Type of tests to list (manual or auto)',
234 |       },
235 |       limit: {
236 |         type: 'integer',
237 |         description: 'Number of tests to retrieve (default: 10)',
238 |       },
239 |       offset: {
240 |         type: 'integer',
241 |         description: 'Offset for pagination (default: 0)',
242 |       },
243 |     },
244 |     required: ['testType'],
245 |   },
246 | };
247 | 
248 | export const GET_PROVIDER_WISE_REPORT_TOOL: CategoryTool = {
249 |   name: 'smartlead_get_provider_wise_report',
250 |   description: 'Get detailed report of a spam test sorted by email providers.',
251 |   category: ToolCategory.SMART_DELIVERY,
252 |   inputSchema: {
253 |     type: 'object',
254 |     properties: {
255 |       spam_test_id: {
256 |         type: 'integer',
257 |         description: 'ID of the spam test to get the provider-wise report for',
258 |       },
259 |     },
260 |     required: ['spam_test_id'],
261 |   },
262 | };
263 | 
264 | export const GET_GROUP_WISE_REPORT_TOOL: CategoryTool = {
265 |   name: 'smartlead_get_group_wise_report',
266 |   description: 'Get detailed report of a spam test sorted by location (region/country).',
267 |   category: ToolCategory.SMART_DELIVERY,
268 |   inputSchema: {
269 |     type: 'object',
270 |     properties: {
271 |       spam_test_id: {
272 |         type: 'integer',
273 |         description: 'ID of the spam test to get the group-wise report for',
274 |       },
275 |     },
276 |     required: ['spam_test_id'],
277 |   },
278 | };
279 | 
280 | export const GET_SENDER_ACCOUNT_WISE_REPORT_TOOL: CategoryTool = {
281 |   name: 'smartlead_get_sender_account_wise_report',
282 |   description: 'Get detailed report of a spam test sorted by sender accounts with details of each email from each mailbox.',
283 |   category: ToolCategory.SMART_DELIVERY,
284 |   inputSchema: {
285 |     type: 'object',
286 |     properties: {
287 |       spam_test_id: {
288 |         type: 'integer',
289 |         description: 'ID of the spam test to get the sender account-wise report for',
290 |       },
291 |     },
292 |     required: ['spam_test_id'],
293 |   },
294 | };
295 | 
296 | export const GET_SPAM_FILTER_DETAILS_TOOL: CategoryTool = {
297 |   name: 'smartlead_get_spam_filter_details',
298 |   description: 'Get spam filter report per sender mailbox showing each spam score with details leading to the score.',
299 |   category: ToolCategory.SMART_DELIVERY,
300 |   inputSchema: {
301 |     type: 'object',
302 |     properties: {
303 |       spam_test_id: {
304 |         type: 'integer',
305 |         description: 'ID of the spam test to get the spam filter details for',
306 |       },
307 |     },
308 |     required: ['spam_test_id'],
309 |   },
310 | };
311 | 
312 | export const GET_DKIM_DETAILS_TOOL: CategoryTool = {
313 |   name: 'smartlead_get_dkim_details',
314 |   description: 'Check if DKIM authentication passed or failed for each sender mailbox and receiver account.',
315 |   category: ToolCategory.SMART_DELIVERY,
316 |   inputSchema: {
317 |     type: 'object',
318 |     properties: {
319 |       spam_test_id: {
320 |         type: 'integer',
321 |         description: 'ID of the spam test to get the DKIM details for',
322 |       },
323 |     },
324 |     required: ['spam_test_id'],
325 |   },
326 | };
327 | 
328 | export const GET_SPF_DETAILS_TOOL: CategoryTool = {
329 |   name: 'smartlead_get_spf_details',
330 |   description: 'Check if SPF authentication passed or failed for the test.',
331 |   category: ToolCategory.SMART_DELIVERY,
332 |   inputSchema: {
333 |     type: 'object',
334 |     properties: {
335 |       spam_test_id: {
336 |         type: 'integer',
337 |         description: 'ID of the spam test to get the SPF details for',
338 |       },
339 |     },
340 |     required: ['spam_test_id'],
341 |   },
342 | };
343 | 
344 | export const GET_RDNS_DETAILS_TOOL: CategoryTool = {
345 |   name: 'smartlead_get_rdns_details',
346 |   description: 'Check if rDNS was correct for an IP sending the email.',
347 |   category: ToolCategory.SMART_DELIVERY,
348 |   inputSchema: {
349 |     type: 'object',
350 |     properties: {
351 |       spam_test_id: {
352 |         type: 'integer',
353 |         description: 'ID of the spam test to get the rDNS details for',
354 |       },
355 |     },
356 |     required: ['spam_test_id'],
357 |   },
358 | };
359 | 
360 | export const GET_SENDER_ACCOUNTS_TOOL: CategoryTool = {
361 |   name: 'smartlead_get_sender_accounts',
362 |   description: 'Get the list of all sender accounts selected for a specific spam test.',
363 |   category: ToolCategory.SMART_DELIVERY,
364 |   inputSchema: {
365 |     type: 'object',
366 |     properties: {
367 |       spam_test_id: {
368 |         type: 'integer',
369 |         description: 'ID of the spam test to get the sender accounts for',
370 |       },
371 |     },
372 |     required: ['spam_test_id'],
373 |   },
374 | };
375 | 
376 | export const GET_BLACKLIST_TOOL: CategoryTool = {
377 |   name: 'smartlead_get_blacklist',
378 |   description: 'Get the list of all blacklists per IP per email sent.',
379 |   category: ToolCategory.SMART_DELIVERY,
380 |   inputSchema: {
381 |     type: 'object',
382 |     properties: {
383 |       spam_test_id: {
384 |         type: 'integer',
385 |         description: 'ID of the spam test to get the blacklist information for',
386 |       },
387 |     },
388 |     required: ['spam_test_id'],
389 |   },
390 | };
391 | 
392 | export const GET_EMAIL_CONTENT_TOOL: CategoryTool = {
393 |   name: 'smartlead_get_email_content',
394 |   description: 'Get details for the email content (raw, HTML) along with campaign and sequence details.',
395 |   category: ToolCategory.SMART_DELIVERY,
396 |   inputSchema: {
397 |     type: 'object',
398 |     properties: {
399 |       spam_test_id: {
400 |         type: 'integer',
401 |         description: 'ID of the spam test to get the email content for',
402 |       },
403 |     },
404 |     required: ['spam_test_id'],
405 |   },
406 | };
407 | 
408 | export const GET_IP_ANALYTICS_TOOL: CategoryTool = {
409 |   name: 'smartlead_get_ip_analytics',
410 |   description: 'Get total blacklist count identified in the test.',
411 |   category: ToolCategory.SMART_DELIVERY,
412 |   inputSchema: {
413 |     type: 'object',
414 |     properties: {
415 |       spam_test_id: {
416 |         type: 'integer',
417 |         description: 'ID of the spam test to get the IP analytics for',
418 |       },
419 |     },
420 |     required: ['spam_test_id'],
421 |   },
422 | };
423 | 
424 | export const GET_EMAIL_HEADERS_TOOL: CategoryTool = {
425 |   name: 'smartlead_get_email_headers',
426 |   description: 'Get details of the email headers for a specific email.',
427 |   category: ToolCategory.SMART_DELIVERY,
428 |   inputSchema: {
429 |     type: 'object',
430 |     properties: {
431 |       spam_test_id: {
432 |         type: 'integer',
433 |         description: 'ID of the spam test',
434 |       },
435 |       reply_id: {
436 |         type: 'integer',
437 |         description: 'ID of the email received by the seed account',
438 |       },
439 |     },
440 |     required: ['spam_test_id', 'reply_id'],
441 |   },
442 | };
443 | 
444 | export const GET_SCHEDULE_HISTORY_TOOL: CategoryTool = {
445 |   name: 'smartlead_get_schedule_history',
446 |   description: 'Get the list and summary of all tests that ran for a particular automated test.',
447 |   category: ToolCategory.SMART_DELIVERY,
448 |   inputSchema: {
449 |     type: 'object',
450 |     properties: {
451 |       spam_test_id: {
452 |         type: 'integer',
453 |         description: 'ID of the automated spam test to get the schedule history for',
454 |       },
455 |     },
456 |     required: ['spam_test_id'],
457 |   },
458 | };
459 | 
460 | export const GET_IP_DETAILS_TOOL: CategoryTool = {
461 |   name: 'smartlead_get_ip_details',
462 |   description: 'Get the list of all blacklists per IP for a specific email.',
463 |   category: ToolCategory.SMART_DELIVERY,
464 |   inputSchema: {
465 |     type: 'object',
466 |     properties: {
467 |       spam_test_id: {
468 |         type: 'integer',
469 |         description: 'ID of the spam test',
470 |       },
471 |       reply_id: {
472 |         type: 'integer',
473 |         description: 'ID of the email received by the seed account',
474 |       },
475 |     },
476 |     required: ['spam_test_id', 'reply_id'],
477 |   },
478 | };
479 | 
480 | export const GET_MAILBOX_SUMMARY_TOOL: CategoryTool = {
481 |   name: 'smartlead_get_mailbox_summary',
482 |   description: 'Get the list of mailboxes used for any Smart Delivery test with overall performance across all tests.',
483 |   category: ToolCategory.SMART_DELIVERY,
484 |   inputSchema: {
485 |     type: 'object',
486 |     properties: {
487 |       limit: {
488 |         type: 'integer',
489 |         description: 'Number of tests to retrieve (default: 10)',
490 |       },
491 |       offset: {
492 |         type: 'integer',
493 |         description: 'Offset for pagination (default: 0)',
494 |       },
495 |     },
496 |   },
497 | };
498 | 
499 | export const GET_MAILBOX_COUNT_TOOL: CategoryTool = {
500 |   name: 'smartlead_get_mailbox_count',
501 |   description: 'Get the count of all mailboxes used for any spam test.',
502 |   category: ToolCategory.SMART_DELIVERY,
503 |   inputSchema: {
504 |     type: 'object',
505 |     properties: {},
506 |   },
507 | };
508 | 
509 | export const GET_ALL_FOLDERS_TOOL: CategoryTool = {
510 |   name: 'smartlead_get_all_folders',
511 |   description: 'Get the list and details of all folders created in Smart Delivery along with tests inside each folder.',
512 |   category: ToolCategory.SMART_DELIVERY,
513 |   inputSchema: {
514 |     type: 'object',
515 |     properties: {
516 |       limit: {
517 |         type: 'integer',
518 |         description: 'Number of folders to retrieve (default: 10)',
519 |       },
520 |       offset: {
521 |         type: 'integer',
522 |         description: 'Offset for pagination (default: 0)',
523 |       },
524 |       name: {
525 |         type: 'string',
526 |         description: 'Filter folders by name',
527 |       },
528 |     },
529 |   },
530 | };
531 | 
532 | export const CREATE_FOLDER_TOOL: CategoryTool = {
533 |   name: 'smartlead_create_folder',
534 |   description: 'Create a folder in Smart Delivery to organize tests.',
535 |   category: ToolCategory.SMART_DELIVERY,
536 |   inputSchema: {
537 |     type: 'object',
538 |     properties: {
539 |       name: {
540 |         type: 'string',
541 |         description: 'Name of the folder to create',
542 |       },
543 |     },
544 |     required: ['name'],
545 |   },
546 | };
547 | 
548 | export const GET_FOLDER_BY_ID_TOOL: CategoryTool = {
549 |   name: 'smartlead_get_folder_by_id',
550 |   description: 'Get details of a specific folder by ID.',
551 |   category: ToolCategory.SMART_DELIVERY,
552 |   inputSchema: {
553 |     type: 'object',
554 |     properties: {
555 |       folder_id: {
556 |         type: 'integer',
557 |         description: 'ID of the folder to retrieve',
558 |       },
559 |     },
560 |     required: ['folder_id'],
561 |   },
562 | };
563 | 
564 | export const DELETE_FOLDER_TOOL: CategoryTool = {
565 |   name: 'smartlead_delete_folder',
566 |   description: 'Delete a folder from Smart Delivery.',
567 |   category: ToolCategory.SMART_DELIVERY,
568 |   inputSchema: {
569 |     type: 'object',
570 |     properties: {
571 |       folder_id: {
572 |         type: 'integer',
573 |         description: 'ID of the folder to delete',
574 |       },
575 |     },
576 |     required: ['folder_id'],
577 |   },
578 | };
579 | 
580 | // Export all tools as an array for easy registration
581 | export const smartDeliveryTools = [
582 |   GET_REGION_WISE_PROVIDERS_TOOL,
583 |   CREATE_MANUAL_PLACEMENT_TEST_TOOL,
584 |   CREATE_AUTOMATED_PLACEMENT_TEST_TOOL,
585 |   GET_SPAM_TEST_DETAILS_TOOL,
586 |   DELETE_SMART_DELIVERY_TESTS_TOOL,
587 |   STOP_AUTOMATED_TEST_TOOL,
588 |   LIST_ALL_TESTS_TOOL,
589 |   GET_PROVIDER_WISE_REPORT_TOOL,
590 |   GET_GROUP_WISE_REPORT_TOOL,
591 |   GET_SENDER_ACCOUNT_WISE_REPORT_TOOL,
592 |   GET_SPAM_FILTER_DETAILS_TOOL,
593 |   GET_DKIM_DETAILS_TOOL,
594 |   GET_SPF_DETAILS_TOOL,
595 |   GET_RDNS_DETAILS_TOOL,
596 |   GET_SENDER_ACCOUNTS_TOOL,
597 |   GET_BLACKLIST_TOOL,
598 |   GET_EMAIL_CONTENT_TOOL,
599 |   GET_IP_ANALYTICS_TOOL,
600 |   GET_EMAIL_HEADERS_TOOL,
601 |   GET_SCHEDULE_HISTORY_TOOL,
602 |   GET_IP_DETAILS_TOOL,
603 |   GET_MAILBOX_SUMMARY_TOOL,
604 |   GET_MAILBOX_COUNT_TOOL,
605 |   GET_ALL_FOLDERS_TOOL,
606 |   CREATE_FOLDER_TOOL,
607 |   GET_FOLDER_BY_ID_TOOL,
608 |   DELETE_FOLDER_TOOL,
609 | ];
610 | 
```
Page 2/3FirstPrevNextLast