203 lines
5.4 KiB
JavaScript

#!/usr/bin/env node
/**
* ActiveCampaign MCP Server
* Complete integration with 60+ tools and 16 apps
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { ActiveCampaignClient } from './client/index.js';
import { createContactTools } from './tools/contacts.js';
import { createDealTools } from './tools/deals.js';
import { createListTools } from './tools/lists.js';
import { createCampaignTools } from './tools/campaigns.js';
import { createAutomationTools } from './tools/automations.js';
import { createFormTools } from './tools/forms.js';
import { createTagTools } from './tools/tags.js';
import { createTaskTools } from './tools/tasks.js';
import { createNoteTools } from './tools/notes.js';
import { createPipelineTools } from './tools/pipelines.js';
import { createAccountTools } from './tools/accounts.js';
import { createWebhookTools } from './tools/webhooks.js';
// 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',
];
class ActiveCampaignServer {
private server: Server;
private client: ActiveCampaignClient;
private allTools: Record<string, any> = {};
constructor() {
const account = process.env.ACTIVECAMPAIGN_ACCOUNT;
const apiKey = process.env.ACTIVECAMPAIGN_API_KEY;
if (!account || !apiKey) {
throw new Error(
'Missing required environment variables: ACTIVECAMPAIGN_ACCOUNT and ACTIVECAMPAIGN_API_KEY'
);
}
this.client = new ActiveCampaignClient(account, apiKey);
this.server = new Server(
{
name: 'activecampaign-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
},
}
);
this.setupTools();
this.setupHandlers();
}
private setupTools() {
// Aggregate all tools from different modules
this.allTools = {
...createContactTools(this.client),
...createDealTools(this.client),
...createListTools(this.client),
...createCampaignTools(this.client),
...createAutomationTools(this.client),
...createFormTools(this.client),
...createTagTools(this.client),
...createTaskTools(this.client),
...createNoteTools(this.client),
...createPipelineTools(this.client),
...createAccountTools(this.client),
...createWebhookTools(this.client),
};
}
private setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: Object.entries(this.allTools).map(([name, tool]) => ({
name,
description: tool.description,
inputSchema: tool.inputSchema,
})),
};
});
// Execute tool
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const toolName = request.params.name;
const tool = this.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 run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('ActiveCampaign MCP Server running on stdio');
}
}
const server = new ActiveCampaignServer();
server.run().catch(console.error);