@ductape/mcp 0.1.34 → 0.1.36
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 +99 -37
- package/package.json +1 -1
- package/src/index.ts +98 -36
package/dist/index.js
CHANGED
|
@@ -722,7 +722,7 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
722
722
|
handler: async (ctx) => {
|
|
723
723
|
// ctx.input – typed feature input
|
|
724
724
|
// ctx.step(tag, fn, rollback?, opts?) – define a durable step
|
|
725
|
-
// ctx.
|
|
725
|
+
// ctx.api.run({ app, event, input }) – call an app action
|
|
726
726
|
// ctx.database.query/insert/update/delete({ database, event, ... })
|
|
727
727
|
// ctx.graph.execute({ graph, action, input })
|
|
728
728
|
// ctx.notification.send/email/push/sms({ notification, event, ... })
|
|
@@ -2069,34 +2069,109 @@ A feature is an orchestrated workflow of durable steps. Steps can call app actio
|
|
|
2069
2069
|
operations, graph queries, storage uploads, notifications, broker publishes, child features,
|
|
2070
2070
|
quotas, fallbacks, and more. Features support rollback, signals, checkpoints, and sleep.
|
|
2071
2071
|
|
|
2072
|
+
━━━ AI DESIGN WORKFLOW — follow this process every time a user asks you to build or plan a feature ━━━
|
|
2073
|
+
|
|
2074
|
+
ALWAYS use features.define (code-first). NEVER use features.create.
|
|
2075
|
+
The feature handler is real code written into the project's source files — find the project's
|
|
2076
|
+
language and framework, write the feature into an appropriate file in the codebase, and call
|
|
2077
|
+
features.define from there. Do not generate inline snippets and stop — write the actual file.
|
|
2078
|
+
|
|
2079
|
+
STEP 1 — UNDERSTAND the goal
|
|
2080
|
+
Ask clarifying questions if the user's intent is unclear. Do not start designing until you
|
|
2081
|
+
understand: what the feature does, what it returns, what can fail and how failures should behave.
|
|
2082
|
+
|
|
2083
|
+
STEP 2 — INVENTORY existing Ductape components
|
|
2084
|
+
Call ductape_execute("products.fetch", [product_tag]) to read the product.
|
|
2085
|
+
Note what already exists:
|
|
2086
|
+
- databases[] → available for ctx.database.insert/query/update/delete steps
|
|
2087
|
+
- apps[] → available for ctx.api.run steps (check app.events[] for event tags)
|
|
2088
|
+
- notifications[] → available for ctx.notification.email/sms/push steps
|
|
2089
|
+
- storage[] → available for ctx.storage.upload/download steps
|
|
2090
|
+
- messageBrokers[] → available for ctx.messaging.produce steps
|
|
2091
|
+
- graphs[] → available for ctx.graph steps
|
|
2092
|
+
- features[] → can be called as child features via ctx.feature()
|
|
2093
|
+
- caches[], sessions[]
|
|
2094
|
+
Do NOT assume a component or event tag exists — verify from the product before using it.
|
|
2095
|
+
|
|
2096
|
+
STEP 3 — PLAN each step
|
|
2097
|
+
For every logical step:
|
|
2098
|
+
a. Identify which existing component handles it, or flag it as needing creation
|
|
2099
|
+
b. Note how inputs flow: ctx.input fields, or return values from earlier steps (plain JS variables — no special notation needed)
|
|
2100
|
+
c. Decide if a rollback handler is needed (e.g. charge → refund on later failure)
|
|
2101
|
+
d. Decide allow_fail: true for non-critical steps (email, analytics, audit logs)
|
|
2102
|
+
|
|
2103
|
+
STEP 4 — PRESENT the plan and get approval BEFORE writing any code or creating anything
|
|
2104
|
+
Show the user:
|
|
2105
|
+
Feature: <name> (<tag>)
|
|
2106
|
+
Input fields: list them with types
|
|
2107
|
+
Steps (in order):
|
|
2108
|
+
1. <step-tag> — <what it does> — <component> [rollback: yes/no] [allow_fail: yes/no]
|
|
2109
|
+
2. ...
|
|
2110
|
+
Return value: describe what the handler returns
|
|
2111
|
+
Components to create: list anything missing with a short description
|
|
2112
|
+
Ask: "Should I proceed? Shall I create the missing components first?"
|
|
2113
|
+
Wait for confirmation before writing code or calling any create tool.
|
|
2114
|
+
|
|
2115
|
+
STEP 5 — CREATE missing components (only with user approval)
|
|
2116
|
+
Use ductape_execute for any missing databases, apps, database actions, notification events, etc.
|
|
2117
|
+
For a missing database action:
|
|
2118
|
+
ductape_execute("databases.action.create", [product_tag, db_tag, { tag, name, type, query }])
|
|
2119
|
+
For a missing child feature, recursively apply this same workflow.
|
|
2120
|
+
Tell the user what you are about to create before each tool call.
|
|
2121
|
+
|
|
2122
|
+
STEP 6 — WRITE the feature into the project codebase
|
|
2123
|
+
- Locate or create an appropriate file in the project (e.g. src/features/feature-name.ts, features/feature_name.py)
|
|
2124
|
+
- Use features.define with an async handler
|
|
2125
|
+
- Step results are plain variables — just await ctx.step(...) and use the return value in the next step
|
|
2126
|
+
- No special notation needed: const user = await ctx.step('create-user', async () => { ... }); then use user.id directly in the next step
|
|
2127
|
+
- Write rollback handlers inline as the third argument to ctx.step()
|
|
2128
|
+
- Return a plain object as the feature's output
|
|
2129
|
+
|
|
2130
|
+
STEP 7 — HANDLE conditionals, loops, and branching (when applicable)
|
|
2131
|
+
Branch on step result (early return in handler):
|
|
2132
|
+
→ Add branchOverrides: { stepTag: { field: value } } so all branches are recorded
|
|
2133
|
+
→ At runtime the executor evaluates the real result and skips or runs later steps accordingly
|
|
2134
|
+
Loop over input array:
|
|
2135
|
+
→ Add recordInput: { items: [{ id: "1" }, { id: "2" }] } so the loop runs during recording
|
|
2136
|
+
→ Use unique step tags per iteration (e.g. "process-" + item.id)
|
|
2137
|
+
Switch/if-else on feature input values:
|
|
2138
|
+
→ Add recordScenarios: [{ type: "a" }, { type: "b" }] — handler runs once per scenario
|
|
2139
|
+
→ Only the scenario whose input matches runs at execution time
|
|
2140
|
+
|
|
2141
|
+
STEP 8 — SET rollbacks for reversible steps
|
|
2142
|
+
Any step that allocates a resource should undo it if a later step fails:
|
|
2143
|
+
const charge = await ctx.step(
|
|
2144
|
+
'charge',
|
|
2145
|
+
async () => ctx.api.run({ app: 'stripe', event: 'create-charge', input: { amount: ctx.input.amount } }),
|
|
2146
|
+
async (result) => ctx.api.run({ app: 'stripe', event: 'refund', input: { chargeId: result.id } })
|
|
2147
|
+
);
|
|
2148
|
+
|
|
2072
2149
|
Step types: action | database | graph | notification | storage | produce | quota | fallback |
|
|
2073
2150
|
vector | child_feature | sleep | wait_for_signal | checkpoint
|
|
2074
2151
|
|
|
2075
|
-
|
|
2076
|
-
|
|
2152
|
+
Define a feature (write this into the project's source files — do NOT use features.create):
|
|
2153
|
+
// src/features/onboard-user.ts (or the equivalent path/language for the project)
|
|
2154
|
+
await ductape.features.define({
|
|
2077
2155
|
tag: "onboard-user",
|
|
2078
2156
|
name: "Onboard User",
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
},
|
|
2098
|
-
],
|
|
2099
|
-
}])
|
|
2157
|
+
handler: async (ctx) => {
|
|
2158
|
+
const account = await ctx.step("create-account", async () =>
|
|
2159
|
+
ctx.database.insert({ database: "core-db", event: "insert-user",
|
|
2160
|
+
data: { userId: ctx.input.userId, email: ctx.input.email } }),
|
|
2161
|
+
async (result) => ctx.database.delete({ database: "core-db", event: "delete-user",
|
|
2162
|
+
where: { id: result.id } }) // rollback
|
|
2163
|
+
);
|
|
2164
|
+
|
|
2165
|
+
await ctx.step("send-welcome", async () =>
|
|
2166
|
+
ctx.notification.email({ notification: "welcome-email", event: "welcome",
|
|
2167
|
+
recipients: [ctx.input.email], subject: {}, template: {} }),
|
|
2168
|
+
null,
|
|
2169
|
+
{ allow_fail: true }
|
|
2170
|
+
);
|
|
2171
|
+
|
|
2172
|
+
return { userId: account.id };
|
|
2173
|
+
},
|
|
2174
|
+
});
|
|
2100
2175
|
|
|
2101
2176
|
Execute at runtime:
|
|
2102
2177
|
→ CALL ductape_generate_payload FIRST (operation_family="features", method="execute", targets={tag})
|
|
@@ -2122,21 +2197,8 @@ Signals and queries (for long-running features):
|
|
|
2122
2197
|
features.signal [{ product, env, feature_id, signal: "payment-confirmed", payload? }]
|
|
2123
2198
|
features.query [{ product, env, feature_id, query: "current-status", params? }]
|
|
2124
2199
|
|
|
2125
|
-
Step input references:
|
|
2126
|
-
$Input{field} → map from feature's declared input
|
|
2127
|
-
$Step{stepTag}{field} → output field from a prior step
|
|
2128
|
-
$StepOutput{field} → current step's own return value
|
|
2129
|
-
$Concat([...parts], delim) → string interpolation
|
|
2130
|
-
|
|
2131
2200
|
Rollback strategies: reverse_all | reverse_critical | compensate | none
|
|
2132
2201
|
Feature statuses: pending | running | completed | failed | rolled_back | rolling_back | paused
|
|
2133
|
-
|
|
2134
|
-
Code-first (define API — compiles async handler to JSON step schema):
|
|
2135
|
-
features.define({ tag, name, input, handler: async (ctx) => { ... } })
|
|
2136
|
-
ctx provides: ctx.step(), ctx.action.run(), ctx.database.query/insert(),
|
|
2137
|
-
ctx.graph.execute(), ctx.notification.send(), ctx.storage.upload(),
|
|
2138
|
-
ctx.messaging.produce(), ctx.quota.execute(), ctx.fallback.execute(),
|
|
2139
|
-
ctx.sleep(), ctx.waitForSignal(), ctx.setState/getState(), ctx.feature()
|
|
2140
2202
|
`.trim(),
|
|
2141
2203
|
events: `
|
|
2142
2204
|
DUCTAPE EVENTS (MESSAGE BROKERS)
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2141,34 +2141,109 @@ A feature is an orchestrated workflow of durable steps. Steps can call app actio
|
|
|
2141
2141
|
operations, graph queries, storage uploads, notifications, broker publishes, child features,
|
|
2142
2142
|
quotas, fallbacks, and more. Features support rollback, signals, checkpoints, and sleep.
|
|
2143
2143
|
|
|
2144
|
+
━━━ AI DESIGN WORKFLOW — follow this process every time a user asks you to build or plan a feature ━━━
|
|
2145
|
+
|
|
2146
|
+
ALWAYS use features.define (code-first). NEVER use features.create.
|
|
2147
|
+
The feature handler is real code written into the project's source files — find the project's
|
|
2148
|
+
language and framework, write the feature into an appropriate file in the codebase, and call
|
|
2149
|
+
features.define from there. Do not generate inline snippets and stop — write the actual file.
|
|
2150
|
+
|
|
2151
|
+
STEP 1 — UNDERSTAND the goal
|
|
2152
|
+
Ask clarifying questions if the user's intent is unclear. Do not start designing until you
|
|
2153
|
+
understand: what the feature does, what it returns, what can fail and how failures should behave.
|
|
2154
|
+
|
|
2155
|
+
STEP 2 — INVENTORY existing Ductape components
|
|
2156
|
+
Call ductape_execute("products.fetch", [product_tag]) to read the product.
|
|
2157
|
+
Note what already exists:
|
|
2158
|
+
- databases[] → available for ctx.database.insert/query/update/delete steps
|
|
2159
|
+
- apps[] → available for ctx.action.run steps (check app.events[] for event tags)
|
|
2160
|
+
- notifications[] → available for ctx.notification.email/sms/push steps
|
|
2161
|
+
- storage[] → available for ctx.storage.upload/download steps
|
|
2162
|
+
- messageBrokers[] → available for ctx.messaging.produce steps
|
|
2163
|
+
- graphs[] → available for ctx.graph steps
|
|
2164
|
+
- features[] → can be called as child features via ctx.feature()
|
|
2165
|
+
- caches[], sessions[]
|
|
2166
|
+
Do NOT assume a component or event tag exists — verify from the product before using it.
|
|
2167
|
+
|
|
2168
|
+
STEP 3 — PLAN each step
|
|
2169
|
+
For every logical step:
|
|
2170
|
+
a. Identify which existing component handles it, or flag it as needing creation
|
|
2171
|
+
b. Note how inputs flow: ctx.input fields, or return values from earlier steps (plain JS variables — no special notation needed)
|
|
2172
|
+
c. Decide if a rollback handler is needed (e.g. charge → refund on later failure)
|
|
2173
|
+
d. Decide allow_fail: true for non-critical steps (email, analytics, audit logs)
|
|
2174
|
+
|
|
2175
|
+
STEP 4 — PRESENT the plan and get approval BEFORE writing any code or creating anything
|
|
2176
|
+
Show the user:
|
|
2177
|
+
Feature: <name> (<tag>)
|
|
2178
|
+
Input fields: list them with types
|
|
2179
|
+
Steps (in order):
|
|
2180
|
+
1. <step-tag> — <what it does> — <component> [rollback: yes/no] [allow_fail: yes/no]
|
|
2181
|
+
2. ...
|
|
2182
|
+
Return value: describe what the handler returns
|
|
2183
|
+
Components to create: list anything missing with a short description
|
|
2184
|
+
Ask: "Should I proceed? Shall I create the missing components first?"
|
|
2185
|
+
Wait for confirmation before writing code or calling any create tool.
|
|
2186
|
+
|
|
2187
|
+
STEP 5 — CREATE missing components (only with user approval)
|
|
2188
|
+
Use ductape_execute for any missing databases, apps, database actions, notification events, etc.
|
|
2189
|
+
For a missing database action:
|
|
2190
|
+
ductape_execute("databases.action.create", [product_tag, db_tag, { tag, name, type, query }])
|
|
2191
|
+
For a missing child feature, recursively apply this same workflow.
|
|
2192
|
+
Tell the user what you are about to create before each tool call.
|
|
2193
|
+
|
|
2194
|
+
STEP 6 — WRITE the feature into the project codebase
|
|
2195
|
+
- Locate or create an appropriate file in the project (e.g. src/features/feature-name.ts, features/feature_name.py)
|
|
2196
|
+
- Use features.define with an async handler
|
|
2197
|
+
- Step results are plain variables — just await ctx.step(...) and use the return value in the next step
|
|
2198
|
+
- No special notation needed: const user = await ctx.step('create-user', async () => { ... }); then use user.id directly in the next step
|
|
2199
|
+
- Write rollback handlers inline as the third argument to ctx.step()
|
|
2200
|
+
- Return a plain object as the feature's output
|
|
2201
|
+
|
|
2202
|
+
STEP 7 — HANDLE conditionals, loops, and branching (when applicable)
|
|
2203
|
+
Branch on step result (early return in handler):
|
|
2204
|
+
→ Add branchOverrides: { stepTag: { field: value } } so all branches are recorded
|
|
2205
|
+
→ At runtime the executor evaluates the real result and skips or runs later steps accordingly
|
|
2206
|
+
Loop over input array:
|
|
2207
|
+
→ Add recordInput: { items: [{ id: "1" }, { id: "2" }] } so the loop runs during recording
|
|
2208
|
+
→ Use unique step tags per iteration (e.g. "process-" + item.id)
|
|
2209
|
+
Switch/if-else on feature input values:
|
|
2210
|
+
→ Add recordScenarios: [{ type: "a" }, { type: "b" }] — handler runs once per scenario
|
|
2211
|
+
→ Only the scenario whose input matches runs at execution time
|
|
2212
|
+
|
|
2213
|
+
STEP 8 — SET rollbacks for reversible steps
|
|
2214
|
+
Any step that allocates a resource should undo it if a later step fails:
|
|
2215
|
+
const charge = await ctx.step(
|
|
2216
|
+
'charge',
|
|
2217
|
+
async () => ctx.action.run({ app: 'stripe', event: 'create-charge', input: { amount: ctx.input.amount } }),
|
|
2218
|
+
async (result) => ctx.action.run({ app: 'stripe', event: 'refund', input: { chargeId: result.id } })
|
|
2219
|
+
);
|
|
2220
|
+
|
|
2144
2221
|
Step types: action | database | graph | notification | storage | produce | quota | fallback |
|
|
2145
2222
|
vector | child_feature | sleep | wait_for_signal | checkpoint
|
|
2146
2223
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2224
|
+
Define a feature (write this into the project's source files — do NOT use features.create):
|
|
2225
|
+
// src/features/onboard-user.ts (or the equivalent path/language for the project)
|
|
2226
|
+
await ductape.features.define({
|
|
2149
2227
|
tag: "onboard-user",
|
|
2150
2228
|
name: "Onboard User",
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
},
|
|
2170
|
-
],
|
|
2171
|
-
}])
|
|
2229
|
+
handler: async (ctx) => {
|
|
2230
|
+
const account = await ctx.step("create-account", async () =>
|
|
2231
|
+
ctx.database.insert({ database: "core-db", event: "insert-user",
|
|
2232
|
+
data: { userId: ctx.input.userId, email: ctx.input.email } }),
|
|
2233
|
+
async (result) => ctx.database.delete({ database: "core-db", event: "delete-user",
|
|
2234
|
+
where: { id: result.id } }) // rollback
|
|
2235
|
+
);
|
|
2236
|
+
|
|
2237
|
+
await ctx.step("send-welcome", async () =>
|
|
2238
|
+
ctx.notification.email({ notification: "welcome-email", event: "welcome",
|
|
2239
|
+
recipients: [ctx.input.email], subject: {}, template: {} }),
|
|
2240
|
+
null,
|
|
2241
|
+
{ allow_fail: true }
|
|
2242
|
+
);
|
|
2243
|
+
|
|
2244
|
+
return { userId: account.id };
|
|
2245
|
+
},
|
|
2246
|
+
});
|
|
2172
2247
|
|
|
2173
2248
|
Execute at runtime:
|
|
2174
2249
|
→ CALL ductape_generate_payload FIRST (operation_family="features", method="execute", targets={tag})
|
|
@@ -2194,21 +2269,8 @@ Signals and queries (for long-running features):
|
|
|
2194
2269
|
features.signal [{ product, env, feature_id, signal: "payment-confirmed", payload? }]
|
|
2195
2270
|
features.query [{ product, env, feature_id, query: "current-status", params? }]
|
|
2196
2271
|
|
|
2197
|
-
Step input references:
|
|
2198
|
-
$Input{field} → map from feature's declared input
|
|
2199
|
-
$Step{stepTag}{field} → output field from a prior step
|
|
2200
|
-
$StepOutput{field} → current step's own return value
|
|
2201
|
-
$Concat([...parts], delim) → string interpolation
|
|
2202
|
-
|
|
2203
2272
|
Rollback strategies: reverse_all | reverse_critical | compensate | none
|
|
2204
2273
|
Feature statuses: pending | running | completed | failed | rolled_back | rolling_back | paused
|
|
2205
|
-
|
|
2206
|
-
Code-first (define API — compiles async handler to JSON step schema):
|
|
2207
|
-
features.define({ tag, name, input, handler: async (ctx) => { ... } })
|
|
2208
|
-
ctx provides: ctx.step(), ctx.action.run(), ctx.database.query/insert(),
|
|
2209
|
-
ctx.graph.execute(), ctx.notification.send(), ctx.storage.upload(),
|
|
2210
|
-
ctx.messaging.produce(), ctx.quota.execute(), ctx.fallback.execute(),
|
|
2211
|
-
ctx.sleep(), ctx.waitForSignal(), ctx.setState/getState(), ctx.feature()
|
|
2212
2274
|
`.trim(),
|
|
2213
2275
|
|
|
2214
2276
|
events: `
|