@emailens/mcp 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -5
- package/dist/index.js +177 -11
- package/dist/index.js.map +1 -1
- package/package.json +59 -56
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ claude mcp add emailens -- npx -y @emailens/mcp
|
|
|
50
50
|
|
|
51
51
|
## Tools
|
|
52
52
|
|
|
53
|
-
The server exposes
|
|
53
|
+
The server exposes four tools:
|
|
54
54
|
|
|
55
55
|
### `preview_email`
|
|
56
56
|
|
|
@@ -88,13 +88,31 @@ Quick CSS compatibility analysis — returns warnings and per-client scores with
|
|
|
88
88
|
**Example prompt:**
|
|
89
89
|
> What's the compatibility score for this React Email template?
|
|
90
90
|
|
|
91
|
+
### `fix_email`
|
|
92
|
+
|
|
93
|
+
Generate a structured fix prompt for email compatibility issues. Analyzes the HTML, classifies each warning as CSS-only or structural (requires HTML restructuring), estimates token usage, and returns a markdown prompt the AI assistant can apply directly. Use after `preview_email` or `analyze_email` to fix the issues found.
|
|
94
|
+
|
|
95
|
+
**Parameters:**
|
|
96
|
+
|
|
97
|
+
| Name | Type | Required | Description |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| `html` | string | Yes | The email HTML source code to fix |
|
|
100
|
+
| `format` | enum | No | Input format: `"html"` (default), `"jsx"`, `"mjml"`, or `"maizzle"`. Controls the fix syntax. |
|
|
101
|
+
| `scope` | enum | No | `"all"` (default) or `"current"` (requires `selectedClientId`) |
|
|
102
|
+
| `selectedClientId` | string | No | Client ID to scope fixes to (e.g. `"outlook-windows"`). Only used when scope is `"current"`. |
|
|
103
|
+
|
|
104
|
+
**Returns:** JSON with `totalWarnings`, `structuralWarnings`, `cssWarnings`, `tokenEstimate`, and the full fix prompt as markdown.
|
|
105
|
+
|
|
106
|
+
**Example prompt:**
|
|
107
|
+
> Fix this email HTML so it works in Outlook Windows — it uses flexbox and word-break which aren't supported.
|
|
108
|
+
|
|
91
109
|
### `list_clients`
|
|
92
110
|
|
|
93
|
-
Lists all 12 supported email client IDs and
|
|
111
|
+
Lists all 12 supported email client IDs, display names, categories, rendering engines, and dark mode support. Useful for discovering valid client IDs to pass to `preview_email` or `fix_email`.
|
|
94
112
|
|
|
95
113
|
**Parameters:** None
|
|
96
114
|
|
|
97
|
-
**Returns:** JSON array of `{ id, name }` objects.
|
|
115
|
+
**Returns:** JSON array of `{ id, name, category, engine, darkModeSupport }` objects.
|
|
98
116
|
|
|
99
117
|
**Example prompt:**
|
|
100
118
|
> What email clients does Emailens support?
|
|
@@ -125,15 +143,24 @@ When you specify a `format`, the fix snippets in warnings are tailored to your f
|
|
|
125
143
|
- **`maizzle`** — References Tailwind CSS classes and Maizzle config (`googleFonts`, MSO conditionals)
|
|
126
144
|
- **`html`** (default) — Generic HTML with VML fallbacks for Outlook
|
|
127
145
|
|
|
146
|
+
## Fix Types (v0.2.0)
|
|
147
|
+
|
|
148
|
+
Every warning now includes a `fixType` field:
|
|
149
|
+
|
|
150
|
+
- **`css`** — Can be fixed with CSS swaps or fallbacks
|
|
151
|
+
- **`structural`** — Requires HTML restructuring (tables, VML, MSO conditionals). CSS-only changes will NOT work.
|
|
152
|
+
|
|
153
|
+
The `fix_email` tool uses this classification to generate targeted fix instructions that the AI assistant can apply directly.
|
|
154
|
+
|
|
128
155
|
## How It Works
|
|
129
156
|
|
|
130
157
|
The MCP server is a thin wrapper around `@emailens/engine`. For each tool call, it:
|
|
131
158
|
|
|
132
159
|
1. Validates inputs with Zod schemas
|
|
133
|
-
2. Calls engine functions (`transformForAllClients`, `analyzeEmail`, `generateCompatibilityScore`, `simulateDarkMode`)
|
|
160
|
+
2. Calls engine functions (`transformForAllClients`, `analyzeEmail`, `generateCompatibilityScore`, `simulateDarkMode`, `generateFixPrompt`, `estimateAiFixTokens`)
|
|
134
161
|
3. Formats and returns JSON results via stdio transport
|
|
135
162
|
|
|
136
|
-
The engine uses [Cheerio](https://cheerio.js.org/) for HTML manipulation and [css-tree](https://github.com/csstree/csstree) for CSS parsing. No external API calls — all analysis runs locally.
|
|
163
|
+
The engine uses [Cheerio](https://cheerio.js.org/) for HTML manipulation and [css-tree](https://github.com/csstree/csstree) for CSS parsing. No external API calls — all analysis and fix prompt generation runs locally. The `fix_email` tool generates a prompt for the AI assistant to apply — it does not call any AI API itself.
|
|
137
164
|
|
|
138
165
|
## Development
|
|
139
166
|
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
analyzeEmail,
|
|
11
11
|
generateCompatibilityScore,
|
|
12
12
|
simulateDarkMode,
|
|
13
|
+
generateFixPrompt,
|
|
14
|
+
estimateAiFixTokens,
|
|
15
|
+
auditEmail,
|
|
16
|
+
structuralWarnings,
|
|
13
17
|
EMAIL_CLIENTS
|
|
14
18
|
} from "@emailens/engine";
|
|
15
19
|
function toFramework(format) {
|
|
@@ -18,7 +22,7 @@ function toFramework(format) {
|
|
|
18
22
|
}
|
|
19
23
|
var server = new McpServer({
|
|
20
24
|
name: "emailens",
|
|
21
|
-
version: "0.
|
|
25
|
+
version: "0.3.0"
|
|
22
26
|
});
|
|
23
27
|
server.registerTool(
|
|
24
28
|
"preview_email",
|
|
@@ -96,7 +100,8 @@ server.registerTool(
|
|
|
96
100
|
severity: w.severity,
|
|
97
101
|
message: w.message,
|
|
98
102
|
suggestion: w.suggestion,
|
|
99
|
-
fix: w.fix
|
|
103
|
+
fix: w.fix,
|
|
104
|
+
fixType: w.fixType
|
|
100
105
|
})),
|
|
101
106
|
clientCount: transforms.length,
|
|
102
107
|
darkModeWarnings: Object.entries(darkMode).reduce(
|
|
@@ -117,11 +122,78 @@ server.registerTool(
|
|
|
117
122
|
};
|
|
118
123
|
}
|
|
119
124
|
);
|
|
125
|
+
server.registerTool(
|
|
126
|
+
"audit_email",
|
|
127
|
+
{
|
|
128
|
+
title: "Audit Email",
|
|
129
|
+
description: "Run ALL email checks in one call: CSS compatibility, spam scoring, link validation, accessibility audit, and image analysis. Returns a unified report. Use --skip to omit specific checks.",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
html: z.string().describe("The email HTML source code"),
|
|
132
|
+
format: z.enum(["html", "jsx", "mjml", "maizzle"]).optional().describe(
|
|
133
|
+
"Input format: 'html' (default), 'jsx' (React Email), 'mjml', or 'maizzle'."
|
|
134
|
+
),
|
|
135
|
+
skip: z.array(z.enum(["spam", "links", "accessibility", "images", "compatibility"])).optional().describe(
|
|
136
|
+
"Array of checks to skip (e.g. ['spam', 'images'])."
|
|
137
|
+
)
|
|
138
|
+
},
|
|
139
|
+
annotations: {
|
|
140
|
+
title: "Audit Email",
|
|
141
|
+
readOnlyHint: true,
|
|
142
|
+
destructiveHint: false,
|
|
143
|
+
idempotentHint: true,
|
|
144
|
+
openWorldHint: false
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
async ({ html, format, skip }) => {
|
|
148
|
+
const report = auditEmail(html, {
|
|
149
|
+
framework: toFramework(format),
|
|
150
|
+
skip
|
|
151
|
+
});
|
|
152
|
+
const scoreValues = Object.values(report.compatibility.scores);
|
|
153
|
+
const overallCompatibility = scoreValues.length > 0 ? Math.round(
|
|
154
|
+
scoreValues.reduce(
|
|
155
|
+
(a, b) => a + b.score,
|
|
156
|
+
0
|
|
157
|
+
) / scoreValues.length
|
|
158
|
+
) : 0;
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: JSON.stringify(
|
|
164
|
+
{
|
|
165
|
+
overallCompatibility,
|
|
166
|
+
compatibility: {
|
|
167
|
+
scores: report.compatibility.scores,
|
|
168
|
+
warningCount: report.compatibility.warnings.length,
|
|
169
|
+
warnings: report.compatibility.warnings.map((w) => ({
|
|
170
|
+
client: w.client,
|
|
171
|
+
property: w.property,
|
|
172
|
+
severity: w.severity,
|
|
173
|
+
message: w.message,
|
|
174
|
+
suggestion: w.suggestion,
|
|
175
|
+
fix: w.fix,
|
|
176
|
+
fixType: w.fixType
|
|
177
|
+
}))
|
|
178
|
+
},
|
|
179
|
+
spam: report.spam,
|
|
180
|
+
links: report.links,
|
|
181
|
+
accessibility: report.accessibility,
|
|
182
|
+
images: report.images
|
|
183
|
+
},
|
|
184
|
+
null,
|
|
185
|
+
2
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
);
|
|
120
192
|
server.registerTool(
|
|
121
193
|
"analyze_email",
|
|
122
194
|
{
|
|
123
195
|
title: "Analyze Email",
|
|
124
|
-
description: "
|
|
196
|
+
description: "CSS compatibility analysis with quality reports \u2014 returns warnings, per-client scores, plus spam, link, accessibility, and image analysis. For compatibility-only results, use skip. For full transforms + dark mode, use preview_email.",
|
|
125
197
|
inputSchema: {
|
|
126
198
|
html: z.string().describe("The email HTML source code"),
|
|
127
199
|
format: z.enum(["html", "jsx", "mjml", "maizzle"]).optional().describe(
|
|
@@ -137,9 +209,8 @@ server.registerTool(
|
|
|
137
209
|
}
|
|
138
210
|
},
|
|
139
211
|
async ({ html, format }) => {
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
const scoreValues = Object.values(scores);
|
|
212
|
+
const report = auditEmail(html, { framework: toFramework(format) });
|
|
213
|
+
const scoreValues = Object.values(report.compatibility.scores);
|
|
143
214
|
const overallScore = scoreValues.length > 0 ? Math.round(
|
|
144
215
|
scoreValues.reduce(
|
|
145
216
|
(a, b) => a + b.score,
|
|
@@ -153,20 +224,115 @@ server.registerTool(
|
|
|
153
224
|
text: JSON.stringify(
|
|
154
225
|
{
|
|
155
226
|
overallScore,
|
|
156
|
-
scores,
|
|
157
|
-
warningCount: warnings.length,
|
|
158
|
-
warnings: warnings.map((w) => ({
|
|
227
|
+
scores: report.compatibility.scores,
|
|
228
|
+
warningCount: report.compatibility.warnings.length,
|
|
229
|
+
warnings: report.compatibility.warnings.map((w) => ({
|
|
159
230
|
client: w.client,
|
|
160
231
|
property: w.property,
|
|
161
232
|
severity: w.severity,
|
|
162
233
|
message: w.message,
|
|
163
234
|
suggestion: w.suggestion,
|
|
164
|
-
fix: w.fix
|
|
165
|
-
|
|
235
|
+
fix: w.fix,
|
|
236
|
+
fixType: w.fixType
|
|
237
|
+
})),
|
|
238
|
+
spam: report.spam,
|
|
239
|
+
links: report.links,
|
|
240
|
+
accessibility: report.accessibility,
|
|
241
|
+
images: report.images
|
|
242
|
+
},
|
|
243
|
+
null,
|
|
244
|
+
2
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
server.registerTool(
|
|
252
|
+
"fix_email",
|
|
253
|
+
{
|
|
254
|
+
title: "Fix Email",
|
|
255
|
+
description: "Generate a structured fix prompt for email compatibility issues. Returns a markdown prompt with the original code, all detected issues (with fix type: CSS or structural), fix snippets, and format-specific instructions. The AI assistant can then apply these fixes directly. Use after preview_email or analyze_email to fix the issues found.",
|
|
256
|
+
inputSchema: {
|
|
257
|
+
html: z.string().describe("The email HTML source code to fix"),
|
|
258
|
+
format: z.enum(["html", "jsx", "mjml", "maizzle"]).optional().describe(
|
|
259
|
+
"Input format: 'html' (default), 'jsx' (React Email), 'mjml', or 'maizzle'. Controls the fix syntax in the prompt."
|
|
260
|
+
),
|
|
261
|
+
scope: z.enum(["all", "current"]).optional().describe(
|
|
262
|
+
"Fix scope: 'all' (default) fixes for all clients, 'current' fixes for a single client (requires selectedClientId)."
|
|
263
|
+
),
|
|
264
|
+
selectedClientId: z.string().optional().describe(
|
|
265
|
+
"Client ID to scope fixes to (e.g. 'outlook-windows'). Only used when scope is 'current'."
|
|
266
|
+
)
|
|
267
|
+
},
|
|
268
|
+
annotations: {
|
|
269
|
+
title: "Fix Email",
|
|
270
|
+
readOnlyHint: true,
|
|
271
|
+
destructiveHint: false,
|
|
272
|
+
idempotentHint: true,
|
|
273
|
+
openWorldHint: false
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
async ({ html, format, scope, selectedClientId }) => {
|
|
277
|
+
const framework = toFramework(format);
|
|
278
|
+
const fixScope = scope === "current" ? "current" : "all";
|
|
279
|
+
const inputFormat = format || "html";
|
|
280
|
+
const warnings = analyzeEmail(html, framework);
|
|
281
|
+
const scores = generateCompatibilityScore(warnings);
|
|
282
|
+
if (warnings.length === 0) {
|
|
283
|
+
return {
|
|
284
|
+
content: [
|
|
285
|
+
{
|
|
286
|
+
type: "text",
|
|
287
|
+
text: JSON.stringify(
|
|
288
|
+
{ message: "No compatibility issues found \u2014 nothing to fix.", overallScore: 100 },
|
|
289
|
+
null,
|
|
290
|
+
2
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
const estimate = await estimateAiFixTokens({
|
|
297
|
+
originalHtml: html,
|
|
298
|
+
warnings,
|
|
299
|
+
scores,
|
|
300
|
+
scope: fixScope,
|
|
301
|
+
selectedClientId,
|
|
302
|
+
format: inputFormat
|
|
303
|
+
});
|
|
304
|
+
const prompt = generateFixPrompt({
|
|
305
|
+
originalHtml: html,
|
|
306
|
+
warnings,
|
|
307
|
+
scores,
|
|
308
|
+
scope: fixScope,
|
|
309
|
+
selectedClientId,
|
|
310
|
+
format: inputFormat
|
|
311
|
+
});
|
|
312
|
+
const structural = structuralWarnings(warnings);
|
|
313
|
+
return {
|
|
314
|
+
content: [
|
|
315
|
+
{
|
|
316
|
+
type: "text",
|
|
317
|
+
text: JSON.stringify(
|
|
318
|
+
{
|
|
319
|
+
totalWarnings: warnings.length,
|
|
320
|
+
structuralWarnings: structural.length,
|
|
321
|
+
cssWarnings: warnings.length - structural.length,
|
|
322
|
+
tokenEstimate: {
|
|
323
|
+
inputTokens: estimate.inputTokens,
|
|
324
|
+
estimatedOutputTokens: estimate.estimatedOutputTokens,
|
|
325
|
+
truncated: estimate.truncated
|
|
326
|
+
},
|
|
327
|
+
note: "Apply the fixes in the prompt below to the email code. Structural issues require HTML restructuring (tables, VML conditionals), not just CSS changes."
|
|
166
328
|
},
|
|
167
329
|
null,
|
|
168
330
|
2
|
|
169
331
|
)
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
type: "text",
|
|
335
|
+
text: prompt
|
|
170
336
|
}
|
|
171
337
|
]
|
|
172
338
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\r\nimport { z } from \"zod\";\r\nimport {\r\n transformForAllClients,\r\n analyzeEmail,\r\n generateCompatibilityScore,\r\n simulateDarkMode,\r\n EMAIL_CLIENTS,\r\n type Framework,\r\n type CSSWarning,\r\n} from \"@emailens/engine\";\r\n\r\nfunction toFramework(format?: string): Framework | undefined {\r\n if (format === \"jsx\" || format === \"mjml\" || format === \"maizzle\") return format;\r\n return undefined;\r\n}\r\n\r\nconst server = new McpServer({\r\n name: \"emailens\",\r\n version: \"0.1.0\",\r\n});\r\n\r\n// ── Tool: preview_email ─────────────────────────────────────────\r\nserver.registerTool(\r\n \"preview_email\",\r\n {\r\n title: \"Preview Email\",\r\n description:\r\n \"Analyze an HTML email and see how it renders across 12 email clients (Gmail, Outlook, Apple Mail, Yahoo, Samsung, Thunderbird, HEY, Superhuman). Returns CSS transforms, compatibility warnings with fix snippets, dark mode simulation, and per-client scores.\",\r\n inputSchema: {\r\n html: z.string().describe(\"The email HTML source code\"),\r\n clients: z\r\n .array(z.string())\r\n .optional()\r\n .describe(\r\n \"Optional array of client IDs to filter (e.g. ['gmail-web', 'outlook-windows']). Omit for all clients.\"\r\n ),\r\n format: z\r\n .enum([\"html\", \"jsx\", \"mjml\", \"maizzle\"])\r\n .optional()\r\n .describe(\r\n \"Input format of the email source: 'html' (default), 'jsx' (React Email), 'mjml', or 'maizzle'. Controls which framework-specific fix snippets appear in the warnings.\"\r\n ),\r\n },\r\n annotations: {\r\n title: \"Preview Email\",\r\n readOnlyHint: true,\r\n destructiveHint: false,\r\n idempotentHint: true,\r\n openWorldHint: false,\r\n },\r\n },\r\n async ({ html, clients, format }) => {\r\n const validClientIds = new Set(EMAIL_CLIENTS.map((c: { id: string }) => c.id));\r\n\r\n let transforms = transformForAllClients(html);\r\n if (clients) {\r\n const filter = clients.filter((c: string) => validClientIds.has(c));\r\n if (filter.length === 0) {\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n {\r\n error: \"No valid client IDs provided\",\r\n validClientIds: Array.from(validClientIds),\r\n },\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n isError: true,\r\n };\r\n }\r\n transforms = transforms.filter((t: { clientId: string }) =>\r\n filter.includes(t.clientId)\r\n );\r\n }\r\n\r\n const warnings = analyzeEmail(html, toFramework(format));\r\n const scores = generateCompatibilityScore(warnings);\r\n\r\n const darkMode: Record<string, { html: string; warnings: CSSWarning[] }> =\r\n {};\r\n for (const t of transforms) {\r\n darkMode[t.clientId] = simulateDarkMode(t.html, t.clientId);\r\n }\r\n\r\n const scoreValues = Object.values(scores);\r\n const overallScore =\r\n scoreValues.length > 0\r\n ? Math.round(\r\n scoreValues.reduce(\r\n (a: number, b: { score: number }) => a + b.score,\r\n 0\r\n ) / scoreValues.length\r\n )\r\n : 0;\r\n\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n {\r\n overallScore,\r\n compatibilityScores: scores,\r\n cssWarnings: warnings.map((w: CSSWarning) => ({\r\n client: w.client,\r\n property: w.property,\r\n severity: w.severity,\r\n message: w.message,\r\n suggestion: w.suggestion,\r\n fix: w.fix,\r\n })),\r\n clientCount: transforms.length,\r\n darkModeWarnings: Object.entries(darkMode).reduce(\r\n (\r\n acc: Record<string, number>,\r\n [clientId, dm]: [string, { warnings: CSSWarning[] }]\r\n ) => {\r\n if (dm.warnings.length > 0) {\r\n acc[clientId] = dm.warnings.length;\r\n }\r\n return acc;\r\n },\r\n {}\r\n ),\r\n },\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n };\r\n }\r\n);\r\n\r\n// ── Tool: analyze_email ─────────────────────────────────────────\r\nserver.registerTool(\r\n \"analyze_email\",\r\n {\r\n title: \"Analyze Email\",\r\n description:\r\n \"Quick CSS compatibility analysis — returns warnings and per-client scores without full transforms. Faster than preview_email when you only need the compatibility report.\",\r\n inputSchema: {\r\n html: z.string().describe(\"The email HTML source code\"),\r\n format: z\r\n .enum([\"html\", \"jsx\", \"mjml\", \"maizzle\"])\r\n .optional()\r\n .describe(\r\n \"Input format: 'html' (default), 'jsx' (React Email), 'mjml', or 'maizzle'. Determines which framework-specific fix snippets are returned.\"\r\n ),\r\n },\r\n annotations: {\r\n title: \"Analyze Email\",\r\n readOnlyHint: true,\r\n destructiveHint: false,\r\n idempotentHint: true,\r\n openWorldHint: false,\r\n },\r\n },\r\n async ({ html, format }) => {\r\n const warnings = analyzeEmail(html, toFramework(format));\r\n const scores = generateCompatibilityScore(warnings);\r\n\r\n const scoreValues = Object.values(scores);\r\n const overallScore =\r\n scoreValues.length > 0\r\n ? Math.round(\r\n scoreValues.reduce(\r\n (a: number, b: { score: number }) => a + b.score,\r\n 0\r\n ) / scoreValues.length\r\n )\r\n : 0;\r\n\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n {\r\n overallScore,\r\n scores,\r\n warningCount: warnings.length,\r\n warnings: warnings.map((w: CSSWarning) => ({\r\n client: w.client,\r\n property: w.property,\r\n severity: w.severity,\r\n message: w.message,\r\n suggestion: w.suggestion,\r\n fix: w.fix,\r\n })),\r\n },\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n };\r\n }\r\n);\r\n\r\n// ── Tool: list_clients ──────────────────────────────────────────\r\nserver.registerTool(\r\n \"list_clients\",\r\n {\r\n title: \"List Email Clients\",\r\n description:\r\n \"List all 12 supported email client IDs, display names, categories, rendering engines, and dark mode support.\",\r\n annotations: {\r\n title: \"List Email Clients\",\r\n readOnlyHint: true,\r\n destructiveHint: false,\r\n idempotentHint: true,\r\n openWorldHint: false,\r\n },\r\n },\r\n async () => {\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n EMAIL_CLIENTS.map(\r\n (c: {\r\n id: string;\r\n name: string;\r\n category: string;\r\n engine: string;\r\n darkModeSupport: boolean;\r\n }) => ({\r\n id: c.id,\r\n name: c.name,\r\n category: c.category,\r\n engine: c.engine,\r\n darkModeSupport: c.darkModeSupport,\r\n })\r\n ),\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n };\r\n }\r\n);\r\n\r\n// ── Start ────────────────────────────────────────────────────────\r\nconst transport = new StdioServerTransport();\r\nawait server.connect(transport);\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\r\nimport { z } from \"zod\";\r\nimport {\r\n transformForAllClients,\r\n analyzeEmail,\r\n generateCompatibilityScore,\r\n simulateDarkMode,\r\n generateFixPrompt,\r\n estimateAiFixTokens,\r\n auditEmail,\r\n structuralWarnings,\r\n EMAIL_CLIENTS,\r\n type Framework,\r\n type CSSWarning,\r\n type AuditReport,\r\n} from \"@emailens/engine\";\r\n\r\nfunction toFramework(format?: string): Framework | undefined {\r\n if (format === \"jsx\" || format === \"mjml\" || format === \"maizzle\") return format;\r\n return undefined;\r\n}\r\n\r\nconst server = new McpServer({\r\n name: \"emailens\",\r\n version: \"0.3.0\",\r\n});\r\n\r\n// ── Tool: preview_email ─────────────────────────────────────────\r\nserver.registerTool(\r\n \"preview_email\",\r\n {\r\n title: \"Preview Email\",\r\n description:\r\n \"Analyze an HTML email and see how it renders across 12 email clients (Gmail, Outlook, Apple Mail, Yahoo, Samsung, Thunderbird, HEY, Superhuman). Returns CSS transforms, compatibility warnings with fix snippets, dark mode simulation, and per-client scores.\",\r\n inputSchema: {\r\n html: z.string().describe(\"The email HTML source code\"),\r\n clients: z\r\n .array(z.string())\r\n .optional()\r\n .describe(\r\n \"Optional array of client IDs to filter (e.g. ['gmail-web', 'outlook-windows']). Omit for all clients.\"\r\n ),\r\n format: z\r\n .enum([\"html\", \"jsx\", \"mjml\", \"maizzle\"])\r\n .optional()\r\n .describe(\r\n \"Input format of the email source: 'html' (default), 'jsx' (React Email), 'mjml', or 'maizzle'. Controls which framework-specific fix snippets appear in the warnings.\"\r\n ),\r\n },\r\n annotations: {\r\n title: \"Preview Email\",\r\n readOnlyHint: true,\r\n destructiveHint: false,\r\n idempotentHint: true,\r\n openWorldHint: false,\r\n },\r\n },\r\n async ({ html, clients, format }) => {\r\n const validClientIds = new Set(EMAIL_CLIENTS.map((c: { id: string }) => c.id));\r\n\r\n let transforms = transformForAllClients(html);\r\n if (clients) {\r\n const filter = clients.filter((c: string) => validClientIds.has(c));\r\n if (filter.length === 0) {\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n {\r\n error: \"No valid client IDs provided\",\r\n validClientIds: Array.from(validClientIds),\r\n },\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n isError: true,\r\n };\r\n }\r\n transforms = transforms.filter((t: { clientId: string }) =>\r\n filter.includes(t.clientId)\r\n );\r\n }\r\n\r\n const warnings = analyzeEmail(html, toFramework(format));\r\n const scores = generateCompatibilityScore(warnings);\r\n\r\n const darkMode: Record<string, { html: string; warnings: CSSWarning[] }> =\r\n {};\r\n for (const t of transforms) {\r\n darkMode[t.clientId] = simulateDarkMode(t.html, t.clientId);\r\n }\r\n\r\n const scoreValues = Object.values(scores);\r\n const overallScore =\r\n scoreValues.length > 0\r\n ? Math.round(\r\n scoreValues.reduce(\r\n (a: number, b: { score: number }) => a + b.score,\r\n 0\r\n ) / scoreValues.length\r\n )\r\n : 0;\r\n\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n {\r\n overallScore,\r\n compatibilityScores: scores,\r\n cssWarnings: warnings.map((w: CSSWarning) => ({\r\n client: w.client,\r\n property: w.property,\r\n severity: w.severity,\r\n message: w.message,\r\n suggestion: w.suggestion,\r\n fix: w.fix,\r\n fixType: w.fixType,\r\n })),\r\n clientCount: transforms.length,\r\n darkModeWarnings: Object.entries(darkMode).reduce(\r\n (\r\n acc: Record<string, number>,\r\n [clientId, dm]: [string, { warnings: CSSWarning[] }]\r\n ) => {\r\n if (dm.warnings.length > 0) {\r\n acc[clientId] = dm.warnings.length;\r\n }\r\n return acc;\r\n },\r\n {}\r\n ),\r\n },\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n };\r\n }\r\n);\r\n\r\n// ── Tool: audit_email ───────────────────────────────────────────\r\nserver.registerTool(\r\n \"audit_email\",\r\n {\r\n title: \"Audit Email\",\r\n description:\r\n \"Run ALL email checks in one call: CSS compatibility, spam scoring, link validation, accessibility audit, and image analysis. Returns a unified report. Use --skip to omit specific checks.\",\r\n inputSchema: {\r\n html: z.string().describe(\"The email HTML source code\"),\r\n format: z\r\n .enum([\"html\", \"jsx\", \"mjml\", \"maizzle\"])\r\n .optional()\r\n .describe(\r\n \"Input format: 'html' (default), 'jsx' (React Email), 'mjml', or 'maizzle'.\"\r\n ),\r\n skip: z\r\n .array(z.enum([\"spam\", \"links\", \"accessibility\", \"images\", \"compatibility\"]))\r\n .optional()\r\n .describe(\r\n \"Array of checks to skip (e.g. ['spam', 'images']).\"\r\n ),\r\n },\r\n annotations: {\r\n title: \"Audit Email\",\r\n readOnlyHint: true,\r\n destructiveHint: false,\r\n idempotentHint: true,\r\n openWorldHint: false,\r\n },\r\n },\r\n async ({ html, format, skip }) => {\r\n const report = auditEmail(html, {\r\n framework: toFramework(format),\r\n skip,\r\n });\r\n\r\n const scoreValues = Object.values(report.compatibility.scores);\r\n const overallCompatibility =\r\n scoreValues.length > 0\r\n ? Math.round(\r\n scoreValues.reduce(\r\n (a: number, b: { score: number }) => a + b.score,\r\n 0\r\n ) / scoreValues.length\r\n )\r\n : 0;\r\n\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n {\r\n overallCompatibility,\r\n compatibility: {\r\n scores: report.compatibility.scores,\r\n warningCount: report.compatibility.warnings.length,\r\n warnings: report.compatibility.warnings.map((w: CSSWarning) => ({\r\n client: w.client,\r\n property: w.property,\r\n severity: w.severity,\r\n message: w.message,\r\n suggestion: w.suggestion,\r\n fix: w.fix,\r\n fixType: w.fixType,\r\n })),\r\n },\r\n spam: report.spam,\r\n links: report.links,\r\n accessibility: report.accessibility,\r\n images: report.images,\r\n },\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n };\r\n }\r\n);\r\n\r\n// ── Tool: analyze_email ─────────────────────────────────────────\r\nserver.registerTool(\r\n \"analyze_email\",\r\n {\r\n title: \"Analyze Email\",\r\n description:\r\n \"CSS compatibility analysis with quality reports — returns warnings, per-client scores, plus spam, link, accessibility, and image analysis. For compatibility-only results, use skip. For full transforms + dark mode, use preview_email.\",\r\n inputSchema: {\r\n html: z.string().describe(\"The email HTML source code\"),\r\n format: z\r\n .enum([\"html\", \"jsx\", \"mjml\", \"maizzle\"])\r\n .optional()\r\n .describe(\r\n \"Input format: 'html' (default), 'jsx' (React Email), 'mjml', or 'maizzle'. Determines which framework-specific fix snippets are returned.\"\r\n ),\r\n },\r\n annotations: {\r\n title: \"Analyze Email\",\r\n readOnlyHint: true,\r\n destructiveHint: false,\r\n idempotentHint: true,\r\n openWorldHint: false,\r\n },\r\n },\r\n async ({ html, format }) => {\r\n const report = auditEmail(html, { framework: toFramework(format) });\r\n\r\n const scoreValues = Object.values(report.compatibility.scores);\r\n const overallScore =\r\n scoreValues.length > 0\r\n ? Math.round(\r\n scoreValues.reduce(\r\n (a: number, b: { score: number }) => a + b.score,\r\n 0\r\n ) / scoreValues.length\r\n )\r\n : 0;\r\n\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n {\r\n overallScore,\r\n scores: report.compatibility.scores,\r\n warningCount: report.compatibility.warnings.length,\r\n warnings: report.compatibility.warnings.map((w: CSSWarning) => ({\r\n client: w.client,\r\n property: w.property,\r\n severity: w.severity,\r\n message: w.message,\r\n suggestion: w.suggestion,\r\n fix: w.fix,\r\n fixType: w.fixType,\r\n })),\r\n spam: report.spam,\r\n links: report.links,\r\n accessibility: report.accessibility,\r\n images: report.images,\r\n },\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n };\r\n }\r\n);\r\n\r\n// ── Tool: fix_email ─────────────────────────────────────────────\r\nserver.registerTool(\r\n \"fix_email\",\r\n {\r\n title: \"Fix Email\",\r\n description:\r\n \"Generate a structured fix prompt for email compatibility issues. Returns a markdown prompt with the original code, all detected issues (with fix type: CSS or structural), fix snippets, and format-specific instructions. The AI assistant can then apply these fixes directly. Use after preview_email or analyze_email to fix the issues found.\",\r\n inputSchema: {\r\n html: z.string().describe(\"The email HTML source code to fix\"),\r\n format: z\r\n .enum([\"html\", \"jsx\", \"mjml\", \"maizzle\"])\r\n .optional()\r\n .describe(\r\n \"Input format: 'html' (default), 'jsx' (React Email), 'mjml', or 'maizzle'. Controls the fix syntax in the prompt.\"\r\n ),\r\n scope: z\r\n .enum([\"all\", \"current\"])\r\n .optional()\r\n .describe(\r\n \"Fix scope: 'all' (default) fixes for all clients, 'current' fixes for a single client (requires selectedClientId).\"\r\n ),\r\n selectedClientId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Client ID to scope fixes to (e.g. 'outlook-windows'). Only used when scope is 'current'.\"\r\n ),\r\n },\r\n annotations: {\r\n title: \"Fix Email\",\r\n readOnlyHint: true,\r\n destructiveHint: false,\r\n idempotentHint: true,\r\n openWorldHint: false,\r\n },\r\n },\r\n async ({ html, format, scope, selectedClientId }) => {\r\n const framework = toFramework(format);\r\n const fixScope = scope === \"current\" ? \"current\" : \"all\";\r\n const inputFormat = format || \"html\";\r\n\r\n const warnings = analyzeEmail(html, framework);\r\n const scores = generateCompatibilityScore(warnings);\r\n\r\n if (warnings.length === 0) {\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n { message: \"No compatibility issues found — nothing to fix.\", overallScore: 100 },\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n };\r\n }\r\n\r\n // Estimate tokens\r\n const estimate = await estimateAiFixTokens({\r\n originalHtml: html,\r\n warnings,\r\n scores,\r\n scope: fixScope,\r\n selectedClientId,\r\n format: inputFormat,\r\n });\r\n\r\n // Generate the fix prompt\r\n const prompt = generateFixPrompt({\r\n originalHtml: html,\r\n warnings,\r\n scores,\r\n scope: fixScope,\r\n selectedClientId,\r\n format: inputFormat,\r\n });\r\n\r\n const structural = structuralWarnings(warnings);\r\n\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n {\r\n totalWarnings: warnings.length,\r\n structuralWarnings: structural.length,\r\n cssWarnings: warnings.length - structural.length,\r\n tokenEstimate: {\r\n inputTokens: estimate.inputTokens,\r\n estimatedOutputTokens: estimate.estimatedOutputTokens,\r\n truncated: estimate.truncated,\r\n },\r\n note: \"Apply the fixes in the prompt below to the email code. Structural issues require HTML restructuring (tables, VML conditionals), not just CSS changes.\",\r\n },\r\n null,\r\n 2\r\n ),\r\n },\r\n {\r\n type: \"text\" as const,\r\n text: prompt,\r\n },\r\n ],\r\n };\r\n }\r\n);\r\n\r\n// ── Tool: list_clients ──────────────────────────────────────────\r\nserver.registerTool(\r\n \"list_clients\",\r\n {\r\n title: \"List Email Clients\",\r\n description:\r\n \"List all 12 supported email client IDs, display names, categories, rendering engines, and dark mode support.\",\r\n annotations: {\r\n title: \"List Email Clients\",\r\n readOnlyHint: true,\r\n destructiveHint: false,\r\n idempotentHint: true,\r\n openWorldHint: false,\r\n },\r\n },\r\n async () => {\r\n return {\r\n content: [\r\n {\r\n type: \"text\" as const,\r\n text: JSON.stringify(\r\n EMAIL_CLIENTS.map(\r\n (c: {\r\n id: string;\r\n name: string;\r\n category: string;\r\n engine: string;\r\n darkModeSupport: boolean;\r\n }) => ({\r\n id: c.id,\r\n name: c.name,\r\n category: c.category,\r\n engine: c.engine,\r\n darkModeSupport: c.darkModeSupport,\r\n })\r\n ),\r\n null,\r\n 2\r\n ),\r\n },\r\n ],\r\n };\r\n }\r\n);\r\n\r\n// ── Start ────────────────────────────────────────────────────────\r\nconst transport = new StdioServerTransport();\r\nawait server.connect(transport);\r\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,56 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@emailens/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for email compatibility analysis — preview, analyze, and list email clients via Claude or any MCP-compatible tool.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"emailens-mcp": "./dist/index.js"
|
|
8
|
-
},
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"import": "./dist/index.js"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"main": "./dist/index.js",
|
|
16
|
-
"types": "./dist/index.d.ts",
|
|
17
|
-
"files": [
|
|
18
|
-
"dist",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsup",
|
|
23
|
-
"dev": "tsup --watch",
|
|
24
|
-
"start": "bun run dist/index.js",
|
|
25
|
-
"typecheck": "tsc --noEmit",
|
|
26
|
-
"prepublishOnly": "bun run build"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"@emailens/engine": "^0.1
|
|
30
|
-
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
31
|
-
"zod": "^3.24.0"
|
|
32
|
-
},
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"@types/node": "^25.3.0",
|
|
35
|
-
"bun-types": "^1.3.9",
|
|
36
|
-
"tsup": "^8.4.0",
|
|
37
|
-
"typescript": "^5.9.3"
|
|
38
|
-
},
|
|
39
|
-
"keywords": [
|
|
40
|
-
"mcp",
|
|
41
|
-
"model-context-protocol",
|
|
42
|
-
"email",
|
|
43
|
-
"email-testing",
|
|
44
|
-
"compatibility",
|
|
45
|
-
"claude"
|
|
46
|
-
],
|
|
47
|
-
"license": "MIT",
|
|
48
|
-
"repository": {
|
|
49
|
-
"type": "git",
|
|
50
|
-
"url": "git+https://github.com/emailens/mcp.git"
|
|
51
|
-
},
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@emailens/mcp",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "MCP server for email compatibility analysis — preview, analyze, fix, and list email clients via Claude or any MCP-compatible tool.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"emailens-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"start": "bun run dist/index.js",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"prepublishOnly": "bun run build"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@emailens/engine": "^0.5.1",
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
31
|
+
"zod": "^3.24.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^25.3.0",
|
|
35
|
+
"bun-types": "^1.3.9",
|
|
36
|
+
"tsup": "^8.4.0",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"mcp",
|
|
41
|
+
"model-context-protocol",
|
|
42
|
+
"email",
|
|
43
|
+
"email-testing",
|
|
44
|
+
"compatibility",
|
|
45
|
+
"claude"
|
|
46
|
+
],
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/emailens/mcp.git"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/emailens/mcp#readme",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/emailens/mcp/issues"
|
|
58
|
+
}
|
|
59
|
+
}
|