@ductape/mcp 0.1.44 → 0.1.45
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 +64 -45
- package/dist/proxy-client.d.ts +1 -1
- package/dist/proxy-client.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +64 -45
- package/src/proxy-client.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { z } from 'zod';
|
|
|
15
15
|
import { executeViaProxy, generateExecutablePayload, getAssetSchemas, } from './proxy-client.js';
|
|
16
16
|
const MODULES = [
|
|
17
17
|
'product', 'app', 'databases', 'graph', 'webhooks', 'notifications',
|
|
18
|
-
'messageBrokers', 'storage', 'vector', 'caches', 'sessions', 'quotas',
|
|
18
|
+
'events', 'messageBrokers', 'storage', 'vector', 'caches', 'sessions', 'quotas',
|
|
19
19
|
'actions', 'features', 'jobs', 'logs', 'resilience', 'health', 'fallback', 'secrets',
|
|
20
20
|
];
|
|
21
21
|
// ─── Exhaustive SDK Method & Params Reference ────────────────────────────────
|
|
@@ -184,7 +184,7 @@ SETUP — register once in AppModule:
|
|
|
184
184
|
║ redisUrl IS REQUIRED TO USE ANY *.dispatch() ║
|
|
185
185
|
║ ║
|
|
186
186
|
║ Every dispatch() call (actions, features, events, databases, storage, ║
|
|
187
|
-
║ graph, notifications,
|
|
187
|
+
║ graph, notifications, quotas, fallback) enqueues jobs ║
|
|
188
188
|
║ via BullMQ over Redis. Without redisUrl the call throws at runtime: ║
|
|
189
189
|
║ "Queues not configured. dispatch() requires a queue connection." ║
|
|
190
190
|
║ ║
|
|
@@ -502,33 +502,33 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
502
502
|
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
503
|
notifications.getMessages [{ product_tag?, env?, notification_tag?, status?, type?, start_date?, end_date?, page?, limit? }]
|
|
504
504
|
|
|
505
|
-
━━━ MODULE: messageBrokers ━━━
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
505
|
+
━━━ MODULE: events (alias: messageBrokers — both accepted; events matches the TS SDK naming) ━━━
|
|
506
|
+
events.create [{ product: string, tag: string, name: string, description?: string, type: "kafka"|"rabbitmq"|"redis"|"sqs", envs: [{ slug: string, connection_url: string }] }]
|
|
507
|
+
events.update [product_tag, broker_tag, data: { name?: string, description?: string, type?: "kafka"|"rabbitmq"|"redis"|"sqs", envs?: [{ slug: string, connection_url: string }] }]
|
|
508
|
+
events.fetch [product_tag, broker_tag]
|
|
509
|
+
events.list [product_tag]
|
|
510
|
+
events.delete [product_tag, broker_tag]
|
|
511
|
+
events.topics.create ← FORBIDDEN with publishable key. Use ductape_cli instead:
|
|
512
512
|
ductape_cli("events topics create -f topic.json")
|
|
513
513
|
topic.json: { tag: "broker-tag:topic-tag", name, description?, sample?, idempotent?, queueUrls?: [{ env_slug, url }] }
|
|
514
514
|
← Always required before consuming. For SQS: must include queueUrls per env.
|
|
515
515
|
← For Pub/Sub, Kafka, RabbitMQ, Redis, NATS: the first produce call auto-registers the topic,
|
|
516
516
|
but you should still create it explicitly so consumers can subscribe before any produce occurs.
|
|
517
|
-
|
|
517
|
+
events.topics.update ← FORBIDDEN with publishable key. Use ductape_cli:
|
|
518
518
|
ductape_cli("events topics update --tag broker:topic -f patch.json")
|
|
519
|
-
|
|
519
|
+
events.topics.delete ← FORBIDDEN with publishable key. Use ductape_cli:
|
|
520
520
|
ductape_cli("events topics delete --tag broker:topic")
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
521
|
+
events.topics.fetch [product_tag, topic_tag] ← safe via ductape_execute
|
|
522
|
+
events.topics.list [product_tag, broker_tag] ← safe via ductape_execute
|
|
523
|
+
events.produce [{ product, env, event: "broker_tag:topic_tag", message: { key: value }, session?, cache? }]
|
|
524
|
+
events.consume [{ product, env, event: "broker_tag:topic_tag", callback: "function_ref" }]
|
|
525
|
+
events.dispatch [{ product, env, broker, event, input: { message }, retries?, session?, cache?, schedule?: { cron?, every?, start_at? } }] — requires redisUrl in ductape initialization
|
|
526
|
+
events.messages.query [{ product, env, brokerTag, topicTag?, producerTag?, consumerTag?, status?, startDate?, endDate?, page?, limit? }]
|
|
527
|
+
events.messages.getProducers [{ product, env, brokerTag, topicTag?, page?, limit? }]
|
|
528
|
+
events.messages.getConsumers [{ product, env, brokerTag, topicTag?, page?, limit? }]
|
|
529
|
+
events.messages.getDeadLetters [{ product, env, brokerTag, topicTag?, consumerTag?, startDate?, endDate?, page?, limit? }]
|
|
530
|
+
events.messages.getStats [{ product, env, brokerTag }]
|
|
531
|
+
events.messages.getDashboard [{ product, env, brokerTag }]
|
|
532
532
|
|
|
533
533
|
━━━ MODULE: storage ━━━
|
|
534
534
|
storage.create [{ product: string, tag: string, name: string, description?: string, envs: [{ slug: string, type: "aws"|"azure"|"gcp", config: { bucket?: string, region?: string, accessKeyId?: string, secretAccessKey?: string, containerName?: string, connectionString?: string, projectId?: string, keyFilename?: string } }] }]
|
|
@@ -1027,12 +1027,12 @@ function resolveSdkCallPath(operationFamily, method) {
|
|
|
1027
1027
|
return m === 'dispatch' ? 'storage.dispatch' : `storage.${m}`;
|
|
1028
1028
|
if (family === 'notification')
|
|
1029
1029
|
return m === 'dispatch' ? 'notifications.dispatch' : `notifications.${m}`;
|
|
1030
|
-
if (family === 'messaging' || family === 'broker') {
|
|
1030
|
+
if (family === 'messaging' || family === 'broker' || family === 'events' || family === 'event') {
|
|
1031
1031
|
if (m === 'dispatch')
|
|
1032
|
-
return '
|
|
1032
|
+
return 'events.dispatch';
|
|
1033
1033
|
if (m === 'send' || m === 'publish' || m === 'produce')
|
|
1034
|
-
return '
|
|
1035
|
-
return `
|
|
1034
|
+
return 'events.produce';
|
|
1035
|
+
return `events.${m}`;
|
|
1036
1036
|
}
|
|
1037
1037
|
if (family === 'quota')
|
|
1038
1038
|
return `quotas.${m}`;
|
|
@@ -2467,7 +2467,7 @@ Import (register an EXISTING cloud resource):
|
|
|
2467
2467
|
Producing to a topic also calls ensureTopicRegistered in the background — but DO NOT rely on
|
|
2468
2468
|
auto-registration for consume paths. Always create topics explicitly.
|
|
2469
2469
|
|
|
2470
|
-
IMPORTANT:
|
|
2470
|
+
IMPORTANT: events.topics.create requires an access key (admin operation).
|
|
2471
2471
|
Use ductape_cli — NOT ductape_execute — to create topics.
|
|
2472
2472
|
|
|
2473
2473
|
Write a topic.json file, then:
|
|
@@ -2494,7 +2494,7 @@ Import (register an EXISTING cloud resource):
|
|
|
2494
2494
|
ductape_cli("events topics delete --tag order-events:order-created")
|
|
2495
2495
|
|
|
2496
2496
|
Read-only fetches (safe with publishable key via ductape_execute):
|
|
2497
|
-
ductape_execute("
|
|
2497
|
+
ductape_execute("events.fetch", [product_tag, "broker-tag"]) → includes topics[]
|
|
2498
2498
|
|
|
2499
2499
|
━━━ STEP 3: PRODUCE — WRITTEN IN APPLICATION CODE ━━━
|
|
2500
2500
|
|
|
@@ -2505,7 +2505,12 @@ Import (register an EXISTING cloud resource):
|
|
|
2505
2505
|
|
|
2506
2506
|
GENERAL BACKEND (TypeScript/Node.js — not NestJS):
|
|
2507
2507
|
import Ductape from '@ductape/sdk';
|
|
2508
|
-
|
|
2508
|
+
// produce() does not need redis_url.
|
|
2509
|
+
// dispatch() requires redis_url in the Ductape initialization options — it throws at runtime without it.
|
|
2510
|
+
const ductape = new Ductape({
|
|
2511
|
+
accessKey: process.env.DUCTAPE_ACCESS_KEY,
|
|
2512
|
+
redis_url: process.env.DUCTAPE_REDIS_URL, // required for any dispatch(); omit only if never dispatching
|
|
2513
|
+
});
|
|
2509
2514
|
await ductape.events.produce({
|
|
2510
2515
|
product: "my-product",
|
|
2511
2516
|
env: "prd",
|
|
@@ -2520,14 +2525,26 @@ Import (register an EXISTING cloud resource):
|
|
|
2520
2525
|
idempotencyTtl?: 86400, // seconds; default 86400 (24h)
|
|
2521
2526
|
});
|
|
2522
2527
|
|
|
2523
|
-
NESTJS — method decorators:
|
|
2528
|
+
NESTJS — initialization + method decorators:
|
|
2529
|
+
// AppModule — redisUrl is required whenever any *.dispatch() is used:
|
|
2530
|
+
DuctapeModule.forRootAsync({
|
|
2531
|
+
useFactory: () => ({
|
|
2532
|
+
accessKey: process.env.DUCTAPE_ACCESS_KEY,
|
|
2533
|
+
product: 'my-product',
|
|
2534
|
+
env: process.env.NODE_ENV === 'production' ? 'prd' : 'snd',
|
|
2535
|
+
redisUrl: process.env.DUCTAPE_REDIS_URL, // required — dispatch() throws without this
|
|
2536
|
+
}),
|
|
2537
|
+
});
|
|
2538
|
+
// Environment: DUCTAPE_REDIS_URL=redis://localhost:6379 (local) or rediss://:<pw>@host:6380 (managed)
|
|
2539
|
+
// produce() and @Events.Consumer do NOT need DUCTAPE_REDIS_URL — only dispatch() does.
|
|
2540
|
+
|
|
2524
2541
|
import { Events } from '@ductape/nestjs';
|
|
2525
2542
|
@Injectable() export class OrdersService {
|
|
2526
2543
|
// Immediate produce — method returns the message payload:
|
|
2527
2544
|
@Events.Produce({ event: 'order-events:order-created' })
|
|
2528
2545
|
emitOrderCreated(payload: { orderId: string; total: number }) { return payload; }
|
|
2529
2546
|
|
|
2530
|
-
// Dispatch with static schedule
|
|
2547
|
+
// Dispatch with static schedule — requires redisUrl in DuctapeModule initialization:
|
|
2531
2548
|
@Events.Dispatch({ broker: 'order-events', event: 'order-events:reminder-due',
|
|
2532
2549
|
schedule: { every: 86400000 } })
|
|
2533
2550
|
scheduleReminder(payload: { message: { orderId: string } }) { return payload; }
|
|
@@ -2560,7 +2577,7 @@ Import (register an EXISTING cloud resource):
|
|
|
2560
2577
|
});
|
|
2561
2578
|
|
|
2562
2579
|
SCHEDULED DISPATCH (background job):
|
|
2563
|
-
ductape_execute("
|
|
2580
|
+
ductape_execute("events.dispatch", [{
|
|
2564
2581
|
product, env,
|
|
2565
2582
|
broker: "order-events", // broker tag
|
|
2566
2583
|
event: "order-events:reminder-due", // "broker:topic"
|
|
@@ -2631,24 +2648,24 @@ Import (register an EXISTING cloud resource):
|
|
|
2631
2648
|
|
|
2632
2649
|
DEAD-LETTER QUEUE (DLQ):
|
|
2633
2650
|
Messages whose callbacks consistently throw are automatically moved to the DLQ.
|
|
2634
|
-
Query: ductape_execute("
|
|
2651
|
+
Query: ductape_execute("events.messages.getDeadLetters",
|
|
2635
2652
|
[{ product, env, brokerTag, topicTag?, consumerTag?, limit? }])
|
|
2636
|
-
Reprocess: ductape_execute("
|
|
2653
|
+
Reprocess: ductape_execute("events.reprocessDLQ",
|
|
2637
2654
|
[{ product, env, brokerTag, topicTag?, messageIds?, limit? }])
|
|
2638
|
-
Replay: ductape_execute("
|
|
2655
|
+
Replay: ductape_execute("events.replayEvent",
|
|
2639
2656
|
[{ product, env, eventId, force? }])
|
|
2640
2657
|
|
|
2641
2658
|
━━━ OBSERVABILITY ━━━
|
|
2642
2659
|
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2660
|
+
events.messages.query [{ product, env, brokerTag, topicTag?, status?, page?, limit? }]
|
|
2661
|
+
events.messages.getStats [{ product, env, brokerTag }]
|
|
2662
|
+
events.messages.getDashboard [{ product, env, brokerTag }]
|
|
2663
|
+
events.messages.getDeadLetters [{ product, env, brokerTag, topicTag?, consumerTag?, limit? }]
|
|
2664
|
+
events.messages.getProducers [{ product, env, brokerTag, topicTag?, page?, limit? }]
|
|
2665
|
+
events.messages.getConsumers [{ product, env, brokerTag, topicTag?, page?, limit? }]
|
|
2666
|
+
events.replayEvent [{ product, env, eventId, force? }]
|
|
2667
|
+
events.reprocessDLQ [{ product, env, brokerTag, topicTag?, messageIds?, limit? }]
|
|
2668
|
+
events.checkIdempotency [{ product, env, brokerTag, idempotency_key }]
|
|
2652
2669
|
`.trim(),
|
|
2653
2670
|
logs: `
|
|
2654
2671
|
DUCTAPE LOGS
|
|
@@ -3377,7 +3394,9 @@ async function main() {
|
|
|
3377
3394
|
if (!key) {
|
|
3378
3395
|
throw new Error('Not authenticated. Set DUCTAPE_PUBLISHABLE_KEY in your MCP server env config, or pass publishable_key on every tool call.');
|
|
3379
3396
|
}
|
|
3380
|
-
|
|
3397
|
+
// The TS SDK uses ductape.events.* for broker operations; the backend proxy uses messageBrokers.
|
|
3398
|
+
const proxyModule = args.module === 'events' ? 'messageBrokers' : args.module;
|
|
3399
|
+
const result = await executeViaProxy(key, proxyModule, args.method, args.params);
|
|
3381
3400
|
return { content: [{ type: 'text', text: JSON.stringify(result ?? null, null, 2) }] };
|
|
3382
3401
|
}
|
|
3383
3402
|
catch (err) {
|
package/dist/proxy-client.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Client for the Ductape backend SDK proxy using Publishable Key.
|
|
3
3
|
*/
|
|
4
4
|
export declare const API_BASE_URL = "https://api.ductape.app";
|
|
5
|
-
export type SDKModule = 'product' | 'app' | 'databases' | 'graph' | 'webhooks' | 'notifications' | 'messageBrokers' | 'storage' | 'vector' | 'caches' | 'sessions' | 'quotas' | 'actions' | 'features' | 'jobs' | 'logs' | 'resilience' | 'health' | 'fallback' | 'secrets';
|
|
5
|
+
export type SDKModule = 'product' | 'app' | 'databases' | 'graph' | 'webhooks' | 'notifications' | 'messageBrokers' | 'events' | 'storage' | 'vector' | 'caches' | 'sessions' | 'quotas' | 'actions' | 'features' | 'jobs' | 'logs' | 'resilience' | 'health' | 'fallback' | 'secrets';
|
|
6
6
|
/**
|
|
7
7
|
* Execute an SDK operation via the backend proxy using a Publishable Key.
|
|
8
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-client.d.ts","sourceRoot":"","sources":["../src/proxy-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,YAAY,4BAA4B,CAAC;AAEtD,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,KAAK,GACL,WAAW,GACX,OAAO,GACP,UAAU,GACV,eAAe,GACf,gBAAgB,GAChB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,SAAS,GACT,UAAU,GACV,MAAM,GACN,MAAM,GACN,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,SAAS,CAAC;AAUd;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAC/C,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,CAAC,CAAC,CAuBZ;AAED,MAAM,WAAW,iCAAiC;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,kCAAkC;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AASD,wBAAsB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYvE;AA4BD,wBAAsB,yBAAyB,CAAC,CAAC,GAAG,kCAAkC,EACpF,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,CAAC,CAAC,CAwBZ"}
|
|
1
|
+
{"version":3,"file":"proxy-client.d.ts","sourceRoot":"","sources":["../src/proxy-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,YAAY,4BAA4B,CAAC;AAEtD,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,KAAK,GACL,WAAW,GACX,OAAO,GACP,UAAU,GACV,eAAe,GACf,gBAAgB,GAChB,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,SAAS,GACT,UAAU,GACV,MAAM,GACN,MAAM,GACN,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,SAAS,CAAC;AAUd;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAC/C,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,CAAC,CAAC,CAuBZ;AAED,MAAM,WAAW,iCAAiC;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,kCAAkC;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AASD,wBAAsB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYvE;AA4BD,wBAAsB,yBAAyB,CAAC,CAAC,GAAG,kCAAkC,EACpF,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,CAAC,CAAC,CAwBZ"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
|
|
23
23
|
const MODULES: SDKModule[] = [
|
|
24
24
|
'product', 'app', 'databases', 'graph', 'webhooks', 'notifications',
|
|
25
|
-
'messageBrokers', 'storage', 'vector', 'caches', 'sessions', 'quotas',
|
|
25
|
+
'events', 'messageBrokers', 'storage', 'vector', 'caches', 'sessions', 'quotas',
|
|
26
26
|
'actions', 'features', 'jobs', 'logs', 'resilience', 'health', 'fallback', 'secrets',
|
|
27
27
|
];
|
|
28
28
|
|
|
@@ -195,7 +195,7 @@ SETUP — register once in AppModule:
|
|
|
195
195
|
║ redisUrl IS REQUIRED TO USE ANY *.dispatch() ║
|
|
196
196
|
║ ║
|
|
197
197
|
║ Every dispatch() call (actions, features, events, databases, storage, ║
|
|
198
|
-
║ graph, notifications,
|
|
198
|
+
║ graph, notifications, quotas, fallback) enqueues jobs ║
|
|
199
199
|
║ via BullMQ over Redis. Without redisUrl the call throws at runtime: ║
|
|
200
200
|
║ "Queues not configured. dispatch() requires a queue connection." ║
|
|
201
201
|
║ ║
|
|
@@ -513,33 +513,33 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
513
513
|
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
514
|
notifications.getMessages [{ product_tag?, env?, notification_tag?, status?, type?, start_date?, end_date?, page?, limit? }]
|
|
515
515
|
|
|
516
|
-
━━━ MODULE: messageBrokers ━━━
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
516
|
+
━━━ MODULE: events (alias: messageBrokers — both accepted; events matches the TS SDK naming) ━━━
|
|
517
|
+
events.create [{ product: string, tag: string, name: string, description?: string, type: "kafka"|"rabbitmq"|"redis"|"sqs", envs: [{ slug: string, connection_url: string }] }]
|
|
518
|
+
events.update [product_tag, broker_tag, data: { name?: string, description?: string, type?: "kafka"|"rabbitmq"|"redis"|"sqs", envs?: [{ slug: string, connection_url: string }] }]
|
|
519
|
+
events.fetch [product_tag, broker_tag]
|
|
520
|
+
events.list [product_tag]
|
|
521
|
+
events.delete [product_tag, broker_tag]
|
|
522
|
+
events.topics.create ← FORBIDDEN with publishable key. Use ductape_cli instead:
|
|
523
523
|
ductape_cli("events topics create -f topic.json")
|
|
524
524
|
topic.json: { tag: "broker-tag:topic-tag", name, description?, sample?, idempotent?, queueUrls?: [{ env_slug, url }] }
|
|
525
525
|
← Always required before consuming. For SQS: must include queueUrls per env.
|
|
526
526
|
← For Pub/Sub, Kafka, RabbitMQ, Redis, NATS: the first produce call auto-registers the topic,
|
|
527
527
|
but you should still create it explicitly so consumers can subscribe before any produce occurs.
|
|
528
|
-
|
|
528
|
+
events.topics.update ← FORBIDDEN with publishable key. Use ductape_cli:
|
|
529
529
|
ductape_cli("events topics update --tag broker:topic -f patch.json")
|
|
530
|
-
|
|
530
|
+
events.topics.delete ← FORBIDDEN with publishable key. Use ductape_cli:
|
|
531
531
|
ductape_cli("events topics delete --tag broker:topic")
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
532
|
+
events.topics.fetch [product_tag, topic_tag] ← safe via ductape_execute
|
|
533
|
+
events.topics.list [product_tag, broker_tag] ← safe via ductape_execute
|
|
534
|
+
events.produce [{ product, env, event: "broker_tag:topic_tag", message: { key: value }, session?, cache? }]
|
|
535
|
+
events.consume [{ product, env, event: "broker_tag:topic_tag", callback: "function_ref" }]
|
|
536
|
+
events.dispatch [{ product, env, broker, event, input: { message }, retries?, session?, cache?, schedule?: { cron?, every?, start_at? } }] — requires redisUrl in ductape initialization
|
|
537
|
+
events.messages.query [{ product, env, brokerTag, topicTag?, producerTag?, consumerTag?, status?, startDate?, endDate?, page?, limit? }]
|
|
538
|
+
events.messages.getProducers [{ product, env, brokerTag, topicTag?, page?, limit? }]
|
|
539
|
+
events.messages.getConsumers [{ product, env, brokerTag, topicTag?, page?, limit? }]
|
|
540
|
+
events.messages.getDeadLetters [{ product, env, brokerTag, topicTag?, consumerTag?, startDate?, endDate?, page?, limit? }]
|
|
541
|
+
events.messages.getStats [{ product, env, brokerTag }]
|
|
542
|
+
events.messages.getDashboard [{ product, env, brokerTag }]
|
|
543
543
|
|
|
544
544
|
━━━ MODULE: storage ━━━
|
|
545
545
|
storage.create [{ product: string, tag: string, name: string, description?: string, envs: [{ slug: string, type: "aws"|"azure"|"gcp", config: { bucket?: string, region?: string, accessKeyId?: string, secretAccessKey?: string, containerName?: string, connectionString?: string, projectId?: string, keyFilename?: string } }] }]
|
|
@@ -1062,10 +1062,10 @@ function resolveSdkCallPath(operationFamily: string, method: string): string {
|
|
|
1062
1062
|
if (family === 'vector') return `vector.${m}`;
|
|
1063
1063
|
if (family === 'storage') return m === 'dispatch' ? 'storage.dispatch' : `storage.${m}`;
|
|
1064
1064
|
if (family === 'notification') return m === 'dispatch' ? 'notifications.dispatch' : `notifications.${m}`;
|
|
1065
|
-
if (family === 'messaging' || family === 'broker') {
|
|
1066
|
-
if (m === 'dispatch') return '
|
|
1067
|
-
if (m === 'send' || m === 'publish' || m === 'produce') return '
|
|
1068
|
-
return `
|
|
1065
|
+
if (family === 'messaging' || family === 'broker' || family === 'events' || family === 'event') {
|
|
1066
|
+
if (m === 'dispatch') return 'events.dispatch';
|
|
1067
|
+
if (m === 'send' || m === 'publish' || m === 'produce') return 'events.produce';
|
|
1068
|
+
return `events.${m}`;
|
|
1069
1069
|
}
|
|
1070
1070
|
if (family === 'quota') return `quotas.${m}`;
|
|
1071
1071
|
if (family === 'fallback') return `fallback.${m}`;
|
|
@@ -2540,7 +2540,7 @@ Import (register an EXISTING cloud resource):
|
|
|
2540
2540
|
Producing to a topic also calls ensureTopicRegistered in the background — but DO NOT rely on
|
|
2541
2541
|
auto-registration for consume paths. Always create topics explicitly.
|
|
2542
2542
|
|
|
2543
|
-
IMPORTANT:
|
|
2543
|
+
IMPORTANT: events.topics.create requires an access key (admin operation).
|
|
2544
2544
|
Use ductape_cli — NOT ductape_execute — to create topics.
|
|
2545
2545
|
|
|
2546
2546
|
Write a topic.json file, then:
|
|
@@ -2567,7 +2567,7 @@ Import (register an EXISTING cloud resource):
|
|
|
2567
2567
|
ductape_cli("events topics delete --tag order-events:order-created")
|
|
2568
2568
|
|
|
2569
2569
|
Read-only fetches (safe with publishable key via ductape_execute):
|
|
2570
|
-
ductape_execute("
|
|
2570
|
+
ductape_execute("events.fetch", [product_tag, "broker-tag"]) → includes topics[]
|
|
2571
2571
|
|
|
2572
2572
|
━━━ STEP 3: PRODUCE — WRITTEN IN APPLICATION CODE ━━━
|
|
2573
2573
|
|
|
@@ -2578,7 +2578,12 @@ Import (register an EXISTING cloud resource):
|
|
|
2578
2578
|
|
|
2579
2579
|
GENERAL BACKEND (TypeScript/Node.js — not NestJS):
|
|
2580
2580
|
import Ductape from '@ductape/sdk';
|
|
2581
|
-
|
|
2581
|
+
// produce() does not need redis_url.
|
|
2582
|
+
// dispatch() requires redis_url in the Ductape initialization options — it throws at runtime without it.
|
|
2583
|
+
const ductape = new Ductape({
|
|
2584
|
+
accessKey: process.env.DUCTAPE_ACCESS_KEY,
|
|
2585
|
+
redis_url: process.env.DUCTAPE_REDIS_URL, // required for any dispatch(); omit only if never dispatching
|
|
2586
|
+
});
|
|
2582
2587
|
await ductape.events.produce({
|
|
2583
2588
|
product: "my-product",
|
|
2584
2589
|
env: "prd",
|
|
@@ -2593,14 +2598,26 @@ Import (register an EXISTING cloud resource):
|
|
|
2593
2598
|
idempotencyTtl?: 86400, // seconds; default 86400 (24h)
|
|
2594
2599
|
});
|
|
2595
2600
|
|
|
2596
|
-
NESTJS — method decorators:
|
|
2601
|
+
NESTJS — initialization + method decorators:
|
|
2602
|
+
// AppModule — redisUrl is required whenever any *.dispatch() is used:
|
|
2603
|
+
DuctapeModule.forRootAsync({
|
|
2604
|
+
useFactory: () => ({
|
|
2605
|
+
accessKey: process.env.DUCTAPE_ACCESS_KEY,
|
|
2606
|
+
product: 'my-product',
|
|
2607
|
+
env: process.env.NODE_ENV === 'production' ? 'prd' : 'snd',
|
|
2608
|
+
redisUrl: process.env.DUCTAPE_REDIS_URL, // required — dispatch() throws without this
|
|
2609
|
+
}),
|
|
2610
|
+
});
|
|
2611
|
+
// Environment: DUCTAPE_REDIS_URL=redis://localhost:6379 (local) or rediss://:<pw>@host:6380 (managed)
|
|
2612
|
+
// produce() and @Events.Consumer do NOT need DUCTAPE_REDIS_URL — only dispatch() does.
|
|
2613
|
+
|
|
2597
2614
|
import { Events } from '@ductape/nestjs';
|
|
2598
2615
|
@Injectable() export class OrdersService {
|
|
2599
2616
|
// Immediate produce — method returns the message payload:
|
|
2600
2617
|
@Events.Produce({ event: 'order-events:order-created' })
|
|
2601
2618
|
emitOrderCreated(payload: { orderId: string; total: number }) { return payload; }
|
|
2602
2619
|
|
|
2603
|
-
// Dispatch with static schedule
|
|
2620
|
+
// Dispatch with static schedule — requires redisUrl in DuctapeModule initialization:
|
|
2604
2621
|
@Events.Dispatch({ broker: 'order-events', event: 'order-events:reminder-due',
|
|
2605
2622
|
schedule: { every: 86400000 } })
|
|
2606
2623
|
scheduleReminder(payload: { message: { orderId: string } }) { return payload; }
|
|
@@ -2633,7 +2650,7 @@ Import (register an EXISTING cloud resource):
|
|
|
2633
2650
|
});
|
|
2634
2651
|
|
|
2635
2652
|
SCHEDULED DISPATCH (background job):
|
|
2636
|
-
ductape_execute("
|
|
2653
|
+
ductape_execute("events.dispatch", [{
|
|
2637
2654
|
product, env,
|
|
2638
2655
|
broker: "order-events", // broker tag
|
|
2639
2656
|
event: "order-events:reminder-due", // "broker:topic"
|
|
@@ -2704,24 +2721,24 @@ Import (register an EXISTING cloud resource):
|
|
|
2704
2721
|
|
|
2705
2722
|
DEAD-LETTER QUEUE (DLQ):
|
|
2706
2723
|
Messages whose callbacks consistently throw are automatically moved to the DLQ.
|
|
2707
|
-
Query: ductape_execute("
|
|
2724
|
+
Query: ductape_execute("events.messages.getDeadLetters",
|
|
2708
2725
|
[{ product, env, brokerTag, topicTag?, consumerTag?, limit? }])
|
|
2709
|
-
Reprocess: ductape_execute("
|
|
2726
|
+
Reprocess: ductape_execute("events.reprocessDLQ",
|
|
2710
2727
|
[{ product, env, brokerTag, topicTag?, messageIds?, limit? }])
|
|
2711
|
-
Replay: ductape_execute("
|
|
2728
|
+
Replay: ductape_execute("events.replayEvent",
|
|
2712
2729
|
[{ product, env, eventId, force? }])
|
|
2713
2730
|
|
|
2714
2731
|
━━━ OBSERVABILITY ━━━
|
|
2715
2732
|
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2733
|
+
events.messages.query [{ product, env, brokerTag, topicTag?, status?, page?, limit? }]
|
|
2734
|
+
events.messages.getStats [{ product, env, brokerTag }]
|
|
2735
|
+
events.messages.getDashboard [{ product, env, brokerTag }]
|
|
2736
|
+
events.messages.getDeadLetters [{ product, env, brokerTag, topicTag?, consumerTag?, limit? }]
|
|
2737
|
+
events.messages.getProducers [{ product, env, brokerTag, topicTag?, page?, limit? }]
|
|
2738
|
+
events.messages.getConsumers [{ product, env, brokerTag, topicTag?, page?, limit? }]
|
|
2739
|
+
events.replayEvent [{ product, env, eventId, force? }]
|
|
2740
|
+
events.reprocessDLQ [{ product, env, brokerTag, topicTag?, messageIds?, limit? }]
|
|
2741
|
+
events.checkIdempotency [{ product, env, brokerTag, idempotency_key }]
|
|
2725
2742
|
`.trim(),
|
|
2726
2743
|
|
|
2727
2744
|
logs: `
|
|
@@ -3475,7 +3492,9 @@ async function main() {
|
|
|
3475
3492
|
if (!key) {
|
|
3476
3493
|
throw new Error('Not authenticated. Set DUCTAPE_PUBLISHABLE_KEY in your MCP server env config, or pass publishable_key on every tool call.');
|
|
3477
3494
|
}
|
|
3478
|
-
|
|
3495
|
+
// The TS SDK uses ductape.events.* for broker operations; the backend proxy uses messageBrokers.
|
|
3496
|
+
const proxyModule: SDKModule = args.module === 'events' ? 'messageBrokers' : args.module;
|
|
3497
|
+
const result = await executeViaProxy(key, proxyModule, args.method, args.params);
|
|
3479
3498
|
return { content: [{ type: 'text', text: JSON.stringify(result ?? null, null, 2) }] };
|
|
3480
3499
|
} catch (err) {
|
|
3481
3500
|
const message = err instanceof Error ? err.message : String(err);
|