@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.
- package/dist/action-contract.d.ts +2 -0
- package/dist/action-contract.d.ts.map +1 -0
- package/dist/action-contract.js +109 -0
- package/dist/index.js +14 -4
- package/package.json +2 -2
|
@@ -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,
|
|
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 —
|
|
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.
|
|
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
|
},
|