groundmail-mcp 0.5.8 → 0.5.11
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/LICENSE +21 -0
- package/dist/index.js +419 -29
- package/package.json +10 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Groundmail
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
CHANGED
|
@@ -82,10 +82,277 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
82
82
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
83
83
|
import { Groundmail } from "groundmail";
|
|
84
84
|
|
|
85
|
+
// src/output-schemas.ts
|
|
86
|
+
var nullableString = (description) => ({ type: ["string", "null"], description });
|
|
87
|
+
var string = (description) => ({ type: "string", description });
|
|
88
|
+
var boolean = (description) => ({ type: "boolean", description });
|
|
89
|
+
var number = (description) => ({ type: "number", description });
|
|
90
|
+
var stringArray = (description) => ({
|
|
91
|
+
type: "array",
|
|
92
|
+
items: { type: "string" },
|
|
93
|
+
description
|
|
94
|
+
});
|
|
95
|
+
function object(properties, required = []) {
|
|
96
|
+
return { type: "object", properties, ...required.length ? { required } : {} };
|
|
97
|
+
}
|
|
98
|
+
function described(schema, description) {
|
|
99
|
+
return { ...schema, description };
|
|
100
|
+
}
|
|
101
|
+
function arrayOf(items, description) {
|
|
102
|
+
return { type: "array", items, description };
|
|
103
|
+
}
|
|
104
|
+
var okOutput = object(
|
|
105
|
+
{ ok: boolean("True when the request completed") },
|
|
106
|
+
["ok"]
|
|
107
|
+
);
|
|
108
|
+
var paginationOutput = {
|
|
109
|
+
has_more: boolean("True when another page of results is available"),
|
|
110
|
+
next_cursor: nullableString("Cursor to pass as `cursor` to fetch the next page; null on the last page")
|
|
111
|
+
};
|
|
112
|
+
function pagedOutput(key, item, description) {
|
|
113
|
+
return object(
|
|
114
|
+
{
|
|
115
|
+
[key]: arrayOf(item, description),
|
|
116
|
+
...paginationOutput
|
|
117
|
+
},
|
|
118
|
+
[key]
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
var emailItem = object(
|
|
122
|
+
{
|
|
123
|
+
id: string("Email id (msg_\u2026)"),
|
|
124
|
+
from: string("Sender address"),
|
|
125
|
+
to: stringArray("Recipient addresses"),
|
|
126
|
+
cc: stringArray("Cc addresses"),
|
|
127
|
+
bcc: stringArray("Bcc addresses"),
|
|
128
|
+
reply_to: stringArray("Reply-To addresses"),
|
|
129
|
+
subject: string("Subject line"),
|
|
130
|
+
status: string("Current delivery status, for example sent, delivered or bounced"),
|
|
131
|
+
last_event: nullableString("Most recent delivery event, if any"),
|
|
132
|
+
bounce_reason: nullableString("Reason reported when the email bounced"),
|
|
133
|
+
tags: { description: "Tags attached to the email" },
|
|
134
|
+
scheduled_at: nullableString("When the email is scheduled to send (ISO 8601)"),
|
|
135
|
+
cancelled_at: nullableString("When the scheduled email was cancelled (ISO 8601)"),
|
|
136
|
+
created_at: string("When the email was created (ISO 8601)"),
|
|
137
|
+
sent_at: nullableString("When the email was handed off for delivery (ISO 8601)"),
|
|
138
|
+
delivered_at: nullableString("When the email was delivered (ISO 8601)"),
|
|
139
|
+
bounced_at: nullableString("When the email bounced (ISO 8601)"),
|
|
140
|
+
opened_at: nullableString("First open (ISO 8601); needs open tracking on the sending domain"),
|
|
141
|
+
clicked_at: nullableString("First tracked-link click (ISO 8601); needs click tracking on the sending domain")
|
|
142
|
+
},
|
|
143
|
+
["id", "from", "to", "subject", "status"]
|
|
144
|
+
);
|
|
145
|
+
var domainItem = object(
|
|
146
|
+
{
|
|
147
|
+
id: string("Domain id"),
|
|
148
|
+
name: string("Domain name, for example example.com"),
|
|
149
|
+
status: string("Verification status: pending, verified or failed"),
|
|
150
|
+
created_at: string("When the domain was added (ISO 8601)"),
|
|
151
|
+
verified_at: nullableString("When the domain was verified (ISO 8601)"),
|
|
152
|
+
open_tracking: boolean("Whether open tracking is enabled"),
|
|
153
|
+
click_tracking: boolean("Whether click tracking is enabled"),
|
|
154
|
+
tracking_status: string("Whether tracking is applied: disabled, pending, active or error"),
|
|
155
|
+
tracking_error: nullableString("Why tracking is not active yet, when pending or in error"),
|
|
156
|
+
tls_mode: string("TLS mode for outgoing mail: opportunistic or enforced"),
|
|
157
|
+
inbound_enabled: boolean("Whether inbound receiving is enabled"),
|
|
158
|
+
inbound_status: string("Inbound status: disabled, pending, active or error"),
|
|
159
|
+
inbound_error: nullableString("Inbound setup error, if any")
|
|
160
|
+
},
|
|
161
|
+
["id", "name", "status"]
|
|
162
|
+
);
|
|
163
|
+
var dnsRecordItem = object(
|
|
164
|
+
{
|
|
165
|
+
type: string("DNS record type, for example TXT, CNAME or MX"),
|
|
166
|
+
host: string("Record host name"),
|
|
167
|
+
value: string("Record value"),
|
|
168
|
+
purpose: string("What the record is for, for example SPF or DKIM"),
|
|
169
|
+
priority: number("MX preference, present on MX records")
|
|
170
|
+
},
|
|
171
|
+
["type", "host", "value"]
|
|
172
|
+
);
|
|
173
|
+
var dnsRecords = arrayOf(dnsRecordItem, "DNS records to publish for the domain");
|
|
174
|
+
var webhookItem = object(
|
|
175
|
+
{
|
|
176
|
+
id: string("Webhook id (wh_\u2026)"),
|
|
177
|
+
url: string("URL that receives events"),
|
|
178
|
+
events: stringArray("Event types delivered to this endpoint"),
|
|
179
|
+
enabled: boolean("Whether the endpoint receives events"),
|
|
180
|
+
created_at: string("When the endpoint was created (ISO 8601)"),
|
|
181
|
+
updated_at: string("When the endpoint was last changed (ISO 8601)")
|
|
182
|
+
},
|
|
183
|
+
["id", "url", "events"]
|
|
184
|
+
);
|
|
185
|
+
var suppressionItem = object(
|
|
186
|
+
{
|
|
187
|
+
id: string("Suppression id"),
|
|
188
|
+
email: string("Suppressed email address"),
|
|
189
|
+
reason: nullableString("Why the address was suppressed"),
|
|
190
|
+
source: string("How the suppression was created, for example api or bounce"),
|
|
191
|
+
created_at: string("When the suppression was created (ISO 8601)"),
|
|
192
|
+
updated_at: string("When the suppression was last changed (ISO 8601)")
|
|
193
|
+
},
|
|
194
|
+
["id", "email"]
|
|
195
|
+
);
|
|
196
|
+
var templateItem = object(
|
|
197
|
+
{
|
|
198
|
+
id: string("Template id (tpl_\u2026)"),
|
|
199
|
+
name: string("Template name"),
|
|
200
|
+
alias: nullableString("Alias that can be used in place of the id"),
|
|
201
|
+
status: string("draft or published"),
|
|
202
|
+
format: string("html or blocks"),
|
|
203
|
+
subject: string("Subject line, may contain variables"),
|
|
204
|
+
preheader: nullableString("Preview text shown after the subject in some inboxes"),
|
|
205
|
+
html: nullableString("HTML body"),
|
|
206
|
+
text: nullableString("Plain-text body"),
|
|
207
|
+
variables: {
|
|
208
|
+
type: ["array", "null"],
|
|
209
|
+
items: { type: "string" },
|
|
210
|
+
description: "Variable names the template uses"
|
|
211
|
+
},
|
|
212
|
+
unsubscribe_enabled: boolean("Whether managed unsubscribe is on for sends using this template"),
|
|
213
|
+
category: string("transactional, product or newsletter"),
|
|
214
|
+
published_version_id: nullableString("Id of the version sends currently use"),
|
|
215
|
+
published_at: nullableString("When the template was last published (ISO 8601)"),
|
|
216
|
+
created_at: string("When the template was created (ISO 8601)"),
|
|
217
|
+
updated_at: string("When the template was last changed (ISO 8601)"),
|
|
218
|
+
warnings: arrayOf(
|
|
219
|
+
object({ code: string("Warning code"), message: string("What to fix") }),
|
|
220
|
+
"Content warnings found when the template was saved"
|
|
221
|
+
)
|
|
222
|
+
},
|
|
223
|
+
["id", "name", "status", "subject"]
|
|
224
|
+
);
|
|
225
|
+
var bucketProperties = {
|
|
226
|
+
total_emails: number("Emails created in the window"),
|
|
227
|
+
attempted: number("Emails with a delivery attempt"),
|
|
228
|
+
sent: number("Emails handed off for delivery"),
|
|
229
|
+
delivered: number("Emails delivered"),
|
|
230
|
+
bounced: number("Emails that bounced"),
|
|
231
|
+
complained: number("Emails reported as spam"),
|
|
232
|
+
failed: number("Emails that failed"),
|
|
233
|
+
suppressed: number("Emails not sent because the recipient was suppressed"),
|
|
234
|
+
scheduled: number("Emails still scheduled"),
|
|
235
|
+
opened: number("Emails opened"),
|
|
236
|
+
clicked: number("Emails with a tracked click"),
|
|
237
|
+
bounce_rate: number("Bounced / attempted, as a fraction"),
|
|
238
|
+
complaint_rate: number("Complained / attempted, as a fraction"),
|
|
239
|
+
delivery_rate: number("Delivered / attempted, as a fraction"),
|
|
240
|
+
open_rate: number("Opened / delivered, as a fraction"),
|
|
241
|
+
click_rate: number("Clicked / delivered, as a fraction")
|
|
242
|
+
};
|
|
243
|
+
var sendEmailOutput = object(
|
|
244
|
+
{
|
|
245
|
+
id: string("Email id (msg_\u2026)"),
|
|
246
|
+
status: string("Status after the request, for example queued or scheduled")
|
|
247
|
+
},
|
|
248
|
+
["id", "status"]
|
|
249
|
+
);
|
|
250
|
+
var getEmailOutput = object({ email: described(emailItem, "The email") }, ["email"]);
|
|
251
|
+
var domainWithRecordsOutput = object(
|
|
252
|
+
{
|
|
253
|
+
domain: described(domainItem, "The domain"),
|
|
254
|
+
records: dnsRecords,
|
|
255
|
+
inbound_records: arrayOf(dnsRecordItem, "MX records to publish when inbound receiving is enabled"),
|
|
256
|
+
inbound_note: string("Guidance about adding the inbound MX records")
|
|
257
|
+
},
|
|
258
|
+
["domain", "records"]
|
|
259
|
+
);
|
|
260
|
+
var verifyDomainOutput = object(
|
|
261
|
+
{
|
|
262
|
+
domain: described(domainItem, "The domain"),
|
|
263
|
+
records: dnsRecords,
|
|
264
|
+
verified: boolean("True when the domain passed verification"),
|
|
265
|
+
checks: described(
|
|
266
|
+
object({
|
|
267
|
+
ownership: boolean("Ownership record found"),
|
|
268
|
+
spf: boolean("SPF record found"),
|
|
269
|
+
dkim: boolean("DKIM record found"),
|
|
270
|
+
dmarc: boolean("DMARC record found")
|
|
271
|
+
}),
|
|
272
|
+
"Result of each DNS check"
|
|
273
|
+
),
|
|
274
|
+
signing_ready: boolean("True when outgoing mail can be signed for this domain"),
|
|
275
|
+
inbound_configured: boolean("True when inbound receiving is set up"),
|
|
276
|
+
inbound_pending: boolean("True while inbound setup is still in progress"),
|
|
277
|
+
inbound_error: nullableString("Inbound setup error, if any")
|
|
278
|
+
},
|
|
279
|
+
["domain", "verified"]
|
|
280
|
+
);
|
|
281
|
+
var applyDnsOutput = object(
|
|
282
|
+
{
|
|
283
|
+
provider: string("DNS provider the records were written to"),
|
|
284
|
+
domain: string("Domain the records belong to"),
|
|
285
|
+
results: arrayOf(
|
|
286
|
+
object(
|
|
287
|
+
{
|
|
288
|
+
purpose: string("What the record is for"),
|
|
289
|
+
host: string("Record host name"),
|
|
290
|
+
action: string("created, updated, unchanged or merged"),
|
|
291
|
+
detail: string("Extra detail about the change")
|
|
292
|
+
},
|
|
293
|
+
["purpose", "host", "action"]
|
|
294
|
+
),
|
|
295
|
+
"Outcome for each record"
|
|
296
|
+
),
|
|
297
|
+
records: dnsRecords
|
|
298
|
+
},
|
|
299
|
+
["provider", "domain", "results"]
|
|
300
|
+
);
|
|
301
|
+
var createWebhookOutput = object(
|
|
302
|
+
{
|
|
303
|
+
webhook: described(webhookItem, "The created webhook endpoint"),
|
|
304
|
+
secret: string("Signing secret for verifying deliveries. Shown only once, so store it")
|
|
305
|
+
},
|
|
306
|
+
["webhook", "secret"]
|
|
307
|
+
);
|
|
308
|
+
var suppressionOutput = object({ suppression: described(suppressionItem, "The suppression entry") }, ["suppression"]);
|
|
309
|
+
var templateOutput = object({ template: described(templateItem, "The template") }, ["template"]);
|
|
310
|
+
var testWebhookOutput = object(
|
|
311
|
+
{
|
|
312
|
+
ok: boolean("True when the simulated event was built"),
|
|
313
|
+
payload: { description: "The event payload that was (or would be) delivered" },
|
|
314
|
+
delivered: boolean("True when delivery to subscribed endpoints was started"),
|
|
315
|
+
deliveries: arrayOf(
|
|
316
|
+
object(
|
|
317
|
+
{
|
|
318
|
+
endpoint_id: string("Webhook endpoint id"),
|
|
319
|
+
url: string("Endpoint URL"),
|
|
320
|
+
success: boolean("Whether the endpoint accepted the event"),
|
|
321
|
+
status: number("HTTP status returned by the endpoint"),
|
|
322
|
+
error: string("Error message when delivery failed")
|
|
323
|
+
},
|
|
324
|
+
["endpoint_id", "url", "success"]
|
|
325
|
+
),
|
|
326
|
+
"Per-endpoint results when delivery ran synchronously; empty otherwise"
|
|
327
|
+
)
|
|
328
|
+
},
|
|
329
|
+
["ok", "delivered"]
|
|
330
|
+
);
|
|
331
|
+
var deliverabilityOutput = object(
|
|
332
|
+
{
|
|
333
|
+
window: string("Window covered: 7d or 30d"),
|
|
334
|
+
window_start: string("Start of the window (ISO 8601)"),
|
|
335
|
+
generated_at: string("When the report was generated (ISO 8601)"),
|
|
336
|
+
summary: { ...object(bucketProperties), description: "Totals across all domains" },
|
|
337
|
+
by_domain: arrayOf(
|
|
338
|
+
object({ domain: string("Sending domain"), ...bucketProperties }, ["domain"]),
|
|
339
|
+
"Metrics for each sending domain"
|
|
340
|
+
),
|
|
341
|
+
top_bounce_reasons: arrayOf(
|
|
342
|
+
object({ reason: string("Bounce reason"), count: number("Bounces with this reason") }),
|
|
343
|
+
"Most common bounce reasons"
|
|
344
|
+
),
|
|
345
|
+
notes: stringArray("Caveats about how the metrics were measured")
|
|
346
|
+
},
|
|
347
|
+
["window", "summary"]
|
|
348
|
+
);
|
|
349
|
+
|
|
85
350
|
// src/tools.ts
|
|
86
351
|
function textResult(value, isError = false) {
|
|
352
|
+
const isObject = value !== null && typeof value === "object" && !Array.isArray(value);
|
|
87
353
|
return {
|
|
88
354
|
content: [{ type: "text", text: typeof value === "string" ? value : JSON.stringify(value, null, 2) }],
|
|
355
|
+
...!isError && isObject ? { structuredContent: value } : {},
|
|
89
356
|
...isError ? { isError: true } : {}
|
|
90
357
|
};
|
|
91
358
|
}
|
|
@@ -352,6 +619,13 @@ var paginationProps = {
|
|
|
352
619
|
limit: { type: "number", description: "Page size (1\u2013100)" },
|
|
353
620
|
cursor: { type: "string", description: "Opaque cursor for the next page" }
|
|
354
621
|
};
|
|
622
|
+
var readOnly = (title) => ({
|
|
623
|
+
title,
|
|
624
|
+
readOnlyHint: true,
|
|
625
|
+
destructiveHint: false,
|
|
626
|
+
idempotentHint: true,
|
|
627
|
+
openWorldHint: false
|
|
628
|
+
});
|
|
355
629
|
var MCP_TOOLS = [
|
|
356
630
|
{
|
|
357
631
|
name: "send_email",
|
|
@@ -361,10 +635,10 @@ var MCP_TOOLS = [
|
|
|
361
635
|
properties: {
|
|
362
636
|
from: { type: "string", description: "Sender address (must match a verified domain or sandbox sender)" },
|
|
363
637
|
to: { type: "string", description: "Recipient email address" },
|
|
364
|
-
subject: { type: "string", description: "Optional when using a published template" },
|
|
365
|
-
html: { type: "string" },
|
|
366
|
-
text: { type: "string" },
|
|
367
|
-
reply_to: { type: "string" },
|
|
638
|
+
subject: { type: "string", description: "Subject line. Optional when using a published template" },
|
|
639
|
+
html: { type: "string", description: "HTML body. Do not combine with template_id or template_alias" },
|
|
640
|
+
text: { type: "string", description: "Plain-text body. Do not combine with template_id or template_alias" },
|
|
641
|
+
reply_to: { type: "string", description: "Address that replies should go to" },
|
|
368
642
|
template_id: { type: "string", description: "Published template UUID" },
|
|
369
643
|
template_alias: { type: "string", description: "Published template alias" },
|
|
370
644
|
variables: {
|
|
@@ -374,6 +648,14 @@ var MCP_TOOLS = [
|
|
|
374
648
|
}
|
|
375
649
|
},
|
|
376
650
|
required: ["from", "to"]
|
|
651
|
+
},
|
|
652
|
+
outputSchema: sendEmailOutput,
|
|
653
|
+
annotations: {
|
|
654
|
+
title: "Send email",
|
|
655
|
+
readOnlyHint: false,
|
|
656
|
+
destructiveHint: false,
|
|
657
|
+
idempotentHint: false,
|
|
658
|
+
openWorldHint: true
|
|
377
659
|
}
|
|
378
660
|
},
|
|
379
661
|
{
|
|
@@ -382,7 +664,9 @@ var MCP_TOOLS = [
|
|
|
382
664
|
inputSchema: {
|
|
383
665
|
type: "object",
|
|
384
666
|
properties: { ...paginationProps }
|
|
385
|
-
}
|
|
667
|
+
},
|
|
668
|
+
outputSchema: pagedOutput("emails", emailItem, "Emails on this page, newest first"),
|
|
669
|
+
annotations: readOnly("List emails")
|
|
386
670
|
},
|
|
387
671
|
{
|
|
388
672
|
name: "get_email",
|
|
@@ -393,7 +677,9 @@ var MCP_TOOLS = [
|
|
|
393
677
|
id: { type: "string", description: "Email id (msg_\u2026)" }
|
|
394
678
|
},
|
|
395
679
|
required: ["id"]
|
|
396
|
-
}
|
|
680
|
+
},
|
|
681
|
+
outputSchema: getEmailOutput,
|
|
682
|
+
annotations: readOnly("Get email")
|
|
397
683
|
},
|
|
398
684
|
{
|
|
399
685
|
name: "list_domains",
|
|
@@ -401,7 +687,9 @@ var MCP_TOOLS = [
|
|
|
401
687
|
inputSchema: {
|
|
402
688
|
type: "object",
|
|
403
689
|
properties: { ...paginationProps }
|
|
404
|
-
}
|
|
690
|
+
},
|
|
691
|
+
outputSchema: pagedOutput("domains", domainItem, "Sending domains on this page"),
|
|
692
|
+
annotations: readOnly("List domains")
|
|
405
693
|
},
|
|
406
694
|
{
|
|
407
695
|
name: "create_domain",
|
|
@@ -413,6 +701,14 @@ var MCP_TOOLS = [
|
|
|
413
701
|
inbound_enabled: { type: "boolean", description: "Enable inbound receiving on this domain" }
|
|
414
702
|
},
|
|
415
703
|
required: ["name"]
|
|
704
|
+
},
|
|
705
|
+
outputSchema: domainWithRecordsOutput,
|
|
706
|
+
annotations: {
|
|
707
|
+
title: "Create domain",
|
|
708
|
+
readOnlyHint: false,
|
|
709
|
+
destructiveHint: false,
|
|
710
|
+
idempotentHint: false,
|
|
711
|
+
openWorldHint: false
|
|
416
712
|
}
|
|
417
713
|
},
|
|
418
714
|
{
|
|
@@ -421,14 +717,28 @@ var MCP_TOOLS = [
|
|
|
421
717
|
inputSchema: {
|
|
422
718
|
type: "object",
|
|
423
719
|
properties: {
|
|
424
|
-
domain: { type: "string" },
|
|
425
|
-
provider: {
|
|
720
|
+
domain: { type: "string", description: "Domain name or id of a domain already added to this account (example.com)" },
|
|
721
|
+
provider: {
|
|
722
|
+
type: "string",
|
|
723
|
+
enum: ["cloudflare", "godaddy", "vercel"],
|
|
724
|
+
description: "DNS provider that hosts the domain"
|
|
725
|
+
},
|
|
426
726
|
credentials: {
|
|
427
727
|
type: "object",
|
|
428
728
|
description: "Optional inline registrar credentials (GoDaddy api key/secret)"
|
|
429
729
|
}
|
|
430
730
|
},
|
|
431
731
|
required: ["domain", "provider"]
|
|
732
|
+
},
|
|
733
|
+
outputSchema: applyDnsOutput,
|
|
734
|
+
// Writes to the user's DNS provider and can update records that already exist at the same host.
|
|
735
|
+
// Re-running it leaves matching records unchanged.
|
|
736
|
+
annotations: {
|
|
737
|
+
title: "Apply domain DNS records",
|
|
738
|
+
readOnlyHint: false,
|
|
739
|
+
destructiveHint: true,
|
|
740
|
+
idempotentHint: true,
|
|
741
|
+
openWorldHint: true
|
|
432
742
|
}
|
|
433
743
|
},
|
|
434
744
|
{
|
|
@@ -437,9 +747,18 @@ var MCP_TOOLS = [
|
|
|
437
747
|
inputSchema: {
|
|
438
748
|
type: "object",
|
|
439
749
|
properties: {
|
|
440
|
-
domain: { type: "string" }
|
|
750
|
+
domain: { type: "string", description: "Domain name or id to check (example.com)" }
|
|
441
751
|
},
|
|
442
752
|
required: ["domain"]
|
|
753
|
+
},
|
|
754
|
+
outputSchema: verifyDomainOutput,
|
|
755
|
+
// Looks up public DNS and records the result on the domain.
|
|
756
|
+
annotations: {
|
|
757
|
+
title: "Verify domain",
|
|
758
|
+
readOnlyHint: false,
|
|
759
|
+
destructiveHint: false,
|
|
760
|
+
idempotentHint: true,
|
|
761
|
+
openWorldHint: true
|
|
443
762
|
}
|
|
444
763
|
},
|
|
445
764
|
{
|
|
@@ -448,7 +767,9 @@ var MCP_TOOLS = [
|
|
|
448
767
|
inputSchema: {
|
|
449
768
|
type: "object",
|
|
450
769
|
properties: { ...paginationProps }
|
|
451
|
-
}
|
|
770
|
+
},
|
|
771
|
+
outputSchema: pagedOutput("webhooks", webhookItem, "Webhook endpoints on this page"),
|
|
772
|
+
annotations: readOnly("List webhooks")
|
|
452
773
|
},
|
|
453
774
|
{
|
|
454
775
|
name: "create_webhook",
|
|
@@ -464,6 +785,14 @@ var MCP_TOOLS = [
|
|
|
464
785
|
}
|
|
465
786
|
},
|
|
466
787
|
required: ["url"]
|
|
788
|
+
},
|
|
789
|
+
outputSchema: createWebhookOutput,
|
|
790
|
+
annotations: {
|
|
791
|
+
title: "Create webhook",
|
|
792
|
+
readOnlyHint: false,
|
|
793
|
+
destructiveHint: false,
|
|
794
|
+
idempotentHint: false,
|
|
795
|
+
openWorldHint: false
|
|
467
796
|
}
|
|
468
797
|
},
|
|
469
798
|
{
|
|
@@ -475,6 +804,14 @@ var MCP_TOOLS = [
|
|
|
475
804
|
id: { type: "string", description: "Webhook id (wh_\u2026)" }
|
|
476
805
|
},
|
|
477
806
|
required: ["id"]
|
|
807
|
+
},
|
|
808
|
+
outputSchema: okOutput,
|
|
809
|
+
annotations: {
|
|
810
|
+
title: "Delete webhook",
|
|
811
|
+
readOnlyHint: false,
|
|
812
|
+
destructiveHint: true,
|
|
813
|
+
idempotentHint: true,
|
|
814
|
+
openWorldHint: false
|
|
478
815
|
}
|
|
479
816
|
},
|
|
480
817
|
{
|
|
@@ -486,7 +823,9 @@ var MCP_TOOLS = [
|
|
|
486
823
|
...paginationProps,
|
|
487
824
|
email: { type: "string", description: "Optional filter by email" }
|
|
488
825
|
}
|
|
489
|
-
}
|
|
826
|
+
},
|
|
827
|
+
outputSchema: pagedOutput("data", suppressionItem, "Suppressed addresses on this page"),
|
|
828
|
+
annotations: readOnly("List suppressions")
|
|
490
829
|
},
|
|
491
830
|
{
|
|
492
831
|
name: "add_suppression",
|
|
@@ -494,10 +833,19 @@ var MCP_TOOLS = [
|
|
|
494
833
|
inputSchema: {
|
|
495
834
|
type: "object",
|
|
496
835
|
properties: {
|
|
497
|
-
email: { type: "string" },
|
|
498
|
-
reason: { type: "string" }
|
|
836
|
+
email: { type: "string", description: "Email address to stop sending to" },
|
|
837
|
+
reason: { type: "string", description: "Optional note on why the address is suppressed" }
|
|
499
838
|
},
|
|
500
839
|
required: ["email"]
|
|
840
|
+
},
|
|
841
|
+
outputSchema: suppressionOutput,
|
|
842
|
+
// Adding an address that is already suppressed updates that entry instead of creating a second one.
|
|
843
|
+
annotations: {
|
|
844
|
+
title: "Add suppression",
|
|
845
|
+
readOnlyHint: false,
|
|
846
|
+
destructiveHint: false,
|
|
847
|
+
idempotentHint: true,
|
|
848
|
+
openWorldHint: false
|
|
501
849
|
}
|
|
502
850
|
},
|
|
503
851
|
{
|
|
@@ -509,6 +857,14 @@ var MCP_TOOLS = [
|
|
|
509
857
|
id: { type: "string", description: "Suppression id" },
|
|
510
858
|
email: { type: "string", description: "Email address (alternative to id)" }
|
|
511
859
|
}
|
|
860
|
+
},
|
|
861
|
+
outputSchema: okOutput,
|
|
862
|
+
annotations: {
|
|
863
|
+
title: "Remove suppression",
|
|
864
|
+
readOnlyHint: false,
|
|
865
|
+
destructiveHint: true,
|
|
866
|
+
idempotentHint: true,
|
|
867
|
+
openWorldHint: false
|
|
512
868
|
}
|
|
513
869
|
},
|
|
514
870
|
{
|
|
@@ -518,9 +874,11 @@ var MCP_TOOLS = [
|
|
|
518
874
|
type: "object",
|
|
519
875
|
properties: {
|
|
520
876
|
...paginationProps,
|
|
521
|
-
status: { type: "string", enum: ["draft", "published"] }
|
|
877
|
+
status: { type: "string", enum: ["draft", "published"], description: "Optional filter by template status" }
|
|
522
878
|
}
|
|
523
|
-
}
|
|
879
|
+
},
|
|
880
|
+
outputSchema: pagedOutput("data", templateItem, "Templates on this page"),
|
|
881
|
+
annotations: readOnly("List templates")
|
|
524
882
|
},
|
|
525
883
|
{
|
|
526
884
|
name: "get_template",
|
|
@@ -531,7 +889,9 @@ var MCP_TOOLS = [
|
|
|
531
889
|
id: { type: "string", description: "Template id (tpl_\u2026)" },
|
|
532
890
|
alias: { type: "string", description: "Published alias (alternative to id)" }
|
|
533
891
|
}
|
|
534
|
-
}
|
|
892
|
+
},
|
|
893
|
+
outputSchema: templateOutput,
|
|
894
|
+
annotations: readOnly("Get template")
|
|
535
895
|
},
|
|
536
896
|
{
|
|
537
897
|
name: "create_template",
|
|
@@ -539,14 +899,22 @@ var MCP_TOOLS = [
|
|
|
539
899
|
inputSchema: {
|
|
540
900
|
type: "object",
|
|
541
901
|
properties: {
|
|
542
|
-
name: { type: "string" },
|
|
543
|
-
subject: { type: "string" },
|
|
544
|
-
format: { type: "string", enum: ["html", "blocks"] },
|
|
545
|
-
html: { type: "string" },
|
|
546
|
-
text: { type: "string" },
|
|
547
|
-
alias: { type: "string" }
|
|
902
|
+
name: { type: "string", description: "Template name" },
|
|
903
|
+
subject: { type: "string", description: "Subject line, may contain {{variables}}" },
|
|
904
|
+
format: { type: "string", enum: ["html", "blocks"], description: "Template format (html or blocks)" },
|
|
905
|
+
html: { type: "string", description: "HTML body" },
|
|
906
|
+
text: { type: "string", description: "Plain-text body" },
|
|
907
|
+
alias: { type: "string", description: "Optional short alias to reference the template by instead of its id" }
|
|
548
908
|
},
|
|
549
909
|
required: ["name", "subject"]
|
|
910
|
+
},
|
|
911
|
+
outputSchema: templateOutput,
|
|
912
|
+
annotations: {
|
|
913
|
+
title: "Create template",
|
|
914
|
+
readOnlyHint: false,
|
|
915
|
+
destructiveHint: false,
|
|
916
|
+
idempotentHint: false,
|
|
917
|
+
openWorldHint: false
|
|
550
918
|
}
|
|
551
919
|
},
|
|
552
920
|
{
|
|
@@ -555,9 +923,18 @@ var MCP_TOOLS = [
|
|
|
555
923
|
inputSchema: {
|
|
556
924
|
type: "object",
|
|
557
925
|
properties: {
|
|
558
|
-
id: { type: "string" },
|
|
559
|
-
alias: { type: "string" }
|
|
926
|
+
id: { type: "string", description: "Template id (tpl_\u2026)" },
|
|
927
|
+
alias: { type: "string", description: "Template alias (alternative to id)" }
|
|
560
928
|
}
|
|
929
|
+
},
|
|
930
|
+
outputSchema: templateOutput,
|
|
931
|
+
// Each publish snapshots a new version; earlier versions are kept.
|
|
932
|
+
annotations: {
|
|
933
|
+
title: "Publish template",
|
|
934
|
+
readOnlyHint: false,
|
|
935
|
+
destructiveHint: false,
|
|
936
|
+
idempotentHint: false,
|
|
937
|
+
openWorldHint: false
|
|
561
938
|
}
|
|
562
939
|
},
|
|
563
940
|
{
|
|
@@ -578,6 +955,14 @@ var MCP_TOOLS = [
|
|
|
578
955
|
}
|
|
579
956
|
},
|
|
580
957
|
required: ["event"]
|
|
958
|
+
},
|
|
959
|
+
outputSchema: testWebhookOutput,
|
|
960
|
+
annotations: {
|
|
961
|
+
title: "Send test webhook event",
|
|
962
|
+
readOnlyHint: false,
|
|
963
|
+
destructiveHint: false,
|
|
964
|
+
idempotentHint: false,
|
|
965
|
+
openWorldHint: true
|
|
581
966
|
}
|
|
582
967
|
},
|
|
583
968
|
{
|
|
@@ -586,9 +971,11 @@ var MCP_TOOLS = [
|
|
|
586
971
|
inputSchema: {
|
|
587
972
|
type: "object",
|
|
588
973
|
properties: {
|
|
589
|
-
window: { type: "string", enum: ["7d", "30d"] }
|
|
974
|
+
window: { type: "string", enum: ["7d", "30d"], description: "Time window to summarize (default 30d)" }
|
|
590
975
|
}
|
|
591
|
-
}
|
|
976
|
+
},
|
|
977
|
+
outputSchema: deliverabilityOutput,
|
|
978
|
+
annotations: readOnly("Get deliverability metrics")
|
|
592
979
|
}
|
|
593
980
|
];
|
|
594
981
|
function resolveApiKey() {
|
|
@@ -603,7 +990,7 @@ function resolveApiKey() {
|
|
|
603
990
|
}
|
|
604
991
|
|
|
605
992
|
// src/server.ts
|
|
606
|
-
var MCP_SERVER_VERSION = "0.5.
|
|
993
|
+
var MCP_SERVER_VERSION = "0.5.11";
|
|
607
994
|
var MCP_SERVER_INSTRUCTIONS = [
|
|
608
995
|
"Groundmail sends transactional email from domains the user has verified.",
|
|
609
996
|
'To test before a domain is verified, call send_email with from "noreply@mail.groundmail.dev" and to set to the Groundmail account email; sandbox sends to any other address are rejected.',
|
|
@@ -621,7 +1008,10 @@ function createMcpServer(apiKey, baseUrl) {
|
|
|
621
1008
|
tools: MCP_TOOLS.map((tool) => ({
|
|
622
1009
|
name: tool.name,
|
|
623
1010
|
description: tool.description,
|
|
624
|
-
|
|
1011
|
+
title: tool.annotations.title,
|
|
1012
|
+
inputSchema: tool.inputSchema,
|
|
1013
|
+
outputSchema: tool.outputSchema,
|
|
1014
|
+
annotations: tool.annotations
|
|
625
1015
|
}))
|
|
626
1016
|
}));
|
|
627
1017
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groundmail-mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.11",
|
|
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",
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
13
|
"README.md",
|
|
14
|
-
"smithery.yaml"
|
|
14
|
+
"smithery.yaml",
|
|
15
|
+
"LICENSE"
|
|
15
16
|
],
|
|
16
17
|
"repository": {
|
|
17
18
|
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/Groundmail/groundmail-
|
|
19
|
+
"url": "git+https://github.com/Groundmail/groundmail-mcp.git"
|
|
19
20
|
},
|
|
20
21
|
"homepage": "https://docs.groundmail.dev/ai/mcp",
|
|
21
22
|
"keywords": [
|
|
@@ -40,11 +41,15 @@
|
|
|
40
41
|
},
|
|
41
42
|
"dependencies": {
|
|
42
43
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
43
|
-
"groundmail": "0.14.
|
|
44
|
+
"groundmail": "0.14.4"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"tsup": "^8.5.0",
|
|
47
48
|
"typescript": "^5",
|
|
48
|
-
"vitest": "^3.2.4"
|
|
49
|
+
"vitest": "^3.2.4",
|
|
50
|
+
"@types/node": "^20"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/Groundmail/groundmail-mcp/issues"
|
|
49
54
|
}
|
|
50
55
|
}
|