@ductape/mcp 0.1.4 → 0.1.6

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 (3) hide show
  1. package/dist/index.js +124 -34
  2. package/package.json +1 -1
  3. package/src/index.ts +129 -34
package/dist/index.js CHANGED
@@ -31,20 +31,39 @@ There are THREE categories of operations. Use the right tool for each:
31
31
  These require an access key and CANNOT be done via ductape_execute (publishable key only).
32
32
  → Use ductape_cli instead. Examples:
33
33
  ductape_cli("products list")
34
- ductape_cli("product create --name \\"My Product\\" --tag my-product")
35
- ductape_cli("environments list my-product")
34
+ ductape_cli("products create --name \\"My Product\\" --tag my-product")
36
35
  ductape_cli("cloud connections list")
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: Environments and app actions are configured in the Workbench UI — there are no CLI commands for them.
39
+
40
+ DECLARATIVE SYNC (apply sessions, notifications, events from code; run DB migrations)
41
+ → Also use ductape_cli. The project must be linked first (ductape init --link).
42
+ ductape_cli("apply") # sync all: sessions + notifications + events
43
+ ductape_cli("apply sessions") # sessions only
44
+ ductape_cli("apply notifications") # notifications only
45
+ ductape_cli("apply events") # event brokers only
46
+ ductape_cli("apply --dry-run") # preview without changes
47
+ ductape_cli("db schema generate") # diff schema.json → write migration files
48
+ ductape_cli("db schema generate --destructive") # also generate drop operations
49
+ ductape_cli("db migrate") # apply pending migration files
50
+ ductape_cli("db migrate --dry-run") # preview without applying
51
+ ductape_cli("db migrate --env prd") # apply to a specific environment
52
+ ductape_cli("db migrate status") # show applied vs pending migrations
53
+ ductape_cli("db migrate status --json") # machine-readable status
54
+ ductape_cli("db migrate rollback") # roll back last migration
55
+ ductape_cli("db migrate rollback -n 3") # roll back last 3 migrations
38
56
 
39
57
  1. ASSET CREATION / UPDATE (create, update, add, register… for ANY asset type)
40
58
  ALL creation and update operations require an access key and CANNOT go through ductape_execute.
41
59
  → Use ductape_cli for every create/update operation. Examples:
42
- ductape_cli("app create --name \\"Email Service\\" --description \\"Transactional email\\"")
60
+ ductape_cli("apps create --name \\"Email Service\\" --description \\"Transactional email\\"")
43
61
  ductape_cli("apps list")
44
- ductape_cli("actions list my-app-tag")
45
- This applies to: products, apps, actions, auths, environments, databases, storage,
46
- graphs, vectors, brokers, sessions, notifications, jobs, features, quotas, fallbacks,
47
- healthchecks, caches, secrets, and webhooks.
62
+ ductape_cli("resources storage list")
63
+ ductape_cli("resources database create -f db-config.json")
64
+ This applies to: products, apps, and resources (databases, storage, caches, etc.),
65
+ cloud connections, and secrets. Environments, app actions, auths, features, quotas,
66
+ fallbacks, jobs, and healthchecks are configured in the Workbench UI.
48
67
 
49
68
  2. RUNTIME OPERATIONS (run, dispatch, execute, start, send, produce, query, insert, update, delete…)
50
69
  The "input" field shape is product- and operation-specific — it is NOT derivable from Joi validators.
@@ -787,28 +806,22 @@ function buildSnippet(language, payload, operationFamily, method) {
787
806
  : buildTypeScriptSnippet(payload, operationFamily, method);
788
807
  }
789
808
  // ─── CLI helpers ─────────────────────────────────────────────────────────────
809
+ // Per-process cache: avoids re-running whoami / workspaces use on every call.
810
+ let authState = 'unknown';
811
+ let workspaceSynced = false;
790
812
  const ADMIN_SUBCOMMANDS = [
791
- 'products', 'product',
792
- 'apps', 'app',
793
- 'workspaces', 'workspace',
794
- 'environments', 'environment',
813
+ 'login', 'logout', 'whoami',
814
+ 'profiles',
815
+ 'workspaces',
816
+ 'link', 'unlink', 'init',
817
+ 'products', 'apps',
818
+ 'resources',
795
819
  'cloud',
796
820
  'secrets',
797
- 'databases',
798
- 'storage',
799
- 'graphs',
800
- 'vectors',
801
- 'brokers',
802
- 'notifications',
803
- 'sessions',
804
- 'caches',
805
- 'jobs',
806
821
  'generate',
807
- 'resources',
808
- 'init',
809
- 'login',
810
- 'logout',
811
- 'whoami',
822
+ 'apply',
823
+ 'db',
824
+ 'graph',
812
825
  ];
813
826
  function checkCli() {
814
827
  try {
@@ -819,6 +832,29 @@ function checkCli() {
819
832
  return { available: false };
820
833
  }
821
834
  }
835
+ function checkLoginState() {
836
+ try {
837
+ execSync('ductape whoami --json', { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
838
+ authState = 'ok';
839
+ return 'ok';
840
+ }
841
+ catch {
842
+ authState = 'none';
843
+ return 'none';
844
+ }
845
+ }
846
+ function syncWorkspace() {
847
+ const target = process.env.DUCTAPE_WORKSPACE;
848
+ workspaceSynced = true; // mark done regardless so we don't retry on every call
849
+ if (!target)
850
+ return;
851
+ try {
852
+ execSync(`ductape workspaces use "${target}"`, { encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] });
853
+ }
854
+ catch {
855
+ // best-effort; if it fails the user will see workspace-mismatch errors on subsequent commands
856
+ }
857
+ }
822
858
  function runCli(command) {
823
859
  const first = command.trim().split(/\s+/)[0];
824
860
  if (!ADMIN_SUBCOMMANDS.includes(first)) {
@@ -827,8 +863,14 @@ function runCli(command) {
827
863
  output: `Subcommand "${first}" is not in the allowed admin subcommand list. Allowed: ${ADMIN_SUBCOMMANDS.join(', ')}.`,
828
864
  };
829
865
  }
866
+ // Auto-inject --workspace on login if DUCTAPE_WORKSPACE is set and caller hasn't specified one
867
+ let finalCommand = command;
868
+ const ws = process.env.DUCTAPE_WORKSPACE;
869
+ if (first === 'login' && ws && !command.includes('--workspace') && !command.includes('--skip-workspace-select')) {
870
+ finalCommand = `${command} --workspace "${ws}"`;
871
+ }
830
872
  try {
831
- const output = execSync(`ductape ${command}`, {
873
+ const output = execSync(`ductape ${finalCommand}`, {
832
874
  encoding: 'utf8',
833
875
  timeout: 30000,
834
876
  stdio: ['pipe', 'pipe', 'pipe'],
@@ -842,10 +884,14 @@ function runCli(command) {
842
884
  }
843
885
  const cliInputSchema = z.object({
844
886
  command: z.string().describe('The ductape CLI command to run, without the leading "ductape" word. ' +
845
- 'Examples: "products list", "product create --name \\"My Product\\" --tag my-product", ' +
846
- '"environments list my-product", "cloud connections list".\n\n' +
847
- 'Use this tool for ALL administrative operations: creating or updating products, apps, ' +
848
- 'environments, resources, cloud connections, and workspace configuration.\n\n' +
887
+ 'Examples: "products list", "products create --name \\"My Product\\" --tag my-product", ' +
888
+ '"apps list", "apps create -f app.json", "resources storage list", ' +
889
+ '"cloud connections list", "link --product my-product --env dev".\n\n' +
890
+ 'Use this tool for administrative operations: creating or updating products, apps, ' +
891
+ 'resources (databases, storage, caches…), cloud connections, secrets, ' +
892
+ 'and for apply/migrate workflows.\n\n' +
893
+ 'Note: environments, app actions, features, quotas, fallbacks, and jobs are configured ' +
894
+ 'in the Workbench UI — there are no CLI commands for them.\n\n' +
849
895
  'The CLI uses the user\'s local logged-in session (ductape login) — no key is required.'),
850
896
  });
851
897
  async function loadMcpSdk() {
@@ -923,7 +969,45 @@ async function main() {
923
969
  isError: true,
924
970
  };
925
971
  }
972
+ const firstWord = args.command.trim().split(/\s+/)[0];
973
+ const isAuthCommand = firstWord === 'login' || firstWord === 'logout';
974
+ if (!isAuthCommand) {
975
+ // Check login status once per process (cached after first call)
976
+ if (authState === 'unknown') {
977
+ checkLoginState();
978
+ }
979
+ if (authState === 'none') {
980
+ const wsFlag = process.env.DUCTAPE_WORKSPACE ? ` --workspace "${process.env.DUCTAPE_WORKSPACE}"` : '';
981
+ return {
982
+ content: [{
983
+ type: 'text',
984
+ text: [
985
+ 'Not logged in to the Ductape CLI.',
986
+ '',
987
+ 'Ask the user for their Ductape email and password, then call:',
988
+ ` ductape_cli("login --email <email> --password <password>${wsFlag}")`,
989
+ '',
990
+ 'Or the user can run `ductape login` in their terminal and then retry.',
991
+ ].join('\n'),
992
+ }],
993
+ isError: true,
994
+ };
995
+ }
996
+ // Sync to the configured workspace once per process (best-effort)
997
+ if (!workspaceSynced) {
998
+ syncWorkspace();
999
+ }
1000
+ }
926
1001
  const result = runCli(args.command);
1002
+ // Update cached state after auth commands
1003
+ if (firstWord === 'login' && result.success) {
1004
+ authState = 'ok';
1005
+ workspaceSynced = false; // re-sync workspace after fresh login
1006
+ }
1007
+ if (firstWord === 'logout' && result.success) {
1008
+ authState = 'none';
1009
+ workspaceSynced = false;
1010
+ }
927
1011
  return {
928
1012
  content: [{ type: 'text', text: result.output || '(no output)' }],
929
1013
  ...(result.success ? {} : { isError: true }),
@@ -1035,11 +1119,17 @@ async function main() {
1035
1119
  title: 'Ductape CLI',
1036
1120
  description: 'Run a Ductape CLI command for administrative operations.\n\n' +
1037
1121
  'USE THIS TOOL for any operation that creates or modifies platform configuration:\n' +
1038
- ' - Creating or updating products, apps, environments\n' +
1039
- ' - Managing cloud connections and resources\n' +
1122
+ ' - Creating or updating products (products create/update) and apps (apps create/update)\n' +
1123
+ ' - Importing an app from a Postman v2.1 or OpenAPI 3.0 file: "apps import <file> -t postman|openapi"\n' +
1124
+ ' - Managing resources via "resources <type> <verb>" (databases, storage, caches…)\n' +
1125
+ ' - Managing cloud connections and cloud-linked resources\n' +
1040
1126
  ' - Listing workspaces, products, secrets\n' +
1041
- ' - Any operation that would require an access key via the SDK\n\n' +
1042
- 'DO NOT use ductape_execute for these it uses a publishable key which only ' +
1127
+ ' - Linking a project folder: "link --product <tag> --env <slug>"\n' +
1128
+ ' - Syncing sessions/notifications/events: "apply" or "apply sessions" etc.\n' +
1129
+ ' - Running database migrations: "db migrate", "db schema generate"\n\n' +
1130
+ 'NOTE: Environments, app actions, auths, features, quotas, fallbacks, and jobs are ' +
1131
+ 'configured in the Workbench UI — the CLI does not have commands for them.\n\n' +
1132
+ 'DO NOT use ductape_execute for admin operations — it uses a publishable key which only ' +
1043
1133
  'covers runtime operations. Administrative operations will fail with "Authentication failed".\n\n' +
1044
1134
  'The CLI uses the user\'s local logged-in session (ductape login). ' +
1045
1135
  'If the CLI is not installed, this tool will return install instructions automatically.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "MCP server that exposes Ductape SDK operations via the backend proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -42,20 +42,39 @@ There are THREE categories of operations. Use the right tool for each:
42
42
  These require an access key and CANNOT be done via ductape_execute (publishable key only).
43
43
  → Use ductape_cli instead. Examples:
44
44
  ductape_cli("products list")
45
- ductape_cli("product create --name \\"My Product\\" --tag my-product")
46
- ductape_cli("environments list my-product")
45
+ ductape_cli("products create --name \\"My Product\\" --tag my-product")
47
46
  ductape_cli("cloud connections list")
47
+ ductape_cli("link --product my-product --env dev")
48
48
  If the CLI is not installed, ductape_cli will return install instructions automatically.
49
+ NOTE: Environments and app actions are configured in the Workbench UI — there are no CLI commands for them.
50
+
51
+ DECLARATIVE SYNC (apply sessions, notifications, events from code; run DB migrations)
52
+ → Also use ductape_cli. The project must be linked first (ductape init --link).
53
+ ductape_cli("apply") # sync all: sessions + notifications + events
54
+ ductape_cli("apply sessions") # sessions only
55
+ ductape_cli("apply notifications") # notifications only
56
+ ductape_cli("apply events") # event brokers only
57
+ ductape_cli("apply --dry-run") # preview without changes
58
+ ductape_cli("db schema generate") # diff schema.json → write migration files
59
+ ductape_cli("db schema generate --destructive") # also generate drop operations
60
+ ductape_cli("db migrate") # apply pending migration files
61
+ ductape_cli("db migrate --dry-run") # preview without applying
62
+ ductape_cli("db migrate --env prd") # apply to a specific environment
63
+ ductape_cli("db migrate status") # show applied vs pending migrations
64
+ ductape_cli("db migrate status --json") # machine-readable status
65
+ ductape_cli("db migrate rollback") # roll back last migration
66
+ ductape_cli("db migrate rollback -n 3") # roll back last 3 migrations
49
67
 
50
68
  1. ASSET CREATION / UPDATE (create, update, add, register… for ANY asset type)
51
69
  ALL creation and update operations require an access key and CANNOT go through ductape_execute.
52
70
  → Use ductape_cli for every create/update operation. Examples:
53
- ductape_cli("app create --name \\"Email Service\\" --description \\"Transactional email\\"")
71
+ ductape_cli("apps create --name \\"Email Service\\" --description \\"Transactional email\\"")
54
72
  ductape_cli("apps list")
55
- ductape_cli("actions list my-app-tag")
56
- This applies to: products, apps, actions, auths, environments, databases, storage,
57
- graphs, vectors, brokers, sessions, notifications, jobs, features, quotas, fallbacks,
58
- healthchecks, caches, secrets, and webhooks.
73
+ ductape_cli("resources storage list")
74
+ ductape_cli("resources database create -f db-config.json")
75
+ This applies to: products, apps, and resources (databases, storage, caches, etc.),
76
+ cloud connections, and secrets. Environments, app actions, auths, features, quotas,
77
+ fallbacks, jobs, and healthchecks are configured in the Workbench UI.
59
78
 
60
79
  2. RUNTIME OPERATIONS (run, dispatch, execute, start, send, produce, query, insert, update, delete…)
61
80
  The "input" field shape is product- and operation-specific — it is NOT derivable from Joi validators.
@@ -831,28 +850,23 @@ function buildSnippet(
831
850
 
832
851
  // ─── CLI helpers ─────────────────────────────────────────────────────────────
833
852
 
853
+ // Per-process cache: avoids re-running whoami / workspaces use on every call.
854
+ let authState: 'unknown' | 'ok' | 'none' = 'unknown';
855
+ let workspaceSynced = false;
856
+
834
857
  const ADMIN_SUBCOMMANDS = [
835
- 'products', 'product',
836
- 'apps', 'app',
837
- 'workspaces', 'workspace',
838
- 'environments', 'environment',
858
+ 'login', 'logout', 'whoami',
859
+ 'profiles',
860
+ 'workspaces',
861
+ 'link', 'unlink', 'init',
862
+ 'products', 'apps',
863
+ 'resources',
839
864
  'cloud',
840
865
  'secrets',
841
- 'databases',
842
- 'storage',
843
- 'graphs',
844
- 'vectors',
845
- 'brokers',
846
- 'notifications',
847
- 'sessions',
848
- 'caches',
849
- 'jobs',
850
866
  'generate',
851
- 'resources',
852
- 'init',
853
- 'login',
854
- 'logout',
855
- 'whoami',
867
+ 'apply',
868
+ 'db',
869
+ 'graph',
856
870
  ];
857
871
 
858
872
  function checkCli(): { available: boolean; version?: string } {
@@ -864,6 +878,28 @@ function checkCli(): { available: boolean; version?: string } {
864
878
  }
865
879
  }
866
880
 
881
+ function checkLoginState(): 'ok' | 'none' {
882
+ try {
883
+ execSync('ductape whoami --json', { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
884
+ authState = 'ok';
885
+ return 'ok';
886
+ } catch {
887
+ authState = 'none';
888
+ return 'none';
889
+ }
890
+ }
891
+
892
+ function syncWorkspace(): void {
893
+ const target = process.env.DUCTAPE_WORKSPACE;
894
+ workspaceSynced = true; // mark done regardless so we don't retry on every call
895
+ if (!target) return;
896
+ try {
897
+ execSync(`ductape workspaces use "${target}"`, { encoding: 'utf8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] });
898
+ } catch {
899
+ // best-effort; if it fails the user will see workspace-mismatch errors on subsequent commands
900
+ }
901
+ }
902
+
867
903
  function runCli(command: string): { success: boolean; output: string } {
868
904
  const first = command.trim().split(/\s+/)[0];
869
905
  if (!ADMIN_SUBCOMMANDS.includes(first)) {
@@ -872,8 +908,14 @@ function runCli(command: string): { success: boolean; output: string } {
872
908
  output: `Subcommand "${first}" is not in the allowed admin subcommand list. Allowed: ${ADMIN_SUBCOMMANDS.join(', ')}.`,
873
909
  };
874
910
  }
911
+ // Auto-inject --workspace on login if DUCTAPE_WORKSPACE is set and caller hasn't specified one
912
+ let finalCommand = command;
913
+ const ws = process.env.DUCTAPE_WORKSPACE;
914
+ if (first === 'login' && ws && !command.includes('--workspace') && !command.includes('--skip-workspace-select')) {
915
+ finalCommand = `${command} --workspace "${ws}"`;
916
+ }
875
917
  try {
876
- const output = execSync(`ductape ${command}`, {
918
+ const output = execSync(`ductape ${finalCommand}`, {
877
919
  encoding: 'utf8',
878
920
  timeout: 30000,
879
921
  stdio: ['pipe', 'pipe', 'pipe'],
@@ -888,10 +930,14 @@ function runCli(command: string): { success: boolean; output: string } {
888
930
  const cliInputSchema = z.object({
889
931
  command: z.string().describe(
890
932
  'The ductape CLI command to run, without the leading "ductape" word. ' +
891
- 'Examples: "products list", "product create --name \\"My Product\\" --tag my-product", ' +
892
- '"environments list my-product", "cloud connections list".\n\n' +
893
- 'Use this tool for ALL administrative operations: creating or updating products, apps, ' +
894
- 'environments, resources, cloud connections, and workspace configuration.\n\n' +
933
+ 'Examples: "products list", "products create --name \\"My Product\\" --tag my-product", ' +
934
+ '"apps list", "apps create -f app.json", "resources storage list", ' +
935
+ '"cloud connections list", "link --product my-product --env dev".\n\n' +
936
+ 'Use this tool for administrative operations: creating or updating products, apps, ' +
937
+ 'resources (databases, storage, caches…), cloud connections, secrets, ' +
938
+ 'and for apply/migrate workflows.\n\n' +
939
+ 'Note: environments, app actions, features, quotas, fallbacks, and jobs are configured ' +
940
+ 'in the Workbench UI — there are no CLI commands for them.\n\n' +
895
941
  'The CLI uses the user\'s local logged-in session (ductape login) — no key is required.',
896
942
  ),
897
943
  });
@@ -981,7 +1027,50 @@ async function main() {
981
1027
  isError: true,
982
1028
  };
983
1029
  }
1030
+
1031
+ const firstWord = args.command.trim().split(/\s+/)[0];
1032
+ const isAuthCommand = firstWord === 'login' || firstWord === 'logout';
1033
+
1034
+ if (!isAuthCommand) {
1035
+ // Check login status once per process (cached after first call)
1036
+ if (authState === 'unknown') {
1037
+ checkLoginState();
1038
+ }
1039
+ if (authState === 'none') {
1040
+ const wsFlag = process.env.DUCTAPE_WORKSPACE ? ` --workspace "${process.env.DUCTAPE_WORKSPACE}"` : '';
1041
+ return {
1042
+ content: [{
1043
+ type: 'text',
1044
+ text: [
1045
+ 'Not logged in to the Ductape CLI.',
1046
+ '',
1047
+ 'Ask the user for their Ductape email and password, then call:',
1048
+ ` ductape_cli("login --email <email> --password <password>${wsFlag}")`,
1049
+ '',
1050
+ 'Or the user can run `ductape login` in their terminal and then retry.',
1051
+ ].join('\n'),
1052
+ }],
1053
+ isError: true,
1054
+ };
1055
+ }
1056
+ // Sync to the configured workspace once per process (best-effort)
1057
+ if (!workspaceSynced) {
1058
+ syncWorkspace();
1059
+ }
1060
+ }
1061
+
984
1062
  const result = runCli(args.command);
1063
+
1064
+ // Update cached state after auth commands
1065
+ if (firstWord === 'login' && result.success) {
1066
+ authState = 'ok';
1067
+ workspaceSynced = false; // re-sync workspace after fresh login
1068
+ }
1069
+ if (firstWord === 'logout' && result.success) {
1070
+ authState = 'none';
1071
+ workspaceSynced = false;
1072
+ }
1073
+
985
1074
  return {
986
1075
  content: [{ type: 'text', text: result.output || '(no output)' }],
987
1076
  ...(result.success ? {} : { isError: true }),
@@ -1124,11 +1213,17 @@ async function main() {
1124
1213
  description:
1125
1214
  'Run a Ductape CLI command for administrative operations.\n\n' +
1126
1215
  'USE THIS TOOL for any operation that creates or modifies platform configuration:\n' +
1127
- ' - Creating or updating products, apps, environments\n' +
1128
- ' - Managing cloud connections and resources\n' +
1216
+ ' - Creating or updating products (products create/update) and apps (apps create/update)\n' +
1217
+ ' - Importing an app from a Postman v2.1 or OpenAPI 3.0 file: "apps import <file> -t postman|openapi"\n' +
1218
+ ' - Managing resources via "resources <type> <verb>" (databases, storage, caches…)\n' +
1219
+ ' - Managing cloud connections and cloud-linked resources\n' +
1129
1220
  ' - Listing workspaces, products, secrets\n' +
1130
- ' - Any operation that would require an access key via the SDK\n\n' +
1131
- 'DO NOT use ductape_execute for these it uses a publishable key which only ' +
1221
+ ' - Linking a project folder: "link --product <tag> --env <slug>"\n' +
1222
+ ' - Syncing sessions/notifications/events: "apply" or "apply sessions" etc.\n' +
1223
+ ' - Running database migrations: "db migrate", "db schema generate"\n\n' +
1224
+ 'NOTE: Environments, app actions, auths, features, quotas, fallbacks, and jobs are ' +
1225
+ 'configured in the Workbench UI — the CLI does not have commands for them.\n\n' +
1226
+ 'DO NOT use ductape_execute for admin operations — it uses a publishable key which only ' +
1132
1227
  'covers runtime operations. Administrative operations will fail with "Authentication failed".\n\n' +
1133
1228
  'The CLI uses the user\'s local logged-in session (ductape login). ' +
1134
1229
  'If the CLI is not installed, this tool will return install instructions automatically.',