groundmail-mcp 0.6.4 → 0.8.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/dist/index.js +83 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -118,6 +118,17 @@ function pagedOutput(key, item, description) {
|
|
|
118
118
|
[key]
|
|
119
119
|
);
|
|
120
120
|
}
|
|
121
|
+
function heldBackItem() {
|
|
122
|
+
return object(
|
|
123
|
+
{
|
|
124
|
+
email: string("The held-back address"),
|
|
125
|
+
reason: string(
|
|
126
|
+
"Why: invalid_address (cannot exist), no_mail_server (domain takes no email), previously_bounced (hard-bounced for a Groundmail sender in the last 90 days) or mailbox_not_found"
|
|
127
|
+
)
|
|
128
|
+
},
|
|
129
|
+
["email", "reason"]
|
|
130
|
+
);
|
|
131
|
+
}
|
|
121
132
|
var emailItem = object(
|
|
122
133
|
{
|
|
123
134
|
id: string("Email id (msg_\u2026)"),
|
|
@@ -135,6 +146,10 @@ var emailItem = object(
|
|
|
135
146
|
enum: ["hard", "block", null],
|
|
136
147
|
description: "Why it bounced: hard (address cannot receive; suppressed) or block (the receiving server refused the sender for reputation or policy; address not suppressed, safe to retry later). Null if it did not bounce"
|
|
137
148
|
},
|
|
149
|
+
held_back: arrayOf(
|
|
150
|
+
heldBackItem(),
|
|
151
|
+
"Recipients Groundmail validation held back before sending, with why; empty when none were"
|
|
152
|
+
),
|
|
138
153
|
tags: { description: "Tags attached to the email" },
|
|
139
154
|
scheduled_at: nullableString("When the email is scheduled to send (ISO 8601)"),
|
|
140
155
|
cancelled_at: nullableString("When the scheduled email was cancelled (ISO 8601)"),
|
|
@@ -248,10 +263,32 @@ var bucketProperties = {
|
|
|
248
263
|
var sendEmailOutput = object(
|
|
249
264
|
{
|
|
250
265
|
id: string("Email id (msg_\u2026)"),
|
|
251
|
-
status: string("Status after the request, for example queued or scheduled")
|
|
266
|
+
status: string("Status after the request, for example queued or scheduled"),
|
|
267
|
+
held_back: arrayOf(
|
|
268
|
+
heldBackItem(),
|
|
269
|
+
"Present when Groundmail validation held some recipients back; the email went to the rest"
|
|
270
|
+
)
|
|
252
271
|
},
|
|
253
272
|
["id", "status"]
|
|
254
273
|
);
|
|
274
|
+
var validateEmailOutput = object(
|
|
275
|
+
{
|
|
276
|
+
email: string("The address as checked (trimmed, lowercased)"),
|
|
277
|
+
domain: nullableString("Domain part of the address; null when there is none"),
|
|
278
|
+
verdict: {
|
|
279
|
+
type: "string",
|
|
280
|
+
enum: ["valid", "risky", "invalid"],
|
|
281
|
+
description: "valid: safe to send; risky: deliverable but worth a second look; invalid: cannot receive email"
|
|
282
|
+
},
|
|
283
|
+
reasons: stringArray(
|
|
284
|
+
"What was found: syntax, no_mail_server, mailbox_not_found, previously_bounced (invalid); disposable, catch_all, possible_typo (risky); role (informational)"
|
|
285
|
+
),
|
|
286
|
+
suggestion: nullableString("The address with a likely-mistyped domain corrected, for example ada@gmail.com for ada@gmial.com"),
|
|
287
|
+
disposable: boolean("A throwaway-inbox service"),
|
|
288
|
+
role: boolean("A team or queue mailbox such as info@ or support@")
|
|
289
|
+
},
|
|
290
|
+
["email", "verdict", "reasons", "suggestion", "disposable", "role"]
|
|
291
|
+
);
|
|
255
292
|
var getEmailOutput = object({ email: described(emailItem, "The email") }, ["email"]);
|
|
256
293
|
var domainWithRecordsOutput = object(
|
|
257
294
|
{
|
|
@@ -366,10 +403,14 @@ var migrationOutput = object(
|
|
|
366
403
|
has_more: boolean("True if the import stopped early; call the tool again with `cursor` to continue"),
|
|
367
404
|
next_cursor: nullableString("Pass as `cursor` to continue an import that stopped early"),
|
|
368
405
|
retry_after_seconds: { type: ["number", "null"], description: "Wait this long before continuing" },
|
|
369
|
-
domains: anyObject(
|
|
406
|
+
domains: anyObject(
|
|
407
|
+
"Totals and one item per domain, with the DNS records and `dns_plan`: each record compared with what the domain publishes today (add, update an existing SPF record, keep the existing DMARC record, or already published), and which old-provider records to keep until the cutover"
|
|
408
|
+
),
|
|
370
409
|
webhooks: anyObject("Totals and one item per webhook (created disabled), with mapped events and the signing secret"),
|
|
371
410
|
templates: anyObject("Totals and one item per template, with its alias and conversion notes"),
|
|
372
|
-
suppressions: anyObject(
|
|
411
|
+
suppressions: anyObject(
|
|
412
|
+
"Totals by type (bounce, complaint, unsubscribe, manual, product_opt_out) and skip reasons (for example bounces caused by a block on the old provider, which are not suppressed)"
|
|
413
|
+
),
|
|
373
414
|
errors: arrayOf(anyObject("A resource that could not be read"), "Resources that could not be read at all"),
|
|
374
415
|
notes: stringArray("What was left out on purpose, and why"),
|
|
375
416
|
attention: stringArray("What the user still needs to do"),
|
|
@@ -544,6 +585,11 @@ async function callMcpTool(client, name, args) {
|
|
|
544
585
|
})
|
|
545
586
|
);
|
|
546
587
|
}
|
|
588
|
+
case "validate_email": {
|
|
589
|
+
const email = optString(args, "email");
|
|
590
|
+
if (!email) return textResult("Missing required argument: email", true);
|
|
591
|
+
return textResult(await client.validate(email));
|
|
592
|
+
}
|
|
547
593
|
case "add_suppression": {
|
|
548
594
|
const email = optString(args, "email");
|
|
549
595
|
if (!email) return textResult("Missing required argument: email", true);
|
|
@@ -640,6 +686,15 @@ async function callMcpTool(client, name, args) {
|
|
|
640
686
|
};
|
|
641
687
|
return textResult({ migration: await client.migrations.run(params) });
|
|
642
688
|
}
|
|
689
|
+
case "get_import_report": {
|
|
690
|
+
let id = optString(args, "id");
|
|
691
|
+
if (!id) {
|
|
692
|
+
const { data } = await client.migrations.list({ limit: 1 });
|
|
693
|
+
if (!data[0]) return textResult("No imports yet. Run import_from_provider first; previews are not stored.", true);
|
|
694
|
+
id = data[0].id;
|
|
695
|
+
}
|
|
696
|
+
return textResult(await client.migrations.get(id));
|
|
697
|
+
}
|
|
643
698
|
default:
|
|
644
699
|
return textResult(`Unknown tool: ${name}`, true);
|
|
645
700
|
}
|
|
@@ -886,6 +941,19 @@ var MCP_TOOLS = [
|
|
|
886
941
|
outputSchema: pagedOutput("data", suppressionItem, "Suppressed addresses on this page"),
|
|
887
942
|
annotations: readOnly("List suppressions")
|
|
888
943
|
},
|
|
944
|
+
{
|
|
945
|
+
name: "validate_email",
|
|
946
|
+
description: "Check one email address with Groundmail validation before accepting it (for example at signup): verdict valid | risky | invalid, reasons, a typo suggestion, and disposable / role flags. Free; sends nothing.",
|
|
947
|
+
inputSchema: {
|
|
948
|
+
type: "object",
|
|
949
|
+
properties: {
|
|
950
|
+
email: { type: "string", description: "Email address to check" }
|
|
951
|
+
},
|
|
952
|
+
required: ["email"]
|
|
953
|
+
},
|
|
954
|
+
outputSchema: validateEmailOutput,
|
|
955
|
+
annotations: readOnly("Validate email address")
|
|
956
|
+
},
|
|
889
957
|
{
|
|
890
958
|
name: "add_suppression",
|
|
891
959
|
description: "Add an address to the suppression list.",
|
|
@@ -1068,6 +1136,18 @@ var MCP_TOOLS = [
|
|
|
1068
1136
|
idempotentHint: true,
|
|
1069
1137
|
openWorldHint: true
|
|
1070
1138
|
}
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
name: "get_import_report",
|
|
1142
|
+
description: "Beta. The stored report of an import from SendGrid, Postmark or Mailgun: what was imported, skipped and failed, the DNS changes per domain, what still needs doing, the code change and a prompt for it. Pass the import id, or omit it for the latest import. Previews are not stored.",
|
|
1143
|
+
inputSchema: {
|
|
1144
|
+
type: "object",
|
|
1145
|
+
properties: {
|
|
1146
|
+
id: { type: "string", description: "Import id (the `id` from import_from_provider). Omit for the latest import." }
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
outputSchema: migrationOutput,
|
|
1150
|
+
annotations: readOnly("Get an import report")
|
|
1071
1151
|
}
|
|
1072
1152
|
];
|
|
1073
1153
|
function resolveApiKey() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groundmail-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"mcpName": "io.github.Groundmail/groundmail",
|
|
5
5
|
"description": "Groundmail MCP server: send transactional email, manage domains, templates, webhooks and suppressions from Claude, Cursor, ChatGPT and other MCP clients",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
44
|
-
"groundmail": "0.
|
|
44
|
+
"groundmail": "0.16.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"tsup": "^8.5.0",
|