Skip to content

Instantly share code, notes, and snippets.

@ngauthier
Created June 28, 2025 16:52
Show Gist options
  • Save ngauthier/12a125ef369f43584a37d54609c5f4f0 to your computer and use it in GitHub Desktop.
Save ngauthier/12a125ef369f43584a37d54609c5f4f0 to your computer and use it in GitHub Desktop.
Drip MCP Server - Complete API Implementation
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import fetch from "node-fetch";
class DripAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = "https://api.getdrip.com/v2";
}
getAuthHeader() {
return `Basic ${Buffer.from(`${this.apiKey}:`).toString('base64')}`;
}
async makeRequest(endpoint, method = 'GET', body = null) {
const url = `${this.baseUrl}${endpoint}`;
const options = {
method,
headers: {
'Authorization': this.getAuthHeader(),
'Content-Type': 'application/json'
}
};
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`Drip API error: ${response.status} ${response.statusText}`);
}
return await response.json();
}
async listAccounts() {
const data = await this.makeRequest('/v2/accounts');
return data.accounts;
}
async fetchAccount(accountId) {
const data = await this.makeRequest(`/v2/accounts/${accountId}`);
return data.accounts[0];
}
// Campaign endpoints
async listCampaigns(accountId) {
const data = await this.makeRequest(`/v2/${accountId}/campaigns`);
return data.campaigns;
}
async fetchCampaign(accountId, campaignId) {
const data = await this.makeRequest(`/v2/${accountId}/campaigns/${campaignId}`);
return data.campaigns[0];
}
async activateCampaign(accountId, campaignId) {
return await this.makeRequest(`/v2/${accountId}/campaigns/${campaignId}/activate`, 'POST');
}
async pauseCampaign(accountId, campaignId) {
return await this.makeRequest(`/v2/${accountId}/campaigns/${campaignId}/pause`, 'POST');
}
async listCampaignSubscribers(accountId, campaignId) {
const data = await this.makeRequest(`/v2/${accountId}/campaigns/${campaignId}/subscribers`);
return data.subscribers;
}
async subscribeToCampaign(accountId, campaignId, subscriberData) {
return await this.makeRequest(`/v2/${accountId}/campaigns/${campaignId}/subscribers`, 'POST', subscriberData);
}
// Subscriber endpoints
async createOrUpdateSubscriber(accountId, subscriberData) {
return await this.makeRequest(`/v2/${accountId}/subscribers`, 'POST', subscriberData);
}
async listSubscribers(accountId, status = null, page = null) {
let endpoint = `/v2/${accountId}/subscribers`;
const params = new URLSearchParams();
if (status) params.append('status', status);
if (page) params.append('page', page);
if (params.toString()) endpoint += `?${params.toString()}`;
const data = await this.makeRequest(endpoint);
return data.subscribers;
}
async fetchSubscriber(accountId, subscriberId) {
const data = await this.makeRequest(`/v2/${accountId}/subscribers/${subscriberId}`);
return data.subscribers[0];
}
async deleteSubscriber(accountId, subscriberId) {
return await this.makeRequest(`/v2/${accountId}/subscribers/${subscriberId}`, 'DELETE');
}
async unsubscribeFromCampaign(accountId, subscriberId, campaignId) {
return await this.makeRequest(`/v2/${accountId}/subscribers/${subscriberId}/unsubscribe`, 'POST', {
campaign_id: campaignId
});
}
// Event endpoints
async recordEvent(accountId, eventData) {
return await this.makeRequest(`/v2/${accountId}/events`, 'POST', eventData);
}
async listEventActions(accountId) {
const data = await this.makeRequest(`/v2/${accountId}/event_actions`);
return data.event_actions;
}
// Custom field endpoints
async listCustomFields(accountId) {
const data = await this.makeRequest(`/v2/${accountId}/custom_field_identifiers`);
return data.custom_field_identifiers;
}
// Form endpoints
async listForms(accountId) {
const data = await this.makeRequest(`/v2/${accountId}/forms`);
return data.forms;
}
async fetchForm(accountId, formId) {
const data = await this.makeRequest(`/v2/${accountId}/forms/${formId}`);
return data.forms[0];
}
// Conversion endpoints
async listConversions(accountId) {
const data = await this.makeRequest(`/v2/${accountId}/goals`);
return data.goals;
}
async fetchConversion(accountId, conversionId) {
const data = await this.makeRequest(`/v2/${accountId}/goals/${conversionId}`);
return data.goals[0];
}
// Webhook endpoints
async listWebhooks(accountId) {
const data = await this.makeRequest(`/v2/${accountId}/webhooks`);
return data.webhooks;
}
async createWebhook(accountId, webhookData) {
return await this.makeRequest(`/v2/${accountId}/webhooks`, 'POST', webhookData);
}
async fetchWebhook(accountId, webhookId) {
const data = await this.makeRequest(`/v2/${accountId}/webhooks/${webhookId}`);
return data.webhooks[0];
}
async updateWebhook(accountId, webhookId, webhookData) {
return await this.makeRequest(`/v2/${accountId}/webhooks/${webhookId}`, 'PUT', webhookData);
}
async deleteWebhook(accountId, webhookId) {
return await this.makeRequest(`/v2/${accountId}/webhooks/${webhookId}`, 'DELETE');
}
// Batch endpoints
async batchSubscribers(accountId, subscribersData) {
return await this.makeRequest(`/v2/${accountId}/subscribers/batches`, 'POST', subscribersData);
}
async batchUnsubscribes(accountId, unsubscribesData) {
return await this.makeRequest(`/v2/${accountId}/unsubscribes/batches`, 'POST', unsubscribesData);
}
async batchEvents(accountId, eventsData) {
return await this.makeRequest(`/v2/${accountId}/events/batches`, 'POST', eventsData);
}
// Shopper Activity endpoints (v3)
async batchCarts(accountId, cartsData) {
return await this.makeRequest(`/v3/${accountId}/shopper_activity/cart/batch`, 'POST', cartsData);
}
async batchOrders(accountId, ordersData) {
return await this.makeRequest(`/v3/${accountId}/shopper_activity/order/batch`, 'POST', ordersData);
}
async batchProducts(accountId, productsData) {
return await this.makeRequest(`/v3/${accountId}/shopper_activity/product/batch`, 'POST', productsData);
}
}
const server = new Server(
{
name: "drip-mcp",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
const baseApiSchema = {
type: "object",
properties: {
api_key: {
type: "string",
description: "Drip API key for authentication",
},
},
required: ["api_key"],
};
const accountApiSchema = {
type: "object",
properties: {
api_key: {
type: "string",
description: "Drip API key for authentication",
},
account_id: {
type: "string",
description: "Account ID",
},
},
required: ["api_key", "account_id"],
};
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "echo",
description: "Echo back the provided text",
inputSchema: {
type: "object",
properties: {
text: {
type: "string",
description: "Text to echo back",
},
},
required: ["text"],
},
},
// Account endpoints
{
name: "list_accounts",
description: "List all Drip accounts",
inputSchema: baseApiSchema,
},
{
name: "fetch_account",
description: "Fetch a specific Drip account",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID to fetch" },
},
required: ["api_key", "account_id"],
},
},
// Campaign endpoints
{
name: "list_campaigns",
description: "List all campaigns for an account",
inputSchema: accountApiSchema,
},
{
name: "fetch_campaign",
description: "Fetch a specific campaign",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
campaign_id: { type: "string", description: "Campaign ID" },
},
required: ["api_key", "account_id", "campaign_id"],
},
},
{
name: "activate_campaign",
description: "Activate a campaign",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
campaign_id: { type: "string", description: "Campaign ID" },
},
required: ["api_key", "account_id", "campaign_id"],
},
},
{
name: "pause_campaign",
description: "Pause a campaign",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
campaign_id: { type: "string", description: "Campaign ID" },
},
required: ["api_key", "account_id", "campaign_id"],
},
},
{
name: "list_campaign_subscribers",
description: "List subscribers for a campaign",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
campaign_id: { type: "string", description: "Campaign ID" },
},
required: ["api_key", "account_id", "campaign_id"],
},
},
{
name: "subscribe_to_campaign",
description: "Subscribe someone to a campaign",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
campaign_id: { type: "string", description: "Campaign ID" },
subscriber_data: { type: "object", description: "Subscriber data" },
},
required: ["api_key", "account_id", "campaign_id", "subscriber_data"],
},
},
// Subscriber endpoints
{
name: "create_or_update_subscriber",
description: "Create or update a subscriber",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
subscriber_data: { type: "object", description: "Subscriber data" },
},
required: ["api_key", "account_id", "subscriber_data"],
},
},
{
name: "list_subscribers",
description: "List subscribers for an account",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
status: { type: "string", description: "Filter by status (optional)" },
page: { type: "string", description: "Page number (optional)" },
},
required: ["api_key", "account_id"],
},
},
{
name: "fetch_subscriber",
description: "Fetch a specific subscriber",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
subscriber_id: { type: "string", description: "Subscriber ID" },
},
required: ["api_key", "account_id", "subscriber_id"],
},
},
{
name: "delete_subscriber",
description: "Delete a subscriber",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
subscriber_id: { type: "string", description: "Subscriber ID" },
},
required: ["api_key", "account_id", "subscriber_id"],
},
},
// Event endpoints
{
name: "record_event",
description: "Record an event for a subscriber",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
event_data: { type: "object", description: "Event data" },
},
required: ["api_key", "account_id", "event_data"],
},
},
{
name: "list_event_actions",
description: "List custom event actions",
inputSchema: accountApiSchema,
},
// Form endpoints
{
name: "list_forms",
description: "List forms for an account",
inputSchema: accountApiSchema,
},
{
name: "fetch_form",
description: "Fetch a specific form",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
form_id: { type: "string", description: "Form ID" },
},
required: ["api_key", "account_id", "form_id"],
},
},
// Custom field endpoints
{
name: "list_custom_fields",
description: "List custom field identifiers",
inputSchema: accountApiSchema,
},
// Conversion endpoints
{
name: "list_conversions",
description: "List conversions/goals for an account",
inputSchema: accountApiSchema,
},
{
name: "fetch_conversion",
description: "Fetch a specific conversion/goal",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
conversion_id: { type: "string", description: "Conversion ID" },
},
required: ["api_key", "account_id", "conversion_id"],
},
},
// Webhook endpoints
{
name: "list_webhooks",
description: "List webhooks for an account",
inputSchema: accountApiSchema,
},
{
name: "create_webhook",
description: "Create a new webhook",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
webhook_data: { type: "object", description: "Webhook data" },
},
required: ["api_key", "account_id", "webhook_data"],
},
},
{
name: "fetch_webhook",
description: "Fetch a specific webhook",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
webhook_id: { type: "string", description: "Webhook ID" },
},
required: ["api_key", "account_id", "webhook_id"],
},
},
{
name: "update_webhook",
description: "Update a webhook",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
webhook_id: { type: "string", description: "Webhook ID" },
webhook_data: { type: "object", description: "Updated webhook data" },
},
required: ["api_key", "account_id", "webhook_id", "webhook_data"],
},
},
{
name: "delete_webhook",
description: "Delete a webhook",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
webhook_id: { type: "string", description: "Webhook ID" },
},
required: ["api_key", "account_id", "webhook_id"],
},
},
// Batch endpoints
{
name: "batch_subscribers",
description: "Create or update subscribers in batch",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
subscribers_data: { type: "object", description: "Batch subscribers data" },
},
required: ["api_key", "account_id", "subscribers_data"],
},
},
{
name: "batch_events",
description: "Record events in batch",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
events_data: { type: "object", description: "Batch events data" },
},
required: ["api_key", "account_id", "events_data"],
},
},
// Shopper Activity endpoints
{
name: "batch_carts",
description: "Submit cart data in batch",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
carts_data: { type: "object", description: "Batch carts data" },
},
required: ["api_key", "account_id", "carts_data"],
},
},
{
name: "batch_orders",
description: "Submit order data in batch",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
orders_data: { type: "object", description: "Batch orders data" },
},
required: ["api_key", "account_id", "orders_data"],
},
},
{
name: "batch_products",
description: "Submit product data in batch",
inputSchema: {
type: "object",
properties: {
api_key: { type: "string", description: "Drip API key" },
account_id: { type: "string", description: "Account ID" },
products_data: { type: "object", description: "Batch products data" },
},
required: ["api_key", "account_id", "products_data"],
},
},
],
};
});
async function handleEcho(args) {
return {
content: [
{
type: "text",
text: `Echo: ${args?.text || ""}`,
},
],
};
}
async function handleListAccounts(args) {
try {
const dripApi = new DripAPI(args?.api_key);
const accounts = await dripApi.listAccounts();
return {
content: [
{
type: "text",
text: `Found ${accounts.length} Drip accounts:\n\n${accounts.map(account =>
`• ${account.name} (ID: ${account.id})${account.url ? ` - ${account.url}` : ''}`
).join('\n')}`,
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error fetching accounts: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
};
}
}
function createHandler(apiMethod, formatResult = null) {
return async (args) => {
try {
const dripApi = new DripAPI(args?.api_key);
const result = await apiMethod(dripApi, args);
return {
content: [
{
type: "text",
text: formatResult ? formatResult(result) : JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
};
}
};
}
const handleFetchAccount = createHandler((api, args) => api.fetchAccount(args.account_id));
const handleListCampaigns = createHandler((api, args) => api.listCampaigns(args.account_id));
const handleFetchCampaign = createHandler((api, args) => api.fetchCampaign(args.account_id, args.campaign_id));
const handleActivateCampaign = createHandler((api, args) => api.activateCampaign(args.account_id, args.campaign_id));
const handlePauseCampaign = createHandler((api, args) => api.pauseCampaign(args.account_id, args.campaign_id));
const handleListCampaignSubscribers = createHandler((api, args) => api.listCampaignSubscribers(args.account_id, args.campaign_id));
const handleSubscribeToCampaign = createHandler((api, args) => api.subscribeToCampaign(args.account_id, args.campaign_id, args.subscriber_data));
const handleCreateOrUpdateSubscriber = createHandler((api, args) => api.createOrUpdateSubscriber(args.account_id, args.subscriber_data));
const handleListSubscribers = createHandler((api, args) => api.listSubscribers(args.account_id, args.status, args.page));
const handleFetchSubscriber = createHandler((api, args) => api.fetchSubscriber(args.account_id, args.subscriber_id));
const handleDeleteSubscriber = createHandler((api, args) => api.deleteSubscriber(args.account_id, args.subscriber_id));
const handleRecordEvent = createHandler((api, args) => api.recordEvent(args.account_id, args.event_data));
const handleListEventActions = createHandler((api, args) => api.listEventActions(args.account_id));
const handleListCustomFields = createHandler((api, args) => api.listCustomFields(args.account_id));
const handleListForms = createHandler((api, args) => api.listForms(args.account_id));
const handleFetchForm = createHandler((api, args) => api.fetchForm(args.account_id, args.form_id));
const handleListConversions = createHandler((api, args) => api.listConversions(args.account_id));
const handleFetchConversion = createHandler((api, args) => api.fetchConversion(args.account_id, args.conversion_id));
const handleListWebhooks = createHandler((api, args) => api.listWebhooks(args.account_id));
const handleCreateWebhook = createHandler((api, args) => api.createWebhook(args.account_id, args.webhook_data));
const handleFetchWebhook = createHandler((api, args) => api.fetchWebhook(args.account_id, args.webhook_id));
const handleUpdateWebhook = createHandler((api, args) => api.updateWebhook(args.account_id, args.webhook_id, args.webhook_data));
const handleDeleteWebhook = createHandler((api, args) => api.deleteWebhook(args.account_id, args.webhook_id));
const handleBatchSubscribers = createHandler((api, args) => api.batchSubscribers(args.account_id, args.subscribers_data));
const handleBatchEvents = createHandler((api, args) => api.batchEvents(args.account_id, args.events_data));
const handleBatchCarts = createHandler((api, args) => api.batchCarts(args.account_id, args.carts_data));
const handleBatchOrders = createHandler((api, args) => api.batchOrders(args.account_id, args.orders_data));
const handleBatchProducts = createHandler((api, args) => api.batchProducts(args.account_id, args.products_data));
const commandHandlers = {
echo: handleEcho,
// Account endpoints
list_accounts: handleListAccounts,
fetch_account: handleFetchAccount,
// Campaign endpoints
list_campaigns: handleListCampaigns,
fetch_campaign: handleFetchCampaign,
activate_campaign: handleActivateCampaign,
pause_campaign: handlePauseCampaign,
list_campaign_subscribers: handleListCampaignSubscribers,
subscribe_to_campaign: handleSubscribeToCampaign,
// Subscriber endpoints
create_or_update_subscriber: handleCreateOrUpdateSubscriber,
list_subscribers: handleListSubscribers,
fetch_subscriber: handleFetchSubscriber,
delete_subscriber: handleDeleteSubscriber,
// Event endpoints
record_event: handleRecordEvent,
list_event_actions: handleListEventActions,
// Form endpoints
list_forms: handleListForms,
fetch_form: handleFetchForm,
// Custom field endpoints
list_custom_fields: handleListCustomFields,
// Conversion endpoints
list_conversions: handleListConversions,
fetch_conversion: handleFetchConversion,
// Webhook endpoints
list_webhooks: handleListWebhooks,
create_webhook: handleCreateWebhook,
fetch_webhook: handleFetchWebhook,
update_webhook: handleUpdateWebhook,
delete_webhook: handleDeleteWebhook,
// Batch endpoints
batch_subscribers: handleBatchSubscribers,
batch_events: handleBatchEvents,
// Shopper Activity endpoints
batch_carts: handleBatchCarts,
batch_orders: handleBatchOrders,
batch_products: handleBatchProducts,
};
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const handler = commandHandlers[name];
if (handler) {
return await handler(args);
}
throw new Error(`Unknown tool: ${name}`);
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});
{
"name": "drip-mcp",
"version": "0.1.0",
"description": "A minimal MCP server",
"type": "module",
"main": "index.js",
"bin": {
"drip-mcp": "index.js"
},
"scripts": {
"start": "node index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^0.5.0",
"node-fetch": "^3.3.0"
},
"engines": {
"node": ">=18"
}
}
#!/usr/bin/env node
import { spawn } from 'child_process';
const server = spawn('node', ['index.js'], {
stdio: ['pipe', 'pipe', 'inherit']
});
// Test MCP protocol - initialize
const initMessage = {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {
protocolVersion: "2024-11-05",
capabilities: {
tools: {}
},
clientInfo: {
name: "test-client",
version: "0.1.0"
}
}
};
server.stdin.write(JSON.stringify(initMessage) + '\n');
// Test listing tools
const listToolsMessage = {
jsonrpc: "2.0",
id: 2,
method: "tools/list",
params: {}
};
setTimeout(() => {
server.stdin.write(JSON.stringify(listToolsMessage) + '\n');
}, 100);
// Test calling echo tool
const callToolMessage = {
jsonrpc: "2.0",
id: 3,
method: "tools/call",
params: {
name: "echo",
arguments: {
text: "Hello MCP!"
}
}
};
// Test calling list_accounts tool (will fail without real API key)
const listAccountsMessage = {
jsonrpc: "2.0",
id: 4,
method: "tools/call",
params: {
name: "list_accounts",
arguments: {
api_key: "test_key_will_fail"
}
}
};
setTimeout(() => {
server.stdin.write(JSON.stringify(callToolMessage) + '\n');
}, 200);
setTimeout(() => {
server.stdin.write(JSON.stringify(listAccountsMessage) + '\n');
}, 300);
server.stdout.on('data', (data) => {
console.log('Server response:', data.toString());
});
setTimeout(() => {
server.kill();
}, 1500);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment