@ductape/mcp 0.2.0 → 0.2.2
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/CHANGELOG.md +6 -0
- package/README.md +14 -1
- package/dist/index.js +259 -43
- package/docs/TOOLS.md +14 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Broadened Feature guidance from durable/event-driven workflows to synchronous or asynchronous named product capabilities.
|
|
6
|
+
- Added evidence-backed `FEATURE`, `FEATURE_STEP`, `DOMAIN_SERVICE`, `UTILITY`, and `INFRASTRUCTURE_ADAPTER` classification guidance.
|
|
7
|
+
- Added synchronous multi-step examples and repository discovery/grouping rules.
|
|
8
|
+
|
|
3
9
|
## 0.2.0 - 2026-07-26
|
|
4
10
|
|
|
5
11
|
- Added exhaustive AI-led migration guidance for TypeScript, Go, Java, and .NET.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Migration guidance is available from `ductape_docs({ topic: "migration" })`. It starts with an original E2E
|
|
4
4
|
baseline, defaults to a separate new codebase, requires contextual review and strict parity evidence, and ends
|
|
5
|
-
against the unchanged original E2E suite. Administrative work
|
|
5
|
+
against the unchanged original E2E suite. Administrative work and marketplace discovery use `ductape_cli`; `ductape_execute` remains
|
|
6
6
|
publishable-key runtime-only. MCP never accepts or forwards `DUCTAPE_ACCESS_KEY`.
|
|
7
7
|
|
|
8
8
|
MCP (Model Context Protocol) server that exposes **Ductape SDK** operations as tools. All calls go through the **Ductape backend proxy** at a fixed URL; the SDK never runs in the MCP process. It is completely stateless; you provide your **Publishable Key** per execution.
|
|
@@ -82,6 +82,19 @@ The server exposes **three tools**:
|
|
|
82
82
|
- ready-to-copy SDK snippet in `typescript` or `python`
|
|
83
83
|
- Intended for engineers and copilots that need executable examples quickly.
|
|
84
84
|
|
|
85
|
+
The `ductape_cli` MCP tool also exposes public app discovery:
|
|
86
|
+
`marketplace search <capability>`, `marketplace categories`, and
|
|
87
|
+
`marketplace get <app_tag>`. Inspect the app before generating or executing an action payload.
|
|
88
|
+
|
|
89
|
+
## Feature classification
|
|
90
|
+
|
|
91
|
+
`ductape_docs({ topic: "features" })` treats a Feature as a named, reusable product capability
|
|
92
|
+
with a stable input/output contract and a useful managed-execution boundary. Features may be
|
|
93
|
+
synchronous and entirely local; Events, signals, schedules, waits, retries, and rollback are
|
|
94
|
+
optional patterns. Repository analysis distinguishes `FEATURE`, `FEATURE_STEP`, `DOMAIN_SERVICE`,
|
|
95
|
+
`UTILITY`, and `INFRASTRUCTURE_ADAPTER`, explains its evidence, and groups related low-level
|
|
96
|
+
operations instead of turning every exported function into a Feature.
|
|
97
|
+
|
|
85
98
|
## Security
|
|
86
99
|
|
|
87
100
|
- Passing the `publishable_key` on a per-request basis guarantees that each execution is isolated. This architecture safely supports deployments that multiplex multiple user connections in a single server thread (e.g., SSE), avoiding cross-tenant leakage.
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,24 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
35
35
|
ductape_cli("cloud connections list")
|
|
36
36
|
ductape_cli("link --product my-product --env dev")
|
|
37
37
|
If the CLI is not installed, ductape_cli will return install instructions automatically.
|
|
38
|
-
NOTE:
|
|
38
|
+
NOTE: App actions are configured in the Workbench UI — there is no CLI command for them.
|
|
39
|
+
Environments DO have CLI commands: ductape_cli("products environments list/get/create/update ...").
|
|
40
|
+
|
|
41
|
+
RESOLVING "No linked project" ERRORS:
|
|
42
|
+
Some commands (declarative sync below, db migrate/schema, products environments *) need a
|
|
43
|
+
linked project — a local .ductape/config.json with a product tag and env slug. The "link"
|
|
44
|
+
command does NOT validate against the server: it just writes that local file. This means you
|
|
45
|
+
can run ductape_cli("link --product <tag> --env <slug>") even before that environment exists on
|
|
46
|
+
the product yet — do not treat "no linked environment exists server-side" as a reason to avoid
|
|
47
|
+
linking first. If the failing command already takes the product tag as an explicit argument
|
|
48
|
+
(e.g. products environments create/update/list/get), linking is not even required for it —
|
|
49
|
+
only commands that need to *infer* the product/env from local project state require a link.
|
|
50
|
+
Also: this MCP server runs the CLI subprocess in the directory named by the DUCTAPE_PROJECT_DIR
|
|
51
|
+
env var (falls back to this server process's own cwd if unset). If a command inexplicably
|
|
52
|
+
reports "no linked project" right after a successful "link" call, the project directory the
|
|
53
|
+
link was written to and the directory this server is running the CLI from may not match — set
|
|
54
|
+
DUCTAPE_PROJECT_DIR explicitly in this server's env (e.g. in the consuming project's .mcp.json)
|
|
55
|
+
to the target project's absolute path.
|
|
39
56
|
|
|
40
57
|
DECLARATIVE SYNC (apply sessions, notifications, events from code; run DB migrations)
|
|
41
58
|
→ Also use ductape_cli. The project must be linked first (ductape init --link).
|
|
@@ -123,9 +140,10 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
123
140
|
ductape_cli("resources storage list")
|
|
124
141
|
ductape_cli("resources database create -f db-config.json")
|
|
125
142
|
This applies to: products, apps, and resources (databases, storage, caches, etc.),
|
|
126
|
-
cloud connections, and secrets. Environments
|
|
127
|
-
jobs, and healthchecks are configured in the
|
|
128
|
-
command because their definitions are code-first
|
|
143
|
+
cloud connections, and secrets. Environments have their own CLI commands (see below);
|
|
144
|
+
app actions, auths, quotas, fallbacks, jobs, and healthchecks are configured in the
|
|
145
|
+
Workbench UI. Features have no CLI create command because their definitions are code-first
|
|
146
|
+
through features.define.
|
|
129
147
|
|
|
130
148
|
⚠ MULTI-ENV REQUIREMENT — applies to ALL product assets (storage, database, cache,
|
|
131
149
|
messageBroker, graph, vector, and any other resource with an envs array):
|
|
@@ -324,9 +342,9 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
324
342
|
IMPORTANT: ALL product.* methods require the access key and will return 403 with a publishable key.
|
|
325
343
|
Use ductape_cli for ALL product operations — never ductape_execute:
|
|
326
344
|
ductape_cli("products get --tag <tag> --json") ← fetch product + full inventory
|
|
327
|
-
ductape_cli("products components list --tag <tag> --json") ← compact non-secret inventory
|
|
328
|
-
ductape_cli("products components get --tag <tag> --type notifications --json")
|
|
329
|
-
ductape_cli("products components get --tag <tag> --type events --json")
|
|
345
|
+
ductape_cli("products components list --product-tag <tag> --json") ← compact non-secret inventory
|
|
346
|
+
ductape_cli("products components get --product-tag <tag> --type notifications --json")
|
|
347
|
+
ductape_cli("products components get --product-tag <tag> --type events --json")
|
|
330
348
|
ductape_cli("products create --name <name> --tag <tag>")
|
|
331
349
|
ductape_cli("products environments list <tag> --json")
|
|
332
350
|
ductape_cli("products environments get <tag> <slug> --json")
|
|
@@ -725,6 +743,10 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
725
743
|
━━━ MODULE: features ━━━
|
|
726
744
|
Feature definitions are code-first. Use features.define in application source; do not call
|
|
727
745
|
administrative create/update/delete methods through ductape_execute.
|
|
746
|
+
A Feature is a named, reusable product capability with a stable input/output contract that
|
|
747
|
+
benefits from managed execution, composition, observability, retries, versioning, policy
|
|
748
|
+
enforcement, or explicit execution steps. It may execute synchronously and entirely locally.
|
|
749
|
+
Signals, Events, schedules, waits, checkpoints, compensation, and rollback are optional patterns.
|
|
728
750
|
features.fetch [product_tag, feature_tag]
|
|
729
751
|
features.fetchAll [product_tag]
|
|
730
752
|
|
|
@@ -1129,7 +1151,7 @@ const ADMIN_SUBCOMMANDS = [
|
|
|
1129
1151
|
'profiles',
|
|
1130
1152
|
'workspaces',
|
|
1131
1153
|
'link', 'unlink', 'init',
|
|
1132
|
-
'products', 'apps',
|
|
1154
|
+
'products', 'apps', 'marketplace',
|
|
1133
1155
|
'resources',
|
|
1134
1156
|
'notifications',
|
|
1135
1157
|
'events',
|
|
@@ -1156,6 +1178,7 @@ function checkCli() {
|
|
|
1156
1178
|
timeout: 5000,
|
|
1157
1179
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1158
1180
|
env: cliEnvironment(),
|
|
1181
|
+
cwd: cliCwd(),
|
|
1159
1182
|
}).trim();
|
|
1160
1183
|
return { available: true, version: out || 'unknown' };
|
|
1161
1184
|
}
|
|
@@ -1173,6 +1196,7 @@ function checkLoginState() {
|
|
|
1173
1196
|
timeout: 10000,
|
|
1174
1197
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1175
1198
|
env: cliEnvironment(),
|
|
1199
|
+
cwd: cliCwd(),
|
|
1176
1200
|
});
|
|
1177
1201
|
authState = 'ok';
|
|
1178
1202
|
return 'ok';
|
|
@@ -1193,6 +1217,7 @@ function syncWorkspace() {
|
|
|
1193
1217
|
timeout: 10000,
|
|
1194
1218
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1195
1219
|
env: cliEnvironment(),
|
|
1220
|
+
cwd: cliCwd(),
|
|
1196
1221
|
});
|
|
1197
1222
|
}
|
|
1198
1223
|
catch {
|
|
@@ -1221,6 +1246,7 @@ function runCli(command) {
|
|
|
1221
1246
|
timeout: 90000,
|
|
1222
1247
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1223
1248
|
env: cliEnvironment(),
|
|
1249
|
+
cwd: cliCwd(),
|
|
1224
1250
|
});
|
|
1225
1251
|
return { success: true, output: output.trim() };
|
|
1226
1252
|
}
|
|
@@ -1274,6 +1300,19 @@ function cliEnvironment() {
|
|
|
1274
1300
|
delete environment.DUCTAPE_ACCESS_KEY;
|
|
1275
1301
|
return environment;
|
|
1276
1302
|
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Directory the `ductape` CLI subprocess runs in. Commands that resolve a linked project
|
|
1305
|
+
* (findProjectConfig walking up from cwd — e.g. `products environments *`, `apply`, `db migrate`)
|
|
1306
|
+
* depend on this being the user's actual project directory, not wherever this MCP server process
|
|
1307
|
+
* itself happened to be spawned from. execSync inherits process.cwd() when no cwd is given, which
|
|
1308
|
+
* is only correct if this server was started from inside the target project — that doesn't hold
|
|
1309
|
+
* for every launch path (e.g. a host attaching this server to an already-running session whose
|
|
1310
|
+
* cwd is unrelated to the project). Set DUCTAPE_PROJECT_DIR explicitly in the server's env
|
|
1311
|
+
* (e.g. in .mcp.json) to pin it; falls back to this process's own cwd otherwise.
|
|
1312
|
+
*/
|
|
1313
|
+
function cliCwd() {
|
|
1314
|
+
return process.env.DUCTAPE_PROJECT_DIR || process.cwd();
|
|
1315
|
+
}
|
|
1277
1316
|
function shellArgument(value) {
|
|
1278
1317
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1279
1318
|
}
|
|
@@ -1372,10 +1411,12 @@ ENVIRONMENTS
|
|
|
1372
1411
|
(production→prd, sandbox→snd, staging→stg) but present uncertain mappings for confirmation.
|
|
1373
1412
|
Before creating any asset, list product environments and require complete per-environment coverage.
|
|
1374
1413
|
Missing environments can be created idempotently through the authenticated standalone CLI:
|
|
1375
|
-
ductape_cli("products environments create <product-tag> -
|
|
1414
|
+
ductape_cli("products environments create <product-tag> --env-file <environment.json> --json")
|
|
1376
1415
|
The JSON requires env_name, description, and a three-character slug. The CLI fetches first, creates only
|
|
1377
|
-
when absent, then fetches again to verify persistence
|
|
1378
|
-
|
|
1416
|
+
when absent, then fetches again to verify persistence (with a short retry on the verification read to
|
|
1417
|
+
absorb replication lag). No linked project is required — the product tag is always the explicit argument.
|
|
1418
|
+
Update and verify with:
|
|
1419
|
+
ductape_cli("products environments update <product-tag> <slug> --env-file <patch.json> --json")
|
|
1379
1420
|
Export authenticated inventory with ductape_cli("products environments list <product-tag> --json"),
|
|
1380
1421
|
save it as evidence, then reconcile locally:
|
|
1381
1422
|
ductape_cli("migration-environments --analysis <analysis.json> --inventory <inventory.json> --strict --json")
|
|
@@ -1436,9 +1477,12 @@ COMPONENT DECISIONS
|
|
|
1436
1477
|
Databases/transactions → Ductape Database; brokers/queues → Events; object stores → Storage;
|
|
1437
1478
|
SMTP/SMS/push/callbacks → Notifications; Redis/cache → Cache; Neo4j/Neptune/Arango/Memgraph → Graph;
|
|
1438
1479
|
Pinecone/Qdrant/Weaviate/OpenSearch → Vector; JWT/session middleware → Sessions.
|
|
1439
|
-
|
|
1480
|
+
Named reusable product capabilities with a stable managed-execution boundary → code-first Features,
|
|
1481
|
+
including synchronous multi-step capabilities and durable/scheduled/signal-driven orchestration.
|
|
1440
1482
|
Retries, health checks, fallbacks, quotas, circuit breakers → Resilience.
|
|
1441
|
-
Keep deterministic
|
|
1483
|
+
Keep low-level deterministic rules as ordinary domain functions when they do not form a useful
|
|
1484
|
+
independent capability boundary. Wrap or compose them into Features when the combined operation
|
|
1485
|
+
represents a reusable product capability.
|
|
1442
1486
|
|
|
1443
1487
|
LANGUAGE RUNTIME SHAPES
|
|
1444
1488
|
TypeScript: @ductape/sdk; NestJS uses @ductape/nestjs, @Events.Consumer, and request-scoped context.
|
|
@@ -1474,7 +1518,8 @@ AI EDITING STANDARD
|
|
|
1474
1518
|
Compilation and happy-path tests alone do not demonstrate parity. Use existing tests, characterization
|
|
1475
1519
|
tests, contract tests, integration tests, failure injection, and snd smoke tests appropriate to the slice.
|
|
1476
1520
|
If parity cannot be demonstrated, stop short of cutover and report the exact unverified behavior.
|
|
1477
|
-
Use Events for internal async boundaries and Apps/Actions for external APIs. Keep deterministic
|
|
1521
|
+
Use Events for internal async boundaries and Apps/Actions for external APIs. Keep low-level deterministic
|
|
1522
|
+
rules in code, while allowing a coherent synchronous capability composed from those rules to be a Feature.
|
|
1478
1523
|
Immediate work propagates the full session; durable work uses approved actor metadata or system context.
|
|
1479
1524
|
Consumers are idempotent, retry ownership is singular and bounded, and external effects are not duplicated.
|
|
1480
1525
|
After each slice: format, build, test, rescan, reconcile assets through ductape_cli, smoke-test in snd,
|
|
@@ -1550,6 +1595,10 @@ AUDITABLE REVIEW LEDGER
|
|
|
1550
1595
|
ductape_cli("migration-review validate --ledger <path>/review-ledger.json --json")
|
|
1551
1596
|
Before proposing cutover, require strict validation:
|
|
1552
1597
|
ductape_cli("migration-review validate --ledger <path>/review-ledger.json --strict --json")
|
|
1598
|
+
|
|
1599
|
+
Before writing SDK integration code, query the exact supported language/version catalog:
|
|
1600
|
+
ductape_cli("migration-capabilities --language <typescript|go|java|dotnet> --version <exact-version> --json")
|
|
1601
|
+
The catalog is source-evidenced but does not replace inspection of the package actually installed in the target.
|
|
1553
1602
|
Strict readiness requires no pending or stale files, reasoned exclusions, complete follow-ups,
|
|
1554
1603
|
covered internal references, recorded file purpose, and verified or reasoned-not-applicable
|
|
1555
1604
|
interface/functional/operational parity.
|
|
@@ -1627,6 +1676,18 @@ DEPENDENCY-AWARE LARGE-REPOSITORY PROPOSAL
|
|
|
1627
1676
|
Verification-matrix records may declare details.depends_on using category.requirement identifiers. A stale
|
|
1628
1677
|
cited dependency transitively invalidates dependent service/package summaries and readiness.
|
|
1629
1678
|
|
|
1679
|
+
MIGRATION ARTIFACT SAFETY
|
|
1680
|
+
Migration JSON is size-limited, secret-material scanned, version checked, and written through exclusive
|
|
1681
|
+
locks plus atomic rename. Mutating manifests retain a recoverable .bak copy. Validate or recover locally:
|
|
1682
|
+
ductape_cli("migration-artifact validate --file <artifact.json> --json")
|
|
1683
|
+
ductape_cli("migration-artifact recover --file <artifact.json> --json")
|
|
1684
|
+
Controlled legacy upgrades never rewrite the source artifact:
|
|
1685
|
+
ductape_cli("migration-artifact migrate --file <legacy.json> --output <v1.json> --json")
|
|
1686
|
+
Inspect the machine-readable schema catalog:
|
|
1687
|
+
ductape_cli("migration-artifact schemas --json")
|
|
1688
|
+
Errors include stable codes such as ARTIFACT_TOO_LARGE, ARTIFACT_SECRET_MATERIAL, ARTIFACT_LOCKED,
|
|
1689
|
+
ARTIFACT_VERSION_UNSUPPORTED, and ARTIFACT_RECOVERY_FAILED. Never bypass these checks with direct JSON edits.
|
|
1690
|
+
|
|
1630
1691
|
PARITY-GATED MIGRATION SLICES
|
|
1631
1692
|
Create a JSON array containing the reviewed repository-relative files for one vertical slice, then:
|
|
1632
1693
|
ductape_cli("migration-slice init --ledger <ledger> --tag <tag> --name <name> --files <files.json> --json")
|
|
@@ -2277,6 +2338,18 @@ An App must be fully set up in Ductape before any code can use it:
|
|
|
2277
2338
|
4. Action endpoints must be defined (each action = one HTTP endpoint spec: method, path, body/query/header shape, response shape)
|
|
2278
2339
|
5. The App must be connected to the product (product.apps.add) and its envs mapped
|
|
2279
2340
|
|
|
2341
|
+
DISCOVER BEFORE CREATING:
|
|
2342
|
+
ductape_cli("marketplace search payments --json")
|
|
2343
|
+
ductape_cli("marketplace search paystack --json")
|
|
2344
|
+
ductape_cli("marketplace categories --json")
|
|
2345
|
+
ductape_cli("marketplace get <app_tag> --json")
|
|
2346
|
+
|
|
2347
|
+
marketplace search matches capability terms against public app names, tags, descriptions,
|
|
2348
|
+
categories, actions, and webhooks. marketplace get returns the complete public app definition,
|
|
2349
|
+
including the exact current-version action tags and body/query/header/param schemas. Never infer
|
|
2350
|
+
Paystack action names such as "initialize" or "verify": inspect the marketplace record first.
|
|
2351
|
+
If no suitable app exists, create one or import Paystack's OpenAPI/Postman definition.
|
|
2352
|
+
|
|
2280
2353
|
ONLY after all five steps can any code call:
|
|
2281
2354
|
ctx.api.run({ app: '<app_tag>', event: '<action_tag>', input: { ... } }) ← in a feature handler
|
|
2282
2355
|
actions.run([{ product, env, app: '<app_tag>', action: '<action_tag>', input }]) ← at runtime
|
|
@@ -2339,6 +2412,22 @@ Run an action at runtime:
|
|
|
2339
2412
|
ductape_execute("actions.run", [{ product, env, app, action, input: { "body:field": value } }])
|
|
2340
2413
|
ductape_execute("actions.dispatch", [{ product, env, app, action, input, schedule? }])
|
|
2341
2414
|
|
|
2415
|
+
In a Ductape feature handler, call the registered action through:
|
|
2416
|
+
await ctx.api.run({ app: "<app_tag>", event: "<action_tag>", input: { ... } })
|
|
2417
|
+
ctx.api is the supported feature-context surface (also described as ctx.action in older code).
|
|
2418
|
+
There is no ctx.apps, ctx.integrations, or generic external-HTTP feature surface.
|
|
2419
|
+
|
|
2420
|
+
PAYSTACK CONFIGURATION:
|
|
2421
|
+
- Store the secret key as a workspace secret such as PAYSTACK_SECRET_KEY.
|
|
2422
|
+
- Reference it from app auth as: Authorization = "Bearer $Secret{PAYSTACK_SECRET_KEY}".
|
|
2423
|
+
- Never place the key in feature input, source code, logs, or marketplace app metadata.
|
|
2424
|
+
- Initialization and verification are raw app actions unless the inspected app explicitly
|
|
2425
|
+
publishes a higher-level contract. Ductape has no universal application payment abstraction.
|
|
2426
|
+
- Model inbound events as app webhook events. Verify x-paystack-signature against the raw
|
|
2427
|
+
request body using HMAC-SHA512 and PAYSTACK_SECRET_KEY before processing.
|
|
2428
|
+
- Acknowledge quickly, process asynchronously, deduplicate by event/reference, and verify the
|
|
2429
|
+
transaction through the inspected verification action before granting value.
|
|
2430
|
+
|
|
2342
2431
|
Auth schemes (how the app authenticates outbound requests):
|
|
2343
2432
|
Setup types: header | bearer | basic | oauth2 | apikey
|
|
2344
2433
|
Configure auth in Workbench (administrative).
|
|
@@ -2995,9 +3084,90 @@ PAYLOAD RECIPES
|
|
|
2995
3084
|
features: `
|
|
2996
3085
|
DUCTAPE FEATURES
|
|
2997
3086
|
|
|
2998
|
-
A
|
|
2999
|
-
|
|
3000
|
-
|
|
3087
|
+
A Feature is a named, reusable product capability with a stable input/output contract that
|
|
3088
|
+
benefits from managed execution, composition, observability, retries, versioning, policy
|
|
3089
|
+
enforcement, or explicit execution steps.
|
|
3090
|
+
|
|
3091
|
+
A Feature may be synchronous or asynchronous and may be entirely local. Signals, Events, waits,
|
|
3092
|
+
schedules, checkpoints, retries, compensation, and rollback are optional capabilities—not
|
|
3093
|
+
prerequisites. Asynchronous behavior is not the primary definition of a Feature.
|
|
3094
|
+
|
|
3095
|
+
Common Feature patterns:
|
|
3096
|
+
- synchronous capability
|
|
3097
|
+
- multi-step computation
|
|
3098
|
+
- event-driven orchestration
|
|
3099
|
+
- signal-driven human workflow
|
|
3100
|
+
- scheduled capability
|
|
3101
|
+
- parent/child Feature composition
|
|
3102
|
+
|
|
3103
|
+
━━━ CAPABILITY CLASSIFICATION — explain the evidence for every classification ━━━
|
|
3104
|
+
|
|
3105
|
+
Use exactly these categories while reviewing application code:
|
|
3106
|
+
FEATURE
|
|
3107
|
+
An independently meaningful product/domain capability with a useful managed-execution boundary.
|
|
3108
|
+
Recommendation: "Make this a standalone Ductape Feature."
|
|
3109
|
+
FEATURE_STEP
|
|
3110
|
+
A meaningful stage inside a larger capability, but not a useful independent execution boundary.
|
|
3111
|
+
Recommendation: "Expose this as a named ctx.step(...) inside another Feature."
|
|
3112
|
+
DOMAIN_SERVICE
|
|
3113
|
+
Reusable domain logic without a useful independent managed-execution boundary.
|
|
3114
|
+
Recommendation: "Keep this as ordinary domain logic called by a Feature."
|
|
3115
|
+
UTILITY
|
|
3116
|
+
A low-level helper such as hashing, formatting, redaction, normalization, conversion, or a type guard.
|
|
3117
|
+
Recommendation: "Keep this as a utility."
|
|
3118
|
+
INFRASTRUCTURE_ADAPTER
|
|
3119
|
+
Database, Event, HTTP, cache, storage, provider, transport, or framework integration code.
|
|
3120
|
+
Recommendation: "Keep this as an infrastructure adapter."
|
|
3121
|
+
|
|
3122
|
+
TypeScript export is only evidence of reuse. It is neither sufficient nor necessary for Feature
|
|
3123
|
+
classification. Never classify every exported function as a Feature.
|
|
3124
|
+
|
|
3125
|
+
Evaluate a candidate by asking:
|
|
3126
|
+
- Does it represent a recognizable product or domain capability?
|
|
3127
|
+
- Does it have a coherent responsibility?
|
|
3128
|
+
- Can it have a stable, typed input/output contract?
|
|
3129
|
+
- Is it reused across entry points, services, or other Features?
|
|
3130
|
+
- Would independent execution or composition be useful?
|
|
3131
|
+
- Would execution history or step-level observability be valuable?
|
|
3132
|
+
- Does it need explicit versioning, authorization, quotas, retries, or policy?
|
|
3133
|
+
- Does it contain several meaningful stages?
|
|
3134
|
+
- Would users or developers naturally name it as a product feature?
|
|
3135
|
+
A positive answer to several questions makes it a Feature candidate even when it is synchronous
|
|
3136
|
+
and local. No single answer is sufficient, and signals, Events, or long runtime are never required.
|
|
3137
|
+
|
|
3138
|
+
Keep low-level deterministic rules as ordinary domain functions when they do not form a useful
|
|
3139
|
+
independent capability boundary. Wrap or compose them into Features when the combined operation
|
|
3140
|
+
represents a reusable product capability.
|
|
3141
|
+
|
|
3142
|
+
When inspecting a TypeScript repository, examine:
|
|
3143
|
+
exported functions; public service methods; controller entry points; Event consumers; scheduled
|
|
3144
|
+
jobs; repeated orchestration sequences; domain operations reused in several locations; functions
|
|
3145
|
+
with substantial typed inputs/outputs; functions composing several stages; and product terminology
|
|
3146
|
+
in documentation and API routes. Do not restrict discovery to *.feature.ts or features.define calls.
|
|
3147
|
+
|
|
3148
|
+
Group related low-level operations into one coherent capability candidate. For example,
|
|
3149
|
+
resolveNationOrders, applyProvinceStockpileProduction, applyProvinceStockpileTransfers, and
|
|
3150
|
+
resolveFormationCommands may collectively suggest resolve-match-boundary or resolve-nation-turn.
|
|
3151
|
+
They must not automatically become four separate Features.
|
|
3152
|
+
|
|
3153
|
+
For repository analysis, return a structured inventory for every recommendation:
|
|
3154
|
+
{
|
|
3155
|
+
"candidate": "resolve-nation-turn",
|
|
3156
|
+
"classification": "FEATURE",
|
|
3157
|
+
"executionStyle": "synchronous-multistep",
|
|
3158
|
+
"evidence": [
|
|
3159
|
+
"Represents a recognizable game capability",
|
|
3160
|
+
"Has a stable input/output boundary",
|
|
3161
|
+
"Composes validation, production, resolution, and reporting",
|
|
3162
|
+
"Useful as an independently observable execution"
|
|
3163
|
+
],
|
|
3164
|
+
"suggestedSteps": ["validate-orders", "apply-production", "resolve-orders", "build-reports"],
|
|
3165
|
+
"signalsRequired": false,
|
|
3166
|
+
"eventsRequired": false,
|
|
3167
|
+
"recommendation": "Make this a standalone Ductape Feature."
|
|
3168
|
+
}
|
|
3169
|
+
Every classification needs concrete code, caller, contract, or product-language evidence. If the
|
|
3170
|
+
boundary remains ambiguous, report both plausible categories and the missing evidence; do not guess.
|
|
3001
3171
|
|
|
3002
3172
|
━━━ AI DESIGN WORKFLOW — follow this process every time a user asks you to build or plan a feature ━━━
|
|
3003
3173
|
|
|
@@ -3033,12 +3203,14 @@ STEP 2 — INVENTORY existing Ductape components
|
|
|
3033
3203
|
|
|
3034
3204
|
STEP 3 — PLAN each step
|
|
3035
3205
|
For every logical step:
|
|
3036
|
-
a. Identify
|
|
3206
|
+
a. Identify whether it is local domain logic, a child Feature, or an existing Ductape component.
|
|
3207
|
+
Local typed domain logic may run inside ctx.step; it does not require an Event or App.
|
|
3037
3208
|
If a step calls an external service, it MUST go through a registered Ductape App.
|
|
3038
3209
|
If no App for that service exists in the product → mark it "App to create: <service name>".
|
|
3039
3210
|
DO NOT plan a raw HTTP call, a direct dispatch to a URL, or any workaround in place of a missing App.
|
|
3040
3211
|
b. Note how inputs flow: ctx.input fields, or return values from earlier steps (plain JS variables — no special notation needed)
|
|
3041
|
-
c. Decide if a rollback handler is needed (e.g. charge → refund on later failure)
|
|
3212
|
+
c. Decide if a rollback handler is needed (e.g. charge → refund on later failure).
|
|
3213
|
+
Rollback is optional and is not a Feature qualification requirement.
|
|
3042
3214
|
d. Decide allow_fail: true for non-critical steps (email, analytics, audit logs)
|
|
3043
3215
|
|
|
3044
3216
|
STEP 4 — PRESENT the plan and get approval BEFORE writing any code or creating anything
|
|
@@ -3092,8 +3264,40 @@ STEP 8 — SET rollbacks for reversible steps
|
|
|
3092
3264
|
async (result) => ctx.api.run({ app: 'stripe', event: 'refund', input: { chargeId: result.id } })
|
|
3093
3265
|
);
|
|
3094
3266
|
|
|
3095
|
-
Step types: action | database | graph | notification | storage | produce | quota |
|
|
3096
|
-
vector | child_feature | sleep | wait_for_signal | checkpoint
|
|
3267
|
+
Step types: local_domain | action | database | graph | notification | storage | produce | quota |
|
|
3268
|
+
fallback | vector | child_feature | sleep | wait_for_signal | checkpoint
|
|
3269
|
+
|
|
3270
|
+
Valid synchronous Feature candidates include generate-world, resolve-nation-turn,
|
|
3271
|
+
calculate-route-capacity, price-subscription, evaluate-entitlement, and build-replay.
|
|
3272
|
+
|
|
3273
|
+
Synchronous multi-step Feature (no Event, schedule, sleep, signal, or external system):
|
|
3274
|
+
await ductape.features.define({
|
|
3275
|
+
product: 'example-product',
|
|
3276
|
+
tag: 'resolve-nation-turn',
|
|
3277
|
+
name: 'Resolve Nation Turn',
|
|
3278
|
+
input: {
|
|
3279
|
+
nationId: { type: 'string', required: true },
|
|
3280
|
+
tick: { type: 'number', required: true },
|
|
3281
|
+
},
|
|
3282
|
+
output: {
|
|
3283
|
+
acceptedOrders: { type: 'number' },
|
|
3284
|
+
rejectedOrders: { type: 'number' },
|
|
3285
|
+
},
|
|
3286
|
+
handler: async (ctx) => {
|
|
3287
|
+
const validated = await ctx.step('validate-orders', async () => {
|
|
3288
|
+
return validateOrders(ctx.input);
|
|
3289
|
+
});
|
|
3290
|
+
const resolved = await ctx.step('resolve-orders', async () => {
|
|
3291
|
+
return resolveOrders(validated);
|
|
3292
|
+
});
|
|
3293
|
+
return ctx.step('build-result', async () => {
|
|
3294
|
+
return buildResult(resolved);
|
|
3295
|
+
});
|
|
3296
|
+
},
|
|
3297
|
+
});
|
|
3298
|
+
This is a valid Feature despite requiring no signal and producing no Event. Its qualification comes
|
|
3299
|
+
from the named capability, stable contract, meaningful stages, reuse/composition value, and useful
|
|
3300
|
+
step-level execution history.
|
|
3097
3301
|
|
|
3098
3302
|
Define a feature (write this into the project's source files — do NOT use features.create):
|
|
3099
3303
|
// src/features/onboard-user.ts (or the equivalent path/language for the project)
|
|
@@ -3166,12 +3370,16 @@ When you call features.define({ handler }), the handler runs TWICE:
|
|
|
3166
3370
|
outer handler body. Code in the outer body runs during recording with proxy values and
|
|
3167
3371
|
may behave unexpectedly (e.g. typeof proxy === 'object' is true but .someField is a proxy).
|
|
3168
3372
|
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3373
|
+
A ctx.step callback may call ordinary local domain functions and injected/application services
|
|
3374
|
+
available to the registration scope. This is the normal shape for a synchronous capability such
|
|
3375
|
+
as pricing, entitlement evaluation, route-capacity calculation, or turn resolution. Keep the
|
|
3376
|
+
meaningful work inside ctx.step callbacks so recording does not execute it.
|
|
3377
|
+
|
|
3378
|
+
Use an Event when the operation genuinely crosses an asynchronous process/service boundary,
|
|
3379
|
+
needs broker delivery semantics, or must be consumed independently. Do not produce an Event merely
|
|
3380
|
+
to reach local domain logic. When an Event is appropriate, use ctx.events.produce in the currently
|
|
3381
|
+
published SDK and consume it in the NestJS service. ctx.publish is deprecated; do not use it.
|
|
3382
|
+
Do not assume a ctx.messaging alias exists unless installed SDK types explicitly expose it.
|
|
3175
3383
|
|
|
3176
3384
|
━━━ ORCHESTRATION DECISION RULE ━━━
|
|
3177
3385
|
|
|
@@ -3181,7 +3389,10 @@ When you call features.define({ handler }), the handler runs TWICE:
|
|
|
3181
3389
|
e.g. ductape.api.dispatch({ ..., schedule: { start_at: ... } })
|
|
3182
3390
|
e.g. ductape.database.dispatch({ ..., schedule: { start_at: ... } })
|
|
3183
3391
|
|
|
3184
|
-
|
|
3392
|
+
A named synchronous or asynchronous product capability with meaningful managed steps:
|
|
3393
|
+
→ define a Feature; execute it directly when immediate, or dispatch it when scheduled/background
|
|
3394
|
+
|
|
3395
|
+
Several durable Ductape component operations in sequence (with optional rollback / retry / state):
|
|
3185
3396
|
→ define a Feature, then features.dispatch to schedule it
|
|
3186
3397
|
|
|
3187
3398
|
Invoke internal application business logic (your own NestJS/backend service code):
|
|
@@ -4120,9 +4331,11 @@ const cliInputSchema = z.object({
|
|
|
4120
4331
|
'Use this tool for administrative operations: creating or updating products, apps, ' +
|
|
4121
4332
|
'resources (databases, storage, caches…), event broker topics, cloud connections, secrets, ' +
|
|
4122
4333
|
'and for apply/migrate workflows.\n\n' +
|
|
4123
|
-
'Note: environments
|
|
4124
|
-
'
|
|
4125
|
-
'
|
|
4334
|
+
'Note: environments have their own CLI commands (products environments list/get/create/update, ' +
|
|
4335
|
+
'no linked project required — the product tag is always an explicit argument). App actions, ' +
|
|
4336
|
+
'quotas, fallbacks, and jobs are configured in the Workbench UI. Features also have no CLI ' +
|
|
4337
|
+
'creation command: define them in application code with features.define so application ' +
|
|
4338
|
+
'boot/runtime registration makes them available.\n\n' +
|
|
4126
4339
|
'The CLI uses the user\'s local logged-in session (ductape login) — no key is required.'),
|
|
4127
4340
|
});
|
|
4128
4341
|
async function loadMcpSdk() {
|
|
@@ -4204,13 +4417,7 @@ async function main() {
|
|
|
4204
4417
|
const firstWord = args.command.trim().split(/\s+/)[0];
|
|
4205
4418
|
const isAuthCommand = firstWord === 'login' || firstWord === 'logout';
|
|
4206
4419
|
const isLocalMigrationGuidance = (firstWord === 'migrate-codebase' && !args.command.includes('--ensure-product')) ||
|
|
4207
|
-
firstWord
|
|
4208
|
-
firstWord === 'migration-slice' ||
|
|
4209
|
-
firstWord === 'migration-portfolio' ||
|
|
4210
|
-
firstWord === 'migration-database' ||
|
|
4211
|
-
firstWord === 'migration-environments' ||
|
|
4212
|
-
firstWord === 'migration-products' ||
|
|
4213
|
-
firstWord === 'migration-secrets';
|
|
4420
|
+
firstWord.startsWith('migration-');
|
|
4214
4421
|
if (!isAuthCommand && !isLocalMigrationGuidance) {
|
|
4215
4422
|
// Cache successful authentication, but re-check a missing/expired session on every call.
|
|
4216
4423
|
// The user may complete `ductape login` in another terminal while this MCP process remains
|
|
@@ -4268,6 +4475,7 @@ async function main() {
|
|
|
4268
4475
|
isError: true,
|
|
4269
4476
|
};
|
|
4270
4477
|
}
|
|
4478
|
+
const client = server.server?.getClientVersion?.();
|
|
4271
4479
|
const command = [
|
|
4272
4480
|
'migrate-codebase',
|
|
4273
4481
|
'--source', shellArgument(args.source),
|
|
@@ -4282,6 +4490,9 @@ async function main() {
|
|
|
4282
4490
|
...(args.exclude.length ? ['--exclude', shellArgument(args.exclude.join(','))] : []),
|
|
4283
4491
|
...(args.ensure_product ? ['--ensure-product'] : []),
|
|
4284
4492
|
...(args.write ? ['--write'] : []),
|
|
4493
|
+
...(client?.name && client?.version
|
|
4494
|
+
? ['--mcp-client-name', shellArgument(client.name), '--mcp-client-version', shellArgument(client.version)]
|
|
4495
|
+
: []),
|
|
4285
4496
|
'--json',
|
|
4286
4497
|
].join(' ');
|
|
4287
4498
|
return cliHandler({ command });
|
|
@@ -4480,6 +4691,10 @@ async function main() {
|
|
|
4480
4691
|
title: 'Ductape AI Migration Guidance',
|
|
4481
4692
|
description: 'Inspect a TypeScript, Go, Java, or .NET repository without exposing secret values. ' +
|
|
4482
4693
|
'Builds a relevant-file review queue, secret-name inventory, checksummed migration evidence, and low-confidence navigation hints. ' +
|
|
4694
|
+
'The review standards classify capability candidates as FEATURE, FEATURE_STEP, DOMAIN_SERVICE, UTILITY, or INFRASTRUCTURE_ADAPTER; ' +
|
|
4695
|
+
'they inspect exports, public methods, entry points, consumers, jobs, repeated orchestration, typed operations, routes, and product terminology. ' +
|
|
4696
|
+
'Synchronous local multi-step capabilities may be Features, while related low-level functions must be grouped rather than promoted one-by-one. ' +
|
|
4697
|
+
'Every recommendation must return classification, execution style, evidence, suggested steps, and whether signals or Events are actually required. ' +
|
|
4483
4698
|
'The AI must review files contextually and maintain an evidence ledger before proposing components or schemas. ' +
|
|
4484
4699
|
'It never generates or rewrites application code or executable assets. ' +
|
|
4485
4700
|
'Supports in-place and new-codebase guidance destinations. Read-only unless write or ensure_product is explicitly enabled.',
|
|
@@ -4572,15 +4787,16 @@ async function main() {
|
|
|
4572
4787
|
' type field = "messageBrokers" (not "messagebrokers" or "events").\n' +
|
|
4573
4788
|
' After importing, create topics first with ductape_cli("events topics create -f topic.json") — SQS requires explicit topic creation with queueUrls. For other providers, topics auto-register on first produce but should still be created explicitly before any consumer subscribes.\n' +
|
|
4574
4789
|
' - Listing workspaces, products, focused product components, secrets\n' +
|
|
4575
|
-
' Prefer "products components list --tag <tag> --json" for compact inventory; use\n' +
|
|
4576
|
-
' "products components get --tag <tag> --type notifications|events --json" for focused detail.\n' +
|
|
4790
|
+
' Prefer "products components list --product-tag <tag> --json" for compact inventory; use\n' +
|
|
4791
|
+
' "products components get --product-tag <tag> --type notifications|events --json" for focused detail.\n' +
|
|
4577
4792
|
' - Managing notification components and message templates through "resources notifications" and "notifications messages"\n' +
|
|
4578
4793
|
' - Linking a project folder: "link --product <tag> --env <slug>"\n' +
|
|
4579
4794
|
' - Syncing sessions/notifications/events: "apply" or "apply sessions" etc.\n' +
|
|
4580
4795
|
' - Running database migrations: "db migrate", "db schema generate"\n\n' +
|
|
4581
|
-
'NOTE: Environments
|
|
4582
|
-
'in the Workbench UI. Features have no
|
|
4583
|
-
'code-first through features.define and
|
|
4796
|
+
'NOTE: Environments have their own CLI commands (products environments *). App actions, ' +
|
|
4797
|
+
'auths, quotas, fallbacks, and jobs are configured in the Workbench UI. Features have no ' +
|
|
4798
|
+
'CLI creation command because definitions are code-first through features.define and ' +
|
|
4799
|
+
'registered by the application runtime.\n\n' +
|
|
4584
4800
|
'DO NOT use ductape_execute for admin operations — it uses a publishable key which only ' +
|
|
4585
4801
|
'covers runtime operations. Administrative operations will fail with "Authentication failed".\n\n' +
|
|
4586
4802
|
'The CLI uses the user\'s local logged-in session (ductape login). ' +
|
package/docs/TOOLS.md
CHANGED
|
@@ -4,6 +4,20 @@ The Ductape MCP server exposes **one tool**. All operations go through the backe
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## Marketplace discovery
|
|
8
|
+
|
|
9
|
+
Use the `ductape_cli` tool before creating an integration or guessing action names:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
ductape_cli("marketplace search payments --json")
|
|
13
|
+
ductape_cli("marketplace search paystack --json")
|
|
14
|
+
ductape_cli("marketplace get <app_tag> --json")
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The detail response is the authority for action tags and their body, query, header, and path
|
|
18
|
+
inputs. After an app is connected to a product, call `ductape_generate_payload` for the selected
|
|
19
|
+
action and then execute it through `actions.run`.
|
|
20
|
+
|
|
7
21
|
## Tool: `ductape_execute`
|
|
8
22
|
|
|
9
23
|
Executes a Ductape SDK operation via the backend proxy. Use this for databases, graph, storage, vector, caches, webhooks, jobs, and all other supported modules.
|