Skip to main content
Glama
server.ts18.2 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { fetchAllTemplates } from "./apicalls/fetchMailmodoTemplates"; import { fetchAllCampaigns, fetchCampaignReport } from "./apicalls/fetchMailmodoCampaigns"; import { eventPropertiesSchema } from "./types/addCustomEventsTypes"; import { addMailmodoEvent } from "./apicalls/sendEvents"; import { addContactToList, bulkAddContactToList, deleteContact, getAllContactLists, getContactDetails, removeContactFromList, resubscribeContact, unsubscribeContact } from "./apicalls/contactManagement"; import { contactPropertiesSchema, datetimeSchema, timezoneRegex } from "./types/addContactsTypes"; import { bulkTriggerMailmodoCampaign, triggerMailmodoCampaign } from "./apicalls/sendCampaign"; import z from "zod"; export function createMcpServer(mmApiKey: string){ const server = new McpServer({ name: "Demo", version: "1.0.0" }); server.resource( "Mailmodo Templates", "mailmodo://templates", {"mimeType": "application/json"}, async (uri) => { const templates = await fetchAllTemplates(mmApiKey); return { contents: [{ uri: uri.href, text: JSON.stringify(templates.templateDetails) }] } } ); server.resource( "Mailmodo Campaigns", "mailmodo://campaigns", {"mimeType": "application/json"}, async (uri) => { const campaigns = await fetchAllCampaigns(mmApiKey); return { contents: [{ uri: uri.href, text: JSON.stringify(campaigns.campaigns) }] } } ); server.resource( "Mailmodo Contact Lists", "mailmodo://contact-lists", {"mimeType": "application/json"}, async (uri) => { const contactLists = await getAllContactLists(mmApiKey); return { contents: [{ uri: uri.href, text: JSON.stringify(contactLists.listDetails) }] } } ); server.tool( "userDetails", "Tool to get all details of a contact ", { email: z.string(), }, async ({ email }) => { const details = await getContactDetails(mmApiKey,email); return{ content: [{ type: "text", text: JSON.stringify(details) }] }} ); server.tool("MailmodoCampainReportTool", "Tool to get the campaign reports for a particular campaign like open, click submission count etc", { campaignId: z.string().uuid(), fromDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, { message: 'fromDate must be in YYYY-MM-DD format', }), toDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, { message: 'toDate must be in YYYY-MM-DD format', }), }, async ({ campaignId, fromDate, toDate }) => { const campaigns = await fetchCampaignReport(mmApiKey,campaignId,fromDate,toDate); return { content: [{ type: "text", text: campaigns.success? JSON.stringify(campaigns.data): "Something went wrong. Please check if correct campaign ID is being passed" }] } } ); server.tool("currentDateTime", "Get Current Date and time for timezone. if timezone is not passed default to UTC", { timezone: z.string().optional().nullable().describe(`The timezone should be a valid IANA timezone name like "America/New_York" or "Asia/Kolkata"`), }, (data) => { const formatter = new Intl.DateTimeFormat("en-US", { timeZone: data.timezone || "UTC", dateStyle: "full", timeStyle: "long", }); return { content: [{ type: "text", text: formatter.format(new Date()), }] } } ); server.tool( "sendEvent", "Send custom events with email, event name and event properties", { email: z.string(), event_name: z.string(), ts: z.number().optional(), event_properties: eventPropertiesSchema.optional(), }, async (params) => { try { const respone = await addMailmodoEvent(mmApiKey,params); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.success?`Successfully sent event '${params.event_name}' for email ${params.email} with payload: ${JSON.stringify(params.event_properties)} with reference id ${respone.ref}`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to send event", }], isError: true }; } } ); server.tool( "addContactToList", "Add Contact to list ", { email: z.string(), listName: z.string(), data: contactPropertiesSchema.optional(), created_at: datetimeSchema.optional(), last_click: datetimeSchema.optional(), last_open: datetimeSchema.optional(), timezone: z .string() .regex( timezoneRegex, { message: "Must be a valid region-format timezone string" } ) .optional(), }, async (params) => { try { const respone = await addContactToList(mmApiKey,params); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.success?`Successfully added contact '${params.email}' to list ${params.listName} with message ${respone.message}.`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to send event", }], isError: true }; } } ); server.tool( "addBulkContactToList", "Add Many Contact to a list in single API", { listName: z.string(), values: z.array(z.object({ email: z.string(), data: contactPropertiesSchema.optional(), created_at: datetimeSchema.optional(), last_click: datetimeSchema.optional(), last_open: datetimeSchema.optional(), timezone: z .string() .regex( timezoneRegex, { message: "Must be a valid region-format timezone string" } ) .optional(), })) }, async (params) => { try { const respone = await bulkAddContactToList(mmApiKey,params); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.listId != '' ?`Successfully added '${params.values.length}' contacts to list ${params.listName} with message ${respone.message}.`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to send event", }], isError: true }; } } ); server.tool( "unsubscribeContact", "Unsubscribe or supress contact in mailmodo", { email: z.string() }, async (params) => { try { const respone = await unsubscribeContact(mmApiKey,params.email); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.success ?`Successfully unsubscribed '${params.email} with message ${respone.message}.`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to unsubscribe", }], isError: true }; } } ); server.tool( "resubscribeContact", "Resubscribe contact in mailmodo", { email: z.string() }, async (params) => { try { const respone = await resubscribeContact(mmApiKey,params.email); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.success ?`Successfully resubscribed '${params.email} with message ${respone.message}.`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to unsubscribe", }], isError: true }; } } ); server.tool( "archiveContact", "permanently archive contact in mailmodo", { email: z.string() }, async (params) => { try { const respone = await deleteContact(mmApiKey,params.email); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.success ?`Successfully deleted '${params.email} with message ${respone.message}`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to delete", }], isError: true }; } } ); server.tool( "removeContactFromList", "Remove a particular contact from the contact list", { email: z.string(), listName: z.string(), }, async (params) => { try { const respone = await removeContactFromList(mmApiKey, params.email, params.listName); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.message ?`Successfully reomved '${params.email} from the list ${params.listName} with message ${respone.message}.`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to delete", }], isError: true }; } } ); server.tool( "sendEmailToCampaign", "Trigger and email for email campaign trigger with personalization parameter added to the email template. ", { campaignId: z.string().describe('Camapign id of the campaign to be triggered'), email: z .string() .email({ message: "Invalid email address" }) .describe("Email address of the contact to whom you want to send the email. This is required."), subject: z .string() .optional() .describe("Optional: Overrides the default subject line provided when creating the campaign."), replyTo: z .string() .optional() .describe("Optional: Overrides the default reply-to email address for the campaign."), fromName: z .string() .optional() .describe("Optional: Overrides the sender name for the campaign."), campaign_data: z .record(z.string()) .optional() .describe("Optional: Transient personalization parameters, not stored in the contact profile."), data: z .record(z.string()) .optional() .describe("Optional: Personalization parameters saved to the contact's profile."), addToList: z .string() .optional() .describe("Optional: List ID to which the contact should be added as part of triggering the campaign."), }, async (params) => { try { const { campaignId, ...newparams } = params; const respone = await triggerMailmodoCampaign(mmApiKey, params.campaignId, newparams); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.message ?`Successfully sent email to '${params.email} for the campaignId ${params.campaignId} with message ${respone.message}.`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to delete", }], isError: true }; } } ); server.tool( "broadcastCampaignToList", "The broadcast campaign API allows the user to trigger campaigns to the entire contact list using a single API request.", { campaignId: z.string().describe('Campaign id of the campaign to be triggered'), listId: z .string({ required_error: 'listId is required', invalid_type_error: 'listId must be a string', }) .describe('Id of the contact list or segment for which the campaign should be triggered.'), subject: z .string() .optional() .describe('Optional subject line of the campaign. This will appear as the subject of the email sent to recipients.'), idempotencyKey: z .string() .optional() .describe('Optional unique key to allow retries of the same campaign within 24 hours. Allows safe resending. For example: "2024-09-05T17:00:00Z".'), campaign_data: z .record(z.string()) .optional() .describe('Optional set of personalization parameters for the campaign. Each key represents a variable (e.g., "first_name") to be used in the email template. If a key is missing, the backend will fetch values from contact properties or default to an empty string.'), }, async (params) => { try { const { campaignId, ...newparams } = params; const respone = await bulkTriggerMailmodoCampaign(mmApiKey, params.campaignId, newparams); // Here you would typically integrate with your event sending system // For example: eventBus.emit(eventName, eventData) // For demonstration, we'll just return a success message return { content: [{ type: "text", text: respone.message ?`Successfully sent email to '${params.listId} for the campaignId ${params.campaignId} with message ${respone.message}.`: `Something went wrong. Please check if the email is correct`, }] }; } catch (error) { return { content: [{ type: "text", text: error instanceof Error ? error.message : "Failed to delete", }], isError: true }; } } ); return server; };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mailmodo/mailmodo-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server