@ductape/mcp 0.2.17 → 0.2.19

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.
@@ -0,0 +1,2 @@
1
+ export declare function normalizeLiveActionContract(value: unknown): unknown;
2
+ //# sourceMappingURL=action-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-contract.d.ts","sourceRoot":"","sources":["../src/action-contract.ts"],"names":[],"mappings":"AAiFA,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAYnE"}
@@ -0,0 +1,109 @@
1
+ function parseSample(value) {
2
+ if (typeof value !== 'string')
3
+ return value;
4
+ try {
5
+ return JSON.parse(value);
6
+ }
7
+ catch {
8
+ return value;
9
+ }
10
+ }
11
+ function legacyPairs(value) {
12
+ const parsed = parseSample(value);
13
+ if (!Array.isArray(parsed))
14
+ return null;
15
+ const result = {};
16
+ for (const item of parsed) {
17
+ if (!item || typeof item !== 'object')
18
+ continue;
19
+ const key = String(item.key ?? '').trim();
20
+ if (key)
21
+ result[key] = item.value;
22
+ }
23
+ return Object.keys(result).length ? result : null;
24
+ }
25
+ function schemaType(value) {
26
+ const type = String(value ?? '').toLowerCase();
27
+ if (type.includes('bool'))
28
+ return 'boolean';
29
+ if (type.includes('int') || type.includes('number') || type.includes('float') || type.includes('double'))
30
+ return 'number';
31
+ if (type.includes('array'))
32
+ return 'array';
33
+ if (type.includes('object') || type === 'json')
34
+ return 'object';
35
+ if (type.includes('string') || type.includes('email') || type.includes('date') || type.includes('uuid') || type.includes('space'))
36
+ return 'string';
37
+ return undefined;
38
+ }
39
+ function schemaFromLocation(value) {
40
+ const location = value && typeof value === 'object' ? value : {};
41
+ const parsedSample = parseSample(location.sample);
42
+ const legacyRows = Array.isArray(parsedSample) ? parsedSample : [];
43
+ const sample = legacyPairs(location.sample) ?? parsedSample;
44
+ const rows = Array.isArray(location.data) ? location.data : [];
45
+ const properties = {};
46
+ const required = [];
47
+ for (const row of rows) {
48
+ if (!row || typeof row !== 'object')
49
+ continue;
50
+ const item = row;
51
+ if (Number(item.level ?? 0) > 0 || String(item.parent_key ?? '').trim())
52
+ continue;
53
+ const key = String(item.key ?? item.name ?? '').trim();
54
+ if (!key)
55
+ continue;
56
+ const property = {};
57
+ const type = schemaType(item.type);
58
+ if (type)
59
+ property.type = type;
60
+ if (item.description)
61
+ property.description = String(item.description);
62
+ if (item.sampleValue !== undefined && item.sampleValue !== '')
63
+ property.example = item.sampleValue;
64
+ properties[key] = property;
65
+ if (item.required === true)
66
+ required.push(key);
67
+ }
68
+ for (const row of legacyRows) {
69
+ if (!row || typeof row !== 'object')
70
+ continue;
71
+ const item = row;
72
+ const key = String(item.key ?? '').trim();
73
+ if (!key || properties[key])
74
+ continue;
75
+ const description = String(item.description ?? '').trim();
76
+ properties[key] = {
77
+ type: 'string',
78
+ ...(description ? { description } : {}),
79
+ ...(item.value !== undefined ? { example: item.value } : {}),
80
+ };
81
+ if (/\(required\)|^required\b/i.test(description))
82
+ required.push(key);
83
+ }
84
+ if (!Object.keys(properties).length && sample && typeof sample === 'object' && !Array.isArray(sample)) {
85
+ for (const [key, example] of Object.entries(sample)) {
86
+ properties[key] = { type: Array.isArray(example) ? 'array' : example === null ? undefined : typeof example, example };
87
+ }
88
+ }
89
+ return {
90
+ type: 'object',
91
+ properties,
92
+ ...(required.length ? { required } : {}),
93
+ additionalProperties: false,
94
+ };
95
+ }
96
+ export function normalizeLiveActionContract(value) {
97
+ if (!value || typeof value !== 'object')
98
+ return value;
99
+ const action = value;
100
+ return {
101
+ ...action,
102
+ input_schema: {
103
+ body: schemaFromLocation(action.body),
104
+ query: schemaFromLocation(action.query),
105
+ params: schemaFromLocation(action.params),
106
+ headers: schemaFromLocation(action.headers),
107
+ },
108
+ };
109
+ }
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import { parseCliCommand } from './cli-command.js';
15
15
  import { homedir } from 'os';
16
16
  import { delimiter, join } from 'path';
17
17
  import { z } from 'zod';
18
+ import { normalizeLiveActionContract } from './action-contract.js';
18
19
  import { executeViaProxy, generateExecutablePayload, getAssetSchemas, } from './proxy-client.js';
19
20
  import { EVENTS_DELIVERY_SEMANTICS, EVENTS_IMPLEMENTATION_WARNING, eventsCapabilityOverview, inspectEventsComponent, searchEventsCapabilities, } from './events-capabilities.js';
20
21
  const MODULES = [
@@ -596,7 +597,7 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
596
597
  fallback.dispatch [{ product: string, env: string, tag: string, input: object, session?: string, cache?: string, schedule?: { cron?: string, delay?: number, at?: string } }] ← CALL ductape_generate_payload FIRST (operation_family="fallback", method="dispatch") — requires redisUrl in ductape initialization
597
598
 
598
599
  ━━━ MODULE: health ━━━
599
- health.create [product_tag, data: { tag: string, name: string, description?: string, app?: string, event?: string, probe?: { type: "app"|"database"|"feature", app?: string, event?: string, input?: object }, interval: number, retries: number, envs: [{ slug: string, input?: object }], onFailure?: { notifications?: [{ notification: string, message: string, channels: { email?: { recipients: string[] }, push?: { recipients?: string[] }, sms?: { recipients: string[] } } }], webhooks?: [{ url: string, method?: "GET"|"POST", headers?: object, body?: object }] } }]
600
+ health.create [product_tag, data: { tag: string, name: string, description?: string, probe: { type: "app"|"database"|"feature"|"graph"|"events"|"storage"|"cache"|"vector"|"notification", app?: string, database?: string, feature?: string, graph?: string, events?: string, storage?: string, cache?: string, vector?: string, notification?: string, channels?: ("sms"|"email"|"push")[], event?: string, input?: object }, interval: number, retries: number, envs: [{ slug: string, input?: object }], onFailure?: object }]
600
601
  health.update [product_tag, health_tag, data: { name?: string, description?: string, app?: string, event?: string, probe?: { type: "app"|"database"|"feature", app?: string, event?: string, input?: object }, interval?: number, retries?: number, envs?: [{ slug: string, input?: object }], onFailure?: object }]
601
602
  health.fetch [product_tag, health_tag]
602
603
  health.list [product_tag]
@@ -3134,7 +3135,7 @@ CONFIGURATION BOUNDARY
3134
3135
  Workbench is also supported. Never route their administrative create/update methods through
3135
3136
  ductape_execute: its publishable-key runtime proxy will fail.
3136
3137
 
3137
- QUOTAS — rate-limited multi-provider pools:
3138
+ QUOTAS — weighted/provider-capacity routing pools (NOT request rate limiting):
3138
3139
  Workbench definition shape:
3139
3140
  {
3140
3141
  tag: "sms-quota",
@@ -3152,6 +3153,8 @@ QUOTAS — rate-limited multi-provider pools:
3152
3153
  ],
3153
3154
  }
3154
3155
  Providers are tried in order until quota is not exhausted.
3156
+ Never use quota to protect OTP, login, or public endpoints. Use sdk.rateLimit.define/run and the
3157
+ Nest @RateLimit + DuctapeRateLimitGuard for atomic keyed request enforcement.
3155
3158
  quotas.run [{ product, env, tag, input }] ← CALL ductape_generate_payload FIRST
3156
3159
  quotas.dispatch [{ product, env, tag, input, schedule? }]
3157
3160
 
@@ -3188,7 +3191,13 @@ HEALTHCHECKS — continuous probe with failure notifications:
3188
3191
  health.check [{ product, env, tag }] → same as run
3189
3192
  health.status [{ product, env, tag }] → current health status
3190
3193
 
3191
- Probe types: app | database | feature | graph | events | storage
3194
+ Probe types: app | database | feature | graph | events | storage | cache | vector | notification
3195
+ Code-first connectivity probes:
3196
+ ctx.probe().cache("cache-tag").action("test_connection")
3197
+ ctx.probe().vector("vector-tag").action("test_connection")
3198
+ ctx.probe().notification("notification-tag").channels(["sms","email","push"]).action("test_connection")
3199
+ Notification test_connection validates configuration and uses non-delivering credential/account
3200
+ checks for SendGrid, Vonage/Nexmo, and Firebase. It never sends a message.
3192
3201
  Canonical Events probe:
3193
3202
  { type: "events", events: "statecraft-events", event: "health" }
3194
3203
  Do not create new probes with type/field message_broker or messageBroker; those names are deprecated
@@ -3225,6 +3234,7 @@ Provider status: available | unavailable
3225
3234
 
3226
3235
  DECISION MATRIX
3227
3236
  Rate/capacity allocation across providers → quota
3237
+ Request frequency keyed by actor/IP/device → rateLimit
3228
3238
  Equivalent provider after operational failure → fallback
3229
3239
  Detect failure before routing provider traffic → health check
3230
3240
  Transient failure of one operation → bounded retry + idempotency policy
@@ -5439,7 +5449,7 @@ async function main() {
5439
5449
  const raw = response.content[0]?.type === 'text' ? response.content[0].text : '';
5440
5450
  let action = raw;
5441
5451
  try {
5442
- action = JSON.parse(raw);
5452
+ action = normalizeLiveActionContract(JSON.parse(raw));
5443
5453
  }
5444
5454
  catch { /* Preserve the CLI diagnostic verbatim. */ }
5445
5455
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
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-portable-functions.mjs && node scripts/check-project-link-guidance.mjs && node scripts/check-graph-vector-projection-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-project-link-guidance.mjs && node scripts/check-graph-vector-projection-guidance.mjs",
19
19
  "start": "node dist/index.js",
20
20
  "dev": "tsx src/index.ts"
21
21
  },