@ductape/mcp 0.1.56 → 0.1.58
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 +92 -14
- package/package.json +1 -1
- package/scripts/check-frontend-analytics-guidance.mjs +8 -0
- package/src/index.ts +92 -14
package/dist/index.js
CHANGED
|
@@ -323,6 +323,9 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
323
323
|
IMPORTANT: ALL product.* methods require the access key and will return 403 with a publishable key.
|
|
324
324
|
Use ductape_cli for ALL product operations — never ductape_execute:
|
|
325
325
|
ductape_cli("products get --tag <tag> --json") ← fetch product + full inventory
|
|
326
|
+
ductape_cli("products components list --tag <tag> --json") ← compact non-secret inventory
|
|
327
|
+
ductape_cli("products components get --tag <tag> --type notifications --json")
|
|
328
|
+
ductape_cli("products components get --tag <tag> --type events --json")
|
|
326
329
|
ductape_cli("products create --name <name> --tag <tag>")
|
|
327
330
|
ductape_cli("products environments list <tag> --json")
|
|
328
331
|
ductape_cli("products environments get <tag> <slug> --json")
|
|
@@ -490,8 +493,8 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
490
493
|
notifications.fetch [product_tag, notif_tag]
|
|
491
494
|
notifications.list [product_tag]
|
|
492
495
|
notifications.delete [product_tag, notif_tag]
|
|
493
|
-
notifications.messages.create [product_tag, data: { tag: string,
|
|
494
|
-
notifications.messages.update [product_tag, msg_tag, data: {
|
|
496
|
+
notifications.messages.create [product_tag, data: { tag: string, name: string, description?: string, push_notification?: { title: string, body: string, data?: object }, email?: { subject: string, template: string }, callback?: object, sms?: string }]
|
|
497
|
+
notifications.messages.update [product_tag, msg_tag, data: { name?: string, description?: string, push_notification?: { title: string, body: string, data?: object }, email?: { subject: string, template: string }, callback?: object, sms?: string }]
|
|
495
498
|
notifications.messages.fetch [product_tag, msg_tag]
|
|
496
499
|
notifications.messages.list [product_tag, notification_tag]
|
|
497
500
|
notifications.send [{ product, env, event, input: { ... } }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="send", targets={notification})
|
|
@@ -499,6 +502,8 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
499
502
|
notifications.push.send [{ product, env, notification, input: { ... }, session?, cache? }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="push.send")
|
|
500
503
|
notifications.sms.send [{ product, env, notification, input: { ... }, session?, cache? }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="sms.send")
|
|
501
504
|
notifications.callback.send [{ product, env, notification, input: { ... }, session?, cache? }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="callback.send")
|
|
505
|
+
notifications.slack.send [{ product, env, notification, input: { text?, blocks?, channel? }, session?, cache? }]
|
|
506
|
+
notifications.discord.send [{ product, env, notification, input: { content?, embeds? }, session?, cache? }]
|
|
502
507
|
notifications.dispatch [{ product, env, notification, event, input, retries?, session?, cache?, schedule?: { cron?, every?, start_at? } }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="dispatch") — requires redisUrl in ductape initialization
|
|
503
508
|
notifications.getMessages [{ product_tag?, env?, notification_tag?, status?, type?, start_date?, end_date?, page?, limit? }]
|
|
504
509
|
|
|
@@ -998,7 +1003,7 @@ const ALLOWED_SNIPPET_METHODS = {
|
|
|
998
1003
|
graph: ['dispatch', 'find', 'insert', 'update', 'delete', 'query', 'execute'],
|
|
999
1004
|
vector: ['query', 'find', 'findSimilar', 'upsert', 'insert', 'delete', 'dispatch'],
|
|
1000
1005
|
storage: ['dispatch', 'upload', 'download', 'remove', 'listFiles', 'getSignedUrl', 'stats'],
|
|
1001
|
-
notification: ['dispatch', 'send', 'email.send', 'push.send', 'sms.send', 'callback.send'],
|
|
1006
|
+
notification: ['dispatch', 'send', 'email.send', 'push.send', 'sms.send', 'callback.send', 'slack.send', 'discord.send'],
|
|
1002
1007
|
messaging: ['dispatch', 'send', 'publish', 'produce', 'consume'],
|
|
1003
1008
|
broker: ['dispatch', 'send', 'publish', 'produce', 'consume'],
|
|
1004
1009
|
quota: ['run', 'dispatch', 'check', 'consume'],
|
|
@@ -1161,6 +1166,7 @@ const ADMIN_SUBCOMMANDS = [
|
|
|
1161
1166
|
'link', 'unlink', 'init',
|
|
1162
1167
|
'products', 'apps',
|
|
1163
1168
|
'resources',
|
|
1169
|
+
'notifications',
|
|
1164
1170
|
'events',
|
|
1165
1171
|
'cloud',
|
|
1166
1172
|
'secrets',
|
|
@@ -2168,27 +2174,73 @@ DUCTAPE NOTIFICATIONS
|
|
|
2168
2174
|
|
|
2169
2175
|
Notifications send messages across multiple channels: email, SMS, push, or HTTP callback.
|
|
2170
2176
|
|
|
2171
|
-
|
|
2177
|
+
ADMINISTRATION — CLI (never ductape_execute)
|
|
2178
|
+
ductape_cli("resources notifications create -f notification.json")
|
|
2179
|
+
ductape_cli("resources notifications get --tag <notification-tag> --json")
|
|
2180
|
+
ductape_cli("resources notifications list --json")
|
|
2181
|
+
ductape_cli("resources notifications update --tag <notification-tag> -f patch.json")
|
|
2182
|
+
ductape_cli("resources notifications delete --tag <notification-tag>")
|
|
2183
|
+
ductape_cli("notifications messages create -f message.json")
|
|
2184
|
+
ductape_cli("notifications messages list --notification <notification-tag> --json")
|
|
2185
|
+
ductape_cli("notifications messages get --tag <notification:message> --json")
|
|
2186
|
+
ductape_cli("notifications messages update --tag <notification:message> -f patch.json")
|
|
2187
|
+
Declarative alternative: ductape/notifications.json then ductape_cli("apply notifications").
|
|
2188
|
+
The file MUST be a top-level JSON array. Each item is a notification definition and may contain
|
|
2189
|
+
a nested "messages" array. {"notifications":[],"messages":[]} is not a valid envelope.
|
|
2190
|
+
|
|
2191
|
+
Notification definition:
|
|
2172
2192
|
{
|
|
2173
|
-
tag: "welcome-email",
|
|
2193
|
+
tag: "welcome-email", // component tags cannot contain ":"
|
|
2174
2194
|
name: "Welcome Email",
|
|
2175
|
-
|
|
2195
|
+
description: "Transactional welcome messages",
|
|
2196
|
+
envs: [{
|
|
2197
|
+
slug: "prd",
|
|
2198
|
+
emails: {
|
|
2199
|
+
provider: "smtp",
|
|
2200
|
+
smtp: {
|
|
2201
|
+
host: "smtp.example.com",
|
|
2202
|
+
port: "$Secret{smtp-port}",
|
|
2203
|
+
sender_email: "hello@example.com",
|
|
2204
|
+
auth: { user: "$Secret{smtp-user}", pass: "$Secret{smtp-password}" },
|
|
2205
|
+
secure: true
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}],
|
|
2209
|
+
messages: []
|
|
2176
2210
|
}
|
|
2177
2211
|
|
|
2178
2212
|
Create a message template through declarative apply or Workbench:
|
|
2179
2213
|
{
|
|
2180
|
-
tag: "welcome-email:default",
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2214
|
+
tag: "welcome-email:default", // message tags use component-tag:message-tag
|
|
2215
|
+
name: "Default welcome",
|
|
2216
|
+
description: "Sent after account creation",
|
|
2217
|
+
push_notification: {
|
|
2218
|
+
title: "Welcome, {{name}}!",
|
|
2219
|
+
body: "Thanks for signing up.",
|
|
2220
|
+
data: {}
|
|
2221
|
+
},
|
|
2222
|
+
email: {
|
|
2223
|
+
subject: "Welcome, {{name}}!",
|
|
2224
|
+
template: "Hi {{name}}, thanks for signing up."
|
|
2225
|
+
}
|
|
2184
2226
|
}
|
|
2185
2227
|
|
|
2228
|
+
TAG AND SMTP RULES
|
|
2229
|
+
Component tag: "game-alerts-critical" (no colon).
|
|
2230
|
+
Message tag: "game-alerts-critical:match-launch" (one colon separator).
|
|
2231
|
+
Do not add "notification", "subject", or "body" at message root.
|
|
2232
|
+
SMTP requires emails.smtp.sender_email.
|
|
2233
|
+
emails.smtp.secure is a boolean and cannot be a $Secret{...} string.
|
|
2234
|
+
Credential strings such as auth.user and auth.pass may use $Secret{...}.
|
|
2235
|
+
|
|
2186
2236
|
Send at runtime (one channel at a time):
|
|
2187
2237
|
→ CALL ductape_generate_payload FIRST (operation_family="notification", method="email.send")
|
|
2188
2238
|
notifications.email.send [{ product, env, notification, input: { recipients, subject?, template? } }]
|
|
2189
2239
|
notifications.push.send [{ product, env, notification, input: { device_tokens, title?, body?, data? } }]
|
|
2190
2240
|
notifications.sms.send [{ product, env, notification, input: { recipients, body? } }]
|
|
2191
2241
|
notifications.callback.send [{ product, env, notification, input: { query?, headers?, params?, body? } }]
|
|
2242
|
+
notifications.slack.send [{ product, env, notification, input: { text?, blocks?, channel? } }]
|
|
2243
|
+
notifications.discord.send [{ product, env, notification, input: { content?, embeds? } }]
|
|
2192
2244
|
|
|
2193
2245
|
Multi-channel send (all channels in parallel):
|
|
2194
2246
|
notifications.send [{ product, env, event: "notif_tag:message_tag", input: { ... } }]
|
|
@@ -2210,7 +2262,28 @@ Supported providers:
|
|
|
2210
2262
|
Push: firebase | expo
|
|
2211
2263
|
Callback: any HTTP endpoint
|
|
2212
2264
|
|
|
2213
|
-
|
|
2265
|
+
Each notification env may configure push_notifications, emails, sms, callbacks, slack, and discord.
|
|
2266
|
+
Email providers: smtp, sendgrid, mailgun, postmark, brevo.
|
|
2267
|
+
SMS providers: twilio, nexmo/vonage, plivo.
|
|
2268
|
+
|
|
2269
|
+
FIREBASE THROUGH A GCP CLOUD CONNECTION
|
|
2270
|
+
Complete and validate a GCP cloud connection with a service account authorized for Firebase
|
|
2271
|
+
Cloud Messaging. Then configure the notification env without copying service-account JSON:
|
|
2272
|
+
{
|
|
2273
|
+
"slug": "snd",
|
|
2274
|
+
"push_notifications": {
|
|
2275
|
+
"type": "firebase",
|
|
2276
|
+
"cloud": "gcp-connection-tag",
|
|
2277
|
+
"authMode": "cloud_connection",
|
|
2278
|
+
"databaseUrl": "https://<project>.firebaseio.com"
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
The SDK requests current GCP credentials from the cloud connection at send time. Never put the
|
|
2282
|
+
service-account private key in the notification file. Expo does not use a GCP cloud connection.
|
|
2283
|
+
The recommended cloud connection scope is "notifications". It expresses Ductape capability/UI
|
|
2284
|
+
intent; creating a notification does not prove Google-side FCM permission. Runtime delivery
|
|
2285
|
+
requires fcm.googleapis.com and roles/firebasecloudmessaging.admin, so validate the connection
|
|
2286
|
+
and perform a delivery test.
|
|
2214
2287
|
Notification tag and message tag are ALWAYS passed together as "notification_tag:message_tag".
|
|
2215
2288
|
`.trim(),
|
|
2216
2289
|
resilience: `
|
|
@@ -3720,8 +3793,10 @@ async function main() {
|
|
|
3720
3793
|
const firstWord = args.command.trim().split(/\s+/)[0];
|
|
3721
3794
|
const isAuthCommand = firstWord === 'login' || firstWord === 'logout';
|
|
3722
3795
|
if (!isAuthCommand) {
|
|
3723
|
-
//
|
|
3724
|
-
|
|
3796
|
+
// Cache successful authentication, but re-check a missing/expired session on every call.
|
|
3797
|
+
// The user may complete `ductape login` in another terminal while this MCP process remains
|
|
3798
|
+
// alive; caching "none" would otherwise make the MCP blind to the newly written session.
|
|
3799
|
+
if (authState === 'unknown' || authState === 'none') {
|
|
3725
3800
|
checkLoginState();
|
|
3726
3801
|
}
|
|
3727
3802
|
if (authState === 'none') {
|
|
@@ -4029,7 +4104,10 @@ async function main() {
|
|
|
4029
4104
|
' Message brokers are import-only (no provision-persist). Import flow is the same as storage.\n' +
|
|
4030
4105
|
' type field = "messageBrokers" (not "messagebrokers" or "events").\n' +
|
|
4031
4106
|
' After importing, create topics first with ductape_cli("events topics create -f topic.json") — SQS requires explicit topic creation with queueUrls. For other providers, topics auto-register on first produce but should still be created explicitly before any consumer subscribes.\n' +
|
|
4032
|
-
' - Listing workspaces, products, secrets\n' +
|
|
4107
|
+
' - Listing workspaces, products, focused product components, secrets\n' +
|
|
4108
|
+
' Prefer "products components list --tag <tag> --json" for compact inventory; use\n' +
|
|
4109
|
+
' "products components get --tag <tag> --type notifications|events --json" for focused detail.\n' +
|
|
4110
|
+
' - Managing notification components and message templates through "resources notifications" and "notifications messages"\n' +
|
|
4033
4111
|
' - Linking a project folder: "link --product <tag> --env <slug>"\n' +
|
|
4034
4112
|
' - Syncing sessions/notifications/events: "apply" or "apply sessions" etc.\n' +
|
|
4035
4113
|
' - Running database migrations: "db migrate", "db schema generate"\n\n' +
|
package/package.json
CHANGED
|
@@ -50,6 +50,14 @@ const safetyChecks = [
|
|
|
50
50
|
['CLI 401 triggers login-required state', /authentication expired or was revoked \(HTTP 401\)[\s\S]*ductape login/],
|
|
51
51
|
['CLI distinguishes endpoint 401 from expired login', /session and active workspace are authenticated[\s\S]*Do not ask the user to log in again/],
|
|
52
52
|
['CLI auth guidance does not request secrets', /Never ask the user to paste their password/],
|
|
53
|
+
['notification CLI administration documented', /resources notifications create[\s\S]*notifications messages create/],
|
|
54
|
+
['notification command allowed by MCP', /ADMIN_SUBCOMMANDS[\s\S]*'notifications'/],
|
|
55
|
+
['notification template uses SDK shape', /push_notification\?: \{ title: string, body: string[\s\S]*email\?: \{ subject: string, template: string/],
|
|
56
|
+
['notification declarations require array shape', /file MUST be a top-level JSON array/],
|
|
57
|
+
['external CLI login is re-read', /authState === 'unknown' \|\| authState === 'none'/],
|
|
58
|
+
['compact product inventory documented', /products components list --tag <tag> --json/],
|
|
59
|
+
['Firebase GCP cloud connection documented', /FIREBASE THROUGH A GCP CLOUD CONNECTION[\s\S]*authMode.*cloud_connection/],
|
|
60
|
+
['Slack and Discord sends documented', /notifications\.slack\.send[\s\S]*notifications\.discord\.send/],
|
|
53
61
|
];
|
|
54
62
|
|
|
55
63
|
for (const [name, pattern] of safetyChecks) {
|
package/src/index.ts
CHANGED
|
@@ -334,6 +334,9 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
334
334
|
IMPORTANT: ALL product.* methods require the access key and will return 403 with a publishable key.
|
|
335
335
|
Use ductape_cli for ALL product operations — never ductape_execute:
|
|
336
336
|
ductape_cli("products get --tag <tag> --json") ← fetch product + full inventory
|
|
337
|
+
ductape_cli("products components list --tag <tag> --json") ← compact non-secret inventory
|
|
338
|
+
ductape_cli("products components get --tag <tag> --type notifications --json")
|
|
339
|
+
ductape_cli("products components get --tag <tag> --type events --json")
|
|
337
340
|
ductape_cli("products create --name <name> --tag <tag>")
|
|
338
341
|
ductape_cli("products environments list <tag> --json")
|
|
339
342
|
ductape_cli("products environments get <tag> <slug> --json")
|
|
@@ -501,8 +504,8 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
501
504
|
notifications.fetch [product_tag, notif_tag]
|
|
502
505
|
notifications.list [product_tag]
|
|
503
506
|
notifications.delete [product_tag, notif_tag]
|
|
504
|
-
notifications.messages.create [product_tag, data: { tag: string,
|
|
505
|
-
notifications.messages.update [product_tag, msg_tag, data: {
|
|
507
|
+
notifications.messages.create [product_tag, data: { tag: string, name: string, description?: string, push_notification?: { title: string, body: string, data?: object }, email?: { subject: string, template: string }, callback?: object, sms?: string }]
|
|
508
|
+
notifications.messages.update [product_tag, msg_tag, data: { name?: string, description?: string, push_notification?: { title: string, body: string, data?: object }, email?: { subject: string, template: string }, callback?: object, sms?: string }]
|
|
506
509
|
notifications.messages.fetch [product_tag, msg_tag]
|
|
507
510
|
notifications.messages.list [product_tag, notification_tag]
|
|
508
511
|
notifications.send [{ product, env, event, input: { ... } }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="send", targets={notification})
|
|
@@ -510,6 +513,8 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
510
513
|
notifications.push.send [{ product, env, notification, input: { ... }, session?, cache? }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="push.send")
|
|
511
514
|
notifications.sms.send [{ product, env, notification, input: { ... }, session?, cache? }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="sms.send")
|
|
512
515
|
notifications.callback.send [{ product, env, notification, input: { ... }, session?, cache? }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="callback.send")
|
|
516
|
+
notifications.slack.send [{ product, env, notification, input: { text?, blocks?, channel? }, session?, cache? }]
|
|
517
|
+
notifications.discord.send [{ product, env, notification, input: { content?, embeds? }, session?, cache? }]
|
|
513
518
|
notifications.dispatch [{ product, env, notification, event, input, retries?, session?, cache?, schedule?: { cron?, every?, start_at? } }] ← CALL ductape_generate_payload FIRST (operation_family="notification", method="dispatch") — requires redisUrl in ductape initialization
|
|
514
519
|
notifications.getMessages [{ product_tag?, env?, notification_tag?, status?, type?, start_date?, end_date?, page?, limit? }]
|
|
515
520
|
|
|
@@ -1037,7 +1042,7 @@ const ALLOWED_SNIPPET_METHODS: Record<string, string[]> = {
|
|
|
1037
1042
|
graph: ['dispatch', 'find', 'insert', 'update', 'delete', 'query', 'execute'],
|
|
1038
1043
|
vector: ['query', 'find', 'findSimilar', 'upsert', 'insert', 'delete', 'dispatch'],
|
|
1039
1044
|
storage: ['dispatch', 'upload', 'download', 'remove', 'listFiles', 'getSignedUrl', 'stats'],
|
|
1040
|
-
notification: ['dispatch', 'send', 'email.send', 'push.send', 'sms.send', 'callback.send'],
|
|
1045
|
+
notification: ['dispatch', 'send', 'email.send', 'push.send', 'sms.send', 'callback.send', 'slack.send', 'discord.send'],
|
|
1041
1046
|
messaging: ['dispatch', 'send', 'publish', 'produce', 'consume'],
|
|
1042
1047
|
broker: ['dispatch', 'send', 'publish', 'produce', 'consume'],
|
|
1043
1048
|
quota: ['run', 'dispatch', 'check', 'consume'],
|
|
@@ -1222,6 +1227,7 @@ const ADMIN_SUBCOMMANDS = [
|
|
|
1222
1227
|
'link', 'unlink', 'init',
|
|
1223
1228
|
'products', 'apps',
|
|
1224
1229
|
'resources',
|
|
1230
|
+
'notifications',
|
|
1225
1231
|
'events',
|
|
1226
1232
|
'cloud',
|
|
1227
1233
|
'secrets',
|
|
@@ -2251,27 +2257,73 @@ DUCTAPE NOTIFICATIONS
|
|
|
2251
2257
|
|
|
2252
2258
|
Notifications send messages across multiple channels: email, SMS, push, or HTTP callback.
|
|
2253
2259
|
|
|
2254
|
-
|
|
2260
|
+
ADMINISTRATION — CLI (never ductape_execute)
|
|
2261
|
+
ductape_cli("resources notifications create -f notification.json")
|
|
2262
|
+
ductape_cli("resources notifications get --tag <notification-tag> --json")
|
|
2263
|
+
ductape_cli("resources notifications list --json")
|
|
2264
|
+
ductape_cli("resources notifications update --tag <notification-tag> -f patch.json")
|
|
2265
|
+
ductape_cli("resources notifications delete --tag <notification-tag>")
|
|
2266
|
+
ductape_cli("notifications messages create -f message.json")
|
|
2267
|
+
ductape_cli("notifications messages list --notification <notification-tag> --json")
|
|
2268
|
+
ductape_cli("notifications messages get --tag <notification:message> --json")
|
|
2269
|
+
ductape_cli("notifications messages update --tag <notification:message> -f patch.json")
|
|
2270
|
+
Declarative alternative: ductape/notifications.json then ductape_cli("apply notifications").
|
|
2271
|
+
The file MUST be a top-level JSON array. Each item is a notification definition and may contain
|
|
2272
|
+
a nested "messages" array. {"notifications":[],"messages":[]} is not a valid envelope.
|
|
2273
|
+
|
|
2274
|
+
Notification definition:
|
|
2255
2275
|
{
|
|
2256
|
-
tag: "welcome-email",
|
|
2276
|
+
tag: "welcome-email", // component tags cannot contain ":"
|
|
2257
2277
|
name: "Welcome Email",
|
|
2258
|
-
|
|
2278
|
+
description: "Transactional welcome messages",
|
|
2279
|
+
envs: [{
|
|
2280
|
+
slug: "prd",
|
|
2281
|
+
emails: {
|
|
2282
|
+
provider: "smtp",
|
|
2283
|
+
smtp: {
|
|
2284
|
+
host: "smtp.example.com",
|
|
2285
|
+
port: "$Secret{smtp-port}",
|
|
2286
|
+
sender_email: "hello@example.com",
|
|
2287
|
+
auth: { user: "$Secret{smtp-user}", pass: "$Secret{smtp-password}" },
|
|
2288
|
+
secure: true
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}],
|
|
2292
|
+
messages: []
|
|
2259
2293
|
}
|
|
2260
2294
|
|
|
2261
2295
|
Create a message template through declarative apply or Workbench:
|
|
2262
2296
|
{
|
|
2263
|
-
tag: "welcome-email:default",
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2297
|
+
tag: "welcome-email:default", // message tags use component-tag:message-tag
|
|
2298
|
+
name: "Default welcome",
|
|
2299
|
+
description: "Sent after account creation",
|
|
2300
|
+
push_notification: {
|
|
2301
|
+
title: "Welcome, {{name}}!",
|
|
2302
|
+
body: "Thanks for signing up.",
|
|
2303
|
+
data: {}
|
|
2304
|
+
},
|
|
2305
|
+
email: {
|
|
2306
|
+
subject: "Welcome, {{name}}!",
|
|
2307
|
+
template: "Hi {{name}}, thanks for signing up."
|
|
2308
|
+
}
|
|
2267
2309
|
}
|
|
2268
2310
|
|
|
2311
|
+
TAG AND SMTP RULES
|
|
2312
|
+
Component tag: "game-alerts-critical" (no colon).
|
|
2313
|
+
Message tag: "game-alerts-critical:match-launch" (one colon separator).
|
|
2314
|
+
Do not add "notification", "subject", or "body" at message root.
|
|
2315
|
+
SMTP requires emails.smtp.sender_email.
|
|
2316
|
+
emails.smtp.secure is a boolean and cannot be a $Secret{...} string.
|
|
2317
|
+
Credential strings such as auth.user and auth.pass may use $Secret{...}.
|
|
2318
|
+
|
|
2269
2319
|
Send at runtime (one channel at a time):
|
|
2270
2320
|
→ CALL ductape_generate_payload FIRST (operation_family="notification", method="email.send")
|
|
2271
2321
|
notifications.email.send [{ product, env, notification, input: { recipients, subject?, template? } }]
|
|
2272
2322
|
notifications.push.send [{ product, env, notification, input: { device_tokens, title?, body?, data? } }]
|
|
2273
2323
|
notifications.sms.send [{ product, env, notification, input: { recipients, body? } }]
|
|
2274
2324
|
notifications.callback.send [{ product, env, notification, input: { query?, headers?, params?, body? } }]
|
|
2325
|
+
notifications.slack.send [{ product, env, notification, input: { text?, blocks?, channel? } }]
|
|
2326
|
+
notifications.discord.send [{ product, env, notification, input: { content?, embeds? } }]
|
|
2275
2327
|
|
|
2276
2328
|
Multi-channel send (all channels in parallel):
|
|
2277
2329
|
notifications.send [{ product, env, event: "notif_tag:message_tag", input: { ... } }]
|
|
@@ -2293,7 +2345,28 @@ Supported providers:
|
|
|
2293
2345
|
Push: firebase | expo
|
|
2294
2346
|
Callback: any HTTP endpoint
|
|
2295
2347
|
|
|
2296
|
-
|
|
2348
|
+
Each notification env may configure push_notifications, emails, sms, callbacks, slack, and discord.
|
|
2349
|
+
Email providers: smtp, sendgrid, mailgun, postmark, brevo.
|
|
2350
|
+
SMS providers: twilio, nexmo/vonage, plivo.
|
|
2351
|
+
|
|
2352
|
+
FIREBASE THROUGH A GCP CLOUD CONNECTION
|
|
2353
|
+
Complete and validate a GCP cloud connection with a service account authorized for Firebase
|
|
2354
|
+
Cloud Messaging. Then configure the notification env without copying service-account JSON:
|
|
2355
|
+
{
|
|
2356
|
+
"slug": "snd",
|
|
2357
|
+
"push_notifications": {
|
|
2358
|
+
"type": "firebase",
|
|
2359
|
+
"cloud": "gcp-connection-tag",
|
|
2360
|
+
"authMode": "cloud_connection",
|
|
2361
|
+
"databaseUrl": "https://<project>.firebaseio.com"
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
The SDK requests current GCP credentials from the cloud connection at send time. Never put the
|
|
2365
|
+
service-account private key in the notification file. Expo does not use a GCP cloud connection.
|
|
2366
|
+
The recommended cloud connection scope is "notifications". It expresses Ductape capability/UI
|
|
2367
|
+
intent; creating a notification does not prove Google-side FCM permission. Runtime delivery
|
|
2368
|
+
requires fcm.googleapis.com and roles/firebasecloudmessaging.admin, so validate the connection
|
|
2369
|
+
and perform a delivery test.
|
|
2297
2370
|
Notification tag and message tag are ALWAYS passed together as "notification_tag:message_tag".
|
|
2298
2371
|
`.trim(),
|
|
2299
2372
|
|
|
@@ -3828,8 +3901,10 @@ async function main() {
|
|
|
3828
3901
|
const isAuthCommand = firstWord === 'login' || firstWord === 'logout';
|
|
3829
3902
|
|
|
3830
3903
|
if (!isAuthCommand) {
|
|
3831
|
-
//
|
|
3832
|
-
|
|
3904
|
+
// Cache successful authentication, but re-check a missing/expired session on every call.
|
|
3905
|
+
// The user may complete `ductape login` in another terminal while this MCP process remains
|
|
3906
|
+
// alive; caching "none" would otherwise make the MCP blind to the newly written session.
|
|
3907
|
+
if (authState === 'unknown' || authState === 'none') {
|
|
3833
3908
|
checkLoginState();
|
|
3834
3909
|
}
|
|
3835
3910
|
if (authState === 'none') {
|
|
@@ -4190,7 +4265,10 @@ async function main() {
|
|
|
4190
4265
|
' Message brokers are import-only (no provision-persist). Import flow is the same as storage.\n' +
|
|
4191
4266
|
' type field = "messageBrokers" (not "messagebrokers" or "events").\n' +
|
|
4192
4267
|
' After importing, create topics first with ductape_cli("events topics create -f topic.json") — SQS requires explicit topic creation with queueUrls. For other providers, topics auto-register on first produce but should still be created explicitly before any consumer subscribes.\n' +
|
|
4193
|
-
' - Listing workspaces, products, secrets\n' +
|
|
4268
|
+
' - Listing workspaces, products, focused product components, secrets\n' +
|
|
4269
|
+
' Prefer "products components list --tag <tag> --json" for compact inventory; use\n' +
|
|
4270
|
+
' "products components get --tag <tag> --type notifications|events --json" for focused detail.\n' +
|
|
4271
|
+
' - Managing notification components and message templates through "resources notifications" and "notifications messages"\n' +
|
|
4194
4272
|
' - Linking a project folder: "link --product <tag> --env <slug>"\n' +
|
|
4195
4273
|
' - Syncing sessions/notifications/events: "apply" or "apply sessions" etc.\n' +
|
|
4196
4274
|
' - Running database migrations: "db migrate", "db schema generate"\n\n' +
|