@ductape/mcp 0.2.31 → 0.2.32
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 +104 -13
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -945,6 +945,9 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
945
945
|
// ctx.input – typed runtime input; always compiles to $Input{} operators
|
|
946
946
|
// ctx.sampleInput – compile-time sample for loop/branch discovery only
|
|
947
947
|
// ctx.step(tag, fn, rollback?, opts?) – define a durable step
|
|
948
|
+
// ctx.when.eq/ne/gt/gte/lt/lte/truthy/falsy(...) – build portable runtime conditions
|
|
949
|
+
// ctx.branch(condition, { then, else? }) – record both paths as conditioned steps
|
|
950
|
+
// ctx.each(ctx.sampleInput.items, async (item, index) => ...) – deterministic compile-time expansion
|
|
948
951
|
// ctx.api.run({ app, action, input }) – call an app action (NOT 'event' -- that field name
|
|
949
952
|
// only applies to ctx.database/ctx.notification/ctx.storage below, never ctx.api/ctx.action)
|
|
950
953
|
// ctx.database.execute({ database, action, input }) for a saved database action
|
|
@@ -3373,6 +3376,20 @@ CONFIGURATION BOUNDARY
|
|
|
3373
3376
|
Workbench is also supported. Never route their administrative create/update methods through
|
|
3374
3377
|
ductape_execute: its publishable-key runtime proxy will fail.
|
|
3375
3378
|
|
|
3379
|
+
CLI COMMANDS (aliases are normalized; use these exact shapes):
|
|
3380
|
+
ductape resources feature list --product <product> --json
|
|
3381
|
+
ductape resources feature get -t <feature> --product <product> --json
|
|
3382
|
+
ductape resources quota list --product <product> --json
|
|
3383
|
+
ductape resources quota get -t <quota> --product <product> --json
|
|
3384
|
+
ductape resources fallback list --product <product> --json
|
|
3385
|
+
ductape resources fallback get -t <fallback> --product <product> --json
|
|
3386
|
+
ductape resources health list --product <product> --json
|
|
3387
|
+
ductape resources health get -t <healthcheck> --product <product> --json
|
|
3388
|
+
Create/update use the same resource type plus -f <asset.json>. Singular/plural feature(s),
|
|
3389
|
+
fallback(s), and healthcheck(s) aliases are accepted, but canonical generated commands should
|
|
3390
|
+
use feature, quota, fallback, and health. These commands are administrative catalogue CRUD;
|
|
3391
|
+
runtime run/execute operations belong to the SDK or authenticated execution surface.
|
|
3392
|
+
|
|
3376
3393
|
QUOTAS — weighted/provider-capacity routing pools (NOT request rate limiting):
|
|
3377
3394
|
Workbench definition shape:
|
|
3378
3395
|
{
|
|
@@ -3929,15 +3946,83 @@ STEP 6 — WRITE the feature into the project codebase
|
|
|
3929
3946
|
script does, invoked only through the explicit CLI command.
|
|
3930
3947
|
|
|
3931
3948
|
STEP 7 — HANDLE conditionals, loops, and branching (when applicable)
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3949
|
+
MANDATORY CONTROL-FLOW RULES:
|
|
3950
|
+
1. A stored Feature executes its compiled steps. The JavaScript handler is NOT rerun at runtime.
|
|
3951
|
+
2. Never use if/else, switch, ?:, ??, &&, ||, optional chaining, map, filter, find, some,
|
|
3952
|
+
every, reduce, for, for..of, forEach, or while on a ctx.step() result or ctx.input proxy.
|
|
3953
|
+
JavaScript would evaluate the recording proxy, not the future runtime value.
|
|
3954
|
+
3. Use ctx.branch(ctx.when.*, { then, else }) for runtime decisions.
|
|
3955
|
+
4. Use ctx.each only with a concrete ctx.sampleInput array to expand a fixed step graph.
|
|
3956
|
+
5. Put runtime-sized collection algorithms and arbitrary business logic in a registered
|
|
3957
|
+
portable Function, then invoke it through ctx.functions inside a step.
|
|
3958
|
+
6. Direct Date.now() and Math.random() are forbidden in handlers. Use ctx.transform.now(),
|
|
3959
|
+
ctx.transform.uuid(), and ctx.transform.concat/replace/substring/upper/lower/trim so values
|
|
3960
|
+
are generated at execution time. Use a portable Function for domain-specific generators.
|
|
3961
|
+
7. Never use branchOverrides in newly generated code. It exists only to migrate old handlers.
|
|
3962
|
+
|
|
3963
|
+
Prefer explicit portable branching for step results:
|
|
3964
|
+
const result = await ctx.step("find", () => ctx.database.execute(...));
|
|
3965
|
+
await ctx.branch(ctx.when.eq(result.count, 0), {
|
|
3966
|
+
then: () => ctx.step("create", () => ctx.database.execute(...)),
|
|
3967
|
+
else: () => ctx.step("reuse", () => ctx.database.execute(...)),
|
|
3968
|
+
});
|
|
3969
|
+
→ ctx.when also supports ne, gt, gte, lt, lte, truthy, falsy, and/or.
|
|
3970
|
+
→ Do not use ordinary if/else, ternary, or ?? directly on an unresolved step result.
|
|
3971
|
+
→ branchOverrides remains a compatibility escape hatch for older Feature source.
|
|
3972
|
+
→ If both paths must feed a later common step, do not assume ctx.branch returns a selected
|
|
3973
|
+
value. Either record the downstream operation inside each path, persist a shared result that
|
|
3974
|
+
the later step can read, or move the find-or-create algorithm into one portable Function.
|
|
3935
3975
|
Loop over input array:
|
|
3936
|
-
→ Add recordInput: { items: [{ id: "1" }, { id: "2" }] } and
|
|
3976
|
+
→ Add recordInput: { items: [{ id: "1" }, { id: "2" }] } and call
|
|
3977
|
+
ctx.each(ctx.sampleInput.items, async (item, index) => { ... })
|
|
3937
3978
|
→ Use unique step tags per iteration (e.g. "process-" + item.id)
|
|
3979
|
+
→ map/filter/find/indexing on ctx.sampleInput are compile-time graph discovery only.
|
|
3980
|
+
→ Runtime-sized map/filter/find/some/every and arbitrary loops belong in a registered portable
|
|
3981
|
+
Function invoked through ctx.functions; never guess or freeze their results during recording.
|
|
3938
3982
|
Switch/if-else on feature input values:
|
|
3939
|
-
→
|
|
3940
|
-
→
|
|
3983
|
+
→ Prefer ctx.branch(ctx.when.eq(ctx.input.type, "a"), { then, else }).
|
|
3984
|
+
→ recordScenarios is for legacy compile-time graph discovery, not a runtime branch primitive.
|
|
3985
|
+
|
|
3986
|
+
SAFE COPY-READY PATTERNS:
|
|
3987
|
+
Runtime input branch:
|
|
3988
|
+
await ctx.branch(ctx.when.eq(ctx.input.kind, "refund"), {
|
|
3989
|
+
then: () => ctx.step("refund", () => ctx.api.run({ app, action: "refund", input: {...} })),
|
|
3990
|
+
else: () => ctx.step("charge", () => ctx.api.run({ app, action: "charge", input: {...} })),
|
|
3991
|
+
});
|
|
3992
|
+
Nested/indexed result check:
|
|
3993
|
+
await ctx.branch(ctx.when.eq(search.data.length, 0), { then: ..., else: ... });
|
|
3994
|
+
Compound condition:
|
|
3995
|
+
await ctx.branch(ctx.when.and(
|
|
3996
|
+
ctx.when.eq(result.status, "ready"),
|
|
3997
|
+
ctx.when.gt(result.count, 0),
|
|
3998
|
+
), { then: ..., else: ... });
|
|
3999
|
+
Runtime payment/reference string (never frozen at sync time):
|
|
4000
|
+
const reference = ctx.transform.concat(
|
|
4001
|
+
"pay_", ctx.transform.now(), "_", ctx.transform.uuid(),
|
|
4002
|
+
);
|
|
4003
|
+
await ctx.step("charge", () => ctx.fallback.execute({
|
|
4004
|
+
fallback: "charge-payment", input: { reference, ... },
|
|
4005
|
+
}));
|
|
4006
|
+
Fixed graph expansion:
|
|
4007
|
+
recordInput: { regions: ["ng", "gh"] },
|
|
4008
|
+
handler: async (ctx) => ctx.each(ctx.sampleInput.regions, async (region, index) => {
|
|
4009
|
+
await ctx.step("sync-region-" + index, () => ctx.functions.invoke(syncFn, "run", { region }));
|
|
4010
|
+
});
|
|
4011
|
+
|
|
4012
|
+
UNSAFE — NEVER GENERATE:
|
|
4013
|
+
if (result.count === 0) await ctx.step(...);
|
|
4014
|
+
const customer = found[0] ?? await ctx.step(...);
|
|
4015
|
+
ctx.input.items.map(item => ctx.step(...));
|
|
4016
|
+
for (const item of ctx.input.items) await ctx.step(...);
|
|
4017
|
+
const reference = "pay_" + Date.now() + "_" + Math.random();
|
|
4018
|
+
|
|
4019
|
+
BEFORE SYNCING A FEATURE:
|
|
4020
|
+
→ Confirm every runtime decision uses ctx.branch + ctx.when.
|
|
4021
|
+
→ Confirm every ctx.step tag is unique across both paths and every expanded iteration.
|
|
4022
|
+
→ Confirm no runtime proxy is consumed by native JavaScript control flow or array iteration.
|
|
4023
|
+
→ Confirm every ctx.step contains a portable ctx component/function call.
|
|
4024
|
+
→ Compile locally, inspect schema.steps, and verify both branch paths, condition, depends_on,
|
|
4025
|
+
and operator-valued inputs are present before persisting.
|
|
3941
4026
|
|
|
3942
4027
|
STEP 8 — SET rollbacks for reversible steps
|
|
3943
4028
|
Any step that allocates a resource should undo it if a later step fails.
|
|
@@ -4047,16 +4132,18 @@ Feature statuses: pending | running | completed | failed | rolled_back | rolling
|
|
|
4047
4132
|
|
|
4048
4133
|
━━━ FEATURE RECORDING SEMANTICS ━━━
|
|
4049
4134
|
|
|
4050
|
-
When you call features.define({ handler }), the handler
|
|
4135
|
+
When you call features.define({ handler }), there are two distinct phases, but the handler itself
|
|
4136
|
+
runs only during compilation/recording:
|
|
4051
4137
|
|
|
4052
4138
|
1. RECORDING PHASE (at define time) — handler is called with a RecordingContext.
|
|
4053
4139
|
All ctx.step() calls return lightweight proxy objects, not real data.
|
|
4054
4140
|
This phase captures the step graph: which steps exist, their types, tags, and declared
|
|
4055
4141
|
inputs/outputs. No real API calls, DB queries, or side effects occur.
|
|
4056
4142
|
Arbitrary JS code OUTSIDE ctx.step() ALSO runs during recording — with proxy values.
|
|
4057
|
-
For loops: supply recordInput and
|
|
4143
|
+
For loops: supply recordInput and use ctx.each(ctx.sampleInput.items, ...) so all iterations are recorded.
|
|
4058
4144
|
ctx.input is always the runtime operator surface and must never expose recordInput literals.
|
|
4059
|
-
For branches: use
|
|
4145
|
+
For branches: use ctx.branch(ctx.when.*, { then, else }) so both paths are captured. Use
|
|
4146
|
+
branchOverrides only for compatibility with existing Feature handlers.
|
|
4060
4147
|
Never make an authorization, validation, tenancy, or other security decision by branching on
|
|
4061
4148
|
ctx.input during recording. Use ctx.sampleInput only to discover graph shape; enforce security
|
|
4062
4149
|
invariants inside a runtime portable Function or recorded step.
|
|
@@ -4066,11 +4153,15 @@ When you call features.define({ handler }), the handler runs TWICE:
|
|
|
4066
4153
|
integrations and project readiness hooks must not start HTTP listeners, provider probes,
|
|
4067
4154
|
schedulers, or Event consumers. Apply the filter before booting unrelated service modules.
|
|
4068
4155
|
|
|
4069
|
-
2. EXECUTION PHASE (at runtime) —
|
|
4070
|
-
|
|
4071
|
-
|
|
4156
|
+
2. EXECUTION PHASE (at runtime) — the stored schema.steps graph is interpreted by FeatureExecutor.
|
|
4157
|
+
The original JavaScript handler is NOT called. Step inputs and conditions resolve operators
|
|
4158
|
+
such as $Input{}, $Sequence{}, $Step{}, and $Now against runtime state. Only recorded portable
|
|
4159
|
+
component/function operations execute.
|
|
4072
4160
|
|
|
4073
|
-
Implication:
|
|
4161
|
+
Implication: arbitrary JavaScript control flow never becomes runtime behavior merely because it
|
|
4162
|
+
appears in the handler. Express runtime branches through ctx.branch/ctx.when, fixed graph expansion
|
|
4163
|
+
through ctx.each(ctx.sampleInput...), and runtime-sized algorithms through ctx.functions. Put all
|
|
4164
|
+
meaningful business logic INSIDE recorded portable operations, not in the
|
|
4074
4165
|
outer handler body. Code in the outer body runs during recording with proxy values and
|
|
4075
4166
|
may behave unexpectedly (e.g. typeof proxy === 'object' is true but .someField is a proxy).
|
|
4076
4167
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ductape/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.32",
|
|
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-project-link-guidance.mjs && node scripts/check-graph-vector-projection-guidance.mjs && node scripts/check-asset-file-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",
|
|
19
19
|
"start": "node dist/index.js",
|
|
20
20
|
"dev": "tsx src/index.ts"
|
|
21
21
|
},
|