@salesforce/b2c-dx-mcp 1.10.1 → 2.1.0

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 (110) hide show
  1. package/README.md +15 -62
  2. package/dist/commands/mcp.d.ts +14 -7
  3. package/dist/commands/mcp.js +91 -12
  4. package/dist/registry.d.ts +3 -3
  5. package/dist/registry.js +7 -14
  6. package/dist/server-context.d.ts +1 -1
  7. package/dist/server-context.js +1 -1
  8. package/dist/services.d.ts +29 -1
  9. package/dist/services.js +48 -1
  10. package/dist/tools/adapter.d.ts +25 -1
  11. package/dist/tools/adapter.js +53 -8
  12. package/dist/tools/cartridges/index.js +14 -3
  13. package/dist/tools/diagnostics/config-inspect.js +3 -2
  14. package/dist/tools/diagnostics/debug-list-sessions.js +1 -1
  15. package/dist/tools/diagnostics/debug-start-session.js +22 -9
  16. package/dist/tools/diagnostics/log-watch-registry.d.ts +3 -0
  17. package/dist/tools/diagnostics/log-watch-registry.js +1 -0
  18. package/dist/tools/diagnostics/logs-watch-list.js +1 -0
  19. package/dist/tools/diagnostics/logs-watch-start.js +6 -1
  20. package/dist/tools/diagnostics/mrt-log-watch-registry.d.ts +3 -0
  21. package/dist/tools/diagnostics/mrt-log-watch-registry.js +0 -0
  22. package/dist/tools/diagnostics/mrt-logs-watch-list.js +1 -0
  23. package/dist/tools/diagnostics/mrt-logs-watch-start.js +7 -1
  24. package/dist/tools/diagnostics/session-registry.d.ts +3 -0
  25. package/dist/tools/diagnostics/session-registry.js +4 -3
  26. package/dist/tools/index.d.ts +0 -1
  27. package/dist/tools/index.js +0 -1
  28. package/dist/tools/mrt/index.js +13 -3
  29. package/dist/tools/project-context.d.ts +53 -0
  30. package/dist/tools/project-context.js +55 -0
  31. package/dist/tools/scapi/metrics-get.js +1 -0
  32. package/dist/tools/scapi/scapi-custom-api-generate-scaffold.d.ts +8 -1
  33. package/dist/tools/scapi/scapi-custom-api-generate-scaffold.js +41 -7
  34. package/dist/tools/scapi/scapi-custom-apis-get-status.js +1 -0
  35. package/dist/tools/scapi/scapi-schemas-list.js +1 -0
  36. package/dist/utils/constants.d.ts +2 -13
  37. package/dist/utils/constants.js +1 -20
  38. package/oclif.manifest.json +2 -2
  39. package/package.json +3 -3
  40. package/content/sfnext/auth.md +0 -62
  41. package/content/sfnext/components.md +0 -123
  42. package/content/sfnext/config.md +0 -180
  43. package/content/sfnext/data-fetching.md +0 -323
  44. package/content/sfnext/extensions.md +0 -80
  45. package/content/sfnext/i18n.md +0 -121
  46. package/content/sfnext/page-designer.md +0 -78
  47. package/content/sfnext/performance.md +0 -80
  48. package/content/sfnext/pitfalls.md +0 -141
  49. package/content/sfnext/quick-reference.md +0 -226
  50. package/content/sfnext/state-management.md +0 -75
  51. package/content/sfnext/styling.md +0 -51
  52. package/content/sfnext/testing.md +0 -232
  53. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +0 -24
  54. package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +0 -53
  55. package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +0 -42
  56. package/dist/tools/storefrontnext/figma/figma-to-component/index.js +0 -326
  57. package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +0 -40
  58. package/dist/tools/storefrontnext/figma/generate-component/decision.js +0 -312
  59. package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +0 -9
  60. package/dist/tools/storefrontnext/figma/generate-component/formatter.js +0 -92
  61. package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +0 -114
  62. package/dist/tools/storefrontnext/figma/generate-component/index.js +0 -99
  63. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +0 -71
  64. package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +0 -260
  65. package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +0 -61
  66. package/dist/tools/storefrontnext/figma/map-tokens/index.js +0 -235
  67. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +0 -65
  68. package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +0 -268
  69. package/dist/tools/storefrontnext/index.d.ts +0 -30
  70. package/dist/tools/storefrontnext/index.js +0 -28
  71. package/dist/tools/storefrontnext/page-designer-decorator/analyzer.d.ts +0 -169
  72. package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +0 -567
  73. package/dist/tools/storefrontnext/page-designer-decorator/index.d.ts +0 -252
  74. package/dist/tools/storefrontnext/page-designer-decorator/index.js +0 -617
  75. package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.d.ts +0 -8
  76. package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.js +0 -65
  77. package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.d.ts +0 -13
  78. package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.js +0 -87
  79. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.d.ts +0 -4
  80. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.js +0 -55
  81. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.d.ts +0 -22
  82. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.js +0 -109
  83. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.d.ts +0 -21
  84. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.js +0 -60
  85. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.d.ts +0 -27
  86. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.js +0 -68
  87. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.d.ts +0 -4
  88. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.js +0 -65
  89. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.d.ts +0 -11
  90. package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.js +0 -92
  91. package/dist/tools/storefrontnext/page-designer-decorator/rules.d.ts +0 -51
  92. package/dist/tools/storefrontnext/page-designer-decorator/rules.js +0 -70
  93. package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.d.ts +0 -116
  94. package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.js +0 -350
  95. package/dist/tools/storefrontnext/sfnext-development-guidelines.d.ts +0 -9
  96. package/dist/tools/storefrontnext/sfnext-development-guidelines.js +0 -141
  97. package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +0 -92
  98. package/dist/tools/storefrontnext/site-theming/color-contrast.js +0 -186
  99. package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +0 -16
  100. package/dist/tools/storefrontnext/site-theming/color-mapping.js +0 -131
  101. package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +0 -11
  102. package/dist/tools/storefrontnext/site-theming/guidance-merger.js +0 -78
  103. package/dist/tools/storefrontnext/site-theming/index.d.ts +0 -14
  104. package/dist/tools/storefrontnext/site-theming/index.js +0 -123
  105. package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +0 -16
  106. package/dist/tools/storefrontnext/site-theming/response-builder.js +0 -316
  107. package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +0 -62
  108. package/dist/tools/storefrontnext/site-theming/theming-store.js +0 -417
  109. package/dist/tools/storefrontnext/site-theming/types.d.ts +0 -35
  110. package/dist/tools/storefrontnext/site-theming/types.js +0 -7
@@ -70,6 +70,7 @@
70
70
  * ```
71
71
  */
72
72
  import { z } from 'zod';
73
+ import { createProjectContextInputSchema, } from './project-context.js';
73
74
  /**
74
75
  * Creates a text-only success result.
75
76
  *
@@ -120,6 +121,25 @@ export function jsonResult(data, indent = 2) {
120
121
  content: [{ type: 'text', text: JSON.stringify(data, null, indent) }],
121
122
  };
122
123
  }
124
+ /** Attach compact resolution provenance while preserving existing tool output. */
125
+ export function attachResolution(result, resolution) {
126
+ let content = result.content;
127
+ let structuredContent = { ...result.structuredContent, resolution };
128
+ if (content.length === 1 && content[0]?.type === 'text') {
129
+ try {
130
+ const parsed = JSON.parse(content[0].text);
131
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
132
+ const output = { ...parsed, resolution };
133
+ content = [{ ...content[0], text: JSON.stringify(output, null, 2) }];
134
+ structuredContent = { ...output, ...result.structuredContent, resolution };
135
+ }
136
+ }
137
+ catch {
138
+ // Plain-text tools expose resolution through structuredContent only.
139
+ }
140
+ }
141
+ return { ...result, content, structuredContent };
142
+ }
123
143
  /**
124
144
  * Formats Zod validation errors into a human-readable string.
125
145
  *
@@ -169,13 +189,24 @@ function formatZodErrors(error) {
169
189
  * ```
170
190
  */
171
191
  export function createToolAdapter(options, loadServices, serverContext) {
172
- const { name, description, inputSchema, toolsets, isGA = true, requiresInstance = false, requiresMrtAuth = false, execute, formatOutput, } = options;
192
+ const { name, description, inputSchema, toolsets, isGA = true, requiresInstance = false, requiresMrtAuth = false, usesProjectContext = false, usesConfigurationContext = false, execute, formatOutput, } = options;
193
+ const projectContextKind = requiresInstance || requiresMrtAuth || usesConfigurationContext
194
+ ? 'configuration'
195
+ : usesProjectContext
196
+ ? 'project'
197
+ : undefined;
198
+ const effectiveInputSchema = projectContextKind
199
+ ? {
200
+ ...createProjectContextInputSchema(projectContextKind, loadServices.projectContextDefaults),
201
+ ...inputSchema,
202
+ }
203
+ : inputSchema;
173
204
  // Create Zod schema from inputSchema definition
174
- const zodSchema = z.object(inputSchema);
205
+ const zodSchema = z.object(effectiveInputSchema);
175
206
  return {
176
207
  name,
177
208
  description,
178
- inputSchema,
209
+ inputSchema: effectiveInputSchema,
179
210
  toolsets,
180
211
  isGA,
181
212
  async handler(rawArgs) {
@@ -185,14 +216,21 @@ export function createToolAdapter(options, loadServices, serverContext) {
185
216
  return errorResult(`Invalid input: ${formatZodErrors(parseResult.error)}`);
186
217
  }
187
218
  const args = parseResult.data;
219
+ let resolution;
188
220
  try {
189
221
  // 2. Load Services to get fresh configuration (re-reads config files)
190
- const services = await loadServices();
222
+ const projectContext = projectContextKind ? args : undefined;
223
+ const services = await loadServices(projectContext);
224
+ const executionResolution = services.getResolution();
225
+ if (projectContextKind === 'project') {
226
+ delete executionResolution.configuration;
227
+ }
228
+ resolution = projectContextKind ? executionResolution : undefined;
191
229
  // 3. Get B2CInstance if required (loaded on each call)
192
230
  let b2cInstance;
193
231
  if (requiresInstance) {
194
232
  if (!services.b2cInstance) {
195
- return errorResult('B2C instance error: Instance configuration required. Provide --server flag, set SFCC_SERVER environment variable, or configure dw.json');
233
+ return attachResolution(errorResult('B2C instance error: Instance configuration required. Provide --server flag, set SFCC_SERVER environment variable, or configure dw.json'), executionResolution);
196
234
  }
197
235
  b2cInstance = services.b2cInstance;
198
236
  }
@@ -200,7 +238,7 @@ export function createToolAdapter(options, loadServices, serverContext) {
200
238
  let mrtConfig;
201
239
  if (requiresMrtAuth) {
202
240
  if (!services.mrtConfig.auth) {
203
- return errorResult('MRT auth error: MRT API key required. Provide --api-key, set MRT_API_KEY environment variable, or configure ~/.mobify');
241
+ return attachResolution(errorResult('MRT auth error: MRT API key required. Provide --api-key, set MRT_API_KEY environment variable, or configure ~/.mobify'), executionResolution);
204
242
  }
205
243
  mrtConfig = {
206
244
  auth: services.mrtConfig.auth,
@@ -215,15 +253,22 @@ export function createToolAdapter(options, loadServices, serverContext) {
215
253
  mrtConfig,
216
254
  services,
217
255
  serverContext,
256
+ resolution: executionResolution,
257
+ setResolvedDirectory(name, value) {
258
+ executionResolution.directories ??= {};
259
+ executionResolution.directories[name] = value;
260
+ },
218
261
  };
219
262
  const output = await execute(args, context);
220
263
  // 6. Format output
221
- return formatOutput(output);
264
+ const result = formatOutput(output);
265
+ return resolution ? attachResolution(result, executionResolution) : result;
222
266
  }
223
267
  catch (error) {
224
268
  // Handle execution errors
225
269
  const message = error instanceof Error ? error.message : String(error);
226
- return errorResult(`Execution error: ${message}`);
270
+ const result = errorResult(`Execution error: ${message}`);
271
+ return resolution ? attachResolution(result, resolution) : result;
227
272
  }
228
273
  },
229
274
  };
@@ -44,12 +44,16 @@ function createCartridgeDeployTool(loadServices, injections) {
44
44
  toolsets: ['CARTRIDGES'],
45
45
  isGA: true,
46
46
  requiresInstance: true,
47
+ usesProjectContext: true,
47
48
  inputSchema: {
49
+ cartridgeDirectory: z
50
+ .string()
51
+ .optional()
52
+ .describe('Optional cartridge discovery root. Relative paths resolve from projectDirectory. Defaults to projectDirectory.'),
48
53
  directory: z
49
54
  .string()
50
55
  .optional()
51
- .describe('Path to directory to search for cartridges. Defaults to current project directory if not specified. ' +
52
- 'The tool will recursively search this directory for .project files to identify cartridges.'),
56
+ .describe('Deprecated alias for cartridgeDirectory. cartridgeDirectory takes precedence when both are supplied.'),
53
57
  cartridges: z
54
58
  .array(z.string())
55
59
  .optional()
@@ -87,7 +91,13 @@ function createCartridgeDeployTool(loadServices, injections) {
87
91
  instance.config.codeVersion = codeVersion;
88
92
  }
89
93
  // Resolve directory path: relative paths are resolved relative to project directory, absolute paths are used as-is
90
- const directory = context.services.resolveWithProjectDirectory(args.directory);
94
+ const projectDirectory = context.services.resolveProjectDirectory(args.projectDirectory);
95
+ const directoryArgument = args.cartridgeDirectory ?? args.directory;
96
+ const directory = context.services.resolveWithProjectDirectory(directoryArgument, args.projectDirectory);
97
+ context.setResolvedDirectory('cartridgeDirectory', {
98
+ path: directory,
99
+ source: directoryArgument ? 'argument' : 'projectDirectory',
100
+ });
91
101
  // Parse options
92
102
  const options = {
93
103
  include: args.cartridges,
@@ -106,6 +116,7 @@ function createCartridgeDeployTool(loadServices, injections) {
106
116
  const result = await findAndDeployCartridgesFn(instance, directory, options);
107
117
  return {
108
118
  ...result,
119
+ projectDirectory,
109
120
  resolvedDirectory: directory,
110
121
  postInstructions: CARTRIDGE_PATH_REMINDER,
111
122
  };
@@ -20,11 +20,12 @@ export function createConfigInspectTool(loadServices) {
20
20
  name: 'config_inspect',
21
21
  description: 'Inspect the resolved B2C Commerce configuration the MCP server is using — instance hostname, auth, SCAPI, MRT, and other settings — along with which source (dw.json, environment variables, flags) provided each value. ' +
22
22
  'Secrets (passwords, client secrets, API keys) are redacted by default. ' +
23
- 'The output includes the effective projectDirectory and how it was resolved (explicit --project-directory / SFCC_PROJECT_DIRECTORY vs. the process working directory), which is useful for diagnosing why the server targets the wrong instance or cannot find a project. ' +
23
+ 'Pass projectDirectory, configPath, and/or instanceName to inspect the same project, configuration catalog, and named instance another tool would use. The detailed source graph is supplemented by the same compact resolution provenance returned by other configuration-aware tools. ' +
24
24
  'Use this first when configuration seems wrong, auth is failing, or the server appears to be operating in the wrong directory.',
25
25
  toolsets: ['DIAGNOSTICS'],
26
26
  isGA: true,
27
27
  requiresInstance: false,
28
+ usesConfigurationContext: true,
28
29
  inputSchema: {
29
30
  unmask: z
30
31
  .boolean()
@@ -33,7 +34,7 @@ export function createConfigInspectTool(loadServices) {
33
34
  },
34
35
  async execute(args, { services }) {
35
36
  const resolved = services.getResolvedConfig();
36
- const projectDirectory = services.resolveProjectDirectory();
37
+ const projectDirectory = services.resolveProjectDirectory(args.projectDirectory);
37
38
  return {
38
39
  config: redactConfigValues(resolved.values, { unmask: args.unmask ?? false }),
39
40
  projectDirectory,
@@ -22,7 +22,6 @@ export function createDebugListSessionsTool(loadServices, serverContext) {
22
22
  return {
23
23
  session_id: entry.sessionId,
24
24
  hostname: entry.hostname,
25
- client_id: entry.clientId,
26
25
  halted_threads: entry.manager
27
26
  .getKnownThreads()
28
27
  .filter((t) => t.status === 'halted')
@@ -31,6 +30,7 @@ export function createDebugListSessionsTool(loadServices, serverContext) {
31
30
  session_cookie: dwsid ? { name: 'dwsid', value: dwsid } : null,
32
31
  created_at: new Date(entry.createdAt).toISOString(),
33
32
  last_activity_at: new Date(entry.lastActivityAt).toISOString(),
33
+ resolution: entry.resolution,
34
34
  };
35
35
  }),
36
36
  };
@@ -3,6 +3,7 @@
3
3
  * SPDX-License-Identifier: Apache-2
4
4
  * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
5
5
  */
6
+ import { randomUUID } from 'node:crypto';
6
7
  import { z } from 'zod';
7
8
  import { createToolAdapter, jsonResult } from '../adapter.js';
8
9
  import { DebugSessionManager, createSourceMapper, } from '@salesforce/b2c-tooling-sdk/operations/debug';
@@ -12,19 +13,17 @@ export function createDebugStartSessionTool(loadServices, serverContext) {
12
13
  return createToolAdapter({
13
14
  name: 'debug_start_session',
14
15
  description: 'Start a script debugger session on a B2C Commerce instance to debug SFRA controllers, custom API scripts, hooks, jobs, or any server-side script. ' +
16
+ 'Uses projectDirectory to load project configuration and discover cartridges. cartridgeDirectory may override only the cartridge discovery/source-mapping root. ' +
15
17
  'Returns a session_id for use with other debug tools, plus discovered cartridge mappings. ' +
16
18
  'WARNING: Debug sessions halt remote request threads on the instance. Always call debug_end_session when finished.',
17
19
  toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'SCAPI'],
18
20
  inputSchema: {
19
- cartridge_directory: z
21
+ cartridgeDirectory: z
20
22
  .string()
21
23
  .optional()
22
- .describe('Path to directory containing cartridges. Defaults to project directory.'),
23
- client_id: z
24
- .string()
25
- .optional()
26
- .describe('Client ID for the debugger API. Defaults to "b2c-cli". Use a different ID to run concurrent sessions on the same host.'),
24
+ .describe('Optional cartridge discovery and debugger source-mapping root. Relative paths resolve from projectDirectory. Defaults to projectDirectory.'),
27
25
  },
26
+ usesConfigurationContext: true,
28
27
  async execute(args, context) {
29
28
  const registry = getRegistry(context);
30
29
  const credentials = context.services.getBasicAuthCredentials();
@@ -33,8 +32,13 @@ export function createDebugStartSessionTool(loadServices, serverContext) {
33
32
  'Set via SFCC_SERVER/SFCC_USERNAME/SFCC_PASSWORD env vars, or dw.json.');
34
33
  }
35
34
  const { hostname, username, password } = credentials;
36
- const clientId = args.client_id ?? 'b2c-cli';
37
- const cartridgeDir = context.services.resolveWithProjectDirectory(args.cartridge_directory);
35
+ const clientId = `b2c-dx-mcp-${randomUUID()}`;
36
+ const projectDirectory = context.services.resolveProjectDirectory(args.projectDirectory);
37
+ const cartridgeDir = context.services.resolveWithProjectDirectory(args.cartridgeDirectory, args.projectDirectory);
38
+ context.setResolvedDirectory('cartridgeDirectory', {
39
+ path: cartridgeDir,
40
+ source: args.cartridgeDirectory ? 'argument' : 'projectDirectory',
41
+ });
38
42
  const cartridges = findCartridges(cartridgeDir);
39
43
  const warnings = [];
40
44
  if (cartridges.length === 0) {
@@ -55,7 +59,14 @@ export function createDebugStartSessionTool(loadServices, serverContext) {
55
59
  };
56
60
  const manager = new DebugSessionManager({ hostname, username, password, clientId, cartridgeRoots: cartridges }, callbacks);
57
61
  await manager.connect();
58
- const entry = registry.registerSession({ hostname, clientId, manager, sourceMapper, cartridges });
62
+ const entry = registry.registerSession({
63
+ hostname,
64
+ clientId,
65
+ manager,
66
+ sourceMapper,
67
+ cartridges,
68
+ resolution: structuredClone(context.resolution),
69
+ });
59
70
  const cartridgeMappings = {};
60
71
  for (const c of cartridges)
61
72
  cartridgeMappings[c.name] = c.src;
@@ -70,6 +81,8 @@ export function createDebugStartSessionTool(loadServices, serverContext) {
70
81
  cartridge_mappings: cartridgeMappings,
71
82
  session_cookie: dwsid ? { name: 'dwsid', value: dwsid } : null,
72
83
  warnings,
84
+ projectDirectory,
85
+ cartridgeDirectory: cartridgeDir,
73
86
  };
74
87
  },
75
88
  formatOutput: (output) => jsonResult(output),
@@ -1,5 +1,6 @@
1
1
  import type { LogEntry, LogFile, TailLogsResult } from '@salesforce/b2c-tooling-sdk/operations/logs';
2
2
  import type { ToolExecutionContext } from '../adapter.js';
3
+ import type { ToolResolution } from '../project-context.js';
3
4
  /** Maximum number of buffered entries before oldest are evicted. */
4
5
  export declare const DEFAULT_BUFFER_CAP = 5000;
5
6
  /**
@@ -38,6 +39,7 @@ export interface LogWatchEntry {
38
39
  createdAt: number;
39
40
  lastActivityAt: number;
40
41
  stopped: boolean;
42
+ resolution?: ToolResolution;
41
43
  }
42
44
  export interface RegisterWatchOptions {
43
45
  hostname: string;
@@ -45,6 +47,7 @@ export interface RegisterWatchOptions {
45
47
  tailResult: TailLogsResult;
46
48
  bufferCap?: number;
47
49
  bufferBytesCap?: number;
50
+ resolution?: ToolResolution;
48
51
  }
49
52
  export declare class LogWatchRegistry {
50
53
  private cleanupTimer;
@@ -194,6 +194,7 @@ export class LogWatchRegistry {
194
194
  stopped: false,
195
195
  totalEntriesSeen: 0,
196
196
  watchId,
197
+ resolution: opts.resolution,
197
198
  };
198
199
  this.watches.set(watchId, entry);
199
200
  return entry;
@@ -27,6 +27,7 @@ export function createLogsWatchListTool(loadServices, serverContext) {
27
27
  stopped: w.stopped,
28
28
  total_entries_seen: w.totalEntriesSeen,
29
29
  watch_id: w.watchId,
30
+ resolution: w.resolution,
30
31
  })),
31
32
  };
32
33
  },
@@ -97,7 +97,12 @@ export function createLogsWatchStartTool(loadServices, serverContext, injections
97
97
  // it isn't orphaned (it would otherwise poll WebDAV until process exit).
98
98
  let entry;
99
99
  try {
100
- entry = registry.registerWatch({ hostname, prefixes, tailResult });
100
+ entry = registry.registerWatch({
101
+ hostname,
102
+ prefixes,
103
+ tailResult,
104
+ resolution: structuredClone(context.resolution),
105
+ });
101
106
  }
102
107
  catch (error) {
103
108
  await tailResult.stop().catch(() => { });
@@ -1,5 +1,6 @@
1
1
  import type { MrtLogEntry, TailMrtLogsResult } from '@salesforce/b2c-tooling-sdk/operations/mrt';
2
2
  import type { ToolExecutionContext } from '../adapter.js';
3
+ import type { ToolResolution } from '../project-context.js';
3
4
  /** Maximum number of buffered entries before oldest are evicted. */
4
5
  export declare const DEFAULT_BUFFER_CAP = 5000;
5
6
  /**
@@ -42,6 +43,7 @@ export interface MrtLogWatchEntry {
42
43
  * watch keeps its buffered entries until drained or idle-reaped.
43
44
  */
44
45
  stopped: boolean;
46
+ resolution?: ToolResolution;
45
47
  }
46
48
  export interface RegisterMrtWatchOptions {
47
49
  project: string;
@@ -50,6 +52,7 @@ export interface RegisterMrtWatchOptions {
50
52
  tailResult: TailMrtLogsResult;
51
53
  bufferCap?: number;
52
54
  bufferBytesCap?: number;
55
+ resolution?: ToolResolution;
53
56
  }
54
57
  /**
55
58
  * Stable dedup key for an MRT log watch. A developer may tail several
@@ -27,6 +27,7 @@ export function createMrtLogsWatchListTool(loadServices, serverContext) {
27
27
  stopped: w.stopped,
28
28
  total_entries_seen: w.totalEntriesSeen,
29
29
  watch_id: w.watchId,
30
+ resolution: w.resolution,
30
31
  })),
31
32
  };
32
33
  },
@@ -113,7 +113,13 @@ export function createMrtLogsWatchStartTool(loadServices, serverContext, injecti
113
113
  // exit).
114
114
  let entry;
115
115
  try {
116
- entry = registry.registerWatch({ project, environment, origin, tailResult });
116
+ entry = registry.registerWatch({
117
+ project,
118
+ environment,
119
+ origin,
120
+ tailResult,
121
+ resolution: structuredClone(context.resolution),
122
+ });
117
123
  }
118
124
  catch (error) {
119
125
  tailResult.stop();
@@ -1,6 +1,7 @@
1
1
  import type { DebugSessionManager, SourceMapper, SdapiBreakpoint, SdapiScriptThread } from '@salesforce/b2c-tooling-sdk/operations/debug';
2
2
  import type { CartridgeMapping } from '@salesforce/b2c-tooling-sdk/operations/code';
3
3
  import type { ToolExecutionContext } from '../adapter.js';
4
+ import type { ToolResolution } from '../project-context.js';
4
5
  export interface HaltWaiter {
5
6
  resolve: (thread: SdapiScriptThread) => void;
6
7
  reject: (error: Error) => void;
@@ -17,6 +18,7 @@ export interface DebugSessionEntry {
17
18
  haltWaiters: HaltWaiter[];
18
19
  createdAt: number;
19
20
  lastActivityAt: number;
21
+ resolution?: ToolResolution;
20
22
  }
21
23
  export interface RegisterSessionOptions {
22
24
  hostname: string;
@@ -24,6 +26,7 @@ export interface RegisterSessionOptions {
24
26
  manager: DebugSessionManager;
25
27
  sourceMapper: SourceMapper;
26
28
  cartridges: CartridgeMapping[];
29
+ resolution?: ToolResolution;
27
30
  }
28
31
  export declare class DebugSessionRegistry {
29
32
  private cleanupTimer;
@@ -64,12 +64,12 @@ export class DebugSessionRegistry {
64
64
  return [...this.sessions.values()];
65
65
  }
66
66
  registerSession(opts) {
67
- const { hostname, clientId, manager, sourceMapper, cartridges } = opts;
67
+ const { hostname, clientId, manager, sourceMapper, cartridges, resolution } = opts;
68
68
  const existing = this.findByHostAndClientId(hostname, clientId);
69
69
  if (existing) {
70
- throw new Error(`A debug session already exists for ${hostname} with client ID "${clientId}" ` +
70
+ throw new Error(`A debug session already exists for ${hostname} ` +
71
71
  `(session_id: "${existing.sessionId}"). ` +
72
- `End it with debug_end_session first, or use a different client_id.`);
72
+ `End it with debug_end_session first.`);
73
73
  }
74
74
  const sessionId = randomUUID();
75
75
  const now = Date.now();
@@ -84,6 +84,7 @@ export class DebugSessionRegistry {
84
84
  haltWaiters: [],
85
85
  createdAt: now,
86
86
  lastActivityAt: now,
87
+ resolution,
87
88
  };
88
89
  this.sessions.set(sessionId, entry);
89
90
  return entry;
@@ -14,4 +14,3 @@ export * from './docs/index.js';
14
14
  export * from './mrt/index.js';
15
15
  export * from './pwav3/index.js';
16
16
  export * from './scapi/index.js';
17
- export * from './storefrontnext/index.js';
@@ -21,5 +21,4 @@ export * from './docs/index.js';
21
21
  export * from './mrt/index.js';
22
22
  export * from './pwav3/index.js';
23
23
  export * from './scapi/index.js';
24
- export * from './storefrontnext/index.js';
25
24
  //# sourceMappingURL=index.js.map
@@ -127,6 +127,7 @@ function createMrtBundlePushTool(loadServices, injections) {
127
127
  isGA: true,
128
128
  // MRT operations use ApiKeyStrategy from MRT_API_KEY or ~/.mobify
129
129
  requiresMrtAuth: true,
130
+ usesProjectContext: true,
130
131
  inputSchema: {
131
132
  buildDirectory: z
132
133
  .string()
@@ -167,12 +168,17 @@ function createMrtBundlePushTool(loadServices, injections) {
167
168
  // Get origin from --cloud-origin flag or mrtOrigin config (optional)
168
169
  const origin = context.mrtConfig?.origin;
169
170
  // Detect project type and get project-type-aware defaults
170
- const projectDir = context.services.resolveWithProjectDirectory();
171
+ const projectDirectory = context.services.resolveProjectDirectory(args.projectDirectory);
172
+ const projectDir = projectDirectory.path;
171
173
  const { projectTypes } = await detectWorkspaceTypeFn(projectDir);
172
174
  const defaults = getDefaultsForProjectTypes(projectTypes);
173
175
  const ssrOnly = args.ssrOnly ? parseGlobPatterns(args.ssrOnly) : defaults.ssrOnly;
174
176
  const ssrShared = args.ssrShared ? parseGlobPatterns(args.ssrShared) : defaults.ssrShared;
175
- const buildDirectory = context.services.resolveWithProjectDirectory(args.buildDirectory ?? defaults.buildDirectory);
177
+ const buildDirectory = context.services.resolveWithProjectDirectory(args.buildDirectory ?? defaults.buildDirectory, args.projectDirectory);
178
+ context.setResolvedDirectory('buildDirectory', {
179
+ path: buildDirectory,
180
+ source: args.buildDirectory ? 'argument' : 'projectDirectory',
181
+ });
176
182
  // Log all computed variables before pushing bundle
177
183
  const logger = getLogger();
178
184
  logger.debug({
@@ -196,7 +202,11 @@ function createMrtBundlePushTool(loadServices, injections) {
196
202
  target: environment,
197
203
  origin, // MRT API origin URL (optional, defaults to https://cloud.mobify.com)
198
204
  }, context.mrtConfig.auth);
199
- return result;
205
+ return {
206
+ ...result,
207
+ projectDirectory,
208
+ resolvedBuildDirectory: buildDirectory,
209
+ };
200
210
  },
201
211
  formatOutput: (output) => jsonResult(output),
202
212
  }, loadServices);
@@ -0,0 +1,53 @@
1
+ import { z, type ZodRawShape } from 'zod';
2
+ /** Input shared by MCP tools that resolve files or configuration from a project. */
3
+ export interface ProjectContextInput {
4
+ /** Per-call project directory override. */
5
+ projectDirectory?: string;
6
+ /** Per-call explicit primary dw.json-format configuration path. */
7
+ configPath?: string;
8
+ /** Per-call named instance selection. */
9
+ instanceName?: string;
10
+ }
11
+ /** Effective project directory and the source that selected it. */
12
+ export interface ProjectDirectoryInfo {
13
+ path: string;
14
+ source: 'argument' | 'config' | 'cwd';
15
+ }
16
+ /** How the selected dw.json file entered the configuration resolver. */
17
+ export type ConfigurationResolutionSource = 'argument' | 'globalDefault' | 'none' | 'projectDirectory' | 'projectEnvironment' | 'server';
18
+ /** Compact selected-configuration provenance returned by project-aware tools. */
19
+ export interface ConfigurationResolutionInfo {
20
+ hostname?: string;
21
+ instanceName?: string;
22
+ path?: string;
23
+ source: ConfigurationResolutionSource;
24
+ }
25
+ /** Provenance for a specialized project-relative directory. */
26
+ export interface DirectoryResolutionInfo {
27
+ path: string;
28
+ source: 'argument' | 'projectDirectory';
29
+ }
30
+ /** Compact, common provenance block returned by project/config-aware tools. */
31
+ export interface ToolResolution {
32
+ configuration?: ConfigurationResolutionInfo;
33
+ directories?: Record<string, DirectoryResolutionInfo>;
34
+ projectDirectory: ProjectDirectoryInfo;
35
+ }
36
+ /** Defaults known when MCP tool schemas are registered. */
37
+ export interface ProjectContextDefaults {
38
+ projectDirectory: ProjectDirectoryInfo;
39
+ }
40
+ /** Whether a tool needs only a project root or full configuration selection. */
41
+ export type ProjectContextKind = 'configuration' | 'project';
42
+ /** Build the canonical project-directory field with the effective fallback embedded in its description. */
43
+ export declare function createProjectDirectoryInput(defaults?: ProjectContextDefaults): z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
44
+ /** Build the canonical explicit primary dw.json field. */
45
+ export declare function createConfigPathInput(): z.ZodOptional<z.ZodString>;
46
+ /** Build the canonical named-instance selection field. */
47
+ export declare function createInstanceNameInput(): z.ZodOptional<z.ZodString>;
48
+ /** Build flat canonical schema fields for a local-project or configuration-aware tool. */
49
+ export declare function createProjectContextInputSchema(kind: ProjectContextKind, defaults?: ProjectContextDefaults): ZodRawShape;
50
+ /** Static field for schemas declared outside the shared adapter. */
51
+ export declare const projectDirectoryInput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
52
+ /** Static configuration schema for legacy/manual tool definitions. */
53
+ export declare const projectContextInputSchema: z.ZodRawShape;
@@ -0,0 +1,55 @@
1
+ /*
2
+ * Copyright (c) 2025, Salesforce, Inc.
3
+ * SPDX-License-Identifier: Apache-2
4
+ * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
5
+ */
6
+ import path from 'node:path';
7
+ import { z } from 'zod';
8
+ function defaultProjectContext() {
9
+ return { projectDirectory: { path: process.cwd(), source: 'cwd' } };
10
+ }
11
+ /** Build the canonical project-directory field with the effective fallback embedded in its description. */
12
+ export function createProjectDirectoryInput(defaults = defaultProjectContext()) {
13
+ const fallback = defaults.projectDirectory;
14
+ const sourceDescription = fallback.source === 'cwd'
15
+ ? 'the MCP process working directory'
16
+ : 'the server-level --project-directory / SFCC_PROJECT_DIRECTORY value';
17
+ return z
18
+ .string()
19
+ .refine((value) => path.isAbsolute(value), 'projectDirectory must be an absolute path')
20
+ .optional()
21
+ .describe(`Optional absolute project root for this call. Overrides the server-level project directory. ` +
22
+ `When omitted, uses ${sourceDescription}: ${fallback.path}`);
23
+ }
24
+ /** Build the canonical explicit primary dw.json field. */
25
+ export function createConfigPathInput() {
26
+ return z
27
+ .string()
28
+ .optional()
29
+ .describe('Optional path to a dw.json-format configuration file. Relative paths resolve from projectDirectory. ' +
30
+ 'Selects the primary file ahead of server and project automatic selection; the shared default dw.json remains available as a fallback and for named-instance lookup.');
31
+ }
32
+ /** Build the canonical named-instance selection field. */
33
+ export function createInstanceNameInput() {
34
+ return z
35
+ .string()
36
+ .min(1)
37
+ .optional()
38
+ .describe('Optional named instance to select from the resolved primary and default dw.json files. The primary file is searched first. When omitted, the active/default instance is used.');
39
+ }
40
+ /** Build flat canonical schema fields for a local-project or configuration-aware tool. */
41
+ export function createProjectContextInputSchema(kind, defaults = defaultProjectContext()) {
42
+ const project = { projectDirectory: createProjectDirectoryInput(defaults) };
43
+ if (kind === 'project')
44
+ return project;
45
+ return {
46
+ ...project,
47
+ configPath: createConfigPathInput(),
48
+ instanceName: createInstanceNameInput(),
49
+ };
50
+ }
51
+ /** Static field for schemas declared outside the shared adapter. */
52
+ export const projectDirectoryInput = createProjectDirectoryInput();
53
+ /** Static configuration schema for legacy/manual tool definitions. */
54
+ export const projectContextInputSchema = createProjectContextInputSchema('configuration');
55
+ //# sourceMappingURL=project-context.js.map
@@ -63,6 +63,7 @@ Retrieve observability metrics time-series for a B2C Commerce tenant. Returns me
63
63
  toolsets: ['SCAPI'],
64
64
  isGA: false,
65
65
  requiresInstance: false, // SCAPI uses OAuth directly
66
+ usesConfigurationContext: true,
66
67
  inputSchema: {
67
68
  category: z
68
69
  .enum(['overall', 'sales', 'ecdn', 'third-party', 'scapi', 'scapi-hooks', 'mrt', 'controller', 'ocapi'])
@@ -1,5 +1,6 @@
1
1
  import type { Services } from '../../services.js';
2
2
  import type { McpTool } from '../../utils/index.js';
3
+ import type { ProjectContextInput, ProjectDirectoryInfo } from '../project-context.js';
3
4
  import type { Scaffold, ResolvedParameters, ResolveParametersOptions } from '@salesforce/b2c-tooling-sdk/scaffold';
4
5
  /** Optional overrides for testing (scaffold not found, missing required). */
5
6
  export interface ScaffoldCustomApiExecuteOverrides {
@@ -12,7 +13,7 @@ export interface ScaffoldCustomApiExecuteOverrides {
12
13
  * Input schema for scapi_custom_api_generate_scaffold tool.
13
14
  * Parameters match the custom-api scaffold: apiName, apiType, cartridgeName, etc.
14
15
  */
15
- interface ScaffoldCustomApiInput {
16
+ interface ScaffoldCustomApiInput extends ProjectContextInput {
16
17
  /** API name (kebab-case, e.g. my-products). Required. */
17
18
  apiName: string;
18
19
  /** Cartridge name that will contain the API. Optional; defaults to first cartridge found in project. */
@@ -22,8 +23,12 @@ interface ScaffoldCustomApiInput {
22
23
  /** Short description of the API. Default: "A custom B2C Commerce API" */
23
24
  apiDescription?: string;
24
25
  /** Project root for cartridge discovery and output. Default: MCP project directory */
26
+ cartridgeDirectory?: string;
27
+ /** @deprecated Use cartridgeDirectory. */
25
28
  projectRoot?: string;
26
29
  /** Output directory override. Default: scaffold default or project root */
30
+ outputDirectory?: string;
31
+ /** @deprecated Use outputDirectory. */
27
32
  outputDir?: string;
28
33
  }
29
34
  /**
@@ -40,6 +45,8 @@ interface ScaffoldCustomApiOutput {
40
45
  }>;
41
46
  postInstructions?: string;
42
47
  error?: string;
48
+ projectDirectory: ProjectDirectoryInfo;
49
+ projectRoot: string;
43
50
  }
44
51
  /**
45
52
  * Core execute logic for the custom API scaffold tool.