notifycx-mcp 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/index.js +205 -44
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -28,8 +28,15 @@ Get your API key from [Credentials](https://notify.cx/dashboard/credentials). Do
|
|
|
28
28
|
|------|-------------|
|
|
29
29
|
| `send_email` | Production transactional send (verified `from`) |
|
|
30
30
|
| `send_test_email` | Non-delivering API rehearsal |
|
|
31
|
-
| `
|
|
31
|
+
| `add_domain` | Add domain + get DNS records (SPF/DKIM) |
|
|
32
|
+
| `check_domain` | Poll verification after DNS is configured |
|
|
33
|
+
| `list_domains` / `get_domain` | Domain / SPF / DKIM status |
|
|
34
|
+
| `list_webhooks` / `create_webhook` / `update_webhook` / `delete_webhook` / `get_webhook` / `test_webhook` | Webhook setup (Pro+) |
|
|
32
35
|
| `list_email_logs` | Recent delivery logs |
|
|
36
|
+
| `get_usage` | Quota / account_verified |
|
|
37
|
+
| `list_api_keys` | Key metadata (prefix only — create keys in the dashboard) |
|
|
38
|
+
|
|
39
|
+
Typical setup path: `add_domain` → add DNS → `check_domain` → `send_email`. Optional: `create_webhook` after a verified domain.
|
|
33
40
|
|
|
34
41
|
## Pricing
|
|
35
42
|
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,8 @@ function requireApiKey() {
|
|
|
17
17
|
function client() {
|
|
18
18
|
return new Notify({
|
|
19
19
|
apiKey: requireApiKey(),
|
|
20
|
-
baseUrl: process.env.NOTIFY_BASE_URL
|
|
20
|
+
baseUrl: process.env.NOTIFY_BASE_URL,
|
|
21
|
+
client: "mcp"
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
24
|
function formatError(err) {
|
|
@@ -36,6 +37,26 @@ function formatError(err) {
|
|
|
36
37
|
if (err instanceof Error) return err.message;
|
|
37
38
|
return String(err);
|
|
38
39
|
}
|
|
40
|
+
function ok(result) {
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function fail(err) {
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: formatError(err) }],
|
|
48
|
+
isError: true
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
var eventTypeEnum = z.enum([
|
|
52
|
+
"Bounce",
|
|
53
|
+
"Click",
|
|
54
|
+
"Complaint",
|
|
55
|
+
"Delivery",
|
|
56
|
+
"DeliveryDelay",
|
|
57
|
+
"Open",
|
|
58
|
+
"Send"
|
|
59
|
+
]);
|
|
39
60
|
var sendShape = {
|
|
40
61
|
to: z.string().email().describe("Recipient email address"),
|
|
41
62
|
from: z.string().min(1).describe(
|
|
@@ -46,9 +67,17 @@ var sendShape = {
|
|
|
46
67
|
message: z.string().min(1).optional().describe("Alias for html/body content"),
|
|
47
68
|
text: z.string().min(1).optional().describe("Alias for html/body content")
|
|
48
69
|
};
|
|
70
|
+
var webhookWriteShape = {
|
|
71
|
+
webhookUrl: z.string().url().describe("HTTPS URL to receive webhook events"),
|
|
72
|
+
subscribedEvents: z.array(eventTypeEnum).min(1).describe("Event types to subscribe to"),
|
|
73
|
+
domainId: z.string().uuid().describe("Verified sending domain id from list_domains"),
|
|
74
|
+
matchMode: z.enum(["domain", "hosts", "froms"]).optional().describe("Optional match scope (default domain)"),
|
|
75
|
+
matchHosts: z.array(z.string()).max(20).optional().describe("When matchMode is hosts"),
|
|
76
|
+
matchFroms: z.array(z.string()).max(20).optional().describe("When matchMode is froms")
|
|
77
|
+
};
|
|
49
78
|
var server = new McpServer({
|
|
50
79
|
name: "notifycx-mcp",
|
|
51
|
-
version: "1.
|
|
80
|
+
version: "1.1.0"
|
|
52
81
|
});
|
|
53
82
|
server.tool(
|
|
54
83
|
"send_email",
|
|
@@ -56,15 +85,9 @@ server.tool(
|
|
|
56
85
|
sendShape,
|
|
57
86
|
async (args) => {
|
|
58
87
|
try {
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
62
|
-
};
|
|
88
|
+
return ok(await client().send(args));
|
|
63
89
|
} catch (err) {
|
|
64
|
-
return
|
|
65
|
-
content: [{ type: "text", text: formatError(err) }],
|
|
66
|
-
isError: true
|
|
67
|
-
};
|
|
90
|
+
return fail(err);
|
|
68
91
|
}
|
|
69
92
|
}
|
|
70
93
|
);
|
|
@@ -74,33 +97,72 @@ server.tool(
|
|
|
74
97
|
sendShape,
|
|
75
98
|
async (args) => {
|
|
76
99
|
try {
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
80
|
-
};
|
|
100
|
+
return ok(await client().sendTest(args));
|
|
81
101
|
} catch (err) {
|
|
82
|
-
return
|
|
83
|
-
content: [{ type: "text", text: formatError(err) }],
|
|
84
|
-
isError: true
|
|
85
|
-
};
|
|
102
|
+
return fail(err);
|
|
86
103
|
}
|
|
87
104
|
}
|
|
88
105
|
);
|
|
89
106
|
server.tool(
|
|
90
107
|
"list_domains",
|
|
91
|
-
"List Notify sending domains and verification status (SPF/DKIM). Use when wiring a production from address or debugging domain auth.",
|
|
108
|
+
"List Notify sending domains and verification status (SPF/DKIM). Use when wiring a production from address or debugging domain auth. Includes DNS tokens when present.",
|
|
92
109
|
{},
|
|
93
110
|
async () => {
|
|
94
111
|
try {
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
98
|
-
};
|
|
112
|
+
return ok(await client().listDomains());
|
|
99
113
|
} catch (err) {
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
return fail(err);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
server.tool(
|
|
119
|
+
"get_domain",
|
|
120
|
+
"Get one Notify sending domain by hostname, including DNS records/tokens for pending verification.",
|
|
121
|
+
{
|
|
122
|
+
domain: z.string().min(1).describe("Domain hostname, e.g. example.com")
|
|
123
|
+
},
|
|
124
|
+
async (args) => {
|
|
125
|
+
try {
|
|
126
|
+
return ok(await client().getDomain(args.domain));
|
|
127
|
+
} catch (err) {
|
|
128
|
+
return fail(err);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
server.tool(
|
|
133
|
+
"add_domain",
|
|
134
|
+
"Add a sending domain and start SPF/DKIM verification. Returns DNS records the user must add. After DNS is configured, call check_domain. Then use send_email with a from address on that domain.",
|
|
135
|
+
{
|
|
136
|
+
domain: z.string().min(1).describe("Domain hostname to verify, e.g. example.com"),
|
|
137
|
+
dnsProvider: z.enum([
|
|
138
|
+
"vercel",
|
|
139
|
+
"cloudflare",
|
|
140
|
+
"squarespace",
|
|
141
|
+
"namecheap",
|
|
142
|
+
"godaddy",
|
|
143
|
+
"route53",
|
|
144
|
+
"other"
|
|
145
|
+
]).optional().describe("Optional DNS provider hint for dashboard UX")
|
|
146
|
+
},
|
|
147
|
+
async (args) => {
|
|
148
|
+
try {
|
|
149
|
+
return ok(await client().addDomain(args));
|
|
150
|
+
} catch (err) {
|
|
151
|
+
return fail(err);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
server.tool(
|
|
156
|
+
"check_domain",
|
|
157
|
+
"Poll SES for domain SPF/DKIM verification status after DNS records were added. Call after add_domain. When both succeed, production send_email with that from domain is unlocked.",
|
|
158
|
+
{
|
|
159
|
+
domain: z.string().min(1).describe("Domain hostname to check")
|
|
160
|
+
},
|
|
161
|
+
async (args) => {
|
|
162
|
+
try {
|
|
163
|
+
return ok(await client().checkDomain(args.domain));
|
|
164
|
+
} catch (err) {
|
|
165
|
+
return fail(err);
|
|
104
166
|
}
|
|
105
167
|
}
|
|
106
168
|
);
|
|
@@ -110,27 +172,126 @@ server.tool(
|
|
|
110
172
|
{
|
|
111
173
|
page: z.number().int().positive().optional().describe("Page number (default 1)"),
|
|
112
174
|
limit: z.number().int().min(1).max(100).optional().describe("Page size (default 20, max 100)"),
|
|
113
|
-
eventType:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
175
|
+
eventType: eventTypeEnum.optional().describe("Filter logs by analytics event type")
|
|
176
|
+
},
|
|
177
|
+
async (args) => {
|
|
178
|
+
try {
|
|
179
|
+
return ok(await client().listLogs(args));
|
|
180
|
+
} catch (err) {
|
|
181
|
+
return fail(err);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
server.tool(
|
|
186
|
+
"get_usage",
|
|
187
|
+
"Get Notify plan usage and quota (hourly/monthly sends, account_verified). Use when debugging limits or confirming production unlock.",
|
|
188
|
+
{},
|
|
189
|
+
async () => {
|
|
190
|
+
try {
|
|
191
|
+
return ok(await client().getUsage());
|
|
192
|
+
} catch (err) {
|
|
193
|
+
return fail(err);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
server.tool(
|
|
198
|
+
"list_api_keys",
|
|
199
|
+
"List API key metadata for the account (id, name, prefix only \u2014 never the full secret). Create or rotate keys in the Notify dashboard Credentials page.",
|
|
200
|
+
{},
|
|
201
|
+
async () => {
|
|
202
|
+
try {
|
|
203
|
+
return ok(await client().listApiKeys());
|
|
204
|
+
} catch (err) {
|
|
205
|
+
return fail(err);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
server.tool(
|
|
210
|
+
"list_webhooks",
|
|
211
|
+
"List Notify webhook subscriptions (URL, events, domain scope).",
|
|
212
|
+
{},
|
|
213
|
+
async () => {
|
|
214
|
+
try {
|
|
215
|
+
return ok(await client().listWebhooks());
|
|
216
|
+
} catch (err) {
|
|
217
|
+
return fail(err);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
server.tool(
|
|
222
|
+
"get_webhook",
|
|
223
|
+
"Get one Notify webhook by id.",
|
|
224
|
+
{
|
|
225
|
+
id: z.string().uuid().describe("Webhook subscription id")
|
|
226
|
+
},
|
|
227
|
+
async (args) => {
|
|
228
|
+
try {
|
|
229
|
+
return ok(await client().getWebhook(args.id));
|
|
230
|
+
} catch (err) {
|
|
231
|
+
return fail(err);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
server.tool(
|
|
236
|
+
"create_webhook",
|
|
237
|
+
"Create a Notify webhook for delivery events on a verified domain. Requires Pro or Scale (free plan has 0 webhooks). Use list_domains to get domainId.",
|
|
238
|
+
webhookWriteShape,
|
|
239
|
+
async (args) => {
|
|
240
|
+
try {
|
|
241
|
+
return ok(await client().createWebhook(args));
|
|
242
|
+
} catch (err) {
|
|
243
|
+
return fail(err);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
server.tool(
|
|
248
|
+
"update_webhook",
|
|
249
|
+
"Update an existing Notify webhook (URL, events, domain scope).",
|
|
250
|
+
{
|
|
251
|
+
id: z.string().uuid().describe("Webhook subscription id"),
|
|
252
|
+
webhookUrl: webhookWriteShape.webhookUrl.optional(),
|
|
253
|
+
subscribedEvents: webhookWriteShape.subscribedEvents.optional(),
|
|
254
|
+
domainId: webhookWriteShape.domainId.optional(),
|
|
255
|
+
matchMode: webhookWriteShape.matchMode,
|
|
256
|
+
matchHosts: webhookWriteShape.matchHosts,
|
|
257
|
+
matchFroms: webhookWriteShape.matchFroms
|
|
258
|
+
},
|
|
259
|
+
async (args) => {
|
|
260
|
+
try {
|
|
261
|
+
const { id, ...input } = args;
|
|
262
|
+
return ok(await client().updateWebhook(id, input));
|
|
263
|
+
} catch (err) {
|
|
264
|
+
return fail(err);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
server.tool(
|
|
269
|
+
"delete_webhook",
|
|
270
|
+
"Delete a Notify webhook subscription by id.",
|
|
271
|
+
{
|
|
272
|
+
id: z.string().uuid().describe("Webhook subscription id")
|
|
273
|
+
},
|
|
274
|
+
async (args) => {
|
|
275
|
+
try {
|
|
276
|
+
return ok(await client().deleteWebhook(args.id));
|
|
277
|
+
} catch (err) {
|
|
278
|
+
return fail(err);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
server.tool(
|
|
283
|
+
"test_webhook",
|
|
284
|
+
"Send a test Notify webhook payload to a URL (does not require an existing subscription id).",
|
|
285
|
+
{
|
|
286
|
+
webhookUrl: z.string().url().describe("HTTPS URL to receive the test payload"),
|
|
287
|
+
eventType: eventTypeEnum.describe("Event type for the test payload"),
|
|
288
|
+
domainId: z.string().uuid().optional().describe("Optional verified domain id for scoped test payloads")
|
|
122
289
|
},
|
|
123
290
|
async (args) => {
|
|
124
291
|
try {
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
128
|
-
};
|
|
292
|
+
return ok(await client().testWebhook(args));
|
|
129
293
|
} catch (err) {
|
|
130
|
-
return
|
|
131
|
-
content: [{ type: "text", text: formatError(err) }],
|
|
132
|
-
isError: true
|
|
133
|
-
};
|
|
294
|
+
return fail(err);
|
|
134
295
|
}
|
|
135
296
|
}
|
|
136
297
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notifycx-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Official MCP server for Notify — transactional email tools for coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
40
|
-
"notifycx": "^1.
|
|
40
|
+
"notifycx": "^1.1.0",
|
|
41
41
|
"zod": "^3.24.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|