@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.
Files changed (2) hide show
  1. package/dist/index.js +187 -15
  2. 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, event, input }) – call an app action
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, reconcile assets through ductape_cli, smoke-test in snd,
1844
- and report unresolved findings. A clean scanner result alone never proves a correct migration.
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>', event: '<action_tag>', input: { ... } }) ← in a feature handler
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>", event: "<action_tag>", input: { ... } })
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', event: 'create-charge', input: { amount: ctx.input.amount } }),
3661
- async (result) => ctx.api.run({ app: 'stripe', event: 'refund', input: { chargeId: result.id } })
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
- 'creation command: define them in application code with features.define so application ' +
5082
- 'boot/runtime registration makes them available.\n\n' +
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
- throw new Error('The DUCTAPE_PUBLISHABLE_KEY was rejected by the runtime proxy. ' +
5366
- 'Use a publishable key for the same workspace/product. The MCP server never accepts or forwards access keys.');
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', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "description": "MCP server that exposes Ductape SDK operations via the backend proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",