|
| 1 | +import { z } from "zod"; |
| 2 | +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; |
| 3 | + |
| 4 | +/** |
| 5 | + * Register reusable prompt templates on the MCP server. |
| 6 | + * These appear in Claude's "Prompts" panel and help users |
| 7 | + * quickly scaffold common OpenMail workflows. |
| 8 | + */ |
| 9 | +export function registerPrompts(server: McpServer) { |
| 10 | + // ── 1. Create a campaign ───────────────────────────────────────────────────── |
| 11 | + server.registerPrompt( |
| 12 | + "create-campaign", |
| 13 | + { |
| 14 | + title: "Create Email Campaign", |
| 15 | + description: |
| 16 | + "Design a complete event-triggered email campaign: define the trigger event, write the email sequence, set up segments, and activate it.", |
| 17 | + argsSchema: { |
| 18 | + goal: z.string().describe("What should this campaign achieve? e.g. 'onboard new users', 'recover churned customers'"), |
| 19 | + triggerEvent: z.string().optional().describe("The event that starts the campaign, e.g. 'user_signed_up'"), |
| 20 | + emailCount: z.number().optional().describe("How many emails in the sequence (default: 3)"), |
| 21 | + }, |
| 22 | + }, |
| 23 | + ({ goal, triggerEvent, emailCount = 3 }) => ({ |
| 24 | + messages: [ |
| 25 | + { |
| 26 | + role: "user", |
| 27 | + content: { |
| 28 | + type: "text", |
| 29 | + text: `You are an email marketing expert using OpenMail. Create a complete email campaign to achieve this goal: |
| 30 | +
|
| 31 | +**Goal:** ${goal} |
| 32 | +${triggerEvent ? `**Trigger event:** \`${triggerEvent}\`` : ""} |
| 33 | +**Email count:** ${emailCount} emails in the sequence |
| 34 | +
|
| 35 | +Use the OpenMail MCP tools to: |
| 36 | +1. First, call \`list_templates\` to see available templates |
| 37 | +2. Create or reuse email templates for each step |
| 38 | +3. Create a campaign with the appropriate trigger (event, segment_enter, or manual) |
| 39 | +4. Add campaign steps (email + wait nodes) in the right sequence |
| 40 | +5. Activate the campaign |
| 41 | +
|
| 42 | +Suggest realistic timing between emails and write compelling subject lines and preview text for each.`, |
| 43 | + }, |
| 44 | + }, |
| 45 | + ], |
| 46 | + }) |
| 47 | + ); |
| 48 | + |
| 49 | + // ── 2. Write and send a broadcast ─────────────────────────────────────────── |
| 50 | + server.registerPrompt( |
| 51 | + "create-broadcast", |
| 52 | + { |
| 53 | + title: "Write & Send Broadcast Email", |
| 54 | + description: |
| 55 | + "Draft a broadcast email to a segment, write compelling copy, review it, and send or schedule it.", |
| 56 | + argsSchema: { |
| 57 | + topic: z.string().describe("What is this email about? e.g. 'new feature launch', 'Black Friday sale'"), |
| 58 | + audience: z.string().optional().describe("Who is the target audience? e.g. 'all pro users', 'inactive users'"), |
| 59 | + tone: z.enum(["professional", "casual", "urgent", "celebratory"]).optional().describe("Email tone"), |
| 60 | + scheduledAt: z.string().optional().describe("ISO 8601 datetime to schedule, or omit to send immediately"), |
| 61 | + }, |
| 62 | + }, |
| 63 | + ({ topic, audience = "all active contacts", tone = "professional", scheduledAt }) => ({ |
| 64 | + messages: [ |
| 65 | + { |
| 66 | + role: "user", |
| 67 | + content: { |
| 68 | + type: "text", |
| 69 | + text: `You are an email copywriter and marketer using OpenMail. Write and send a broadcast email. |
| 70 | +
|
| 71 | +**Topic:** ${topic} |
| 72 | +**Audience:** ${audience} |
| 73 | +**Tone:** ${tone} |
| 74 | +${scheduledAt ? `**Schedule for:** ${scheduledAt}` : "**Send:** immediately"} |
| 75 | +
|
| 76 | +Use the OpenMail MCP tools to: |
| 77 | +1. \`list_segments\` — find the right segment for this audience (create one with \`create_segment\` if needed) |
| 78 | +2. Write the full HTML email with a compelling subject line, preview text, and clear CTA |
| 79 | +3. \`create_broadcast\` — create the draft with the HTML content |
| 80 | +4. \`send_broadcast\` (or schedule with \`scheduledAt\`) when ready |
| 81 | +5. After sending, call \`get_broadcast_analytics\` to check performance |
| 82 | +
|
| 83 | +Write professional, concise copy that respects the subscriber's time. Include an unsubscribe link in the footer.`, |
| 84 | + }, |
| 85 | + }, |
| 86 | + ], |
| 87 | + }) |
| 88 | + ); |
| 89 | + |
| 90 | + // ── 3. Build a segment ─────────────────────────────────────────────────────── |
| 91 | + server.registerPrompt( |
| 92 | + "build-segment", |
| 93 | + { |
| 94 | + title: "Build Audience Segment", |
| 95 | + description: |
| 96 | + "Define a dynamic audience segment using contact attributes, event history, or group membership.", |
| 97 | + argsSchema: { |
| 98 | + description: z.string().describe("Describe the audience in plain English, e.g. 'users on pro plan who upgraded in last 30 days'"), |
| 99 | + purpose: z.string().optional().describe("How will this segment be used? e.g. 'upsell campaign', 'churn prevention'"), |
| 100 | + }, |
| 101 | + }, |
| 102 | + ({ description, purpose }) => ({ |
| 103 | + messages: [ |
| 104 | + { |
| 105 | + role: "user", |
| 106 | + content: { |
| 107 | + type: "text", |
| 108 | + text: `You are a data analyst using OpenMail. Build a precise audience segment. |
| 109 | +
|
| 110 | +**Audience:** ${description} |
| 111 | +${purpose ? `**Purpose:** ${purpose}` : ""} |
| 112 | +
|
| 113 | +Use the OpenMail MCP tools to: |
| 114 | +1. \`get_analytics\` — understand the current workspace data |
| 115 | +2. Translate the description into segment conditions using these field types: |
| 116 | + - \`attributes.<key>\` — contact attributes (eq, ne, contains, gt, lt, gte, lte, is_set, is_not_set) |
| 117 | + - \`event.<event_name>\` — whether a contact has triggered an event (is_set / is_not_set) |
| 118 | + - \`group.<group_type>\` — group membership (eq with group_key value) |
| 119 | + - Standard fields: \`email\`, \`firstName\`, \`lastName\`, \`unsubscribed\` |
| 120 | +3. \`create_segment\` — create the segment with conditionLogic "and" or "or" |
| 121 | +4. \`list_segments\` with the new segment id to verify it returns the expected contacts |
| 122 | +
|
| 123 | +Explain your condition logic and estimated audience size.`, |
| 124 | + }, |
| 125 | + }, |
| 126 | + ], |
| 127 | + }) |
| 128 | + ); |
| 129 | + |
| 130 | + // ── 4. Analyze performance ─────────────────────────────────────────────────── |
| 131 | + server.registerPrompt( |
| 132 | + "analyze-performance", |
| 133 | + { |
| 134 | + title: "Analyze Email Performance", |
| 135 | + description: "Pull analytics data and provide actionable recommendations to improve open rates, click rates, and conversions.", |
| 136 | + argsSchema: { |
| 137 | + scope: z.enum(["workspace", "broadcast"]).optional().describe("Analyze the whole workspace or a specific broadcast"), |
| 138 | + broadcastId: z.string().optional().describe("Broadcast ID if scope=broadcast"), |
| 139 | + }, |
| 140 | + }, |
| 141 | + ({ scope = "workspace", broadcastId }) => ({ |
| 142 | + messages: [ |
| 143 | + { |
| 144 | + role: "user", |
| 145 | + content: { |
| 146 | + type: "text", |
| 147 | + text: `You are an email marketing analyst using OpenMail. Analyze performance and give recommendations. |
| 148 | +
|
| 149 | +**Scope:** ${scope}${broadcastId ? ` — broadcast ${broadcastId}` : ""} |
| 150 | +
|
| 151 | +Use the OpenMail MCP tools to: |
| 152 | +${ |
| 153 | + scope === "broadcast" && broadcastId |
| 154 | + ? `1. \`get_broadcast_analytics\` for broadcast ${broadcastId} |
| 155 | +2. \`get_analytics\` for workspace baseline comparison` |
| 156 | + : `1. \`get_analytics\` for 30-day workspace overview |
| 157 | +2. \`list_broadcasts\` — review recent broadcasts` |
| 158 | +} |
| 159 | +
|
| 160 | +Then provide: |
| 161 | +- **Summary**: Key metrics (open rate %, click rate %, unsubscribes) |
| 162 | +- **Benchmarks**: Compare to industry averages (SaaS: ~25% open, ~3% click) |
| 163 | +- **Top issues**: What's underperforming and why |
| 164 | +- **3 concrete actions**: Specific changes to improve results (subject lines, send times, segmentation, etc.)`, |
| 165 | + }, |
| 166 | + }, |
| 167 | + ], |
| 168 | + }) |
| 169 | + ); |
| 170 | + |
| 171 | + // ── 5. Track events setup ──────────────────────────────────────────────────── |
| 172 | + server.registerPrompt( |
| 173 | + "setup-event-tracking", |
| 174 | + { |
| 175 | + title: "Set Up Event Tracking", |
| 176 | + description: "Generate the exact code to track user events from your app to trigger OpenMail campaigns.", |
| 177 | + argsSchema: { |
| 178 | + stack: z.enum(["nextjs", "react", "node", "python", "curl"]).describe("Your tech stack"), |
| 179 | + events: z.string().describe("Comma-separated events to track, e.g. 'user_signed_up, plan_upgraded, feature_used'"), |
| 180 | + apiUrl: z.string().optional().describe("Your OpenMail API URL (defaults to https://api.openmail.win)"), |
| 181 | + }, |
| 182 | + }, |
| 183 | + ({ stack, events, apiUrl = "https://api.openmail.win" }) => ({ |
| 184 | + messages: [ |
| 185 | + { |
| 186 | + role: "user", |
| 187 | + content: { |
| 188 | + type: "text", |
| 189 | + text: `Generate complete, production-ready code to track these events from a ${stack} app to OpenMail. |
| 190 | +
|
| 191 | +**Stack:** ${stack} |
| 192 | +**Events to track:** ${events} |
| 193 | +**API URL:** ${apiUrl} |
| 194 | +
|
| 195 | +Requirements: |
| 196 | +- Use the @openmail/sdk package (npm install @openmail/sdk) |
| 197 | +- Show identify() call on user login/signup |
| 198 | +- Show track() calls for each event with relevant properties |
| 199 | +- Show group() call if tracking workspace/org memberships |
| 200 | +- Include proper error handling and flush() before process exit |
| 201 | +- Add comments explaining WHAT triggers each event |
| 202 | +
|
| 203 | +For Next.js: show both client-side (useTrack hook) and server-side (serverTrack) patterns. |
| 204 | +For Node: show the singleton pattern with flush on shutdown. |
| 205 | +
|
| 206 | +The API key should come from environment variables (OPENMAIL_API_KEY server-side, NEXT_PUBLIC_OPENMAIL_KEY client-side).`, |
| 207 | + }, |
| 208 | + }, |
| 209 | + ], |
| 210 | + }) |
| 211 | + ); |
| 212 | + |
| 213 | + // ── 6. Group identify workflow ─────────────────────────────────────────────── |
| 214 | + server.registerPrompt( |
| 215 | + "group-identify", |
| 216 | + { |
| 217 | + title: "Group Identify Workflow", |
| 218 | + description: "Set up group/organization tracking: link contacts to companies or teams, and use group membership in segments.", |
| 219 | + argsSchema: { |
| 220 | + groupType: z.string().optional().describe("Type of group: company, team, project (default: company)"), |
| 221 | + useCase: z.string().describe("What are you grouping? e.g. 'SaaS customers by company', 'users by team within a company'"), |
| 222 | + }, |
| 223 | + }, |
| 224 | + ({ groupType = "company", useCase }) => ({ |
| 225 | + messages: [ |
| 226 | + { |
| 227 | + role: "user", |
| 228 | + content: { |
| 229 | + type: "text", |
| 230 | + text: `You are setting up group/organization tracking in OpenMail. |
| 231 | +
|
| 232 | +**Group type:** ${groupType} |
| 233 | +**Use case:** ${useCase} |
| 234 | +
|
| 235 | +Use the OpenMail MCP tools to: |
| 236 | +1. Check existing groups: \`list_contacts\` to understand your data model |
| 237 | +2. Create a sample group to test the workflow — use the \`create_group\` tool if available or the /api/v1/groups REST endpoint |
| 238 | +3. Link contacts to the group |
| 239 | +4. Create a segment that filters by group membership: |
| 240 | + - Field: \`group.${groupType}\` |
| 241 | + - Operator: \`eq\` with the group key as value |
| 242 | +5. Verify the segment returns the right contacts |
| 243 | +
|
| 244 | +Also explain how to track group identify from code: |
| 245 | +\`\`\`ts |
| 246 | +// Server-side |
| 247 | +await openmail.group("acme-corp", { name: "Acme Corp", plan: "enterprise" }, { |
| 248 | + userId: "alice@example.com", |
| 249 | + groupType: "${groupType}", |
| 250 | +}); |
| 251 | +
|
| 252 | +// Then use in segments: |
| 253 | +// { field: "group.${groupType}", operator: "eq", value: "acme-corp" } |
| 254 | +\`\`\``, |
| 255 | + }, |
| 256 | + }, |
| 257 | + ], |
| 258 | + }) |
| 259 | + ); |
| 260 | +} |
0 commit comments