notifycx-mcp 1.1.0 → 1.2.1

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 CHANGED
@@ -6,6 +6,8 @@ Use this in Cursor, Claude Code, or other MCP hosts when a coding agent should c
6
6
 
7
7
  ## Install (Cursor)
8
8
 
9
+ **Already have a key:**
10
+
9
11
  ```json
10
12
  {
11
13
  "mcpServers": {
@@ -20,23 +22,27 @@ Use this in Cursor, Claude Code, or other MCP hosts when a coding agent should c
20
22
  }
21
23
  ```
22
24
 
23
- Get your API key from [Credentials](https://notify.cx/dashboard/credentials). Docs: [notify.cx/mcp](https://notify.cx/mcp).
25
+ **No key yet:** omit `NOTIFY_API_KEY`, call `bootstrap_start` → user pastes email OTP → `bootstrap_confirm` → put the returned `api_key` into MCP env. See [agent bootstrap docs](https://notify.cx/docs/agent-bootstrap-no-dashboard).
26
+
27
+ Never paste API key secrets into chat logs.
24
28
 
25
29
  ## Tools
26
30
 
27
31
  | Tool | When to use |
28
32
  |------|-------------|
33
+ | `bootstrap_start` / `bootstrap_confirm` | Sign up or recover without the dashboard (no API key required) |
29
34
  | `send_email` | Production transactional send (verified `from`) |
30
35
  | `send_test_email` | Non-delivering API rehearsal |
31
- | `add_domain` | Add domain + get DNS records (SPF/DKIM) |
36
+ | `add_domain` | Add domain + structured `dnsRecords` (type/name/value) for the user to publish at their host |
32
37
  | `check_domain` | Poll verification after DNS is configured |
33
- | `list_domains` / `get_domain` | Domain / SPF / DKIM status |
38
+ | `list_domains` / `get_domain` | Domain status + `dnsRecords` |
34
39
  | `list_webhooks` / `create_webhook` / `update_webhook` / `delete_webhook` / `get_webhook` / `test_webhook` | Webhook setup (Pro+) |
35
40
  | `list_email_logs` | Recent delivery logs |
36
41
  | `get_usage` | Quota / account_verified |
37
- | `list_api_keys` | Key metadata (prefix only — create keys in the dashboard) |
42
+ | `list_api_keys` / `create_api_key` / `regenerate_api_key` / `delete_api_key` | Manage keys |
43
+ | `create_billing_checkout` | Stripe Checkout URL for Pro/Scale |
38
44
 
39
- Typical setup path: `add_domain` → add DNS → `check_domain` → `send_email`. Optional: `create_webhook` after a verified domain.
45
+ Typical setup path: bootstrap → `add_domain` → user publishes `dnsRecords` → `check_domain` → `send_email`.
40
46
 
41
47
  ## Pricing
42
48
 
package/dist/index.d.ts CHANGED
@@ -1,2 +1 @@
1
-
2
- export { }
1
+ #!/usr/bin/env node
package/dist/index.js CHANGED
@@ -1,15 +1,22 @@
1
1
  #!/usr/bin/env node
2
+ #!/usr/bin/env node
2
3
 
3
4
  // src/index.ts
4
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import Notify, { NotifyError } from "notifycx";
7
+ import Notify, { NotifyError, NOTIFYCX_VERSION } from "notifycx";
7
8
  import { z } from "zod";
9
+ function baseUrl() {
10
+ return process.env.NOTIFY_BASE_URL;
11
+ }
12
+ function optionalApiKey() {
13
+ return process.env.NOTIFY_API_KEY;
14
+ }
8
15
  function requireApiKey() {
9
- const apiKey = process.env.NOTIFY_API_KEY;
16
+ const apiKey = optionalApiKey();
10
17
  if (!apiKey) {
11
18
  throw new Error(
12
- "Missing NOTIFY_API_KEY. Set it in your MCP server env (dashboard \u2192 Credentials)."
19
+ "Missing NOTIFY_API_KEY. Run bootstrap_start + bootstrap_confirm first, then set NOTIFY_API_KEY in MCP env to the returned api_key (never paste secrets into chat)."
13
20
  );
14
21
  }
15
22
  return apiKey;
@@ -17,7 +24,7 @@ function requireApiKey() {
17
24
  function client() {
18
25
  return new Notify({
19
26
  apiKey: requireApiKey(),
20
- baseUrl: process.env.NOTIFY_BASE_URL,
27
+ baseUrl: baseUrl(),
21
28
  client: "mcp"
22
29
  });
23
30
  }
@@ -77,8 +84,57 @@ var webhookWriteShape = {
77
84
  };
78
85
  var server = new McpServer({
79
86
  name: "notifycx-mcp",
80
- version: "1.1.0"
87
+ // Keep MCP package.json version aligned with notifycx (NOTIFYCX_VERSION).
88
+ version: NOTIFYCX_VERSION
81
89
  });
90
+ server.tool(
91
+ "bootstrap_start",
92
+ "Start Notify agent signup or key recovery without the dashboard. Sends a 6-digit email OTP. No NOTIFY_API_KEY required. Ask the user to paste the code, then call bootstrap_confirm. Requires acceptTerms: true (https://notify.cx/terms).",
93
+ {
94
+ email: z.string().email().describe("User email for the Notify account"),
95
+ intent: z.enum(["signup", "recover"]).optional().describe("signup (default) or recover existing account"),
96
+ acceptTerms: z.boolean().describe("Must be true \u2014 user accepts Terms and AUP")
97
+ },
98
+ async (args) => {
99
+ try {
100
+ return ok(
101
+ await Notify.bootstrapStart({
102
+ email: args.email,
103
+ intent: args.intent,
104
+ acceptTerms: args.acceptTerms,
105
+ baseUrl: baseUrl()
106
+ })
107
+ );
108
+ } catch (err) {
109
+ return fail(err);
110
+ }
111
+ }
112
+ );
113
+ server.tool(
114
+ "bootstrap_confirm",
115
+ "Confirm the email OTP from bootstrap_start. Returns api_key once \u2014 instruct the user to put it in MCP env NOTIFY_API_KEY (do not leave the secret in chat). Then use add_domain / send_email. No NOTIFY_API_KEY required for this call.",
116
+ {
117
+ email: z.string().email(),
118
+ code: z.string().regex(/^\d{6}$/).describe("6-digit code from email"),
119
+ intent: z.enum(["signup", "recover"]).optional(),
120
+ clientHint: z.string().max(64).optional().describe("e.g. cursor, chatgpt")
121
+ },
122
+ async (args) => {
123
+ try {
124
+ return ok(
125
+ await Notify.bootstrapConfirm({
126
+ email: args.email,
127
+ code: args.code,
128
+ intent: args.intent,
129
+ clientHint: args.clientHint,
130
+ baseUrl: baseUrl()
131
+ })
132
+ );
133
+ } catch (err) {
134
+ return fail(err);
135
+ }
136
+ }
137
+ );
82
138
  server.tool(
83
139
  "send_email",
84
140
  "Send transactional email via Notify (password reset, receipt, magic link, invoice, etc.) with a verified from domain. Use when the user wants production delivery. Prefer html for the body. For non-delivering API rehearsal before domain verification, use send_test_email instead.",
@@ -105,7 +161,7 @@ server.tool(
105
161
  );
106
162
  server.tool(
107
163
  "list_domains",
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.",
164
+ "List Notify sending domains and verification status (SPF/DKIM). Includes structured dnsRecords (type/name/value) for the user to publish at their DNS host.",
109
165
  {},
110
166
  async () => {
111
167
  try {
@@ -117,7 +173,7 @@ server.tool(
117
173
  );
118
174
  server.tool(
119
175
  "get_domain",
120
- "Get one Notify sending domain by hostname, including DNS records/tokens for pending verification.",
176
+ "Get one Notify sending domain by hostname, including dnsRecords (type/name/value) and raw DNS tokens for pending verification. Show dnsRecords to the user so they can add them at their hosting provider.",
121
177
  {
122
178
  domain: z.string().min(1).describe("Domain hostname, e.g. example.com")
123
179
  },
@@ -131,7 +187,7 @@ server.tool(
131
187
  );
132
188
  server.tool(
133
189
  "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.",
190
+ "Add a sending domain and start SPF/DKIM verification. Returns dnsRecords (type/name/value) the user must add at their DNS host \u2014 Notify does not publish DNS. After DNS is configured, call check_domain. Then use send_email with a from address on that domain.",
135
191
  {
136
192
  domain: z.string().min(1).describe("Domain hostname to verify, e.g. example.com"),
137
193
  dnsProvider: z.enum([
@@ -142,7 +198,7 @@ server.tool(
142
198
  "godaddy",
143
199
  "route53",
144
200
  "other"
145
- ]).optional().describe("Optional DNS provider hint for dashboard UX")
201
+ ]).optional().describe("Optional DNS provider hint for host/name formatting")
146
202
  },
147
203
  async (args) => {
148
204
  try {
@@ -154,7 +210,7 @@ server.tool(
154
210
  );
155
211
  server.tool(
156
212
  "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.",
213
+ "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 and the plan email limit lifts.",
158
214
  {
159
215
  domain: z.string().min(1).describe("Domain hostname to check")
160
216
  },
@@ -196,7 +252,7 @@ server.tool(
196
252
  );
197
253
  server.tool(
198
254
  "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.",
255
+ "List API key metadata for the account (id, name, prefix only \u2014 never the full secret).",
200
256
  {},
201
257
  async () => {
202
258
  try {
@@ -206,6 +262,64 @@ server.tool(
206
262
  }
207
263
  }
208
264
  );
265
+ server.tool(
266
+ "create_api_key",
267
+ "Create an API key (full_access required). Free plan allows one key. Prefer sending_access + domainId for app keys on Pro+.",
268
+ {
269
+ name: z.string().min(1),
270
+ permission: z.enum(["full_access", "sending_access"]).optional(),
271
+ domainId: z.string().uuid().optional()
272
+ },
273
+ async (args) => {
274
+ try {
275
+ return ok(await client().createApiKey(args));
276
+ } catch (err) {
277
+ return fail(err);
278
+ }
279
+ }
280
+ );
281
+ server.tool(
282
+ "regenerate_api_key",
283
+ "Rotate an API key secret by keyId. Returns the new api_key once \u2014 update NOTIFY_API_KEY / app secrets.",
284
+ {
285
+ keyId: z.string().uuid()
286
+ },
287
+ async (args) => {
288
+ try {
289
+ return ok(await client().regenerateApiKey(args.keyId));
290
+ } catch (err) {
291
+ return fail(err);
292
+ }
293
+ }
294
+ );
295
+ server.tool(
296
+ "delete_api_key",
297
+ "Deactivate an API key by keyId. Cannot delete the last full_access key.",
298
+ {
299
+ keyId: z.string().uuid()
300
+ },
301
+ async (args) => {
302
+ try {
303
+ return ok(await client().deleteApiKey(args.keyId));
304
+ } catch (err) {
305
+ return fail(err);
306
+ }
307
+ }
308
+ );
309
+ server.tool(
310
+ "create_billing_checkout",
311
+ "Get a Stripe Checkout URL for Pro or Scale (full_access key). User opens the URL in a browser to pay \u2014 not the Notify dashboard.",
312
+ {
313
+ plan: z.enum(["pro", "scale"]).optional()
314
+ },
315
+ async (args) => {
316
+ try {
317
+ return ok(await client().createBillingCheckout({ plan: args.plan }));
318
+ } catch (err) {
319
+ return fail(err);
320
+ }
321
+ }
322
+ );
209
323
  server.tool(
210
324
  "list_webhooks",
211
325
  "List Notify webhook subscriptions (URL, events, domain scope).",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notifycx-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Official MCP server for Notify — transactional email tools for coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,7 +27,7 @@
27
27
  "homepage": "https://notify.cx/mcp",
28
28
  "repository": {
29
29
  "type": "git",
30
- "url": "https://github.com/sunil-sandhu/notifycx.git",
30
+ "url": "https://github.com/notifycx/notifycx.git",
31
31
  "directory": "mcp"
32
32
  },
33
33
  "author": "notify.cx",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@modelcontextprotocol/sdk": "^1.12.1",
40
- "notifycx": "^1.1.0",
40
+ "notifycx": "^1.2.1",
41
41
  "zod": "^3.24.2"
42
42
  },
43
43
  "devDependencies": {