Jake Shore 04f254c40b constant-contact: Complete production MCP server rebuild
- Deleted single-file stub, rebuilt from scratch
- API client with OAuth2, pagination, rate limiting, error handling
- 50+ tools across 9 domains:
  * 12 contact tools (list, get, create, update, delete, search, tags, import/export, activity)
  * 11 campaign tools (list, get, create, update, delete, schedule, send, stats, activities, clone, test)
  * 9 list tools (list, get, create, update, delete, add/remove contacts, membership, stats)
  * 6 segment tools (list, get, create, update, delete, get contacts)
  * 2 template tools (list, get)
  * 11 reporting tools (campaign stats, contact stats, bounces, clicks, opens, forwards, optouts, sends, links)
  * 7 landing page tools (list, get, create, update, delete, publish, stats)
  * 6 social tools (list, get, create, update, delete, publish)
  * 6 tag tools (list, get, create, update, delete, usage)
- 17 React apps (dark theme, standalone Vite, client-side state):
  * contact-dashboard, contact-detail, contact-grid
  * campaign-dashboard, campaign-detail, campaign-builder
  * list-manager, segment-builder, template-gallery
  * report-dashboard, report-detail, bounce-report, engagement-chart
  * landing-page-grid, social-manager, tag-manager, import-wizard
- Full TypeScript types for Constant Contact API v3
- Production-ready: server.ts, main.ts (stdio), comprehensive README
- .env.example, .gitignore, package.json with MCP SDK 1.0.4
2026-02-12 17:22:25 -05:00

176 lines
4.7 KiB
TypeScript

import type { ConstantContactClient } from '../clients/constant-contact.js';
import type { Segment } from '../types/index.js';
export function registerSegmentsTools(client: ConstantContactClient) {
return {
// List segments
segments_list: {
description: 'List all contact segments',
parameters: {
type: 'object',
properties: {
limit: {
type: 'number',
description: 'Maximum number of segments to return'
}
}
},
handler: async (args: any) => {
const params = args.limit ? { limit: args.limit } : undefined;
return await client.getPaginated<Segment>('/segments', params, args.limit);
}
},
// Get segment by ID
segments_get: {
description: 'Get a specific segment by ID',
parameters: {
type: 'object',
properties: {
segment_id: {
type: 'string',
description: 'Segment ID',
required: true
}
},
required: ['segment_id']
},
handler: async (args: any) => {
return await client.get<Segment>(`/segments/${args.segment_id}`);
}
},
// Create segment
segments_create: {
description: 'Create a new contact segment with criteria',
parameters: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Segment name',
required: true
},
segment_criteria: {
type: 'string',
description: 'JSON string defining segment criteria (e.g., {"field":"email_domain","operator":"equals","value":"gmail.com"})',
required: true
}
},
required: ['name', 'segment_criteria']
},
handler: async (args: any) => {
let criteria;
try {
criteria = typeof args.segment_criteria === 'string'
? JSON.parse(args.segment_criteria)
: args.segment_criteria;
} catch {
throw new Error('Invalid segment_criteria JSON');
}
const segmentData = {
name: args.name,
segment_criteria: criteria
};
return await client.post<Segment>('/segments', segmentData);
}
},
// Update segment
segments_update: {
description: 'Update an existing segment',
parameters: {
type: 'object',
properties: {
segment_id: {
type: 'string',
description: 'Segment ID',
required: true
},
name: {
type: 'string',
description: 'New segment name'
},
segment_criteria: {
type: 'string',
description: 'JSON string of updated segment criteria'
}
},
required: ['segment_id']
},
handler: async (args: any) => {
const { segment_id, ...updates } = args;
if (updates.segment_criteria) {
try {
updates.segment_criteria = typeof updates.segment_criteria === 'string'
? JSON.parse(updates.segment_criteria)
: updates.segment_criteria;
} catch {
throw new Error('Invalid segment_criteria JSON');
}
}
return await client.put<Segment>(`/segments/${segment_id}`, updates);
}
},
// Delete segment
segments_delete: {
description: 'Delete a segment',
parameters: {
type: 'object',
properties: {
segment_id: {
type: 'string',
description: 'Segment ID to delete',
required: true
}
},
required: ['segment_id']
},
handler: async (args: any) => {
await client.delete(`/segments/${args.segment_id}`);
return { success: true, message: `Segment ${args.segment_id} deleted` };
}
},
// Get segment contacts
segments_get_contacts: {
description: 'Get all contacts in a segment',
parameters: {
type: 'object',
properties: {
segment_id: {
type: 'string',
description: 'Segment ID',
required: true
},
limit: {
type: 'number',
description: 'Maximum number of contacts to return'
}
},
required: ['segment_id']
},
handler: async (args: any) => {
const params: any = {
segment_ids: args.segment_id
};
if (args.limit) params.limit = args.limit;
const contacts = await client.getPaginated(
'/contacts',
params,
args.limit
);
return { contacts, count: contacts.length };
}
}
};
}