@neschadin/sendgrid-mcp 0.0.0-stage → 3.0.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/LICENSE +21 -0
- package/MCP_TOOLS.md +457 -0
- package/README.md +175 -2
- package/bin/sendgrid-mcp +2 -0
- package/mcp.json.example +9 -0
- package/package.json +63 -4
- package/src/client.ts +1585 -0
- package/src/config.ts +197 -0
- package/src/http.ts +132 -0
- package/src/index.ts +147 -0
- package/src/logger.ts +43 -0
- package/src/redact.ts +91 -0
- package/src/tool_signal.ts +50 -0
- package/src/tools/account.ts +566 -0
- package/src/tools/classify_error.ts +133 -0
- package/src/tools/console_settings.ts +329 -0
- package/src/tools/delivery_trace.ts +291 -0
- package/src/tools/diagnostics.ts +1557 -0
- package/src/tools/email.ts +486 -0
- package/src/tools/output_schemas.ts +488 -0
- package/src/tools/preflight.ts +768 -0
- package/src/tools/sync.ts +139 -0
- package/src/tools/templates.ts +602 -0
- package/src/tools/tool_utils.ts +341 -0
- package/src/webhook_receiver.ts +467 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Neschadin
|
|
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/MCP_TOOLS.md
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# SendGrid MCP Tools
|
|
2
|
+
|
|
3
|
+
Scope: transactional email hardening and SendGrid-side diagnostics.
|
|
4
|
+
Out of scope: contact/list marketing CRUD.
|
|
5
|
+
|
|
6
|
+
**Handshake metadata (v2):** Server name is `sendgrid-mcp-server`. The handshake `instructions` carry the safe-send and delivery workflow for this process (region, API base, from address). All tools are exposed as `sendgrid_<name>`. Mutating tools with required `confirmToken` automatically append `Requires confirmToken="CONFIRM".` to the tool `description`. Read tools expose `outputSchema`, `structuredContent`, optional `response_format` (`markdown`|`json`), and list pagination metadata (`total_count`, `count`, `offset`, `has_more`, `next_offset`). A tool call whose arguments contain more than 10000 combined array elements and object members is rejected before the handler runs. Tool results redact `oauth_client_secret` and `api_key`.
|
|
7
|
+
|
|
8
|
+
## Core Runbooks
|
|
9
|
+
|
|
10
|
+
### Runbook: Safe Send
|
|
11
|
+
|
|
12
|
+
1. `sendgrid_validate_send_request`
|
|
13
|
+
2. `sendgrid_send_with_preflight` (or `sendgrid_send_email_advanced`)
|
|
14
|
+
3. On failure: `sendgrid_classify_sendgrid_error`
|
|
15
|
+
4. On delivery doubts: `sendgrid_triage_delivery_issue`
|
|
16
|
+
|
|
17
|
+
### Runbook: Delivery Incident
|
|
18
|
+
|
|
19
|
+
1. `sendgrid_search_message_activity` / `sendgrid_get_message_activity` (or `sendgrid_search_email_logs` when Activity is empty or 403, especially on EU)
|
|
20
|
+
2. `sendgrid_check_suppression` and `sendgrid_list_suppressions`; lift a hit with `sendgrid_delete_suppression`
|
|
21
|
+
3. `sendgrid_triage_delivery_issue`
|
|
22
|
+
4. If webhook exists: `sendgrid_get_received_webhook_events` + `sendgrid_analyze_engagement_anomalies`
|
|
23
|
+
|
|
24
|
+
### Runbook: Webhook Operations
|
|
25
|
+
|
|
26
|
+
1. `sendgrid_list_event_webhooks`
|
|
27
|
+
2. `sendgrid_update_event_webhook` / `sendgrid_toggle_event_webhook_signature`
|
|
28
|
+
3. `sendgrid_get_webhook_receiver_status`
|
|
29
|
+
4. Send test traffic and inspect `sendgrid_get_received_webhook_events`
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Tool Catalog
|
|
34
|
+
|
|
35
|
+
### `sendgrid_list_templates`
|
|
36
|
+
- **Purpose:** List all dynamic templates from SendGrid (full pagination).
|
|
37
|
+
- **Inputs:** none.
|
|
38
|
+
- **Typical flow:** Inventory before edits/migrations.
|
|
39
|
+
- **Caveats:** Returns dynamic templates only.
|
|
40
|
+
|
|
41
|
+
### `sendgrid_rename_template`
|
|
42
|
+
- **Purpose:** Rename one template by template ID.
|
|
43
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `templateId`, `newName`.
|
|
44
|
+
- **Typical flow:** Safe targeted rename.
|
|
45
|
+
- **Caveats:** Changes template display name only.
|
|
46
|
+
|
|
47
|
+
### `sendgrid_rename_templates_bulk`
|
|
48
|
+
- **Purpose:** Bulk rename by `templateId` and/or `oldName`.
|
|
49
|
+
- **Inputs:** `renames[]`, optional `dryRun`, `confirmToken`, `stopOnError`, `requireUniqueOldName`.
|
|
50
|
+
- **Typical flow:** Large naming migrations.
|
|
51
|
+
- **Caveats:** Use `dryRun=true` first on production accounts.
|
|
52
|
+
|
|
53
|
+
### `sendgrid_get_template_html`
|
|
54
|
+
- **Purpose:** Read HTML of active or explicit template version.
|
|
55
|
+
- **Inputs:** `templateId`, optional `versionId`.
|
|
56
|
+
- **Output:** `structuredContent` with `templateId`, `versionId`, `active`, `name`, `subject`, `updatedAt`, `htmlContent`.
|
|
57
|
+
- **Typical flow:** Inspect/render debugging.
|
|
58
|
+
- **Caveats:** If `versionId` omitted, active version is used.
|
|
59
|
+
|
|
60
|
+
### `sendgrid_create_template`
|
|
61
|
+
- **Purpose:** Create a dynamic template with first active version.
|
|
62
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `name`, `versionName`, `subject`, `htmlContent`.
|
|
63
|
+
- **Typical flow:** Bootstrap new notification templates.
|
|
64
|
+
- **Caveats:** Creates both template and version in one operation.
|
|
65
|
+
|
|
66
|
+
### `sendgrid_update_template_html`
|
|
67
|
+
- **Purpose:** Update version-level content/subject/name.
|
|
68
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `templateId`, `versionId`, optional `htmlContent`, `subject`, `name`.
|
|
69
|
+
- **Typical flow:** Iterative edits to an existing version.
|
|
70
|
+
- **Caveats:** Does not auto-activate version.
|
|
71
|
+
|
|
72
|
+
### `sendgrid_activate_template_version`
|
|
73
|
+
- **Purpose:** Activate a template version.
|
|
74
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `templateId`, `versionId`.
|
|
75
|
+
- **Typical flow:** Release updated template content.
|
|
76
|
+
- **Caveats:** Only one version can be active.
|
|
77
|
+
|
|
78
|
+
### `sendgrid_prune_inactive_template_versions`
|
|
79
|
+
- **Purpose:** Delete inactive versions for one or more templates, keeping active versions.
|
|
80
|
+
- **Inputs:** `templateIds[]`, optional `dryRun` (default `true`), optional `confirmToken` required when `dryRun=false`.
|
|
81
|
+
- **Typical flow:** Cleanup after template version migrations.
|
|
82
|
+
- **Caveats:** Destructive when `dryRun=false`; use dry-run first.
|
|
83
|
+
|
|
84
|
+
### `sendgrid_delete_template`
|
|
85
|
+
- **Purpose:** Permanently delete a template.
|
|
86
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `templateId`.
|
|
87
|
+
- **Typical flow:** Cleanup unused templates.
|
|
88
|
+
- **Caveats:** Irreversible.
|
|
89
|
+
|
|
90
|
+
### `sendgrid_sync_template_ids`
|
|
91
|
+
- **Purpose:** Compare local `SENDGRID_TEMPLATES` constants with live SendGrid templates.
|
|
92
|
+
- **Inputs:** `constantsPath`.
|
|
93
|
+
- **Typical flow:** Keep backend constants aligned with real template IDs.
|
|
94
|
+
- **Caveats:** Local constants parsing is regex-based.
|
|
95
|
+
|
|
96
|
+
### `sendgrid_validate_send_request`
|
|
97
|
+
- **Purpose:** Preflight checks before send. Run this before any `send_*` call. Validates `/v3/mail/send` payload shape, active dynamic template, sender identity (domain authentication or verified sender), link branding alignment, recipient suppressions (including ASM group unsubscribe when `asm.groupId` is set), DMARC warn-list domains from `GET /v3/verified_senders/domains`, and scheduling limits (`send_at` future + within 72 hours).
|
|
98
|
+
- **Inputs:** `request`, optional `partnerAccountId`, `checkSenderIdentity`.
|
|
99
|
+
- **Output:** `structuredContent` with `ok`, `blockers`, `warnings`, `info`.
|
|
100
|
+
- **Typical flow:** Dry-run review before every production send.
|
|
101
|
+
- **Caveats:** Does not send email.
|
|
102
|
+
|
|
103
|
+
### `sendgrid_send_with_preflight`
|
|
104
|
+
- **Purpose:** Preferred production send path: runs `sendgrid_validate_send_request` checks first, then `POST /v3/mail/send` only when no blockers (and optionally no warnings with `abortOnWarnings`).
|
|
105
|
+
- **Inputs:** `request`, optional `abortOnWarnings`, sender/account checks.
|
|
106
|
+
- **Output:** `structuredContent` with `sent`, `report`, `statusCode`, `messageId`.
|
|
107
|
+
- **Typical flow:** Default send path for automation. Use `sendgrid_validate_send_request` alone when you only need a dry-run.
|
|
108
|
+
- **Caveats:** `abortOnWarnings=true` can block sends even without blockers.
|
|
109
|
+
|
|
110
|
+
### `sendgrid_send_email_advanced`
|
|
111
|
+
- **Purpose:** Full `/v3/mail/send` payload send.
|
|
112
|
+
- **Inputs:** `request` (all advanced fields).
|
|
113
|
+
- **Typical flow:** Use when payload already validated externally.
|
|
114
|
+
- **Caveats:** No automatic preflight.
|
|
115
|
+
|
|
116
|
+
### `sendgrid_send_template_email_advanced`
|
|
117
|
+
- **Purpose:** Advanced dynamic template send.
|
|
118
|
+
- **Inputs:** `to`, `templateId`, `dynamicTemplateData`, optional cc/bcc/asm/schedule.
|
|
119
|
+
- **Typical flow:** Template-based transactional mails.
|
|
120
|
+
- **Caveats:** Template/data mismatch can still fail if skipped preflight.
|
|
121
|
+
|
|
122
|
+
### `sendgrid_send_sandbox_email`
|
|
123
|
+
- **Purpose:** Send in SendGrid sandbox mode.
|
|
124
|
+
- **Inputs:** `request`.
|
|
125
|
+
- **Typical flow:** Validate payload integration without live delivery.
|
|
126
|
+
- **Caveats:** No recipient delivery occurs.
|
|
127
|
+
|
|
128
|
+
### `sendgrid_send_test_email`
|
|
129
|
+
- **Purpose:** Thin convenience wrapper for template test send; sandbox mode is used by default.
|
|
130
|
+
- **Inputs:** `to`, `templateId`, `mockData`, optional from override, optional `liveDelivery` + `confirmToken="CONFIRM"`.
|
|
131
|
+
- **Typical flow:** Quick manual smoke tests.
|
|
132
|
+
- **Caveats:** No live delivery occurs unless `liveDelivery=true` and `confirmToken` is provided.
|
|
133
|
+
|
|
134
|
+
### `sendgrid_create_batch_id`
|
|
135
|
+
- **Purpose:** Create batch ID for scheduling controls.
|
|
136
|
+
- **Inputs:** none.
|
|
137
|
+
- **Typical flow:** Prior to scheduled campaigns.
|
|
138
|
+
- **Caveats:** Batch lifecycle controls require this ID.
|
|
139
|
+
|
|
140
|
+
### `sendgrid_schedule_email`
|
|
141
|
+
- **Purpose:** Schedule send (`send_at`) with optional batch auto-create.
|
|
142
|
+
- **Inputs:** `request`, `sendAt`, optional `batchId`, `autoCreateBatchId`.
|
|
143
|
+
- **Typical flow:** Deferred delivery and pacing.
|
|
144
|
+
- **Caveats:** `sendAt` must be in the future and within SendGrid's 72-hour scheduling window.
|
|
145
|
+
|
|
146
|
+
### `sendgrid_list_scheduled_sends`
|
|
147
|
+
- **Purpose:** List batches that are paused or canceled (`GET /v3/user/scheduled_sends`).
|
|
148
|
+
- **Inputs:** none.
|
|
149
|
+
- **Caveats:** A `send_at` batch that was never paused or canceled is absent.
|
|
150
|
+
|
|
151
|
+
### `sendgrid_get_scheduled_send`
|
|
152
|
+
- **Purpose:** Read pause/cancel state for one `batch_id`.
|
|
153
|
+
- **Inputs:** `batchId`.
|
|
154
|
+
- **Caveats:** A missing batch comes back from SendGrid as `200` and `[]`, not 404. This tool reports that as not found.
|
|
155
|
+
|
|
156
|
+
### `sendgrid_pause_scheduled_send`
|
|
157
|
+
- **Purpose:** Pause scheduled batch.
|
|
158
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `batchId`.
|
|
159
|
+
- **Typical flow:** Temporary stop before campaign window.
|
|
160
|
+
- **Caveats:** Requires valid existing batch state.
|
|
161
|
+
|
|
162
|
+
### `sendgrid_resume_scheduled_send`
|
|
163
|
+
- **Purpose:** Resume by removing pause/cancel state.
|
|
164
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `batchId`.
|
|
165
|
+
- **Typical flow:** Continue paused/canceled batch.
|
|
166
|
+
- **Caveats:** Behavior is API `DELETE` of scheduled-send state entry.
|
|
167
|
+
|
|
168
|
+
### `sendgrid_cancel_scheduled_send`
|
|
169
|
+
- **Purpose:** Cancel scheduled batch.
|
|
170
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `batchId`.
|
|
171
|
+
- **Typical flow:** Emergency stop.
|
|
172
|
+
- **Caveats:** Near-send-time cancellation is not guaranteed by SendGrid.
|
|
173
|
+
|
|
174
|
+
### `sendgrid_classify_sendgrid_error`
|
|
175
|
+
- **Purpose:** Map SendGrid errors to probable causes and actions.
|
|
176
|
+
- **Inputs:** `statusCode`, `errorMessage`, `rawBody`.
|
|
177
|
+
- **Typical flow:** First response after API failure.
|
|
178
|
+
- **Caveats:** Heuristic classification; combine with activity/suppressions.
|
|
179
|
+
|
|
180
|
+
### `sendgrid_triage_delivery_issue`
|
|
181
|
+
- **Purpose:** Scenario-based delivery runbook with live checks.
|
|
182
|
+
- **Inputs:** `scenario` + optional recipient/from/template/message/activity params.
|
|
183
|
+
- **Typical flow:** `202 accepted`, `processing`, template/auth/DMARC/deferral incidents.
|
|
184
|
+
- **Caveats:** Depth depends on API access and provided identifiers.
|
|
185
|
+
|
|
186
|
+
### `sendgrid_search_message_activity`
|
|
187
|
+
- **Purpose:** Query Email Activity (`GET /v3/messages`) by SendGrid query syntax.
|
|
188
|
+
- **Inputs:** `query` and/or `xMessageId`, optional `limit`. `offset` above 0 is rejected: this API has no offset.
|
|
189
|
+
- **Typical flow:** Identify affected messages in an incident. Pass the Mail Send `x-message-id` response header as `xMessageId`; it is compiled to `msg_id LIKE '<id>%'`.
|
|
190
|
+
- **Caveats:** May require the Email Activity add-on. `limit` is 1–1000. There is no cursor. On 403/404, and on an empty result when `SENDGRID_REGION=eu`, the tool falls back to `POST /v3/logs`.
|
|
191
|
+
|
|
192
|
+
### `sendgrid_search_email_logs`
|
|
193
|
+
- **Purpose:** Search Email Logs (`POST /v3/logs`) when Activity is missing, especially for EU regional subusers.
|
|
194
|
+
- **Inputs:** optional `query`, `limit` (1–1000), `subusers` (parent account, exactly one username).
|
|
195
|
+
- **Typical flow:** `to_email='user@example.com'`, `status IN ('bounced','deferred')`, or `sg_message_id='<full id>'`.
|
|
196
|
+
- **Caveats:** Allowed fields are `sg_message_id`, `subject`, `to_email`, `status`, `reason`, `categories`, `sg_message_id_created_at`. Operators are `=` / `IN` / time comparisons. Combine with `AND`. No nesting. No event chain.
|
|
197
|
+
|
|
198
|
+
### `sendgrid_get_message_activity`
|
|
199
|
+
- **Purpose:** Fetch one message by `msg_id`, including the event chain (`reason`, `bounce_type`, `mx_server`, `asm_group_id`, `outbound_ip`).
|
|
200
|
+
- **Inputs:** `msgId` — full `msg_id` or the Mail Send `x-message-id`.
|
|
201
|
+
- **Typical flow:** Deep dive after search. An `x-message-id` is resolved with `msg_id LIKE` and then re-fetched.
|
|
202
|
+
- **Caveats:** Activity may require the add-on. A full id that 403/404s is loaded from Email Logs, which has status and reason but no events. EU regional subusers often have no Activity detail.
|
|
203
|
+
|
|
204
|
+
### `sendgrid_list_asm_groups`
|
|
205
|
+
- **Purpose:** List ASM unsubscribe groups (`GET /v3/asm/groups`) so `asm.groupId` does not have to be guessed.
|
|
206
|
+
- **Inputs:** none.
|
|
207
|
+
- **Caveats:** Not a marketing contact list.
|
|
208
|
+
|
|
209
|
+
### `sendgrid_create_asm_group`
|
|
210
|
+
- **Purpose:** Create an ASM unsubscribe group.
|
|
211
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `name`, `description`, optional `isDefault`.
|
|
212
|
+
- **Caveats:** Does not add recipients.
|
|
213
|
+
|
|
214
|
+
### `sendgrid_list_categories`
|
|
215
|
+
- **Purpose:** List category names (`GET /v3/categories`) for `sendgrid_get_email_stats` `dimension=category`.
|
|
216
|
+
- **Inputs:** optional `category`, `limit`, `offset`.
|
|
217
|
+
- **Caveats:** Names only, not metrics.
|
|
218
|
+
|
|
219
|
+
### `sendgrid_list_suppressions`
|
|
220
|
+
- **Purpose:** Enumerate suppression entries by type.
|
|
221
|
+
- **Inputs:** `type`, optional pagination/time/email filters.
|
|
222
|
+
- **Typical flow:** Bulk suppression audits.
|
|
223
|
+
- **Caveats:** Does not mutate suppression lists. `global_unsubscribes` is an alias for SendGrid global unsubscribe listing.
|
|
224
|
+
|
|
225
|
+
### `sendgrid_check_suppression`
|
|
226
|
+
- **Purpose:** Check bounce, block, global unsubscribe, spam report, invalid-email, and ASM group suppression flags for one recipient (`GET /v3/asm/suppressions/{email}`).
|
|
227
|
+
- **Inputs:** `email`.
|
|
228
|
+
- **Typical flow:** Per-recipient delivery triage. Group rows include `id`, `name`, and `suppressed`.
|
|
229
|
+
- **Caveats:** The ASM call returns every group, not only suppressed ones. A failed group lookup is reported in `groupLookupError` and does not clear the other flags.
|
|
230
|
+
|
|
231
|
+
### `sendgrid_delete_suppression`
|
|
232
|
+
- **Purpose:** Remove one recipient from bounce, block, spam report, invalid email, global unsubscribe, or a single ASM group.
|
|
233
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `email`, `type` (`bounce` | `block` | `spam_report` | `invalid_email` | `global` | `group`), `groupId` when `type=group`.
|
|
234
|
+
- **Typical flow:** After `check_suppression` shows the list that is dropping mail.
|
|
235
|
+
- **Caveats:** Does not delete the ASM group. `READ_ONLY=true` blocks the call before the API request.
|
|
236
|
+
|
|
237
|
+
### `sendgrid_get_email_stats`
|
|
238
|
+
- **Purpose:** Aggregate delivery metrics. `dimension=global` is the daily account rollup.
|
|
239
|
+
- **Inputs:** `startDate`, optional `endDate`, `dimension` (`global` | `category` | `category_sums` | `mailbox_provider` | `geo` | `browser` | `device` | `client`), `aggregatedBy` (`day` | `week` | `month`). `category` requires `categories`. Optional `mailboxProviders`, `country`, `browsers`, `limit`.
|
|
240
|
+
- **Typical flow:** Trend and blast-radius checks. Use `category` for categories sent on the mail payload, `mailbox_provider` for provider-specific drops.
|
|
241
|
+
- **Caveats:** Aggregated metrics, not per-message forensics. Browser, device, and client stats retain about 7 days.
|
|
242
|
+
|
|
243
|
+
### `sendgrid_list_subusers`
|
|
244
|
+
- **Purpose:** List subusers on the parent account (`GET /v3/subusers`), including region when `includeRegion` is true (the default).
|
|
245
|
+
- **Inputs:** optional `username`, `region` (`all` | `global` | `eu`), `limit`, `offset`, `includeRegion`.
|
|
246
|
+
- **Typical flow:** Pick `username`, then pass it as `onBehalfOf` on later tools. `onBehalfOf` is optional on every tool. `onBehalfOf="parent"` ignores `SENDGRID_ON_BEHALF_OF` for that call.
|
|
247
|
+
- **Caveats:** The parent API key must be allowed to list subusers. A key already scoped with `SENDGRID_ON_BEHALF_OF` is not the parent; pass `onBehalfOf="parent"` for this list.
|
|
248
|
+
|
|
249
|
+
### `sendgrid_get_scopes`
|
|
250
|
+
- **Purpose:** List scopes on the current API key (`GET /v3/scopes`).
|
|
251
|
+
- **Inputs:** none.
|
|
252
|
+
- **Typical flow:** After a 403, before guessing which scope is missing.
|
|
253
|
+
- **Caveats:** Read-only. With `SENDGRID_ON_BEHALF_OF` set, scopes are evaluated for that subuser/customer account.
|
|
254
|
+
|
|
255
|
+
### `sendgrid_list_event_webhooks`
|
|
256
|
+
- **Purpose:** List Event Webhook configurations in SendGrid.
|
|
257
|
+
- **Inputs:** optional `includeAccountStatusChange`.
|
|
258
|
+
- **Typical flow:** Validate webhook fleet and event subscriptions.
|
|
259
|
+
- **Caveats:** Read-only inventory.
|
|
260
|
+
|
|
261
|
+
### `sendgrid_get_event_webhook`
|
|
262
|
+
- **Purpose:** Read one webhook configuration by ID.
|
|
263
|
+
- **Inputs:** `id`, optional include flag.
|
|
264
|
+
- **Typical flow:** Confirm exact event toggles and URL.
|
|
265
|
+
- **Caveats:** Requires valid webhook ID.
|
|
266
|
+
|
|
267
|
+
### `sendgrid_update_event_webhook`
|
|
268
|
+
- **Purpose:** Update webhook URL, enabled flag, and event toggles.
|
|
269
|
+
- **Inputs:** `id` + selected fields.
|
|
270
|
+
- **Typical flow:** Enable missing events / switch endpoint URL.
|
|
271
|
+
- **Caveats:** Signature mode is managed separately.
|
|
272
|
+
|
|
273
|
+
### `sendgrid_manage_event_webhook`
|
|
274
|
+
- **Purpose:** Create, delete, or test an Event Webhook (`POST /user/webhooks/event/settings`, `DELETE /user/webhooks/event/settings/{id}`, `POST /user/webhooks/event/test`).
|
|
275
|
+
- **Inputs:** `confirmToken="CONFIRM"`, `action` (`create` | `delete` | `test`), `url` for create, `id` for delete. Test uses `url`, or the saved webhook URL when only `id` is set. Optional event toggles match `update_event_webhook`.
|
|
276
|
+
- **Typical flow:** Point `url` at the local receiver, create, then test.
|
|
277
|
+
- **Caveats:** `oauth_client_secret` is redacted in the tool result. Delete does not remove the local receiver.
|
|
278
|
+
|
|
279
|
+
### `sendgrid_toggle_event_webhook_signature`
|
|
280
|
+
- **Purpose:** Enable/disable signed Event Webhook mode.
|
|
281
|
+
- **Inputs:** `id`, `enabled`.
|
|
282
|
+
- **Typical flow:** Enforce cryptographic source verification.
|
|
283
|
+
- **Caveats:** Public key changes can require receiver updates.
|
|
284
|
+
|
|
285
|
+
### `sendgrid_get_webhook_receiver_status`
|
|
286
|
+
- **Purpose:** Show local receiver runtime state and counters.
|
|
287
|
+
- **Inputs:** none.
|
|
288
|
+
- **Typical flow:** Verify receiver health after config or ngrok changes.
|
|
289
|
+
- **Caveats:** Local receiver must be enabled via env.
|
|
290
|
+
|
|
291
|
+
### `sendgrid_get_received_webhook_events`
|
|
292
|
+
- **Purpose:** Read buffered incoming Event Webhook payloads.
|
|
293
|
+
- **Inputs:** optional `limit`, `eventType`, `email`, `messageId`, `onlyVerified`.
|
|
294
|
+
- **Typical flow:** Incident reconstruction from near-real-time events.
|
|
295
|
+
- **Caveats:** In-memory buffer only; not persistent storage.
|
|
296
|
+
|
|
297
|
+
### `sendgrid_clear_received_webhook_events`
|
|
298
|
+
- **Purpose:** Clear local buffered webhook events.
|
|
299
|
+
- **Inputs:** `confirm=true`.
|
|
300
|
+
- **Typical flow:** Reset local buffer between test runs.
|
|
301
|
+
- **Caveats:** Irreversible in-memory clear.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Account & Console Settings
|
|
306
|
+
|
|
307
|
+
### Runbook: Account Audit (read-only)
|
|
308
|
+
|
|
309
|
+
1. `sendgrid_get_account_info` + `sendgrid_get_user_credits`
|
|
310
|
+
2. `sendgrid_list_verified_senders` + `sendgrid_list_authenticated_domains` + `sendgrid_list_branded_links`
|
|
311
|
+
3. `sendgrid_list_mail_settings` / `sendgrid_list_tracking_settings` + targeted `get_*_setting`
|
|
312
|
+
4. `sendgrid_list_alerts` + `sendgrid_list_inbound_parse_settings`
|
|
313
|
+
|
|
314
|
+
### Runbook: Console Change (mutating)
|
|
315
|
+
|
|
316
|
+
1. Read current state with Phase 1 tools
|
|
317
|
+
2. Apply change with `confirmToken=CONFIRM` on mutating tool
|
|
318
|
+
3. Re-read setting and run delivery preflight if sender/domain related
|
|
319
|
+
|
|
320
|
+
### Phase 1 — Account read tools
|
|
321
|
+
|
|
322
|
+
All Phase 1 read tools return `structuredContent` + `outputSchema` (account/profile/credits as JSON records; list tools return `{ count, <items> }`).
|
|
323
|
+
|
|
324
|
+
- `sendgrid_get_account_info` — `GET /user/account`
|
|
325
|
+
- `sendgrid_get_user_profile` — `GET /user/profile`
|
|
326
|
+
- `sendgrid_get_user_credits` — `GET /user/credits`
|
|
327
|
+
- `sendgrid_list_verified_senders` / `sendgrid_get_verified_sender`
|
|
328
|
+
- `sendgrid_list_authenticated_domains` / `sendgrid_get_authenticated_domain`
|
|
329
|
+
- `sendgrid_list_branded_links` / `sendgrid_get_branded_link`
|
|
330
|
+
- `sendgrid_list_alerts` / `sendgrid_get_alert`
|
|
331
|
+
- `sendgrid_get_enforced_tls` — `GET /user/settings/enforced_tls`
|
|
332
|
+
- `sendgrid_list_mail_settings` / `sendgrid_get_mail_setting`
|
|
333
|
+
- `sendgrid_list_tracking_settings` / `sendgrid_get_tracking_setting`
|
|
334
|
+
- `sendgrid_list_inbound_parse_settings`
|
|
335
|
+
|
|
336
|
+
### Phase 2 — Console mutation tools
|
|
337
|
+
|
|
338
|
+
All mutating tools require `confirmToken: "CONFIRM"` (also stated in each tool's MCP `description`).
|
|
339
|
+
|
|
340
|
+
- `sendgrid_create_verified_sender` / `sendgrid_resend_verified_sender_verification` / `sendgrid_delete_verified_sender`
|
|
341
|
+
- `sendgrid_create_authenticated_domain` / `sendgrid_validate_authenticated_domain` / `sendgrid_validate_branded_link`
|
|
342
|
+
- `sendgrid_update_branded_link`
|
|
343
|
+
- `sendgrid_create_alert` / `sendgrid_update_alert` / `sendgrid_delete_alert`
|
|
344
|
+
- `sendgrid_update_mail_setting` — PATCH `/mail_settings/{name}`
|
|
345
|
+
- `sendgrid_update_tracking_setting` — PATCH `/tracking_settings/{name}`
|
|
346
|
+
- `sendgrid_create_inbound_parse_setting` / `sendgrid_update_inbound_parse_setting` / `sendgrid_delete_inbound_parse_setting`
|
|
347
|
+
|
|
348
|
+
**Caveats:** API key must include scopes for the target endpoint (e.g. `mail_settings.footer.update`, `tracking_settings.click.update`, `alerts.create`). Some settings are account-plan dependent.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Optional Event Webhook Receiver (ngrok-ready)
|
|
353
|
+
|
|
354
|
+
Enable by setting env:
|
|
355
|
+
|
|
356
|
+
- `SENDGRID_EVENT_WEBHOOK_PORT` (required)
|
|
357
|
+
- `SENDGRID_EVENT_WEBHOOK_HOST` (default `0.0.0.0`)
|
|
358
|
+
- `SENDGRID_EVENT_WEBHOOK_PATH` (default `/sendgrid/events`)
|
|
359
|
+
- `SENDGRID_EVENT_WEBHOOK_HEALTH_PATH` (default `/sendgrid/events/health`)
|
|
360
|
+
- `SENDGRID_EVENT_WEBHOOK_MAX_EVENTS` (default `5000`)
|
|
361
|
+
|
|
362
|
+
Signature mode:
|
|
363
|
+
|
|
364
|
+
- `SENDGRID_EVENT_WEBHOOK_REQUIRE_SIGNATURE` (`true`/`false`)
|
|
365
|
+
- `SENDGRID_EVENT_WEBHOOK_PUBLIC_KEY` (required if signature required)
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Risk Matrix
|
|
370
|
+
|
|
371
|
+
Risk levels:
|
|
372
|
+
|
|
373
|
+
- `read-only` — no mutation in SendGrid/local state
|
|
374
|
+
- `send` — can enqueue/send email
|
|
375
|
+
- `mutates-sendgrid` — changes remote SendGrid configuration/state
|
|
376
|
+
- `mutates-local` — changes local in-memory/buffer state
|
|
377
|
+
|
|
378
|
+
Tool risk classification:
|
|
379
|
+
|
|
380
|
+
- `sendgrid_list_templates`: `read-only`
|
|
381
|
+
- `sendgrid_rename_template`: `mutates-sendgrid`
|
|
382
|
+
- `sendgrid_rename_templates_bulk`: `mutates-sendgrid`
|
|
383
|
+
- `sendgrid_get_template_html`: `read-only`
|
|
384
|
+
- `sendgrid_create_template`: `mutates-sendgrid`
|
|
385
|
+
- `sendgrid_update_template_html`: `mutates-sendgrid`
|
|
386
|
+
- `sendgrid_activate_template_version`: `mutates-sendgrid`
|
|
387
|
+
- `sendgrid_prune_inactive_template_versions`: `mutates-sendgrid`
|
|
388
|
+
- `sendgrid_delete_template`: `mutates-sendgrid`
|
|
389
|
+
- `sendgrid_sync_template_ids`: `read-only`
|
|
390
|
+
- `sendgrid_validate_send_request`: `read-only`
|
|
391
|
+
- `sendgrid_send_with_preflight`: `send`
|
|
392
|
+
- `sendgrid_send_email_advanced`: `send`
|
|
393
|
+
- `sendgrid_send_template_email_advanced`: `send`
|
|
394
|
+
- `sendgrid_send_sandbox_email`: `send`
|
|
395
|
+
- `sendgrid_send_test_email`: `send`
|
|
396
|
+
- `sendgrid_create_batch_id`: `mutates-sendgrid`
|
|
397
|
+
- `sendgrid_list_scheduled_sends`: `read-only`
|
|
398
|
+
- `sendgrid_get_scheduled_send`: `read-only`
|
|
399
|
+
- `sendgrid_schedule_email`: `send`
|
|
400
|
+
- `sendgrid_pause_scheduled_send`: `mutates-sendgrid`
|
|
401
|
+
- `sendgrid_resume_scheduled_send`: `mutates-sendgrid`
|
|
402
|
+
- `sendgrid_cancel_scheduled_send`: `mutates-sendgrid`
|
|
403
|
+
- `sendgrid_search_message_activity`: `read-only`
|
|
404
|
+
- `sendgrid_search_email_logs`: `read-only`
|
|
405
|
+
- `sendgrid_get_message_activity`: `read-only`
|
|
406
|
+
- `sendgrid_list_event_webhooks`: `read-only`
|
|
407
|
+
- `sendgrid_get_event_webhook`: `read-only`
|
|
408
|
+
- `sendgrid_update_event_webhook`: `mutates-sendgrid`
|
|
409
|
+
- `sendgrid_toggle_event_webhook_signature`: `mutates-sendgrid`
|
|
410
|
+
- `sendgrid_manage_event_webhook`: `mutates-sendgrid`
|
|
411
|
+
- `sendgrid_get_webhook_receiver_status`: `read-only`
|
|
412
|
+
- `sendgrid_get_received_webhook_events`: `read-only`
|
|
413
|
+
- `sendgrid_clear_received_webhook_events`: `mutates-local`
|
|
414
|
+
- `sendgrid_classify_sendgrid_error`: `read-only`
|
|
415
|
+
- `sendgrid_triage_delivery_issue`: `read-only`
|
|
416
|
+
- `sendgrid_analyze_engagement_anomalies`: `read-only`
|
|
417
|
+
- `sendgrid_list_asm_groups`: `read-only`
|
|
418
|
+
- `sendgrid_create_asm_group`: `mutates-sendgrid`
|
|
419
|
+
- `sendgrid_list_categories`: `read-only`
|
|
420
|
+
- `sendgrid_list_suppressions`: `read-only`
|
|
421
|
+
- `sendgrid_check_suppression`: `read-only`
|
|
422
|
+
- `sendgrid_delete_suppression`: `mutates-sendgrid`
|
|
423
|
+
- `sendgrid_get_email_stats`: `read-only`
|
|
424
|
+
- `sendgrid_get_scopes`: `read-only`
|
|
425
|
+
- `sendgrid_list_subusers`: `read-only`
|
|
426
|
+
- `sendgrid_get_account_info`: `read-only`
|
|
427
|
+
- `sendgrid_get_user_profile`: `read-only`
|
|
428
|
+
- `sendgrid_get_user_credits`: `read-only`
|
|
429
|
+
- `sendgrid_list_verified_senders`: `read-only`
|
|
430
|
+
- `sendgrid_get_verified_sender`: `read-only`
|
|
431
|
+
- `sendgrid_list_authenticated_domains`: `read-only`
|
|
432
|
+
- `sendgrid_get_authenticated_domain`: `read-only`
|
|
433
|
+
- `sendgrid_list_branded_links`: `read-only`
|
|
434
|
+
- `sendgrid_get_branded_link`: `read-only`
|
|
435
|
+
- `sendgrid_list_alerts`: `read-only`
|
|
436
|
+
- `sendgrid_get_alert`: `read-only`
|
|
437
|
+
- `sendgrid_get_enforced_tls`: `read-only`
|
|
438
|
+
- `sendgrid_list_mail_settings`: `read-only`
|
|
439
|
+
- `sendgrid_get_mail_setting`: `read-only`
|
|
440
|
+
- `sendgrid_list_tracking_settings`: `read-only`
|
|
441
|
+
- `sendgrid_get_tracking_setting`: `read-only`
|
|
442
|
+
- `sendgrid_list_inbound_parse_settings`: `read-only`
|
|
443
|
+
- `sendgrid_create_verified_sender`: `mutates-sendgrid`
|
|
444
|
+
- `sendgrid_resend_verified_sender_verification`: `mutates-sendgrid`
|
|
445
|
+
- `sendgrid_delete_verified_sender`: `mutates-sendgrid`
|
|
446
|
+
- `sendgrid_create_authenticated_domain`: `mutates-sendgrid`
|
|
447
|
+
- `sendgrid_validate_authenticated_domain`: `mutates-sendgrid`
|
|
448
|
+
- `sendgrid_validate_branded_link`: `mutates-sendgrid`
|
|
449
|
+
- `sendgrid_update_branded_link`: `mutates-sendgrid`
|
|
450
|
+
- `sendgrid_create_alert`: `mutates-sendgrid`
|
|
451
|
+
- `sendgrid_update_alert`: `mutates-sendgrid`
|
|
452
|
+
- `sendgrid_delete_alert`: `mutates-sendgrid`
|
|
453
|
+
- `sendgrid_update_mail_setting`: `mutates-sendgrid`
|
|
454
|
+
- `sendgrid_update_tracking_setting`: `mutates-sendgrid`
|
|
455
|
+
- `sendgrid_create_inbound_parse_setting`: `mutates-sendgrid`
|
|
456
|
+
- `sendgrid_update_inbound_parse_setting`: `mutates-sendgrid`
|
|
457
|
+
- `sendgrid_delete_inbound_parse_setting`: `mutates-sendgrid`
|
package/README.md
CHANGED
|
@@ -1,3 +1,176 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/sendgrid-logo.png" alt="SendGrid" width="320" />
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
# SendGrid MCP Server
|
|
6
|
+
|
|
7
|
+
This server is for **transactional send and delivery**, not a map of the whole SendGrid API.
|
|
8
|
+
|
|
9
|
+
A thin SendGrid MCP treats `POST /v3/mail/send` as one call and spends the rest of its tools on contacts, lists, and campaigns. Those marketing APIs are out of scope here. The tools follow the checks SendGrid's own delivery troubleshooting expects:
|
|
10
|
+
|
|
11
|
+
1. **Before send.** `sendgrid_validate_send_request` checks the `/v3/mail/send` payload, that the dynamic template version is active, sender identity (domain authentication or a verified sender), link branding, recipient suppressions including the ASM group on the send, the DMARC warn list (`GET /v3/verified_senders/domains`), and that `send_at` is in the future and inside 72 hours. `sendgrid_send_with_preflight` posts only when that report has no blockers. Sandbox mode accepts the payload and does not deliver.
|
|
12
|
+
2. **After accept.** The `x-message-id` response header is not a `msg_id`. Pass it as `xMessageId`; search compiles `msg_id LIKE '<id>%'`, then `sendgrid_get_message_activity` reads the event chain (`reason`, `bounce_type`, `asm_group_id`, `outbound_ip`). Email Activity has no real offset. If Activity returns 403/404, or the region is `eu` and Activity is empty, use Email Logs (`POST /v3/logs`) with `to_email` equality. Logs rejects `msg_id`, `from_email`, and `LIKE`.
|
|
13
|
+
3. **When it did not arrive.** `sendgrid_check_suppression` and `sendgrid_triage_delivery_issue` cover bounce, block, spam, invalid, global unsubscribe, and ASM groups. `sendgrid_delete_suppression` lifts one entry. `sendgrid_classify_sendgrid_error` and `sendgrid_get_scopes` explain a 403. Paused or canceled scheduled batches are listed; a batch that was only given `send_at` is not in that list until you pause or cancel it.
|
|
14
|
+
4. **The pipe around the send.** Dynamic templates (including bulk rename and pruning inactive versions), Event Webhook create/update/test plus an optional local receiver, and the console settings that change delivery: domains, link branding, enforced TLS, mail and tracking settings, alerts, inbound parse.
|
|
15
|
+
|
|
16
|
+
Install: `bunx --no-env-file x @neschadin/sendgrid-mcp`.
|
|
17
|
+
|
|
18
|
+
Full catalog and runbooks: [`MCP_TOOLS.md`](./MCP_TOOLS.md).
|
|
19
|
+
|
|
20
|
+
This project is community-maintained and is not affiliated with, endorsed by, or sponsored by Twilio SendGrid.
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- [Bun](https://bun.sh) ≥ 1.4.2
|
|
25
|
+
- A [SendGrid API key](https://app.sendgrid.com/settings/api_keys) with scopes for the tools you call. Keys normally start with `SG.`; any other prefix only logs a warning
|
|
26
|
+
- A verified sender address in `SENDGRID_FROM_EMAIL`
|
|
27
|
+
- An MCP client (Cursor, Claude Desktop, VS Code, etc.)
|
|
28
|
+
|
|
29
|
+
One process, one API key. Do not share a hosted instance across tenants.
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
### Required
|
|
34
|
+
|
|
35
|
+
| Variable | Description |
|
|
36
|
+
| --------------------- | -------------------------------------- |
|
|
37
|
+
| `SENDGRID_API_KEY` | SendGrid API key |
|
|
38
|
+
| `SENDGRID_FROM_EMAIL` | Default From address (verified sender) |
|
|
39
|
+
|
|
40
|
+
### SendGrid
|
|
41
|
+
|
|
42
|
+
| Variable | Default | Description |
|
|
43
|
+
| ------------------------ | -------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
44
|
+
| `SENDGRID_FROM_NAME` | `SendGrid MCP` | Default From display name |
|
|
45
|
+
| `SENDGRID_REGION` | `global` | `global` or `eu` (case-insensitive). `eu` uses `https://api.eu.sendgrid.com/v3` and falls back to Email Logs when Activity is empty |
|
|
46
|
+
| `SENDGRID_API_BASE_URL` | from region | Override the API host. `https://` or `http://` |
|
|
47
|
+
| `SENDGRID_ON_BEHALF_OF` | — | Default `on-behalf-of` header: a subuser username, or `account-id <id>` |
|
|
48
|
+
| `READ_ONLY` | `false` | `true` blocks send and mutating tools before any request |
|
|
49
|
+
| `SENDGRID_MCP_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error` |
|
|
50
|
+
|
|
51
|
+
Every tool also accepts `onBehalfOf`. The value `"parent"` skips `SENDGRID_ON_BEHALF_OF` for that call. List subusers with `sendgrid_list_subusers` (the parent key needs that scope; this key may 403).
|
|
52
|
+
|
|
53
|
+
Handshake `instructions` repeat the region, API base, from address, and the workflow above. A tool call with more than 10000 combined array elements and object members is rejected. Results replace `oauth_client_secret` and `api_key` values with a length marker.
|
|
54
|
+
|
|
55
|
+
### Remote HTTP
|
|
56
|
+
|
|
57
|
+
Leave `MCP_TRANSPORT` unset for stdio. Set `MCP_TRANSPORT=http` to serve Streamable HTTP on `/mcp` (`/health` is unauthenticated).
|
|
58
|
+
|
|
59
|
+
| Variable | Default | Description |
|
|
60
|
+
| --------------------- | -------------- | --------------------------------------------------------------------------- |
|
|
61
|
+
| `MCP_HTTP_HOST` | `127.0.0.1` | Bind address |
|
|
62
|
+
| `MCP_HTTP_PORT` | `3000` | Port |
|
|
63
|
+
| `MCP_AUTH_MODE` | `token` | `token` or `none`. `none` is refused when the bind address is not loopback |
|
|
64
|
+
| `MCP_AUTH_TOKEN` | — | Bearer token. Required for `token` mode |
|
|
65
|
+
| `MCP_ALLOWED_HOSTS` | loopback names | Comma-separated `Host` allowlist. Required off loopback |
|
|
66
|
+
| `MCP_ALLOWED_ORIGINS` | loopback names | Comma-separated `Origin` allowlist. A missing `Origin` is allowed |
|
|
67
|
+
| `MCP_TLS_KEY_FILE` | — | TLS key. Set together with `MCP_TLS_CERT_FILE` |
|
|
68
|
+
| `MCP_TLS_CERT_FILE` | — | TLS certificate |
|
|
69
|
+
| `MCP_TRUST_PROXY` | `false` | Allow plaintext HTTP off loopback only behind your own TLS proxy |
|
|
70
|
+
|
|
71
|
+
### Local Event Webhook receiver
|
|
72
|
+
|
|
73
|
+
Off until `SENDGRID_EVENT_WEBHOOK_PORT` is set. The buffer is in-memory (default 5000 events) and is dropped when the process exits.
|
|
74
|
+
|
|
75
|
+
| Variable | Default |
|
|
76
|
+
| ------------------------------------------ | ------------------------- |
|
|
77
|
+
| `SENDGRID_EVENT_WEBHOOK_HOST` | `0.0.0.0` |
|
|
78
|
+
| `SENDGRID_EVENT_WEBHOOK_PATH` | `/sendgrid/events` |
|
|
79
|
+
| `SENDGRID_EVENT_WEBHOOK_HEALTH_PATH` | `/sendgrid/events/health` |
|
|
80
|
+
| `SENDGRID_EVENT_WEBHOOK_MAX_EVENTS` | `5000` |
|
|
81
|
+
| `SENDGRID_EVENT_WEBHOOK_VERBOSE` | `false` |
|
|
82
|
+
| `SENDGRID_EVENT_WEBHOOK_REQUIRE_SIGNATURE` | `false` |
|
|
83
|
+
| `SENDGRID_EVENT_WEBHOOK_PUBLIC_KEY` | required when signature is required |
|
|
84
|
+
|
|
85
|
+
Point the SendGrid Event Webhook URL at the tunnel, for example `https://<ngrok-host>/sendgrid/events`. Read what arrived with `sendgrid_get_received_webhook_events` and `sendgrid_get_webhook_receiver_status`. Create or test the SendGrid-side webhook with `sendgrid_manage_event_webhook`.
|
|
86
|
+
|
|
87
|
+
## MCP client setup
|
|
88
|
+
|
|
89
|
+
### Cursor
|
|
90
|
+
|
|
91
|
+
[`mcp.json.example`](./mcp.json.example). `--no-env-file` stops Bun from also reading a `.env` in the client cwd. In `~/.cursor/mcp.json`, `envFile` must be an absolute path.
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"mcpServers": {
|
|
96
|
+
"sendgrid": {
|
|
97
|
+
"command": "bunx",
|
|
98
|
+
"args": ["--no-env-file", "x", "@neschadin/sendgrid-mcp"],
|
|
99
|
+
"envFile": "${workspaceFolder}/.env"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Remote HTTP, after the server process is started with `MCP_TRANSPORT=http`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcpServers": {
|
|
110
|
+
"sendgrid": {
|
|
111
|
+
"url": "https://mcp.example.com/mcp",
|
|
112
|
+
"headers": {
|
|
113
|
+
"Authorization": "Bearer <MCP_AUTH_TOKEN>"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Claude Desktop
|
|
121
|
+
|
|
122
|
+
No `envFile`. The desktop cwd is not your repo, so pass an absolute `--env-file`:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"mcpServers": {
|
|
127
|
+
"sendgrid": {
|
|
128
|
+
"command": "bunx",
|
|
129
|
+
"args": [
|
|
130
|
+
"--no-env-file",
|
|
131
|
+
"--env-file=/absolute/path/.env",
|
|
132
|
+
"x",
|
|
133
|
+
"@neschadin/sendgrid-mcp"
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Restart the client after changing MCP config.
|
|
141
|
+
|
|
142
|
+
## Safety
|
|
143
|
+
|
|
144
|
+
- Send tools can enqueue real mail. They do not ask for `confirmToken`. Use `sendgrid_send_with_preflight`, or set `READ_ONLY=true`.
|
|
145
|
+
- Tools that change SendGrid (templates, suppressions, webhooks, domains, settings, scheduled-send pause/cancel) require `confirmToken` `"CONFIRM"`.
|
|
146
|
+
- `READ_ONLY=true` blocks both sends and those mutations before the HTTP call.
|
|
147
|
+
- Email Activity (`/v3/messages`) may require the [Email Activity add-on](https://www.twilio.com/docs/sendgrid/api-reference/email-activity/filter-all-messages).
|
|
148
|
+
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
A git tag `vX.Y.Z` runs tests, publishes `@neschadin/sendgrid-mcp` to npm, then publishes `io.github.Neschadin/sendgrid-mcp` to the MCP registry. The release has no binary assets.
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
git clone https://github.com/Neschadin/sendgrid-mcp.git
|
|
155
|
+
cd sendgrid-mcp
|
|
156
|
+
bun install
|
|
157
|
+
bun run dev # stdio MCP; loads .env
|
|
158
|
+
bun run lint
|
|
159
|
+
bun run typecheck
|
|
160
|
+
bun run smoke
|
|
161
|
+
bun test
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
bun run inspect
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Docs
|
|
169
|
+
|
|
170
|
+
- [MCP_TOOLS.md](./MCP_TOOLS.md) — tools, runbooks, risk matrix
|
|
171
|
+
- [SendGrid API reference](https://www.twilio.com/docs/sendgrid/api-reference)
|
|
172
|
+
- [SendGrid for developers](https://www.twilio.com/docs/sendgrid/for-developers)
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT — see [LICENSE](./LICENSE).
|
package/bin/sendgrid-mcp
ADDED