@codespar/mcp-sendgrid 0.1.0 → 0.2.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 +158 -14
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/index.ts +158 -14
package/dist/index.js
CHANGED
|
@@ -14,18 +14,27 @@
|
|
|
14
14
|
* - Suppressions per unsubscribe group (list / add)
|
|
15
15
|
* - Global stats (sent / delivered / opens / clicks)
|
|
16
16
|
*
|
|
17
|
-
* Tools (
|
|
18
|
-
* send_mail
|
|
19
|
-
* send_template
|
|
20
|
-
* add_contact
|
|
21
|
-
* list_contacts
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
17
|
+
* Tools (20):
|
|
18
|
+
* send_mail — POST /mail/send (personalizations, content, attachments)
|
|
19
|
+
* send_template — POST /mail/send using a dynamic template_id
|
|
20
|
+
* add_contact — PUT /marketing/contacts (upsert, async job)
|
|
21
|
+
* list_contacts — GET /marketing/contacts
|
|
22
|
+
* get_contact — GET /marketing/contacts/{id}
|
|
23
|
+
* delete_contact — DELETE /marketing/contacts?ids=...
|
|
24
|
+
* search_contacts — POST /marketing/contacts/search (SGQL)
|
|
25
|
+
* list_lists — GET /marketing/lists
|
|
26
|
+
* create_list — POST /marketing/lists
|
|
27
|
+
* delete_list — DELETE /marketing/lists/{id}
|
|
28
|
+
* list_templates — GET /templates?generations=dynamic
|
|
29
|
+
* create_template — POST /templates
|
|
30
|
+
* list_unsubscribe_groups — GET /asm/groups
|
|
31
|
+
* list_suppressions — GET /asm/groups/{group_id}/suppressions
|
|
32
|
+
* add_suppression — POST /asm/groups/{group_id}/suppressions
|
|
33
|
+
* get_bounces — GET /suppression/bounces
|
|
34
|
+
* delete_bounce — DELETE /suppression/bounces/{email}
|
|
35
|
+
* cancel_scheduled_send — POST /user/scheduled_sends (cancel/pause by batch_id)
|
|
36
|
+
* get_event_webhook_settings — GET /user/webhooks/event/settings
|
|
37
|
+
* get_stats — GET /stats?start_date=X&end_date=Y
|
|
29
38
|
*
|
|
30
39
|
* Authentication
|
|
31
40
|
* Authorization: Bearer <SENDGRID_API_KEY>
|
|
@@ -83,7 +92,7 @@ function buildQuery(params) {
|
|
|
83
92
|
const s = q.toString();
|
|
84
93
|
return s ? `?${s}` : "";
|
|
85
94
|
}
|
|
86
|
-
const server = new Server({ name: "mcp-sendgrid", version: "0.
|
|
95
|
+
const server = new Server({ name: "mcp-sendgrid", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
87
96
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
88
97
|
tools: [
|
|
89
98
|
{
|
|
@@ -185,6 +194,51 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
185
194
|
required: ["query"],
|
|
186
195
|
},
|
|
187
196
|
},
|
|
197
|
+
{
|
|
198
|
+
name: "get_contact",
|
|
199
|
+
description: "Retrieve a single Marketing Campaigns contact by id via GET /marketing/contacts/{id}. Returns full contact record including custom fields and list_ids.",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
id: { type: "string", description: "Contact UUID" },
|
|
204
|
+
},
|
|
205
|
+
required: ["id"],
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "list_lists",
|
|
210
|
+
description: "List all Marketing Campaigns contact lists via GET /marketing/lists. Returns list UUIDs, names, and contact_count. Supports pagination.",
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: "object",
|
|
213
|
+
properties: {
|
|
214
|
+
page_size: { type: "number", description: "Results per page (default 100, max 1000)" },
|
|
215
|
+
page_token: { type: "string", description: "Pagination token from previous response" },
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: "create_list",
|
|
221
|
+
description: "Create a Marketing Campaigns contact list via POST /marketing/lists. Returns the new list UUID. Use the id with add_contact's list_ids to populate it.",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
name: { type: "string", description: "List name (max 100 chars, must be unique)" },
|
|
226
|
+
},
|
|
227
|
+
required: ["name"],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: "delete_list",
|
|
232
|
+
description: "Delete a Marketing Campaigns contact list via DELETE /marketing/lists/{id}. Contacts are NOT deleted by default — set delete_contacts=true to also remove contacts that belong ONLY to this list.",
|
|
233
|
+
inputSchema: {
|
|
234
|
+
type: "object",
|
|
235
|
+
properties: {
|
|
236
|
+
id: { type: "string", description: "List UUID" },
|
|
237
|
+
delete_contacts: { type: "boolean", description: "If true, also delete contacts that are exclusive to this list (async job)" },
|
|
238
|
+
},
|
|
239
|
+
required: ["id"],
|
|
240
|
+
},
|
|
241
|
+
},
|
|
188
242
|
{
|
|
189
243
|
name: "list_templates",
|
|
190
244
|
description: "List transactional templates via GET /templates. By default returns dynamic templates (recommended); set generations='legacy' for legacy.",
|
|
@@ -232,6 +286,60 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
232
286
|
required: ["group_id", "recipient_emails"],
|
|
233
287
|
},
|
|
234
288
|
},
|
|
289
|
+
{
|
|
290
|
+
name: "list_unsubscribe_groups",
|
|
291
|
+
description: "List all unsubscribe groups on the account via GET /asm/groups. Returns [{id, name, description, is_default, unsubscribes}]. Use the id with list_suppressions / add_suppression.",
|
|
292
|
+
inputSchema: {
|
|
293
|
+
type: "object",
|
|
294
|
+
properties: {
|
|
295
|
+
id: { type: "number", description: "Optional: filter by a single group id" },
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "get_bounces",
|
|
301
|
+
description: "Retrieve bounced recipients via GET /suppression/bounces. Returns [{email, created, reason, status}]. Filter by time window with start_time/end_time (Unix seconds).",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
start_time: { type: "number", description: "Unix timestamp (seconds) lower bound" },
|
|
306
|
+
end_time: { type: "number", description: "Unix timestamp (seconds) upper bound" },
|
|
307
|
+
limit: { type: "number", description: "Max results to return" },
|
|
308
|
+
offset: { type: "number", description: "Offset for pagination" },
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: "delete_bounce",
|
|
314
|
+
description: "Remove a bounced address from the bounce suppression list via DELETE /suppression/bounces/{email}. Call this after the recipient confirms the underlying issue (e.g. mailbox full) is resolved.",
|
|
315
|
+
inputSchema: {
|
|
316
|
+
type: "object",
|
|
317
|
+
properties: {
|
|
318
|
+
email: { type: "string", description: "Bounced email address to clear" },
|
|
319
|
+
},
|
|
320
|
+
required: ["email"],
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "cancel_scheduled_send",
|
|
325
|
+
description: "Cancel or pause a scheduled send by batch_id via POST /user/scheduled_sends. A send_mail call with `send_at` + `batch_id` can be aborted until the send runs. Set status to 'cancel' or 'pause'.",
|
|
326
|
+
inputSchema: {
|
|
327
|
+
type: "object",
|
|
328
|
+
properties: {
|
|
329
|
+
batch_id: { type: "string", description: "The batch_id that was attached to the scheduled /mail/send call" },
|
|
330
|
+
status: { type: "string", enum: ["cancel", "pause"], description: "`cancel` aborts; `pause` holds the batch (can be resumed by deleting the status)" },
|
|
331
|
+
},
|
|
332
|
+
required: ["batch_id", "status"],
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: "get_event_webhook_settings",
|
|
337
|
+
description: "Retrieve the Event Webhook configuration via GET /user/webhooks/event/settings. Returns {url, enabled, delivered, open, click, bounce, dropped, spam_report, unsubscribe, ...} — useful to verify which SendGrid events are being forwarded.",
|
|
338
|
+
inputSchema: {
|
|
339
|
+
type: "object",
|
|
340
|
+
properties: {},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
235
343
|
{
|
|
236
344
|
name: "get_stats",
|
|
237
345
|
description: "Global email stats via GET /stats. Returns sent/delivered/opens/clicks/bounces/spam_reports aggregated between start_date and end_date.",
|
|
@@ -319,6 +427,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
319
427
|
}
|
|
320
428
|
case "search_contacts":
|
|
321
429
|
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("POST", "/marketing/contacts/search", { query: a.query }), null, 2) }] };
|
|
430
|
+
case "get_contact":
|
|
431
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/marketing/contacts/${a.id}`), null, 2) }] };
|
|
432
|
+
case "list_lists": {
|
|
433
|
+
const q = buildQuery({
|
|
434
|
+
page_size: a.page_size,
|
|
435
|
+
page_token: a.page_token,
|
|
436
|
+
});
|
|
437
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/marketing/lists${q}`), null, 2) }] };
|
|
438
|
+
}
|
|
439
|
+
case "create_list":
|
|
440
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("POST", "/marketing/lists", { name: a.name }), null, 2) }] };
|
|
441
|
+
case "delete_list": {
|
|
442
|
+
const q = buildQuery({
|
|
443
|
+
delete_contacts: a.delete_contacts ? "true" : undefined,
|
|
444
|
+
});
|
|
445
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("DELETE", `/marketing/lists/${a.id}${q}`), null, 2) }] };
|
|
446
|
+
}
|
|
322
447
|
case "list_templates": {
|
|
323
448
|
const q = buildQuery({
|
|
324
449
|
generations: a.generations ?? "dynamic",
|
|
@@ -339,6 +464,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
339
464
|
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/asm/groups/${a.group_id}/suppressions`), null, 2) }] };
|
|
340
465
|
case "add_suppression":
|
|
341
466
|
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("POST", `/asm/groups/${a.group_id}/suppressions`, { recipient_emails: a.recipient_emails }), null, 2) }] };
|
|
467
|
+
case "list_unsubscribe_groups": {
|
|
468
|
+
const q = buildQuery({ id: a.id });
|
|
469
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/asm/groups${q}`), null, 2) }] };
|
|
470
|
+
}
|
|
471
|
+
case "get_bounces": {
|
|
472
|
+
const q = buildQuery({
|
|
473
|
+
start_time: a.start_time,
|
|
474
|
+
end_time: a.end_time,
|
|
475
|
+
limit: a.limit,
|
|
476
|
+
offset: a.offset,
|
|
477
|
+
});
|
|
478
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/suppression/bounces${q}`), null, 2) }] };
|
|
479
|
+
}
|
|
480
|
+
case "delete_bounce":
|
|
481
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("DELETE", `/suppression/bounces/${encodeURIComponent(String(a.email))}`), null, 2) }] };
|
|
482
|
+
case "cancel_scheduled_send":
|
|
483
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("POST", "/user/scheduled_sends", { batch_id: a.batch_id, status: a.status }), null, 2) }] };
|
|
484
|
+
case "get_event_webhook_settings":
|
|
485
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", "/user/webhooks/event/settings"), null, 2) }] };
|
|
342
486
|
case "get_stats": {
|
|
343
487
|
const q = buildQuery({
|
|
344
488
|
start_date: a.start_date,
|
|
@@ -374,7 +518,7 @@ async function main() {
|
|
|
374
518
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
375
519
|
t.onclose = () => { if (t.sessionId)
|
|
376
520
|
transports.delete(t.sessionId); };
|
|
377
|
-
const s = new Server({ name: "mcp-sendgrid", version: "0.
|
|
521
|
+
const s = new Server({ name: "mcp-sendgrid", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
378
522
|
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
379
523
|
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
380
524
|
await s.connect(t);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-sendgrid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP server for SendGrid — global transactional + marketing email (Twilio-owned). Pairs with @codespar/mcp-twilio for full messaging coverage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/communication/sendgrid"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.2.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@codespar/mcp-sendgrid",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.2.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/index.ts
CHANGED
|
@@ -15,18 +15,27 @@
|
|
|
15
15
|
* - Suppressions per unsubscribe group (list / add)
|
|
16
16
|
* - Global stats (sent / delivered / opens / clicks)
|
|
17
17
|
*
|
|
18
|
-
* Tools (
|
|
19
|
-
* send_mail
|
|
20
|
-
* send_template
|
|
21
|
-
* add_contact
|
|
22
|
-
* list_contacts
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
18
|
+
* Tools (20):
|
|
19
|
+
* send_mail — POST /mail/send (personalizations, content, attachments)
|
|
20
|
+
* send_template — POST /mail/send using a dynamic template_id
|
|
21
|
+
* add_contact — PUT /marketing/contacts (upsert, async job)
|
|
22
|
+
* list_contacts — GET /marketing/contacts
|
|
23
|
+
* get_contact — GET /marketing/contacts/{id}
|
|
24
|
+
* delete_contact — DELETE /marketing/contacts?ids=...
|
|
25
|
+
* search_contacts — POST /marketing/contacts/search (SGQL)
|
|
26
|
+
* list_lists — GET /marketing/lists
|
|
27
|
+
* create_list — POST /marketing/lists
|
|
28
|
+
* delete_list — DELETE /marketing/lists/{id}
|
|
29
|
+
* list_templates — GET /templates?generations=dynamic
|
|
30
|
+
* create_template — POST /templates
|
|
31
|
+
* list_unsubscribe_groups — GET /asm/groups
|
|
32
|
+
* list_suppressions — GET /asm/groups/{group_id}/suppressions
|
|
33
|
+
* add_suppression — POST /asm/groups/{group_id}/suppressions
|
|
34
|
+
* get_bounces — GET /suppression/bounces
|
|
35
|
+
* delete_bounce — DELETE /suppression/bounces/{email}
|
|
36
|
+
* cancel_scheduled_send — POST /user/scheduled_sends (cancel/pause by batch_id)
|
|
37
|
+
* get_event_webhook_settings — GET /user/webhooks/event/settings
|
|
38
|
+
* get_stats — GET /stats?start_date=X&end_date=Y
|
|
30
39
|
*
|
|
31
40
|
* Authentication
|
|
32
41
|
* Authorization: Bearer <SENDGRID_API_KEY>
|
|
@@ -96,7 +105,7 @@ function buildQuery(params: Record<string, unknown>): string {
|
|
|
96
105
|
}
|
|
97
106
|
|
|
98
107
|
const server = new Server(
|
|
99
|
-
{ name: "mcp-sendgrid", version: "0.
|
|
108
|
+
{ name: "mcp-sendgrid", version: "0.2.0" },
|
|
100
109
|
{ capabilities: { tools: {} } }
|
|
101
110
|
);
|
|
102
111
|
|
|
@@ -201,6 +210,51 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
201
210
|
required: ["query"],
|
|
202
211
|
},
|
|
203
212
|
},
|
|
213
|
+
{
|
|
214
|
+
name: "get_contact",
|
|
215
|
+
description: "Retrieve a single Marketing Campaigns contact by id via GET /marketing/contacts/{id}. Returns full contact record including custom fields and list_ids.",
|
|
216
|
+
inputSchema: {
|
|
217
|
+
type: "object",
|
|
218
|
+
properties: {
|
|
219
|
+
id: { type: "string", description: "Contact UUID" },
|
|
220
|
+
},
|
|
221
|
+
required: ["id"],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "list_lists",
|
|
226
|
+
description: "List all Marketing Campaigns contact lists via GET /marketing/lists. Returns list UUIDs, names, and contact_count. Supports pagination.",
|
|
227
|
+
inputSchema: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: {
|
|
230
|
+
page_size: { type: "number", description: "Results per page (default 100, max 1000)" },
|
|
231
|
+
page_token: { type: "string", description: "Pagination token from previous response" },
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: "create_list",
|
|
237
|
+
description: "Create a Marketing Campaigns contact list via POST /marketing/lists. Returns the new list UUID. Use the id with add_contact's list_ids to populate it.",
|
|
238
|
+
inputSchema: {
|
|
239
|
+
type: "object",
|
|
240
|
+
properties: {
|
|
241
|
+
name: { type: "string", description: "List name (max 100 chars, must be unique)" },
|
|
242
|
+
},
|
|
243
|
+
required: ["name"],
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: "delete_list",
|
|
248
|
+
description: "Delete a Marketing Campaigns contact list via DELETE /marketing/lists/{id}. Contacts are NOT deleted by default — set delete_contacts=true to also remove contacts that belong ONLY to this list.",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
id: { type: "string", description: "List UUID" },
|
|
253
|
+
delete_contacts: { type: "boolean", description: "If true, also delete contacts that are exclusive to this list (async job)" },
|
|
254
|
+
},
|
|
255
|
+
required: ["id"],
|
|
256
|
+
},
|
|
257
|
+
},
|
|
204
258
|
{
|
|
205
259
|
name: "list_templates",
|
|
206
260
|
description: "List transactional templates via GET /templates. By default returns dynamic templates (recommended); set generations='legacy' for legacy.",
|
|
@@ -248,6 +302,60 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
248
302
|
required: ["group_id", "recipient_emails"],
|
|
249
303
|
},
|
|
250
304
|
},
|
|
305
|
+
{
|
|
306
|
+
name: "list_unsubscribe_groups",
|
|
307
|
+
description: "List all unsubscribe groups on the account via GET /asm/groups. Returns [{id, name, description, is_default, unsubscribes}]. Use the id with list_suppressions / add_suppression.",
|
|
308
|
+
inputSchema: {
|
|
309
|
+
type: "object",
|
|
310
|
+
properties: {
|
|
311
|
+
id: { type: "number", description: "Optional: filter by a single group id" },
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "get_bounces",
|
|
317
|
+
description: "Retrieve bounced recipients via GET /suppression/bounces. Returns [{email, created, reason, status}]. Filter by time window with start_time/end_time (Unix seconds).",
|
|
318
|
+
inputSchema: {
|
|
319
|
+
type: "object",
|
|
320
|
+
properties: {
|
|
321
|
+
start_time: { type: "number", description: "Unix timestamp (seconds) lower bound" },
|
|
322
|
+
end_time: { type: "number", description: "Unix timestamp (seconds) upper bound" },
|
|
323
|
+
limit: { type: "number", description: "Max results to return" },
|
|
324
|
+
offset: { type: "number", description: "Offset for pagination" },
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: "delete_bounce",
|
|
330
|
+
description: "Remove a bounced address from the bounce suppression list via DELETE /suppression/bounces/{email}. Call this after the recipient confirms the underlying issue (e.g. mailbox full) is resolved.",
|
|
331
|
+
inputSchema: {
|
|
332
|
+
type: "object",
|
|
333
|
+
properties: {
|
|
334
|
+
email: { type: "string", description: "Bounced email address to clear" },
|
|
335
|
+
},
|
|
336
|
+
required: ["email"],
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "cancel_scheduled_send",
|
|
341
|
+
description: "Cancel or pause a scheduled send by batch_id via POST /user/scheduled_sends. A send_mail call with `send_at` + `batch_id` can be aborted until the send runs. Set status to 'cancel' or 'pause'.",
|
|
342
|
+
inputSchema: {
|
|
343
|
+
type: "object",
|
|
344
|
+
properties: {
|
|
345
|
+
batch_id: { type: "string", description: "The batch_id that was attached to the scheduled /mail/send call" },
|
|
346
|
+
status: { type: "string", enum: ["cancel", "pause"], description: "`cancel` aborts; `pause` holds the batch (can be resumed by deleting the status)" },
|
|
347
|
+
},
|
|
348
|
+
required: ["batch_id", "status"],
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: "get_event_webhook_settings",
|
|
353
|
+
description: "Retrieve the Event Webhook configuration via GET /user/webhooks/event/settings. Returns {url, enabled, delivered, open, click, bounce, dropped, spam_report, unsubscribe, ...} — useful to verify which SendGrid events are being forwarded.",
|
|
354
|
+
inputSchema: {
|
|
355
|
+
type: "object",
|
|
356
|
+
properties: {},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
251
359
|
{
|
|
252
360
|
name: "get_stats",
|
|
253
361
|
description: "Global email stats via GET /stats. Returns sent/delivered/opens/clicks/bounces/spam_reports aggregated between start_date and end_date.",
|
|
@@ -319,6 +427,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
319
427
|
}
|
|
320
428
|
case "search_contacts":
|
|
321
429
|
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("POST", "/marketing/contacts/search", { query: a.query }), null, 2) }] };
|
|
430
|
+
case "get_contact":
|
|
431
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/marketing/contacts/${a.id}`), null, 2) }] };
|
|
432
|
+
case "list_lists": {
|
|
433
|
+
const q = buildQuery({
|
|
434
|
+
page_size: a.page_size,
|
|
435
|
+
page_token: a.page_token,
|
|
436
|
+
});
|
|
437
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/marketing/lists${q}`), null, 2) }] };
|
|
438
|
+
}
|
|
439
|
+
case "create_list":
|
|
440
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("POST", "/marketing/lists", { name: a.name }), null, 2) }] };
|
|
441
|
+
case "delete_list": {
|
|
442
|
+
const q = buildQuery({
|
|
443
|
+
delete_contacts: a.delete_contacts ? "true" : undefined,
|
|
444
|
+
});
|
|
445
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("DELETE", `/marketing/lists/${a.id}${q}`), null, 2) }] };
|
|
446
|
+
}
|
|
322
447
|
case "list_templates": {
|
|
323
448
|
const q = buildQuery({
|
|
324
449
|
generations: a.generations ?? "dynamic",
|
|
@@ -337,6 +462,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
337
462
|
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/asm/groups/${a.group_id}/suppressions`), null, 2) }] };
|
|
338
463
|
case "add_suppression":
|
|
339
464
|
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("POST", `/asm/groups/${a.group_id}/suppressions`, { recipient_emails: a.recipient_emails }), null, 2) }] };
|
|
465
|
+
case "list_unsubscribe_groups": {
|
|
466
|
+
const q = buildQuery({ id: a.id });
|
|
467
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/asm/groups${q}`), null, 2) }] };
|
|
468
|
+
}
|
|
469
|
+
case "get_bounces": {
|
|
470
|
+
const q = buildQuery({
|
|
471
|
+
start_time: a.start_time,
|
|
472
|
+
end_time: a.end_time,
|
|
473
|
+
limit: a.limit,
|
|
474
|
+
offset: a.offset,
|
|
475
|
+
});
|
|
476
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", `/suppression/bounces${q}`), null, 2) }] };
|
|
477
|
+
}
|
|
478
|
+
case "delete_bounce":
|
|
479
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("DELETE", `/suppression/bounces/${encodeURIComponent(String(a.email))}`), null, 2) }] };
|
|
480
|
+
case "cancel_scheduled_send":
|
|
481
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("POST", "/user/scheduled_sends", { batch_id: a.batch_id, status: a.status }), null, 2) }] };
|
|
482
|
+
case "get_event_webhook_settings":
|
|
483
|
+
return { content: [{ type: "text", text: JSON.stringify(await sendgridRequest("GET", "/user/webhooks/event/settings"), null, 2) }] };
|
|
340
484
|
case "get_stats": {
|
|
341
485
|
const q = buildQuery({
|
|
342
486
|
start_date: a.start_date,
|
|
@@ -368,7 +512,7 @@ async function main() {
|
|
|
368
512
|
if (!sid && isInitializeRequest(req.body)) {
|
|
369
513
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
370
514
|
t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
|
|
371
|
-
const s = new Server({ name: "mcp-sendgrid", version: "0.
|
|
515
|
+
const s = new Server({ name: "mcp-sendgrid", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
372
516
|
(server as unknown as { _requestHandlers: Map<unknown, unknown> })._requestHandlers.forEach((v, k) => (s as unknown as { _requestHandlers: Map<unknown, unknown> })._requestHandlers.set(k, v));
|
|
373
517
|
(server as unknown as { _notificationHandlers?: Map<unknown, unknown> })._notificationHandlers?.forEach((v, k) => (s as unknown as { _notificationHandlers: Map<unknown, unknown> })._notificationHandlers.set(k, v));
|
|
374
518
|
await s.connect(t);
|