@ductape/mcp 0.2.1 → 0.2.3

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 +89 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,6 +11,8 @@
11
11
  */
12
12
  import { createRequire } from 'module';
13
13
  import { execSync } from 'child_process';
14
+ import { homedir } from 'os';
15
+ import { delimiter, join } from 'path';
14
16
  import { z } from 'zod';
15
17
  import { executeViaProxy, generateExecutablePayload, getAssetSchemas, } from './proxy-client.js';
16
18
  const MODULES = [
@@ -35,7 +37,24 @@ There are THREE categories of operations. Use the right tool for each:
35
37
  ductape_cli("cloud connections list")
36
38
  ductape_cli("link --product my-product --env dev")
37
39
  If the CLI is not installed, ductape_cli will return install instructions automatically.
38
- NOTE: Environments and app actions are configured in the Workbench UI — there are no CLI commands for them.
40
+ NOTE: App actions are configured in the Workbench UI — there is no CLI command for them.
41
+ Environments DO have CLI commands: ductape_cli("products environments list/get/create/update ...").
42
+
43
+ RESOLVING "No linked project" ERRORS:
44
+ Some commands (declarative sync below, db migrate/schema, products environments *) need a
45
+ linked project — a local .ductape/config.json with a product tag and env slug. The "link"
46
+ command does NOT validate against the server: it just writes that local file. This means you
47
+ can run ductape_cli("link --product <tag> --env <slug>") even before that environment exists on
48
+ the product yet — do not treat "no linked environment exists server-side" as a reason to avoid
49
+ linking first. If the failing command already takes the product tag as an explicit argument
50
+ (e.g. products environments create/update/list/get), linking is not even required for it —
51
+ only commands that need to *infer* the product/env from local project state require a link.
52
+ Also: this MCP server runs the CLI subprocess in the directory named by the DUCTAPE_PROJECT_DIR
53
+ env var (falls back to this server process's own cwd if unset). If a command inexplicably
54
+ reports "no linked project" right after a successful "link" call, the project directory the
55
+ link was written to and the directory this server is running the CLI from may not match — set
56
+ DUCTAPE_PROJECT_DIR explicitly in this server's env (e.g. in the consuming project's .mcp.json)
57
+ to the target project's absolute path.
39
58
 
40
59
  DECLARATIVE SYNC (apply sessions, notifications, events from code; run DB migrations)
41
60
  → Also use ductape_cli. The project must be linked first (ductape init --link).
@@ -123,9 +142,11 @@ There are THREE categories of operations. Use the right tool for each:
123
142
  ductape_cli("resources storage list")
124
143
  ductape_cli("resources database create -f db-config.json")
125
144
  This applies to: products, apps, and resources (databases, storage, caches, etc.),
126
- cloud connections, and secrets. Environments, app actions, auths, quotas, fallbacks,
127
- jobs, and healthchecks are configured in the Workbench UI. Features have no CLI create
128
- command because their definitions are code-first through features.define.
145
+ cloud connections, and secrets. Environments have their own CLI commands (see below);
146
+ App actions and auths are configured in the Workbench UI. Quotas, fallbacks, jobs, and
147
+ healthchecks are administrative resources managed with ductape_cli("resources <type> ...").
148
+ Features have no CLI create command because their definitions are code-first through
149
+ features.define.
129
150
 
130
151
  ⚠ MULTI-ENV REQUIREMENT — applies to ALL product assets (storage, database, cache,
131
152
  messageBroker, graph, vector, and any other resource with an envs array):
@@ -324,9 +345,9 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
324
345
  IMPORTANT: ALL product.* methods require the access key and will return 403 with a publishable key.
325
346
  Use ductape_cli for ALL product operations — never ductape_execute:
326
347
  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")
348
+ ductape_cli("products components list --product-tag <tag> --json") ← compact non-secret inventory
349
+ ductape_cli("products components get --product-tag <tag> --type notifications --json")
350
+ ductape_cli("products components get --product-tag <tag> --type events --json")
330
351
  ductape_cli("products create --name <name> --tag <tag>")
331
352
  ductape_cli("products environments list <tag> --json")
332
353
  ductape_cli("products environments get <tag> <slug> --json")
@@ -1157,14 +1178,19 @@ function checkCli() {
1157
1178
  try {
1158
1179
  const out = execSync('ductape --version', {
1159
1180
  encoding: 'utf8',
1160
- timeout: 5000,
1181
+ timeout: 15000,
1161
1182
  stdio: ['pipe', 'pipe', 'pipe'],
1162
1183
  env: cliEnvironment(),
1184
+ cwd: cliCwd(),
1163
1185
  }).trim();
1164
1186
  return { available: true, version: out || 'unknown' };
1165
1187
  }
1166
- catch {
1167
- return { available: false };
1188
+ catch (error) {
1189
+ const message = String(error?.stderr || error?.message || error);
1190
+ const commandMissing = error?.code === 'ENOENT' ||
1191
+ error?.status === 127 ||
1192
+ /\b(command not found|not recognized as an internal|no such file or directory)\b/i.test(message);
1193
+ return { available: !commandMissing };
1168
1194
  }
1169
1195
  }
1170
1196
  function checkLoginState() {
@@ -1177,6 +1203,7 @@ function checkLoginState() {
1177
1203
  timeout: 10000,
1178
1204
  stdio: ['pipe', 'pipe', 'pipe'],
1179
1205
  env: cliEnvironment(),
1206
+ cwd: cliCwd(),
1180
1207
  });
1181
1208
  authState = 'ok';
1182
1209
  return 'ok';
@@ -1197,6 +1224,7 @@ function syncWorkspace() {
1197
1224
  timeout: 10000,
1198
1225
  stdio: ['pipe', 'pipe', 'pipe'],
1199
1226
  env: cliEnvironment(),
1227
+ cwd: cliCwd(),
1200
1228
  });
1201
1229
  }
1202
1230
  catch {
@@ -1225,6 +1253,7 @@ function runCli(command) {
1225
1253
  timeout: 90000,
1226
1254
  stdio: ['pipe', 'pipe', 'pipe'],
1227
1255
  env: cliEnvironment(),
1256
+ cwd: cliCwd(),
1228
1257
  });
1229
1258
  return { success: true, output: output.trim() };
1230
1259
  }
@@ -1276,8 +1305,33 @@ function runCli(command) {
1276
1305
  function cliEnvironment() {
1277
1306
  const environment = { ...process.env };
1278
1307
  delete environment.DUCTAPE_ACCESS_KEY;
1308
+ const pathKey = Object.keys(environment).find((key) => key.toLowerCase() === 'path') ?? 'PATH';
1309
+ const configuredPath = environment[pathKey] ?? '';
1310
+ const fallbackBins = [
1311
+ join(homedir(), '.npm-global', 'bin'),
1312
+ join(homedir(), '.local', 'bin'),
1313
+ '/opt/homebrew/bin',
1314
+ '/usr/local/bin',
1315
+ ];
1316
+ environment[pathKey] = [...new Set([
1317
+ ...configuredPath.split(delimiter).filter(Boolean),
1318
+ ...fallbackBins,
1319
+ ])].join(delimiter);
1279
1320
  return environment;
1280
1321
  }
1322
+ /**
1323
+ * Directory the `ductape` CLI subprocess runs in. Commands that resolve a linked project
1324
+ * (findProjectConfig walking up from cwd — e.g. `products environments *`, `apply`, `db migrate`)
1325
+ * depend on this being the user's actual project directory, not wherever this MCP server process
1326
+ * itself happened to be spawned from. execSync inherits process.cwd() when no cwd is given, which
1327
+ * is only correct if this server was started from inside the target project — that doesn't hold
1328
+ * for every launch path (e.g. a host attaching this server to an already-running session whose
1329
+ * cwd is unrelated to the project). Set DUCTAPE_PROJECT_DIR explicitly in the server's env
1330
+ * (e.g. in .mcp.json) to pin it; falls back to this process's own cwd otherwise.
1331
+ */
1332
+ function cliCwd() {
1333
+ return process.env.DUCTAPE_PROJECT_DIR || process.cwd();
1334
+ }
1281
1335
  function shellArgument(value) {
1282
1336
  return `'${value.replace(/'/g, `'\\''`)}'`;
1283
1337
  }
@@ -1376,10 +1430,12 @@ ENVIRONMENTS
1376
1430
  (production→prd, sandbox→snd, staging→stg) but present uncertain mappings for confirmation.
1377
1431
  Before creating any asset, list product environments and require complete per-environment coverage.
1378
1432
  Missing environments can be created idempotently through the authenticated standalone CLI:
1379
- ductape_cli("products environments create <product-tag> -f <environment.json> --json")
1433
+ ductape_cli("products environments create <product-tag> --env-file <environment.json> --json")
1380
1434
  The JSON requires env_name, description, and a three-character slug. The CLI fetches first, creates only
1381
- when absent, then fetches again to verify persistence. Update and verify with:
1382
- ductape_cli("products environments update <product-tag> <slug> -f <patch.json> --json")
1435
+ when absent, then fetches again to verify persistence (with a short retry on the verification read to
1436
+ absorb replication lag). No linked project is required — the product tag is always the explicit argument.
1437
+ Update and verify with:
1438
+ ductape_cli("products environments update <product-tag> <slug> --env-file <patch.json> --json")
1383
1439
  Export authenticated inventory with ductape_cli("products environments list <product-tag> --json"),
1384
1440
  save it as evidence, then reconcile locally:
1385
1441
  ductape_cli("migration-environments --analysis <analysis.json> --inventory <inventory.json> --strict --json")
@@ -2751,10 +2807,13 @@ Resilience covers three mechanisms: quotas (rate-limited provider pools), fallba
2751
2807
  provider switching), and healthchecks (continuous probe monitoring with failure actions).
2752
2808
 
2753
2809
  CONFIGURATION BOUNDARY
2754
- Quotas, fallbacks, and health checks are administrative product configuration. Configure them in
2755
- Workbench (or a future access-key administrative tool explicitly documented for the asset).
2756
- Never route administrative create/update methods through ductape_execute: its publishable-key
2757
- runtime proxy will fail.
2810
+ Quotas, fallbacks, and health checks are administrative product configuration. Manage them with
2811
+ the authenticated CLI:
2812
+ ductape_cli("resources quota ...")
2813
+ ductape_cli("resources fallback ...")
2814
+ ductape_cli("resources health ...")
2815
+ Workbench is also supported. Never route their administrative create/update methods through
2816
+ ductape_execute: its publishable-key runtime proxy will fail.
2758
2817
 
2759
2818
  QUOTAS — rate-limited multi-provider pools:
2760
2819
  Workbench definition shape:
@@ -4294,9 +4353,12 @@ const cliInputSchema = z.object({
4294
4353
  'Use this tool for administrative operations: creating or updating products, apps, ' +
4295
4354
  'resources (databases, storage, caches…), event broker topics, cloud connections, secrets, ' +
4296
4355
  'and for apply/migrate workflows.\n\n' +
4297
- 'Note: environments, app actions, quotas, fallbacks, and jobs are configured in the ' +
4298
- 'Workbench UI. Features also have no CLI creation command: define them in application code ' +
4299
- 'with features.define so application boot/runtime registration makes them available.\n\n' +
4356
+ 'Note: environments have their own CLI commands (products environments list/get/create/update, ' +
4357
+ 'no linked project required the product tag is always an explicit argument). Quotas, ' +
4358
+ 'fallbacks, jobs, and healthchecks use resources commands. App actions and auths are ' +
4359
+ 'configured in the Workbench UI. Features have no CLI ' +
4360
+ 'creation command: define them in application code with features.define so application ' +
4361
+ 'boot/runtime registration makes them available.\n\n' +
4300
4362
  'The CLI uses the user\'s local logged-in session (ductape login) — no key is required.'),
4301
4363
  });
4302
4364
  async function loadMcpSdk() {
@@ -4748,15 +4810,17 @@ async function main() {
4748
4810
  ' type field = "messageBrokers" (not "messagebrokers" or "events").\n' +
4749
4811
  ' 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' +
4750
4812
  ' - Listing workspaces, products, focused product components, secrets\n' +
4751
- ' Prefer "products components list --tag <tag> --json" for compact inventory; use\n' +
4752
- ' "products components get --tag <tag> --type notifications|events --json" for focused detail.\n' +
4813
+ ' Prefer "products components list --product-tag <tag> --json" for compact inventory; use\n' +
4814
+ ' "products components get --product-tag <tag> --type notifications|events|healthchecks|features --json" for focused detail.\n' +
4753
4815
  ' - Managing notification components and message templates through "resources notifications" and "notifications messages"\n' +
4754
4816
  ' - Linking a project folder: "link --product <tag> --env <slug>"\n' +
4755
4817
  ' - Syncing sessions/notifications/events: "apply" or "apply sessions" etc.\n' +
4756
4818
  ' - Running database migrations: "db migrate", "db schema generate"\n\n' +
4757
- 'NOTE: Environments, app actions, auths, quotas, fallbacks, and jobs are configured ' +
4758
- 'in the Workbench UI. Features have no CLI creation command because definitions are ' +
4759
- 'code-first through features.define and registered by the application runtime.\n\n' +
4819
+ 'NOTE: Environments have their own CLI commands (products environments *). App actions ' +
4820
+ 'and auths are configured in the Workbench UI. Quotas, fallbacks, jobs, and healthchecks ' +
4821
+ 'are managed with resources commands. Features have no ' +
4822
+ 'CLI creation command because definitions are code-first through features.define and ' +
4823
+ 'registered by the application runtime.\n\n' +
4760
4824
  'DO NOT use ductape_execute for admin operations — it uses a publishable key which only ' +
4761
4825
  'covers runtime operations. Administrative operations will fail with "Authentication failed".\n\n' +
4762
4826
  'The CLI uses the user\'s local logged-in session (ductape login). ' +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "MCP server that exposes Ductape SDK operations via the backend proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",