@ductape/mcp 0.2.33 → 0.2.34

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.
Files changed (2) hide show
  1. package/dist/index.js +75 -9
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -311,6 +311,23 @@ When the target application is a NestJS service or controller, always use @ducta
311
311
  instead of instantiating @ductape/sdk directly. It provides NestJS DI integration,
312
312
  global interceptors, decorators, and type-safe resource handles.
313
313
 
314
+ PRODUCT RUNTIME SNAPSHOT — REQUIRED FOR SERVER INITIALIZATION:
315
+ Always provide both product and env at module initialization. Nest awaits the SDK's initial
316
+ product runtime snapshot during onModuleInit, before the application accepts traffic. The
317
+ snapshot preloads connected App versions/actions plus database actions/schema metadata, graphs,
318
+ vectors, storage, brokers/topics, sessions, notifications, resilience assets, agents, functions,
319
+ and caches into the execution bootstrap cache. External provider/database connections remain
320
+ lazy and pooled.
321
+
322
+ Runtime synchronization is enabled by default when product + env are present. Do not set
323
+ runtimeSync:false merely to simplify generated code. Configure it only when the user has an
324
+ explicit operational reason:
325
+ runtimeSync: { intervalMs: 30_000, jitter: 0.2, maxBackoffMs: 300_000 }
326
+
327
+ Polling uses runtimeRevision + HTTP ETag/304. databaseRevision is a refresh hint included in the
328
+ umbrella runtimeRevision, so schema and Database Action changes are detected. Failed refreshes
329
+ retain the last-known-good snapshot. Nest stops polling through onModuleDestroy automatically.
330
+
314
331
  SETUP — register once in AppModule:
315
332
 
316
333
  ╔══════════════════════════════════════════════════════════════════════════╗
@@ -338,6 +355,7 @@ SETUP — register once in AppModule:
338
355
  product: 'my-product',
339
356
  env: process.env.NODE_ENV === 'production' ? 'prd' : 'snd',
340
357
  redisUrl: process.env.DUCTAPE_REDIS_URL, // required — no dispatch() works without this
358
+ runtimeSync: { intervalMs: 30_000, jitter: 0.2 },
341
359
  }),
342
360
  ],
343
361
  })
@@ -352,6 +370,7 @@ SETUP — register once in AppModule:
352
370
  product: cfg.get('DUCTAPE_PRODUCT'),
353
371
  env: cfg.get('DUCTAPE_ENV'),
354
372
  redisUrl: cfg.get('DUCTAPE_REDIS_URL'), // required — no dispatch() works without this
373
+ runtimeSync: { intervalMs: 30_000, jitter: 0.2 },
355
374
  }),
356
375
  })
357
376
 
@@ -1312,18 +1331,24 @@ function buildTypeScriptSnippet(payload, operationFamily, method) {
1312
1331
  const invocationArgs = buildSdkInvocationArgs(payload);
1313
1332
  return `import Ductape from "@ductape/sdk";
1314
1333
 
1334
+ const payload = ${toPrettyJson(payload)};
1315
1335
  const ductape = new Ductape({
1316
- workspace_id: process.env.DUCTAPE_WORKSPACE_ID!,
1317
- user_id: process.env.DUCTAPE_USER_ID!,
1318
- public_key: process.env.DUCTAPE_PUBLIC_KEY!,
1336
+ accessKey: process.env.DUCTAPE_ACCESS_KEY!,
1337
+ product: String(payload.product),
1338
+ env: String(payload.env),
1319
1339
  redis_url: process.env.DUCTAPE_REDIS_URL,
1340
+ runtime_sync: { interval_ms: 30_000, jitter: 0.2 },
1320
1341
  });
1321
1342
 
1322
1343
  async function run() {
1323
- const payload = ${toPrettyJson(payload)};
1344
+ await ductape.ready();
1324
1345
  const args = ${toPrettyJson(invocationArgs)};
1325
- const result = await ductape.${callPath}(args);
1326
- return { payload, result };
1346
+ try {
1347
+ const result = await ductape.${callPath}(args);
1348
+ return { payload, result };
1349
+ } finally {
1350
+ await ductape.close();
1351
+ }
1327
1352
  }
1328
1353
 
1329
1354
  run().catch(console.error);
@@ -1598,7 +1623,7 @@ function shellArgument(value) {
1598
1623
  }
1599
1624
  const docsInputSchema = z.object({
1600
1625
  topic: z.string().describe('Feature topic to look up. Supported: ' +
1601
- 'transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
1626
+ 'transactions, presave, triggers, aggregations, migrations, indexes, performance, runtime-sync, actions, ' +
1602
1627
  'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
1603
1628
  'notifications, resilience, features, portable-functions, events, logs, migration, cli-authentication, frontend, frontend-analytics, client, react, vue'),
1604
1629
  });
@@ -2509,6 +2534,39 @@ DUCTAPE DATABASE PERFORMANCE GUIDANCE
2509
2534
  5. Caching
2510
2535
  - For read-heavy, rarely-changing data use caches.get before databases.query.
2511
2536
  - Invalidate cache keys in an afterWrite trigger (see ductape_docs({ topic: "triggers" })).
2537
+ `.trim(),
2538
+ 'runtime-sync': `
2539
+ DUCTAPE PRODUCT RUNTIME SNAPSHOTS
2540
+
2541
+ For every long-lived TypeScript/Node backend, initialize @ductape/sdk with accessKey, product,
2542
+ and env, then await ready() before accepting work:
2543
+
2544
+ const ductape = new Ductape({
2545
+ accessKey: process.env.DUCTAPE_ACCESS_KEY!,
2546
+ product: "payments",
2547
+ env: "prd",
2548
+ runtime_sync: { interval_ms: 30_000, jitter: 0.2, max_backoff_ms: 300_000 },
2549
+ });
2550
+ await ductape.ready();
2551
+
2552
+ This fetches one environment-scoped product snapshot and primes the existing bootstrap cache with
2553
+ connected App versions/actions, databases/actions/table schemas, graphs, vectors, storage,
2554
+ brokers/topics, sessions, notifications, resilience assets, agents, functions, and caches.
2555
+ Connections remain lazy and pooled; snapshot bootstrap does not call external providers.
2556
+
2557
+ Runtime synchronization is automatic when product + env are supplied:
2558
+ - Conditional polling sends If-None-Match with runtimeRevision; unchanged state returns HTTP 304.
2559
+ - runtimeRevision is authoritative. databaseRevision, connectionRevision, secretRevision, and
2560
+ assetRevision are narrower change hints. Any databaseRevision change also changes runtimeRevision.
2561
+ - A changed snapshot is loaded off-path and atomically replaces cache entries. Deleted assets are removed.
2562
+ - Refresh failure retains the last-known-good snapshot and retries with jittered exponential backoff.
2563
+ - Use runtimeSnapshotStatus() for revision/error telemetry and refreshRuntime() for an explicit pull.
2564
+ - Call close() during process shutdown. Do not create one Ductape instance per request.
2565
+
2566
+ For NestJS, use DuctapeModule.forIntegration({ accessKey, product, env, runtimeSync }) instead of
2567
+ new Ductape(). @ductape/nestjs awaits ready() in onModuleInit and closes the poller in
2568
+ onModuleDestroy. Never omit product/env when the service has stable defaults: doing so disables
2569
+ product snapshot bootstrap and returns execution to lazy per-asset bootstrap misses.
2512
2570
  `.trim(),
2513
2571
  actions: `
2514
2572
  DUCTAPE DATABASE ACTIONS
@@ -3202,8 +3260,11 @@ Product structure (IProduct fields):
3202
3260
  workflows[] (features), models[], agents[], jobs[]
3203
3261
 
3204
3262
  Bootstrap (single API call returning product context + component config + private key):
3205
- Each service makes a single bootstrap call at first use; results are cached in BootstrapCache
3206
- (Redis when available). This avoids repeated round-trips in high-frequency paths.
3263
+ Initialize @ductape/sdk with product + env and await ductape.ready(). It fetches the full
3264
+ environment-scoped product runtime snapshot and primes BootstrapCache for connected App actions
3265
+ and product components. A service falls back to its targeted bootstrap endpoint only when the
3266
+ requested asset is absent. Polling uses ETag/304 and atomically refreshes changed state.
3267
+ See ductape_docs({ topic: "runtime-sync" }).
3207
3268
  `.trim(),
3208
3269
  sessions: `
3209
3270
  DUCTAPE SESSIONS
@@ -4537,8 +4598,12 @@ Import (register an EXISTING cloud resource):
4537
4598
  // dispatch() requires redis_url in the Ductape initialization options — it throws at runtime without it.
4538
4599
  const ductape = new Ductape({
4539
4600
  accessKey: process.env.DUCTAPE_ACCESS_KEY,
4601
+ product: "my-product",
4602
+ env: "prd",
4540
4603
  redis_url: process.env.DUCTAPE_REDIS_URL, // required for any dispatch(); omit only if never dispatching
4604
+ runtime_sync: { interval_ms: 30_000, jitter: 0.2 },
4541
4605
  });
4606
+ await ductape.ready();
4542
4607
  await ductape.events.produce({
4543
4608
  product: "my-product",
4544
4609
  env: "prd",
@@ -4561,6 +4626,7 @@ Import (register an EXISTING cloud resource):
4561
4626
  product: 'my-product',
4562
4627
  env: process.env.NODE_ENV === 'production' ? 'prd' : 'snd',
4563
4628
  redisUrl: process.env.DUCTAPE_REDIS_URL, // required — dispatch() throws without this
4629
+ runtimeSync: { intervalMs: 30_000, jitter: 0.2 },
4564
4630
  }),
4565
4631
  });
4566
4632
  // Environment: DUCTAPE_REDIS_URL=redis://localhost:6379 (local) or rediss://:<pw>@host:6380 (managed)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.2.33",
3
+ "version": "0.2.34",
4
4
  "description": "MCP server that exposes Ductape SDK operations via the backend proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "tsc",
18
- "test": "npm run build && node scripts/check-cli-command-security.mjs && node scripts/check-frontend-analytics-guidance.mjs && node scripts/check-events-discovery.mjs && node scripts/check-schema-fallback.mjs && node scripts/check-paystack-action-schema.mjs && node scripts/check-portable-functions.mjs && node scripts/check-feature-control-flow.mjs && node scripts/check-project-link-guidance.mjs && node scripts/check-graph-vector-projection-guidance.mjs && node scripts/check-asset-file-guidance.mjs && node scripts/check-database-action-contract-guidance.mjs",
18
+ "test": "npm run build && node scripts/check-cli-command-security.mjs && node scripts/check-frontend-analytics-guidance.mjs && node scripts/check-events-discovery.mjs && node scripts/check-schema-fallback.mjs && node scripts/check-paystack-action-schema.mjs && node scripts/check-portable-functions.mjs && node scripts/check-feature-control-flow.mjs && node scripts/check-project-link-guidance.mjs && node scripts/check-graph-vector-projection-guidance.mjs && node scripts/check-asset-file-guidance.mjs && node scripts/check-database-action-contract-guidance.mjs && node scripts/check-runtime-sync-guidance.mjs",
19
19
  "start": "node dist/index.js",
20
20
  "dev": "tsx src/index.ts"
21
21
  },