@ductape/mcp 0.2.20 → 0.2.22
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 +187 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -203,7 +203,9 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
203
203
|
App actions and auths are configured in the Workbench UI. Quotas, fallbacks, jobs, and
|
|
204
204
|
healthchecks are administrative resources managed with ductape_cli("resources <type> ...").
|
|
205
205
|
Features have no CLI create command because their definitions are code-first through
|
|
206
|
-
features.define.
|
|
206
|
+
features.define. They DO have a CLI persistence command — ductape_cli("features sync") —
|
|
207
|
+
which runs the project's own "features:sync" npm script to register ductape/features/
|
|
208
|
+
definitions against the live product; see the DEPLOYING features.define CALLS section below.
|
|
207
209
|
|
|
208
210
|
⚠ MULTI-ENV REQUIREMENT — applies to ALL product assets (storage, database, cache,
|
|
209
211
|
messageBroker, graph, vector, and any other resource with an envs array):
|
|
@@ -876,7 +878,8 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
876
878
|
// ctx.input – typed runtime input; always compiles to $Input{} operators
|
|
877
879
|
// ctx.sampleInput – compile-time sample for loop/branch discovery only
|
|
878
880
|
// ctx.step(tag, fn, rollback?, opts?) – define a durable step
|
|
879
|
-
// ctx.api.run({ app,
|
|
881
|
+
// ctx.api.run({ app, action, input }) – call an app action (NOT 'event' -- that field name
|
|
882
|
+
// only applies to ctx.database/ctx.notification/ctx.storage below, never ctx.api/ctx.action)
|
|
880
883
|
// ctx.database.query/insert/update/delete({ database, event, ... })
|
|
881
884
|
// ctx.graph.execute({ graph, action, input })
|
|
882
885
|
// ctx.notification.send/email/push/sms({ notification, event, ... })
|
|
@@ -1678,6 +1681,15 @@ PRIMITIVES-FIRST CAPABILITY EXTRACTION — REQUIRED FOR EVERY MIGRATION SLICE
|
|
|
1678
1681
|
5. For each Function, record pure/WASM-candidate versus framework-dependent classification.
|
|
1679
1682
|
6. Call ductape_function_setup and implement verified local plus remote availability.
|
|
1680
1683
|
|
|
1684
|
+
Persist migrated Features the same way as newly designed ones: define every Feature under
|
|
1685
|
+
ductape/features/ (e.g. ductape/features/src/my-feature.ts), never inline in the app's normal
|
|
1686
|
+
startup path. Register only local function/operation handlers (ductape.sdk.functions.register(...))
|
|
1687
|
+
at boot — no network call. Persist Feature definitions to the live product with
|
|
1688
|
+
"ductape features sync" (ductape_cli("features sync")), which runs the project's own
|
|
1689
|
+
"features:sync" npm script — never automatically on app boot, the same way a migrated database
|
|
1690
|
+
schema is applied via "ductape db migrate" rather than at startup. See "DEPLOYING features.define
|
|
1691
|
+
CALLS" below for the full convention and required "features:sync" script shape.
|
|
1692
|
+
|
|
1681
1693
|
Never create a Function that merely hides database, session, Events, storage, notification, graph,
|
|
1682
1694
|
vector, quota, fallback, healthcheck, cache, secret, or connected-App work that the Feature can
|
|
1683
1695
|
express directly. Never fragment an original atomic transaction just to maximize primitive count.
|
|
@@ -1741,6 +1753,17 @@ ENVIRONMENTS
|
|
|
1741
1753
|
Report matched, missing, extra, and ambiguous normalized aliases. Never guess an ambiguous mapping.
|
|
1742
1754
|
Environment mutation is administrative CLI work and must never be routed through ductape_execute.
|
|
1743
1755
|
|
|
1756
|
+
INACTIVE ENVIRONMENTS (e.g. an auto-created "snd"): whether an inactive environment needs a real
|
|
1757
|
+
physical provider resource behind it, or can stay a placeholder with no live infrastructure, is
|
|
1758
|
+
not settled platform behavior — do not assume either way. Resource-create payloads that require
|
|
1759
|
+
complete environment coverage (see above) still need an entry for every environment returned by
|
|
1760
|
+
"products environments list", active or not; whether that entry must point at a real backing
|
|
1761
|
+
resource for an inactive env is a product-owner decision to surface explicitly, not infer.
|
|
1762
|
+
Separately: "products environments update <product> <slug>" with {"active":false} may report
|
|
1763
|
+
"updated": true without the change actually persisting — confirmed via independent read-back,
|
|
1764
|
+
not just replication lag. If deactivating an environment is important, verify with a follow-up
|
|
1765
|
+
"products environments get" rather than trusting the update response.
|
|
1766
|
+
|
|
1744
1767
|
PRODUCT AND ASSET BOOTSTRAP
|
|
1745
1768
|
Product creation is idempotent: fetch by tag, create only when absent, then link the destination.
|
|
1746
1769
|
Use ductape_cli for products, apps, resources, cloud connections, secrets, apply, and migrations.
|
|
@@ -1797,6 +1820,8 @@ COMPONENT DECISIONS
|
|
|
1797
1820
|
Pinecone/Qdrant/Weaviate/OpenSearch → Vector; JWT/session middleware → Sessions.
|
|
1798
1821
|
Named reusable product capabilities with a stable managed-execution boundary → code-first Features,
|
|
1799
1822
|
including synchronous multi-step capabilities and durable/scheduled/signal-driven orchestration.
|
|
1823
|
+
Define Features under ductape/features/ and persist them via "ductape features sync", never at
|
|
1824
|
+
app boot — see PRIMITIVES-FIRST CAPABILITY EXTRACTION above.
|
|
1800
1825
|
Retries, health checks, fallbacks, quotas, circuit breakers → Resilience.
|
|
1801
1826
|
Keep low-level deterministic rules as ordinary domain functions when they do not form a useful
|
|
1802
1827
|
independent capability boundary. Wrap or compose them into Features when the combined operation
|
|
@@ -1840,8 +1865,10 @@ AI EDITING STANDARD
|
|
|
1840
1865
|
rules in code, while allowing a coherent synchronous capability composed from those rules to be a Feature.
|
|
1841
1866
|
Immediate work propagates the full session; durable work uses approved actor metadata or system context.
|
|
1842
1867
|
Consumers are idempotent, retry ownership is singular and bounded, and external effects are not duplicated.
|
|
1843
|
-
After each slice: format, build, test, rescan,
|
|
1844
|
-
|
|
1868
|
+
After each slice: format, build, test, rescan, persist any migrated Feature definitions via
|
|
1869
|
+
ductape_cli("features sync") (never automatically on app boot), reconcile assets through
|
|
1870
|
+
ductape_cli, smoke-test in snd, and report unresolved findings. A clean scanner result alone
|
|
1871
|
+
never proves a correct migration.
|
|
1845
1872
|
|
|
1846
1873
|
CONTEXTUAL FILE REVIEW PROTOCOL
|
|
1847
1874
|
Use review_queue as a coverage aid. Review relevant files individually, starting with repository
|
|
@@ -2501,7 +2528,8 @@ Import an existing resource and register it on the product:
|
|
|
2501
2528
|
File is a JSON ARRAY — one entry per env, same product + component tag across all entries.
|
|
2502
2529
|
Each entry: { cloud, service, type, product, component, env, resource, region?, dbName? }
|
|
2503
2530
|
Supported service identifiers: s3, gcs, blob, rds, postgresql, cloudsql, sqs, pubsub,
|
|
2504
|
-
servicebus, neptune, cosmos-gremlin, opensearch, azure-search, atlas-cluster, aura-instance
|
|
2531
|
+
servicebus, neptune, cosmos-gremlin, opensearch, azure-search, atlas-cluster, aura-instance,
|
|
2532
|
+
vertex-vector-search, spanner-graph, dynamodb, keyspaces, mysql
|
|
2505
2533
|
|
|
2506
2534
|
Provision a brand-new resource and register it:
|
|
2507
2535
|
ductape_cli("cloud resources provision-persist-all -f all-envs.json --json")
|
|
@@ -2509,6 +2537,20 @@ Provision a brand-new resource and register it:
|
|
|
2509
2537
|
List available tiers first: ductape_cli("cloud tiers --provider aws --type database --db-type postgresql --json")
|
|
2510
2538
|
NEVER infer region, tier, or cost — always list and confirm with the user first.
|
|
2511
2539
|
Atlas and Neo4j Aura are IMPORT-ONLY — provision is not supported for these providers.
|
|
2540
|
+
Per-provider provisionable services: aws → s3, sqs, rds, neptune, opensearch, dynamodb, keyspaces.
|
|
2541
|
+
gcp → gcs, cloudsql, pubsub, spanner-graph, vertex-vector-search.
|
|
2542
|
+
azure → blob, servicebus, postgresql, mysql, cosmos-gremlin, azure-search.
|
|
2543
|
+
|
|
2544
|
+
vertex-vector-search (GCP): additional per-entry params: dimensions (default 1536), metric
|
|
2545
|
+
("cosine"|"euclidean"|"dotproduct", default cosine), algorithm ("tree-ah"|"brute-force",
|
|
2546
|
+
default tree-ah), deployIndex (default true — set false to create the Index and Index
|
|
2547
|
+
Endpoint without deploying, avoiding ongoing compute cost until you deploy manually),
|
|
2548
|
+
machineType (default "e2-standard-2"), minReplicaCount/maxReplicaCount (default 1).
|
|
2549
|
+
Provisioning creates the Index Endpoint, the actual Index (STREAM_UPDATE method, required
|
|
2550
|
+
for runtime vector.upsert/upsertOne), and deploys the Index to the Endpoint — all three are
|
|
2551
|
+
needed for the result to be queryable. A DEPLOYED index runs dedicated compute continuously
|
|
2552
|
+
(real ongoing cost, not free-tier) — always confirm machine type/replica count with the user
|
|
2553
|
+
before provisioning, the same as any other tier/cost decision.
|
|
2512
2554
|
|
|
2513
2555
|
VPC connector (private networking):
|
|
2514
2556
|
ductape_cli("cloud connections vpc update <tag> --vpc-id vpc-xxx --subnet-ids subnet-1,subnet-2")
|
|
@@ -2679,8 +2721,20 @@ CONNECT A DISCOVERED APP THROUGH MCP:
|
|
|
2679
2721
|
Never guess environment mappings or action tags. Connecting mutates product configuration;
|
|
2680
2722
|
obtain user approval when the user has not already requested the connection.
|
|
2681
2723
|
|
|
2724
|
+
CONFIRMED LIMITATION (2026-08-17): both ductape_marketplace_connect and the equivalent
|
|
2725
|
+
ductape_cli("products apps connect --product <tag> --app <app-tag> --env-map ...") reject any
|
|
2726
|
+
app that is not marked public in the marketplace, with error
|
|
2727
|
+
App "<tag>" is not public in the marketplace. This includes apps a user creates themselves under
|
|
2728
|
+
their own tag (e.g. a private "ductape:paystack") — private/workspace-owned apps CANNOT be
|
|
2729
|
+
connected to a product through MCP or CLI at all, only through Workbench, even though
|
|
2730
|
+
ductape_marketplace_inspect can still read a private app's full action catalogue (inspect and
|
|
2731
|
+
connect have different visibility rules). If you hit this, do not keep retrying — tell the user
|
|
2732
|
+
the app must be connected via Workbench, and once they confirm it's connected, verify with
|
|
2733
|
+
ductape_cli("products apps list --product <product_tag> --json") rather than attempting the
|
|
2734
|
+
connect call again.
|
|
2735
|
+
|
|
2682
2736
|
ONLY after all five steps can any code call:
|
|
2683
|
-
ctx.api.run({ app: '<app_tag>',
|
|
2737
|
+
ctx.api.run({ app: '<app_tag>', action: '<action_tag>', input: { ... } }) ← in a feature handler
|
|
2684
2738
|
actions.run([{ product, env, app: '<app_tag>', action: '<action_tag>', input }]) ← at runtime
|
|
2685
2739
|
|
|
2686
2740
|
ctx.api (alias for ctx.action) is NOT a generic HTTP call. It ONLY invokes a pre-registered
|
|
@@ -2745,8 +2799,11 @@ Run an action at runtime:
|
|
|
2745
2799
|
ductape_execute("actions.dispatch", [{ product, env, app, action, input, schedule? }])
|
|
2746
2800
|
|
|
2747
2801
|
In a Ductape feature handler, call the registered action through:
|
|
2748
|
-
await ctx.api.run({ app: "<app_tag>",
|
|
2802
|
+
await ctx.api.run({ app: "<app_tag>", action: "<action_tag>", input: { ... } })
|
|
2749
2803
|
ctx.api is the supported feature-context surface (also described as ctx.action in older code).
|
|
2804
|
+
NEVER use 'event' as the field name here -- that is only correct for ctx.database/ctx.notification/
|
|
2805
|
+
ctx.storage steps. ctx.api.run's real field is 'action'; passing 'event' instead silently produces
|
|
2806
|
+
an unset step and fails feature compilation with "did not record a portable operation".
|
|
2750
2807
|
There is no ctx.apps, ctx.integrations, or generic external-HTTP feature surface.
|
|
2751
2808
|
|
|
2752
2809
|
PAYSTACK CONFIGURATION:
|
|
@@ -3060,6 +3117,23 @@ TAG AND SMTP RULES
|
|
|
3060
3117
|
emails.smtp.secure is a boolean and cannot be a $Secret{...} string.
|
|
3061
3118
|
Credential strings such as auth.user and auth.pass may use $Secret{...}.
|
|
3062
3119
|
|
|
3120
|
+
notifications messages create/update/get had NO --product override before CLI 0.3.17 — it
|
|
3121
|
+
silently resolved the product from whatever is linked in the current working directory, with
|
|
3122
|
+
no way to override. Confirmed root cause of a real multi-hour misdiagnosis: it kept resolving
|
|
3123
|
+
to a different product than intended, and the resulting "Notification X not found" error never
|
|
3124
|
+
named which product it actually checked. If seeing this on an older CLI, verify which project
|
|
3125
|
+
is linked in cwd before assuming the notification component itself is missing.
|
|
3126
|
+
|
|
3127
|
+
SendGrid field names (confirmed against the live Joi validator, not just this doc — cross-check
|
|
3128
|
+
the SDK source when in doubt): { provider: "sendgrid", sendgrid: { apiKey: "$Secret{...}",
|
|
3129
|
+
sender_email: "$Secret{...}" } }. The field is sender_email, not sender — "sender" is silently
|
|
3130
|
+
rejected with a required-field error naming sender_email specifically, not a helpful diff.
|
|
3131
|
+
|
|
3132
|
+
Nexmo/Vonage SMS field names: { provider: "nexmo", apiKey: "$Secret{...}",
|
|
3133
|
+
apiSecret: "$Secret{...}", sender: "YourSenderId" }. The credential fields are apiKey/apiSecret
|
|
3134
|
+
— NOT accountSid (that is a Twilio-only field name and is rejected with "not allowed" if used
|
|
3135
|
+
for nexmo).
|
|
3136
|
+
|
|
3063
3137
|
Send at runtime (one channel at a time):
|
|
3064
3138
|
→ CALL ductape_generate_payload FIRST (operation_family="notification", method="email.send")
|
|
3065
3139
|
notifications.email.send [{ product, env, notification, input: { recipients, subject?, template? } }]
|
|
@@ -3111,6 +3185,16 @@ FIREBASE THROUGH A GCP CLOUD CONNECTION
|
|
|
3111
3185
|
intent; creating a notification does not prove Google-side FCM permission. Runtime delivery
|
|
3112
3186
|
requires fcm.googleapis.com and roles/firebasecloudmessaging.admin, so validate the connection
|
|
3113
3187
|
and perform a delivery test.
|
|
3188
|
+
|
|
3189
|
+
CONFIRMED CONTRADICTION (2026-08-17): despite the "never put the private key in the file"
|
|
3190
|
+
guidance above, resources notifications create with authMode: "cloud_connection" currently
|
|
3191
|
+
rejects the payload progressively demanding envs[].push_notifications.credentials.project_id,
|
|
3192
|
+
then .private_key_id, then .client_email, then finally the raw .private_key itself — the full
|
|
3193
|
+
manual service-account shape, defeating the documented purpose of cloud_connection mode. Do NOT
|
|
3194
|
+
fabricate a placeholder private_key to satisfy this — that field is genuinely sensitive and a
|
|
3195
|
+
fake value risks being mistaken for real credential material later. If you hit this, drop the
|
|
3196
|
+
push_notifications block and register email/SMS only until this is fixed, and tell the user
|
|
3197
|
+
explicitly rather than working around it with fake data.
|
|
3114
3198
|
Notification tag and message tag are ALWAYS passed together as "notification_tag:message_tag".
|
|
3115
3199
|
`.trim(),
|
|
3116
3200
|
resilience: `
|
|
@@ -3641,6 +3725,55 @@ STEP 6 — WRITE the feature into the project codebase
|
|
|
3641
3725
|
- Write rollback handlers inline as the third argument to ctx.step()
|
|
3642
3726
|
- Return a plain object as the feature's output
|
|
3643
3727
|
|
|
3728
|
+
━━━ DEPLOYING features.define CALLS — DO NOT run them on every app boot ━━━
|
|
3729
|
+
|
|
3730
|
+
features.define() has a network-bound RECORDING PHASE (see FEATURE RECORDING SEMANTICS below) that
|
|
3731
|
+
calls the live Ductape API to compile and register the Feature's step graph. This is administrative
|
|
3732
|
+
configuration work, not a request-serving concern — treat it exactly like a database migration:
|
|
3733
|
+
defined in source, but APPLIED via an explicit, separate command, never automatically on every
|
|
3734
|
+
process start.
|
|
3735
|
+
|
|
3736
|
+
Calling features.define() from inside a framework's normal startup lifecycle hook (NestJS
|
|
3737
|
+
onModuleInit, Express app bootstrap, a Lambda cold-start path, etc.) makes ordinary app startup
|
|
3738
|
+
depend on Ductape API reachability and latency for every single boot/restart/replica — including
|
|
3739
|
+
local dev restarts, autoscaling events, and health-check-triggered restarts. A slow or unreachable
|
|
3740
|
+
Ductape API then blocks the app from ever starting to serve requests, even for routes that have
|
|
3741
|
+
nothing to do with the affected Feature.
|
|
3742
|
+
|
|
3743
|
+
REQUIRED CONVENTION — this is what "ductape features sync" (CLI) expects:
|
|
3744
|
+
1. Put every Feature definition under ductape/features/ (e.g. ductape/features/src/my-feature.ts),
|
|
3745
|
+
each calling ductape.feature.define({ ... }) from a registerXFeature(ductape) export. This
|
|
3746
|
+
mirrors ductape/database/migrations/ — a fixed, discoverable location for Ductape-managed
|
|
3747
|
+
source, regardless of the surrounding project structure.
|
|
3748
|
+
2. In the app's normal startup path, register ONLY local function/operation handlers
|
|
3749
|
+
(ductape.sdk.functions.register(...)) — no network call, safe on every boot. Never call
|
|
3750
|
+
features.define(...) here.
|
|
3751
|
+
3. Add a "features:sync" script to package.json. It owns booting just enough of the app to
|
|
3752
|
+
construct real dependencies (in NestJS, use NestFactory.createApplicationContext(module) —
|
|
3753
|
+
same DI container, no HTTP listener) and calling every registerXFeature(...) once, then
|
|
3754
|
+
exiting. Ductape cannot run this step for you generically — a Feature handler is real
|
|
3755
|
+
application code that typically depends on the app's own services, unlike a migration file,
|
|
3756
|
+
which is declarative data the Ductape backend can apply directly.
|
|
3757
|
+
4. Persist Features by running "ductape features sync" (or ductape_cli("features sync")), which
|
|
3758
|
+
finds the linked project and runs its "features:sync" script — never automatically on boot.
|
|
3759
|
+
Pass an optional filter argument to scope it: "ductape features sync payments".
|
|
3760
|
+
|
|
3761
|
+
A NestJS service with both concerns split looks like:
|
|
3762
|
+
async onModuleInit() {
|
|
3763
|
+
this.ductape.sdk.functions.register({ ...myFunctions, operations: { ... /* local handlers */ } });
|
|
3764
|
+
}
|
|
3765
|
+
async syncFeatures(): Promise<void> {
|
|
3766
|
+
await registerMyFeature(this.ductape.sdk); // the features.define(...) call
|
|
3767
|
+
}
|
|
3768
|
+
The project's own "features:sync" script resolves each service with a syncFeatures() method via a
|
|
3769
|
+
headless application context and calls it — mirroring how a migration runner applies pending
|
|
3770
|
+
migrations explicitly. "ductape features sync" is a thin, framework-agnostic front door onto that
|
|
3771
|
+
script; it does not itself execute application code.
|
|
3772
|
+
|
|
3773
|
+
This applies to any framework, not just NestJS — the only requirement is that whatever construct
|
|
3774
|
+
the app boots FOR REQUESTS never itself calls features.define(); the project's own "features:sync"
|
|
3775
|
+
script does, invoked only through the explicit CLI command.
|
|
3776
|
+
|
|
3644
3777
|
STEP 7 — HANDLE conditionals, loops, and branching (when applicable)
|
|
3645
3778
|
Branch on step result (early return in handler):
|
|
3646
3779
|
→ Add branchOverrides: { stepTag: { field: value } } so all branches are recorded
|
|
@@ -3657,8 +3790,8 @@ STEP 8 — SET rollbacks for reversible steps
|
|
|
3657
3790
|
ctx.api.run requires a pre-registered Ductape App — 'stripe' below is the tag of a registered App:
|
|
3658
3791
|
const charge = await ctx.step(
|
|
3659
3792
|
'charge',
|
|
3660
|
-
async () => ctx.api.run({ app: 'stripe',
|
|
3661
|
-
async (result) => ctx.api.run({ app: 'stripe',
|
|
3793
|
+
async () => ctx.api.run({ app: 'stripe', action: 'create-charge', input: { amount: ctx.input.amount } }),
|
|
3794
|
+
async (result) => ctx.api.run({ app: 'stripe', action: 'refund', input: { chargeId: result.id } })
|
|
3662
3795
|
);
|
|
3663
3796
|
|
|
3664
3797
|
Code-first ctx step types currently record: function | action | database | graph | vector | session |
|
|
@@ -5077,9 +5210,11 @@ const cliInputSchema = z.object({
|
|
|
5077
5210
|
'Note: environments have their own CLI commands (products environments list/get/create/update, ' +
|
|
5078
5211
|
'no linked project required — the product tag is always an explicit argument). Quotas, ' +
|
|
5079
5212
|
'fallbacks, jobs, and healthchecks use resources commands. App actions and auths are ' +
|
|
5080
|
-
'configured in the Workbench UI. Features have no CLI ' +
|
|
5081
|
-
'
|
|
5082
|
-
'
|
|
5213
|
+
'configured in the Workbench UI. Features have no CLI creation command: define them in ' +
|
|
5214
|
+
'application code with features.define under ductape/features/. Persist them with ' +
|
|
5215
|
+
'"features sync" (runs the project\'s own "features:sync" npm script) — never call ' +
|
|
5216
|
+
'features.define from the app\'s normal startup path, since that blocks every boot on ' +
|
|
5217
|
+
'Ductape API reachability. See ductape_docs for the full convention.\n\n' +
|
|
5083
5218
|
'The CLI uses the user\'s local logged-in session (ductape login) — no key is required.'),
|
|
5084
5219
|
});
|
|
5085
5220
|
async function loadMcpSdk() {
|
|
@@ -5355,6 +5490,26 @@ async function main() {
|
|
|
5355
5490
|
return p;
|
|
5356
5491
|
});
|
|
5357
5492
|
}
|
|
5493
|
+
// Most non-setup publishable-key runtime calls require params[0] to carry a "session" field
|
|
5494
|
+
// (confirmed 2026-08-18 against live backend logs: the proxy's own error is "When using
|
|
5495
|
+
// publishable key, params must include a session (session token from backend)." — the HTTP
|
|
5496
|
+
// response to the client only ever says the much vaguer "Authentication failed", so this
|
|
5497
|
+
// failure mode is otherwise very hard to diagnose from the client side alone). Fail fast
|
|
5498
|
+
// locally with a specific message instead of making a doomed network call.
|
|
5499
|
+
const sessionRequiredModules = new Set([
|
|
5500
|
+
'actions', 'features', 'databases', 'graph', 'vector', 'storage',
|
|
5501
|
+
'notifications', 'messageBrokers', 'events', 'quotas', 'fallback', 'health',
|
|
5502
|
+
]);
|
|
5503
|
+
const sessionExemptMethods = new Set(['consume', 'status', 'check', 'fetch', 'list']);
|
|
5504
|
+
const requiresSession = sessionRequiredModules.has(proxyModule) && !sessionExemptMethods.has(args.method.toLowerCase());
|
|
5505
|
+
const paramsHaveSession = params.some((p) => p && typeof p === 'object' && !Array.isArray(p) && 'session' in p);
|
|
5506
|
+
if (requiresSession && !paramsHaveSession) {
|
|
5507
|
+
throw new Error(`"${args.module}.${args.method}" is a runtime operation and requires a "session" field in ` +
|
|
5508
|
+
'params[0] — none was provided. ductape_execute cannot supply a session on your behalf. ' +
|
|
5509
|
+
'Call ductape_generate_payload for this operation to see the exact param shape, obtain a ' +
|
|
5510
|
+
'real session token (e.g. via sessions.start against this product/env — not a placeholder ' +
|
|
5511
|
+
'string), and include it as params[0].session before retrying.');
|
|
5512
|
+
}
|
|
5358
5513
|
let result;
|
|
5359
5514
|
try {
|
|
5360
5515
|
result = await executeViaProxy(key, proxyModule, args.method, params);
|
|
@@ -5362,8 +5517,17 @@ async function main() {
|
|
|
5362
5517
|
catch (error) {
|
|
5363
5518
|
const message = error instanceof Error ? error.message : String(error);
|
|
5364
5519
|
if (/authentication failed|unauthorized|invalid.*key/i.test(message)) {
|
|
5365
|
-
|
|
5366
|
-
'
|
|
5520
|
+
const sessionHint = !paramsHaveSession
|
|
5521
|
+
? ' No "session" field was present in params — most non-setup publishable-key runtime ' +
|
|
5522
|
+
'calls require one (a real token from sessions.start, not a placeholder). This is the ' +
|
|
5523
|
+
'most common cause of an opaque "Authentication failed" response; call ' +
|
|
5524
|
+
'ductape_generate_payload first to see whether this operation expects a session field, ' +
|
|
5525
|
+
'then obtain a real session token before retrying.'
|
|
5526
|
+
: ' A "session" field was present, so a missing session is likely not the cause here — ' +
|
|
5527
|
+
'check that the publishable key itself belongs to the same workspace/product, and that ' +
|
|
5528
|
+
'any module/action restrictions on the key (Workbench → Tokens → Publishable Key) ' +
|
|
5529
|
+
'permit this operation.';
|
|
5530
|
+
throw new Error(`Runtime authentication was rejected (backend said: "${message}").` + sessionHint);
|
|
5367
5531
|
}
|
|
5368
5532
|
throw error;
|
|
5369
5533
|
}
|
|
@@ -5540,7 +5704,15 @@ async function main() {
|
|
|
5540
5704
|
'Two-step rule for runtime operations:\n' +
|
|
5541
5705
|
' 1. Call ductape_generate_payload first to get the canonical payload template.\n' +
|
|
5542
5706
|
' This reveals the exact "input" field keys — they are product/env/operation-specific.\n' +
|
|
5543
|
-
' 2. Fill in the values from the template, then call ductape_execute
|
|
5707
|
+
' 2. Fill in the values from the template, then call ductape_execute.\n\n' +
|
|
5708
|
+
'Session requirement: most non-setup operations on actions, features, databases, graph, ' +
|
|
5709
|
+
'vector, storage, notifications, message brokers/events, quotas, fallback, and health ' +
|
|
5710
|
+
'require a real session token in params[0].session (from sessions.start against the same ' +
|
|
5711
|
+
'product/env — never a placeholder string). This tool checks for that locally and fails ' +
|
|
5712
|
+
'fast with a specific error before calling the backend if it is missing for an operation ' +
|
|
5713
|
+
'that needs one. The backend itself only reports a generic "Authentication failed" for ' +
|
|
5714
|
+
'this case, so a missing session and a genuinely bad key look identical unless you read ' +
|
|
5715
|
+
'this local check first.',
|
|
5544
5716
|
inputSchema: executeInputSchema,
|
|
5545
5717
|
}, executeHandler);
|
|
5546
5718
|
server.registerTool('ductape_generate_payload', {
|