@salesforce/b2c-dx-mcp 0.3.2 → 0.4.1

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 (54) hide show
  1. package/README.md +47 -27
  2. package/content/page-designer.md +4 -4
  3. package/dist/commands/mcp.d.ts +14 -2
  4. package/dist/commands/mcp.js +21 -9
  5. package/dist/registry.d.ts +4 -4
  6. package/dist/registry.js +13 -13
  7. package/dist/services.d.ts +75 -1
  8. package/dist/services.js +124 -1
  9. package/dist/tools/adapter.d.ts +29 -21
  10. package/dist/tools/adapter.js +34 -24
  11. package/dist/tools/cartridges/index.d.ts +5 -3
  12. package/dist/tools/cartridges/index.js +55 -25
  13. package/dist/tools/index.d.ts +1 -0
  14. package/dist/tools/index.js +1 -0
  15. package/dist/tools/mrt/index.d.ts +2 -2
  16. package/dist/tools/mrt/index.js +29 -10
  17. package/dist/tools/page-designer-decorator/analyzer.d.ts +169 -0
  18. package/dist/tools/page-designer-decorator/analyzer.js +535 -0
  19. package/dist/tools/page-designer-decorator/index.d.ts +252 -0
  20. package/dist/tools/page-designer-decorator/index.js +597 -0
  21. package/dist/tools/page-designer-decorator/rules/1-mode-selection.d.ts +8 -0
  22. package/dist/tools/page-designer-decorator/rules/1-mode-selection.js +65 -0
  23. package/dist/tools/page-designer-decorator/rules/2a-auto-mode.d.ts +13 -0
  24. package/dist/tools/page-designer-decorator/rules/2a-auto-mode.js +87 -0
  25. package/dist/tools/page-designer-decorator/rules/2b-0-interactive-overview.d.ts +4 -0
  26. package/dist/tools/page-designer-decorator/rules/2b-0-interactive-overview.js +55 -0
  27. package/dist/tools/page-designer-decorator/rules/2b-1-interactive-analyze.d.ts +22 -0
  28. package/dist/tools/page-designer-decorator/rules/2b-1-interactive-analyze.js +109 -0
  29. package/dist/tools/page-designer-decorator/rules/2b-2-interactive-select-props.d.ts +21 -0
  30. package/dist/tools/page-designer-decorator/rules/2b-2-interactive-select-props.js +60 -0
  31. package/dist/tools/page-designer-decorator/rules/2b-3-interactive-configure-attrs.d.ts +27 -0
  32. package/dist/tools/page-designer-decorator/rules/2b-3-interactive-configure-attrs.js +68 -0
  33. package/dist/tools/page-designer-decorator/rules/2b-4-interactive-configure-regions.d.ts +4 -0
  34. package/dist/tools/page-designer-decorator/rules/2b-4-interactive-configure-regions.js +65 -0
  35. package/dist/tools/page-designer-decorator/rules/2b-5-interactive-confirm-generation.d.ts +11 -0
  36. package/dist/tools/page-designer-decorator/rules/2b-5-interactive-confirm-generation.js +92 -0
  37. package/dist/tools/page-designer-decorator/rules.d.ts +51 -0
  38. package/dist/tools/page-designer-decorator/rules.js +70 -0
  39. package/dist/tools/page-designer-decorator/templates/decorator-generator.d.ts +116 -0
  40. package/dist/tools/page-designer-decorator/templates/decorator-generator.js +350 -0
  41. package/dist/tools/pwav3/index.d.ts +2 -2
  42. package/dist/tools/pwav3/index.js +13 -13
  43. package/dist/tools/scapi/index.d.ts +10 -2
  44. package/dist/tools/scapi/index.js +5 -56
  45. package/dist/tools/scapi/scapi-custom-apis-status.d.ts +9 -0
  46. package/dist/tools/scapi/scapi-custom-apis-status.js +152 -0
  47. package/dist/tools/scapi/scapi-schemas-list.d.ts +12 -0
  48. package/dist/tools/scapi/scapi-schemas-list.js +248 -0
  49. package/dist/tools/storefrontnext/developer-guidelines.d.ts +2 -2
  50. package/dist/tools/storefrontnext/developer-guidelines.js +3 -3
  51. package/dist/tools/storefrontnext/index.d.ts +2 -2
  52. package/dist/tools/storefrontnext/index.js +13 -13
  53. package/oclif.manifest.json +20 -6
  54. package/package.json +10 -5
package/dist/services.js CHANGED
@@ -44,6 +44,7 @@
44
44
  import fs from 'node:fs';
45
45
  import path from 'node:path';
46
46
  import os from 'node:os';
47
+ import { createCustomApisClient, createScapiSchemasClient, toOrganizationId, WebDavClient, } from '@salesforce/b2c-tooling-sdk/clients';
47
48
  /**
48
49
  * Services class that provides utilities for MCP tools.
49
50
  *
@@ -73,9 +74,16 @@ export class Services {
73
74
  * Resolved once at server startup from MrtCommand flags and ~/.mobify.
74
75
  */
75
76
  mrtConfig;
76
- constructor(opts = {}) {
77
+ /**
78
+ * Resolved configuration for accessing SCAPI settings.
79
+ * Provides access to shortCode, tenantId, and OAuth credentials.
80
+ * @private
81
+ */
82
+ resolvedConfig;
83
+ constructor(opts) {
77
84
  this.b2cInstance = opts.b2cInstance;
78
85
  this.mrtConfig = opts.mrtConfig ?? {};
86
+ this.resolvedConfig = opts.resolvedConfig;
79
87
  }
80
88
  /**
81
89
  * Creates a Services instance from an already-resolved configuration.
@@ -102,6 +110,7 @@ export class Services {
102
110
  return new Services({
103
111
  b2cInstance,
104
112
  mrtConfig,
113
+ resolvedConfig: config,
105
114
  });
106
115
  }
107
116
  // ============================================
@@ -117,6 +126,25 @@ export class Services {
117
126
  exists(targetPath) {
118
127
  return fs.existsSync(targetPath);
119
128
  }
129
+ /**
130
+ * Get Custom APIs client for managing custom SCAPI endpoints.
131
+ * Requires shortCode, tenantId, and OAuth credentials to be configured.
132
+ *
133
+ * @throws Error if shortCode, tenantId, or OAuth credentials are missing
134
+ * @returns Typed Custom APIs client
135
+ */
136
+ getCustomApisClient() {
137
+ const { shortCode, tenantId } = this.resolvedConfig.values;
138
+ if (!shortCode) {
139
+ throw new Error('SCAPI short code required. Provide --short-code, set SFCC_SHORTCODE, or configure short-code in dw.json.');
140
+ }
141
+ if (!tenantId) {
142
+ throw new Error('Tenant ID required. Provide --tenant-id, set SFCC_TENANT_ID, or configure tenant-id in dw.json.');
143
+ }
144
+ // This will throw if OAuth credentials are missing
145
+ const oauthStrategy = this.getOAuthStrategy();
146
+ return createCustomApisClient({ shortCode, tenantId }, oauthStrategy);
147
+ }
120
148
  /**
121
149
  * Get the current working directory.
122
150
  */
@@ -129,18 +157,94 @@ export class Services {
129
157
  getHomeDir() {
130
158
  return os.homedir();
131
159
  }
160
+ /**
161
+ * Get organization ID for SCAPI API calls.
162
+ * Ensures the tenant ID has the required f_ecom_ prefix.
163
+ *
164
+ * @throws Error if tenantId is not configured
165
+ * @returns Organization ID with f_ecom_ prefix
166
+ */
167
+ getOrganizationId() {
168
+ const { tenantId } = this.resolvedConfig.values;
169
+ if (!tenantId) {
170
+ throw new Error('Tenant ID required. Provide --tenant-id, set SFCC_TENANT_ID, or configure tenant-id in dw.json.');
171
+ }
172
+ return toOrganizationId(tenantId);
173
+ }
132
174
  /**
133
175
  * Get OS platform information.
134
176
  */
135
177
  getPlatform() {
136
178
  return os.platform();
137
179
  }
180
+ /**
181
+ * Get SCAPI Schemas client for discovering available SCAPI APIs.
182
+ * Requires shortCode, tenantId, and OAuth credentials to be configured.
183
+ *
184
+ * @throws Error if shortCode, tenantId, or OAuth credentials are missing
185
+ * @returns Typed SCAPI Schemas client
186
+ */
187
+ getScapiSchemasClient() {
188
+ const { shortCode, tenantId } = this.resolvedConfig.values;
189
+ if (!shortCode) {
190
+ throw new Error('SCAPI short code required. Provide --short-code, set SFCC_SHORTCODE, or configure short-code in dw.json.');
191
+ }
192
+ if (!tenantId) {
193
+ throw new Error('Tenant ID required. Provide --tenant-id, set SFCC_TENANT_ID, or configure tenant-id in dw.json.');
194
+ }
195
+ // This will throw if OAuth credentials are missing
196
+ const oauthStrategy = this.getOAuthStrategy();
197
+ return createScapiSchemasClient({ shortCode, tenantId }, oauthStrategy);
198
+ }
199
+ /**
200
+ * Get SCAPI shortCode from configuration.
201
+ * Returns undefined if not configured.
202
+ *
203
+ * @returns shortCode or undefined
204
+ */
205
+ getShortCode() {
206
+ return this.resolvedConfig.values.shortCode;
207
+ }
208
+ /**
209
+ * Get tenant ID from configuration.
210
+ * Returns undefined if not configured.
211
+ *
212
+ * @returns tenantId or undefined
213
+ */
214
+ getTenantId() {
215
+ return this.resolvedConfig.values.tenantId;
216
+ }
138
217
  /**
139
218
  * Get system temporary directory.
140
219
  */
141
220
  getTmpDir() {
142
221
  return os.tmpdir();
143
222
  }
223
+ /**
224
+ * Get WebDAV client for file operations on B2C instances.
225
+ * Requires hostname and WebDAV credentials to be configured.
226
+ *
227
+ * @throws Error if hostname or B2C instance is missing
228
+ * @returns WebDAV client instance
229
+ */
230
+ getWebDavClient() {
231
+ if (!this.b2cInstance) {
232
+ throw new Error('B2C instance required for WebDAV operations. Configure hostname and authentication in dw.json.');
233
+ }
234
+ return this.b2cInstance.webdav;
235
+ }
236
+ /**
237
+ * Get the project working directory.
238
+ * Falls back to process.cwd() if not explicitly set.
239
+ *
240
+ * This is the directory where the project is located, which may differ from process.cwd()
241
+ * when MCP clients spawn servers from a different location (e.g., home directory).
242
+ *
243
+ * @returns Project working directory path
244
+ */
245
+ getWorkingDirectory() {
246
+ return this.resolvedConfig.values.workingDirectory ?? process.cwd();
247
+ }
144
248
  /**
145
249
  * Join path segments.
146
250
  *
@@ -159,6 +263,9 @@ export class Services {
159
263
  listDirectory(dirPath) {
160
264
  return fs.readdirSync(dirPath, { withFileTypes: true });
161
265
  }
266
+ // ============================================
267
+ // SCAPI Helper Methods
268
+ // ============================================
162
269
  /**
163
270
  * Read a file from the filesystem.
164
271
  *
@@ -187,5 +294,21 @@ export class Services {
187
294
  stat(targetPath) {
188
295
  return fs.statSync(targetPath);
189
296
  }
297
+ /**
298
+ * Get OAuth strategy from resolved configuration.
299
+ * Mirrors the pattern from OAuthCommand.getOAuthStrategy().
300
+ *
301
+ * @throws Error if OAuth credentials are not configured
302
+ * @returns OAuth auth strategy
303
+ * @private
304
+ */
305
+ getOAuthStrategy() {
306
+ if (!this.resolvedConfig.hasOAuthConfig()) {
307
+ throw new Error('OAuth client ID required. Provide --client-id, set SFCC_CLIENT_ID, or configure in dw.json.');
308
+ }
309
+ // Use resolvedConfig factory to create OAuth strategy
310
+ // This handles client-credentials vs implicit flow automatically
311
+ return this.resolvedConfig.createOAuth();
312
+ }
190
313
  }
191
314
  //# sourceMappingURL=services.js.map
@@ -3,22 +3,22 @@
3
3
  *
4
4
  * This module provides utilities for creating standardized MCP tools that:
5
5
  * - Validate input using Zod schemas
6
- * - Inject pre-resolved B2CInstance for WebDAV/OCAPI operations (requiresInstance)
7
- * - Inject pre-resolved MRT auth for MRT API operations (requiresMrtAuth)
6
+ * - Inject loaded B2CInstance for WebDAV/OCAPI operations (requiresInstance)
7
+ * - Inject loaded MRT auth for MRT API operations (requiresMrtAuth)
8
8
  * - Format output consistently (textResult, jsonResult, errorResult)
9
9
  *
10
10
  * ## Configuration Resolution
11
11
  *
12
- * Both B2C instance and MRT auth are resolved once at server startup via
13
- * {@link Services.fromResolvedConfig} and reused for all tool calls:
12
+ * Both B2C instance and MRT auth are loaded before each tool call via
13
+ * a loader function that calls {@link Services.fromResolvedConfig}:
14
14
  *
15
- * - **B2CInstance**: Resolved from flags + dw.json. Available when `requiresInstance: true`.
16
- * - **MRT Auth**: Resolved from --api-key → SFCC_MRT_API_KEY → ~/.mobify. Available when `requiresMrtAuth: true`.
15
+ * - **B2CInstance**: Loaded from flags + dw.json on each call. Available when `requiresInstance: true`.
16
+ * - **MRT Auth**: Loaded from --api-key → SFCC_MRT_API_KEY → ~/.mobify on each call. Available when `requiresMrtAuth: true`.
17
17
  *
18
- * This "resolve eagerly at startup" pattern provides:
19
- * - Fail-fast behavior (configuration errors surface at startup)
20
- * - Consistent mental model (both resolved the same way)
21
- * - Better performance (no resolution on each tool call)
18
+ * This "load on each call" pattern provides:
19
+ * - Fresh configuration on each tool invocation (picks up changes to config files)
20
+ * - Consistent mental model (both loaded the same way)
21
+ * - Tools can respond to configuration changes without server restart
22
22
  *
23
23
  * @module tools/adapter
24
24
  *
@@ -42,8 +42,11 @@
42
42
  *
43
43
  * @example MRT tool (MRT API)
44
44
  * ```typescript
45
- * // Services created from already-resolved config at startup
46
- * const services = Services.fromResolvedConfig(this.resolvedConfig);
45
+ * // Loader function that loads config and creates Services on each tool call
46
+ * const loadServices = () => {
47
+ * const config = this.loadConfiguration();
48
+ * return Services.fromResolvedConfig(config);
49
+ * };
47
50
  *
48
51
  * const mrtTool = createToolAdapter({
49
52
  * name: 'mrt_bundle_push',
@@ -53,12 +56,12 @@
53
56
  * inputSchema: {
54
57
  * projectSlug: z.string().describe('MRT project slug'),
55
58
  * },
56
- * execute: async (args, { mrtAuth }) => {
57
- * const result = await pushBundle({ projectSlug: args.projectSlug }, mrtAuth);
59
+ * execute: async (args, { mrtConfig }) => {
60
+ * const result = await pushBundle({ projectSlug: args.projectSlug }, mrtConfig.auth);
58
61
  * return result;
59
62
  * },
60
63
  * formatOutput: (output) => jsonResult(output),
61
- * }, services);
64
+ * }, loadServices);
62
65
  * ```
63
66
  */
64
67
  import { type ZodRawShape } from 'zod';
@@ -78,7 +81,7 @@ export interface ToolExecutionContext {
78
81
  b2cInstance?: B2CInstance;
79
82
  /**
80
83
  * MRT configuration (auth, project, environment, origin).
81
- * Pre-resolved at server startup.
84
+ * Loaded before each tool call.
82
85
  * Only populated when requiresMrtAuth is true.
83
86
  */
84
87
  mrtConfig?: MrtConfig;
@@ -176,7 +179,7 @@ export declare function jsonResult(data: unknown, indent?: number): ToolResult;
176
179
  * @template TInput - The validated input type (inferred from inputSchema)
177
180
  * @template TOutput - The output type from the execute function
178
181
  * @param options - Tool adapter configuration
179
- * @param services - Services instance for dependency injection
182
+ * @param loadServices - Function that loads configuration and returns Services instance
180
183
  * @returns An McpTool ready for registration
181
184
  *
182
185
  * @example
@@ -184,18 +187,23 @@ export declare function jsonResult(data: unknown, indent?: number): ToolResult;
184
187
  * import { z } from 'zod';
185
188
  * import { createToolAdapter, jsonResult, errorResult } from './adapter.js';
186
189
  *
190
+ * const loadServices = () => {
191
+ * const config = this.loadConfiguration();
192
+ * return Services.fromResolvedConfig(config);
193
+ * };
194
+ *
187
195
  * const listCodeVersionsTool = createToolAdapter({
188
196
  * name: 'code_version_list',
189
197
  * description: 'List all code versions on the instance',
190
198
  * toolsets: ['CARTRIDGES'],
191
199
  * inputSchema: {},
192
- * execute: async (_args, { instance }) => {
193
- * const result = await instance.ocapi.GET('/code_versions', {});
200
+ * execute: async (_args, { b2cInstance }) => {
201
+ * const result = await b2cInstance.ocapi.GET('/code_versions', {});
194
202
  * if (result.error) throw new Error(result.error.message);
195
203
  * return result.data;
196
204
  * },
197
205
  * formatOutput: (data) => jsonResult(data),
198
- * }, services);
206
+ * }, loadServices);
199
207
  * ```
200
208
  */
201
- export declare function createToolAdapter<TInput, TOutput>(options: ToolAdapterOptions<TInput, TOutput>, services: Services): McpTool;
209
+ export declare function createToolAdapter<TInput, TOutput>(options: ToolAdapterOptions<TInput, TOutput>, loadServices: () => Services): McpTool;
@@ -8,22 +8,22 @@
8
8
  *
9
9
  * This module provides utilities for creating standardized MCP tools that:
10
10
  * - Validate input using Zod schemas
11
- * - Inject pre-resolved B2CInstance for WebDAV/OCAPI operations (requiresInstance)
12
- * - Inject pre-resolved MRT auth for MRT API operations (requiresMrtAuth)
11
+ * - Inject loaded B2CInstance for WebDAV/OCAPI operations (requiresInstance)
12
+ * - Inject loaded MRT auth for MRT API operations (requiresMrtAuth)
13
13
  * - Format output consistently (textResult, jsonResult, errorResult)
14
14
  *
15
15
  * ## Configuration Resolution
16
16
  *
17
- * Both B2C instance and MRT auth are resolved once at server startup via
18
- * {@link Services.fromResolvedConfig} and reused for all tool calls:
17
+ * Both B2C instance and MRT auth are loaded before each tool call via
18
+ * a loader function that calls {@link Services.fromResolvedConfig}:
19
19
  *
20
- * - **B2CInstance**: Resolved from flags + dw.json. Available when `requiresInstance: true`.
21
- * - **MRT Auth**: Resolved from --api-key → SFCC_MRT_API_KEY → ~/.mobify. Available when `requiresMrtAuth: true`.
20
+ * - **B2CInstance**: Loaded from flags + dw.json on each call. Available when `requiresInstance: true`.
21
+ * - **MRT Auth**: Loaded from --api-key → SFCC_MRT_API_KEY → ~/.mobify on each call. Available when `requiresMrtAuth: true`.
22
22
  *
23
- * This "resolve eagerly at startup" pattern provides:
24
- * - Fail-fast behavior (configuration errors surface at startup)
25
- * - Consistent mental model (both resolved the same way)
26
- * - Better performance (no resolution on each tool call)
23
+ * This "load on each call" pattern provides:
24
+ * - Fresh configuration on each tool invocation (picks up changes to config files)
25
+ * - Consistent mental model (both loaded the same way)
26
+ * - Tools can respond to configuration changes without server restart
27
27
  *
28
28
  * @module tools/adapter
29
29
  *
@@ -47,8 +47,11 @@
47
47
  *
48
48
  * @example MRT tool (MRT API)
49
49
  * ```typescript
50
- * // Services created from already-resolved config at startup
51
- * const services = Services.fromResolvedConfig(this.resolvedConfig);
50
+ * // Loader function that loads config and creates Services on each tool call
51
+ * const loadServices = () => {
52
+ * const config = this.loadConfiguration();
53
+ * return Services.fromResolvedConfig(config);
54
+ * };
52
55
  *
53
56
  * const mrtTool = createToolAdapter({
54
57
  * name: 'mrt_bundle_push',
@@ -58,12 +61,12 @@
58
61
  * inputSchema: {
59
62
  * projectSlug: z.string().describe('MRT project slug'),
60
63
  * },
61
- * execute: async (args, { mrtAuth }) => {
62
- * const result = await pushBundle({ projectSlug: args.projectSlug }, mrtAuth);
64
+ * execute: async (args, { mrtConfig }) => {
65
+ * const result = await pushBundle({ projectSlug: args.projectSlug }, mrtConfig.auth);
63
66
  * return result;
64
67
  * },
65
68
  * formatOutput: (output) => jsonResult(output),
66
- * }, services);
69
+ * }, loadServices);
67
70
  * ```
68
71
  */
69
72
  import { z } from 'zod';
@@ -138,7 +141,7 @@ function formatZodErrors(error) {
138
141
  * @template TInput - The validated input type (inferred from inputSchema)
139
142
  * @template TOutput - The output type from the execute function
140
143
  * @param options - Tool adapter configuration
141
- * @param services - Services instance for dependency injection
144
+ * @param loadServices - Function that loads configuration and returns Services instance
142
145
  * @returns An McpTool ready for registration
143
146
  *
144
147
  * @example
@@ -146,21 +149,26 @@ function formatZodErrors(error) {
146
149
  * import { z } from 'zod';
147
150
  * import { createToolAdapter, jsonResult, errorResult } from './adapter.js';
148
151
  *
152
+ * const loadServices = () => {
153
+ * const config = this.loadConfiguration();
154
+ * return Services.fromResolvedConfig(config);
155
+ * };
156
+ *
149
157
  * const listCodeVersionsTool = createToolAdapter({
150
158
  * name: 'code_version_list',
151
159
  * description: 'List all code versions on the instance',
152
160
  * toolsets: ['CARTRIDGES'],
153
161
  * inputSchema: {},
154
- * execute: async (_args, { instance }) => {
155
- * const result = await instance.ocapi.GET('/code_versions', {});
162
+ * execute: async (_args, { b2cInstance }) => {
163
+ * const result = await b2cInstance.ocapi.GET('/code_versions', {});
156
164
  * if (result.error) throw new Error(result.error.message);
157
165
  * return result.data;
158
166
  * },
159
167
  * formatOutput: (data) => jsonResult(data),
160
- * }, services);
168
+ * }, loadServices);
161
169
  * ```
162
170
  */
163
- export function createToolAdapter(options, services) {
171
+ export function createToolAdapter(options, loadServices) {
164
172
  const { name, description, inputSchema, toolsets, isGA = true, requiresInstance = false, requiresMrtAuth = false, execute, formatOutput, } = options;
165
173
  // Create Zod schema from inputSchema definition
166
174
  const zodSchema = z.object(inputSchema);
@@ -178,7 +186,9 @@ export function createToolAdapter(options, services) {
178
186
  }
179
187
  const args = parseResult.data;
180
188
  try {
181
- // 2. Get B2CInstance if required (pre-resolved at startup)
189
+ // 2. Load Services to get fresh configuration (re-reads config files)
190
+ const services = loadServices();
191
+ // 3. Get B2CInstance if required (loaded on each call)
182
192
  let b2cInstance;
183
193
  if (requiresInstance) {
184
194
  if (!services.b2cInstance) {
@@ -186,7 +196,7 @@ export function createToolAdapter(options, services) {
186
196
  }
187
197
  b2cInstance = services.b2cInstance;
188
198
  }
189
- // 3. Get MRT config if required (pre-resolved at startup)
199
+ // 4. Get MRT config if required (loaded on each call)
190
200
  let mrtConfig;
191
201
  if (requiresMrtAuth) {
192
202
  if (!services.mrtConfig.auth) {
@@ -199,14 +209,14 @@ export function createToolAdapter(options, services) {
199
209
  origin: services.mrtConfig.origin,
200
210
  };
201
211
  }
202
- // 4. Execute the operation
212
+ // 5. Execute the operation
203
213
  const context = {
204
214
  b2cInstance,
205
215
  mrtConfig,
206
216
  services,
207
217
  };
208
218
  const output = await execute(args, context);
209
- // 5. Format output
219
+ // 6. Format output
210
220
  return formatOutput(output);
211
221
  }
212
222
  catch (error) {
@@ -1,6 +1,6 @@
1
1
  import type { McpTool } from '../../utils/index.js';
2
2
  import type { Services } from '../../services.js';
3
- import type { DeployResult, DeployOptions } from '@salesforce/b2c-tooling-sdk/operations/code';
3
+ import type { DeployResult, DeployOptions, CodeVersion } from '@salesforce/b2c-tooling-sdk/operations/code';
4
4
  import type { B2CInstance } from '@salesforce/b2c-tooling-sdk';
5
5
  /**
6
6
  * Optional dependency injections for testing.
@@ -8,13 +8,15 @@ import type { B2CInstance } from '@salesforce/b2c-tooling-sdk';
8
8
  interface CartridgeToolInjections {
9
9
  /** Mock findAndDeployCartridges function for testing */
10
10
  findAndDeployCartridges?: (instance: B2CInstance, directory: string, options: DeployOptions) => Promise<DeployResult>;
11
+ /** Mock getActiveCodeVersion function for testing */
12
+ getActiveCodeVersion?: (instance: B2CInstance) => Promise<CodeVersion | undefined>;
11
13
  }
12
14
  /**
13
15
  * Creates all tools for the CARTRIDGES toolset.
14
16
  *
15
- * @param services - MCP services
17
+ * @param loadServices - Function that loads configuration and returns Services instance
16
18
  * @param injections - Optional dependency injections for testing
17
19
  * @returns Array of MCP tools
18
20
  */
19
- export declare function createCartridgesTools(services: Services, injections?: CartridgeToolInjections): McpTool[];
21
+ export declare function createCartridgesTools(loadServices: () => Services, injections?: CartridgeToolInjections): McpTool[];
20
22
  export {};
@@ -10,9 +10,10 @@
10
10
  *
11
11
  * @module tools/cartridges
12
12
  */
13
+ import path from 'node:path';
13
14
  import { z } from 'zod';
14
15
  import { createToolAdapter, jsonResult } from '../adapter.js';
15
- import { findAndDeployCartridges } from '@salesforce/b2c-tooling-sdk/operations/code';
16
+ import { findAndDeployCartridges, getActiveCodeVersion } from '@salesforce/b2c-tooling-sdk/operations/code';
16
17
  import { getLogger } from '@salesforce/b2c-tooling-sdk/logging';
17
18
  /**
18
19
  * Creates the cartridge_deploy tool.
@@ -23,12 +24,13 @@ import { getLogger } from '@salesforce/b2c-tooling-sdk/logging';
23
24
  * 3. Uploads the zip to WebDAV and triggers server-side unzip
24
25
  * 4. Optionally reloads the code version after deploy
25
26
  *
26
- * @param services - MCP services
27
+ * @param loadServices - Function that loads configuration and returns Services instance
27
28
  * @param injections - Optional dependency injections for testing
28
29
  * @returns The cartridge_deploy tool
29
30
  */
30
- function createCartridgeDeployTool(services, injections) {
31
+ function createCartridgeDeployTool(loadServices, injections) {
31
32
  const findAndDeployCartridgesFn = injections?.findAndDeployCartridges || findAndDeployCartridges;
33
+ const getActiveCodeVersionFn = injections?.getActiveCodeVersion || getActiveCodeVersion;
32
34
  return createToolAdapter({
33
35
  name: 'cartridge_deploy',
34
36
  description: 'Finds and deploys cartridges to a B2C Commerce instance via WebDAV. ' +
@@ -65,37 +67,65 @@ function createCartridgeDeployTool(services, injections) {
65
67
  async execute(args, context) {
66
68
  // Get instance from context (guaranteed by adapter when requiresInstance is true)
67
69
  const instance = context.b2cInstance;
68
- // Default directory to current directory
69
- const directory = args.directory || '.';
70
- // Parse options
71
- const options = {
72
- include: args.cartridges,
73
- exclude: args.exclude,
74
- reload: args.reload,
75
- };
76
- // Log all computed variables before deploying
77
70
  const logger = getLogger();
78
- logger.debug({
79
- directory,
80
- include: options.include,
81
- exclude: options.exclude,
82
- reload: options.reload,
83
- }, '[Cartridges] Deploying cartridges with computed options');
84
- // Deploy cartridges
85
- const result = await findAndDeployCartridgesFn(instance, directory, options);
86
- return result;
71
+ try {
72
+ // If no code version specified, get the active one
73
+ let codeVersion = instance.config.codeVersion;
74
+ if (!codeVersion) {
75
+ logger.debug('No code version specified, getting active version...');
76
+ const active = await getActiveCodeVersionFn(instance);
77
+ if (!active?.id) {
78
+ throw new Error('No code version specified and no active code version found. ' +
79
+ 'Specify a code version using one of: ' +
80
+ '--code-version flag, SFCC_CODE_VERSION environment variable, ' +
81
+ 'or code-version field in dw.json configuration file.');
82
+ }
83
+ codeVersion = active.id;
84
+ instance.config.codeVersion = codeVersion;
85
+ }
86
+ // Resolve directory path: relative paths are resolved relative to working directory, absolute paths are used as-is
87
+ const directory = args.directory
88
+ ? path.isAbsolute(args.directory)
89
+ ? args.directory
90
+ : path.resolve(context.services.getWorkingDirectory(), args.directory)
91
+ : context.services.getWorkingDirectory();
92
+ // Parse options
93
+ const options = {
94
+ include: args.cartridges,
95
+ exclude: args.exclude,
96
+ reload: args.reload,
97
+ };
98
+ // Log all computed variables before deploying
99
+ logger.debug({
100
+ directory,
101
+ codeVersion,
102
+ include: options.include,
103
+ exclude: options.exclude,
104
+ reload: options.reload,
105
+ }, '[Cartridges] Deploying cartridges with computed options');
106
+ // Deploy cartridges
107
+ const result = await findAndDeployCartridgesFn(instance, directory, options);
108
+ return result;
109
+ }
110
+ catch (error) {
111
+ // Handle communication and authentication errors
112
+ const errorMessage = error instanceof Error ? error.message : String(error);
113
+ throw new Error(`Failed to communicate with B2C instance. Check your authentication credentials and network connection. ` +
114
+ `If no code version is specified, ensure the instance is accessible and has an active code version. ` +
115
+ `Original error: ${errorMessage}`);
116
+ }
87
117
  },
88
118
  formatOutput: (output) => jsonResult(output),
89
- }, services);
119
+ }, loadServices);
90
120
  }
91
121
  /**
92
122
  * Creates all tools for the CARTRIDGES toolset.
93
123
  *
94
- * @param services - MCP services
124
+ * @param loadServices - Function that loads configuration and returns Services instance
95
125
  * @param injections - Optional dependency injections for testing
96
126
  * @returns Array of MCP tools
97
127
  */
98
- export function createCartridgesTools(services, injections) {
99
- return [createCartridgeDeployTool(services, injections)];
128
+ export function createCartridgesTools(loadServices, injections) {
129
+ return [createCartridgeDeployTool(loadServices, injections)];
100
130
  }
101
131
  //# sourceMappingURL=index.js.map
@@ -14,4 +14,5 @@ export * from './adapter.js';
14
14
  export * from './cartridges/index.js';
15
15
  export * from './mrt/index.js';
16
16
  export * from './pwav3/index.js';
17
+ export * from './scapi/index.js';
17
18
  export * from './storefrontnext/index.js';
@@ -21,5 +21,6 @@ export * from './adapter.js';
21
21
  export * from './cartridges/index.js';
22
22
  export * from './mrt/index.js';
23
23
  export * from './pwav3/index.js';
24
+ export * from './scapi/index.js';
24
25
  export * from './storefrontnext/index.js';
25
26
  //# sourceMappingURL=index.js.map
@@ -12,9 +12,9 @@ interface MrtToolInjections {
12
12
  /**
13
13
  * Creates all tools for the MRT toolset.
14
14
  *
15
- * @param services - MCP services
15
+ * @param loadServices - Function that loads configuration and returns Services instance
16
16
  * @param injections - Optional dependency injections for testing
17
17
  * @returns Array of MCP tools
18
18
  */
19
- export declare function createMrtTools(services: Services, injections?: MrtToolInjections): McpTool[];
19
+ export declare function createMrtTools(loadServices: () => Services, injections?: MrtToolInjections): McpTool[];
20
20
  export {};