/** * ActiveCampaign MCP Server Class * Implements lazy-loaded tool modules for optimal performance */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { ActiveCampaignClient } from './client/index.js'; type ToolModule = Record; // Available app resources const APPS = [ 'contact-manager', 'deal-pipeline', 'list-builder', 'campaign-dashboard', 'automation-builder', 'form-manager', 'tag-organizer', 'task-center', 'notes-viewer', 'pipeline-settings', 'account-directory', 'webhook-manager', 'email-analytics', 'segment-viewer', 'site-tracking', 'score-dashboard', ]; export class ActiveCampaignMCPServer { private server: Server; private client: ActiveCampaignClient; private toolModules: Map Promise>; private loadedTools: ToolModule | null = null; constructor(client: ActiveCampaignClient) { this.client = client; this.toolModules = new Map(); this.server = new Server( { name: 'activecampaign-server', version: '1.0.0', }, { capabilities: { tools: {}, resources: {}, }, } ); this.setupToolModules(); this.setupHandlers(); } private setupToolModules() { // Register lazy-loaded tool modules this.toolModules.set('contacts', async () => { const module = await import('./tools/contacts.js'); return module.createContactTools(this.client); }); this.toolModules.set('deals', async () => { const module = await import('./tools/deals.js'); return module.createDealTools(this.client); }); this.toolModules.set('lists', async () => { const module = await import('./tools/lists.js'); return module.createListTools(this.client); }); this.toolModules.set('campaigns', async () => { const module = await import('./tools/campaigns.js'); return module.createCampaignTools(this.client); }); this.toolModules.set('automations', async () => { const module = await import('./tools/automations.js'); return module.createAutomationTools(this.client); }); this.toolModules.set('forms', async () => { const module = await import('./tools/forms.js'); return module.createFormTools(this.client); }); this.toolModules.set('tags', async () => { const module = await import('./tools/tags.js'); return module.createTagTools(this.client); }); this.toolModules.set('tasks', async () => { const module = await import('./tools/tasks.js'); return module.createTaskTools(this.client); }); this.toolModules.set('notes', async () => { const module = await import('./tools/notes.js'); return module.createNoteTools(this.client); }); this.toolModules.set('pipelines', async () => { const module = await import('./tools/pipelines.js'); return module.createPipelineTools(this.client); }); this.toolModules.set('accounts', async () => { const module = await import('./tools/accounts.js'); return module.createAccountTools(this.client); }); this.toolModules.set('webhooks', async () => { const module = await import('./tools/webhooks.js'); return module.createWebhookTools(this.client); }); } private async loadAllTools(): Promise { if (this.loadedTools) { return this.loadedTools; } const allTools: ToolModule = {}; for (const [name, loader] of this.toolModules.entries()) { const tools = await loader(); Object.assign(allTools, tools); } this.loadedTools = allTools; return allTools; } private setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { const allTools = await this.loadAllTools(); return { tools: Object.entries(allTools).map(([name, tool]) => ({ name, description: tool.description, inputSchema: tool.inputSchema, })), }; }); // Execute tool this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const allTools = await this.loadAllTools(); const toolName = request.params.name; const tool = allTools[toolName]; if (!tool) { throw new Error(`Unknown tool: ${toolName}`); } try { const result = await tool.handler(request.params.arguments || {}); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }); // List app resources this.server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: APPS.map((app) => ({ uri: `activecampaign://app/${app}`, mimeType: 'text/html', name: app .split('-') .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) .join(' '), })), }; }); // Read app resource this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const uri = request.params.uri; const match = uri.match(/^activecampaign:\/\/app\/(.+)$/); if (!match) { throw new Error(`Invalid resource URI: ${uri}`); } const appName = match[1]; if (!APPS.includes(appName)) { throw new Error(`Unknown app: ${appName}`); } try { // Dynamically import the app const appModule = await import(`./apps/${appName}/index.js`); const html = appModule.default(); return { contents: [ { uri, mimeType: 'text/html', text: html, }, ], }; } catch (error) { throw new Error(`Failed to load app ${appName}: ${error}`); } }); } async connect(transport: any) { await this.server.connect(transport); } }