@salesforce/b2c-dx-mcp 0.4.13 → 0.4.14

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.
@@ -81,7 +81,7 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
81
81
  * 3. dw.json file (via --config flag or auto-discovered from --project-directory)
82
82
  * 4. ~/.mobify file (for MRT API key)
83
83
  */
84
- protected loadConfiguration(): ResolvedB2CConfig;
84
+ protected loadConfiguration(): Promise<ResolvedB2CConfig>;
85
85
  /**
86
86
  * Loads configuration and creates a new Services instance.
87
87
  *
@@ -91,7 +91,7 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
91
91
  *
92
92
  * @returns A new Services instance with loaded configuration
93
93
  */
94
- protected loadServices(): Services;
94
+ protected loadServices(): Promise<Services>;
95
95
  /**
96
96
  * Main entry point - starts the MCP server.
97
97
  *
@@ -236,7 +236,7 @@ export default class McpServerCommand extends BaseCommand {
236
236
  * 3. dw.json file (via --config flag or auto-discovered from --project-directory)
237
237
  * 4. ~/.mobify file (for MRT API key)
238
238
  */
239
- loadConfiguration() {
239
+ async loadConfiguration() {
240
240
  const mrt = extractMrtFlags(this.flags);
241
241
  const options = {
242
242
  ...this.getBaseConfigOptions(),
@@ -258,8 +258,8 @@ export default class McpServerCommand extends BaseCommand {
258
258
  *
259
259
  * @returns A new Services instance with loaded configuration
260
260
  */
261
- loadServices() {
262
- const config = this.loadConfiguration();
261
+ async loadServices() {
262
+ const config = await this.loadConfiguration();
263
263
  return Services.fromResolvedConfig(config);
264
264
  }
265
265
  /**
@@ -14,7 +14,7 @@ export type ToolRegistry = Record<Toolset, McpTool[]>;
14
14
  * @param loadServices - Function that loads configuration and returns Services instance
15
15
  * @returns Complete tool registry
16
16
  */
17
- export declare function createToolRegistry(loadServices: () => Services): ToolRegistry;
17
+ export declare function createToolRegistry(loadServices: () => Promise<Services> | Services): ToolRegistry;
18
18
  /**
19
19
  * Register tools with the MCP server based on startup flags.
20
20
  *
@@ -34,4 +34,4 @@ export declare function createToolRegistry(loadServices: () => Services): ToolRe
34
34
  * @param server - B2CDxMcpServer instance
35
35
  * @param loadServices - Function that loads configuration and returns Services instance
36
36
  */
37
- export declare function registerToolsets(flags: StartupFlags, server: B2CDxMcpServer, loadServices: () => Services): Promise<void>;
37
+ export declare function registerToolsets(flags: StartupFlags, server: B2CDxMcpServer, loadServices: () => Promise<Services> | Services): Promise<void>;
@@ -206,4 +206,4 @@ export declare function jsonResult(data: unknown, indent?: number): ToolResult;
206
206
  * }, loadServices);
207
207
  * ```
208
208
  */
209
- export declare function createToolAdapter<TInput, TOutput>(options: ToolAdapterOptions<TInput, TOutput>, loadServices: () => Services): McpTool;
209
+ export declare function createToolAdapter<TInput, TOutput>(options: ToolAdapterOptions<TInput, TOutput>, loadServices: () => Promise<Services> | Services): McpTool;
@@ -187,7 +187,7 @@ export function createToolAdapter(options, loadServices) {
187
187
  const args = parseResult.data;
188
188
  try {
189
189
  // 2. Load Services to get fresh configuration (re-reads config files)
190
- const services = loadServices();
190
+ const services = await loadServices();
191
191
  // 3. Get B2CInstance if required (loaded on each call)
192
192
  let b2cInstance;
193
193
  if (requiresInstance) {
@@ -18,5 +18,5 @@ interface CartridgeToolInjections {
18
18
  * @param injections - Optional dependency injections for testing
19
19
  * @returns Array of MCP tools
20
20
  */
21
- export declare function createCartridgesTools(loadServices: () => Services, injections?: CartridgeToolInjections): McpTool[];
21
+ export declare function createCartridgesTools(loadServices: () => Promise<Services> | Services, injections?: CartridgeToolInjections): McpTool[];
22
22
  export {};
@@ -21,5 +21,5 @@ interface MrtToolInjections {
21
21
  * @param injections - Optional dependency injections for testing
22
22
  * @returns Array of MCP tools
23
23
  */
24
- export declare function createMrtTools(loadServices: () => Services, injections?: MrtToolInjections): McpTool[];
24
+ export declare function createMrtTools(loadServices: () => Promise<Services> | Services, injections?: MrtToolInjections): McpTool[];
25
25
  export {};
@@ -19,4 +19,4 @@ import type { Services } from '../../services.js';
19
19
  * @param loadServices - Function that loads configuration and returns Services instance
20
20
  * @returns Array of MCP tools
21
21
  */
22
- export declare function createPwav3Tools(loadServices: () => Services): McpTool[];
22
+ export declare function createPwav3Tools(loadServices: () => Promise<Services> | Services): McpTool[];
@@ -6,4 +6,4 @@ import type { Services } from '../../services.js';
6
6
  * @param loadServices - Function that loads configuration and returns Services instance
7
7
  * @returns The configured MCP tool
8
8
  */
9
- export declare function createDeveloperGuidelinesTool(loadServices: () => Services): McpTool;
9
+ export declare function createDeveloperGuidelinesTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -14,4 +14,4 @@ import type { Services } from '../../services.js';
14
14
  * @param loadServices - Function that loads configuration and returns Services instance
15
15
  * @returns Array of MCP tools
16
16
  */
17
- export declare function createScapiTools(loadServices: () => Services): McpTool[];
17
+ export declare function createScapiTools(loadServices: () => Promise<Services> | Services): McpTool[];
@@ -56,5 +56,5 @@ export declare function executeScaffoldCustomApi(args: ScaffoldCustomApiInput, s
56
56
  * @param loadServices - Function that returns Services (used by adapter on each call).
57
57
  * @param executeOverrides - Optional overrides for testing (getScaffold, resolveScaffoldParameters).
58
58
  */
59
- export declare function createScaffoldCustomApiTool(loadServices: () => Services, executeOverrides?: ScaffoldCustomApiExecuteOverrides): McpTool;
59
+ export declare function createScaffoldCustomApiTool(loadServices: () => Promise<Services> | Services, executeOverrides?: ScaffoldCustomApiExecuteOverrides): McpTool;
60
60
  export {};
@@ -6,4 +6,4 @@ import type { McpTool } from '../../utils/index.js';
6
6
  * Mirrors CLI: b2c scapi custom status. All flags supported; agent chooses what to use.
7
7
  * See: https://salesforcecommercecloud.github.io/b2c-developer-tooling/cli/custom-apis.html#b2c-scapi-custom-status
8
8
  */
9
- export declare function createScapiCustomApisStatusTool(loadServices: () => Services): McpTool;
9
+ export declare function createScapiCustomApisStatusTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -9,4 +9,4 @@ import type { McpTool } from '../../utils/index.js';
9
9
  * @param loadServices - Function that loads configuration and returns Services instance
10
10
  * @returns MCP tool for listing/fetching SCAPI schemas
11
11
  */
12
- export declare function createScapiSchemasListTool(loadServices: () => Services): McpTool;
12
+ export declare function createScapiSchemasListTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -39,4 +39,4 @@ export declare function generateWorkflowResponse(figmaUrl: string, workflowFileP
39
39
  * @param loadServices - Function that loads configuration and returns Services instance
40
40
  * @returns MCP tool for workflow orchestration
41
41
  */
42
- export declare function createFigmaToComponentTool(loadServices: () => Services): McpTool;
42
+ export declare function createFigmaToComponentTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -111,4 +111,4 @@ export declare function generateComponentRecommendation(input: GenerateComponent
111
111
  * @param loadServices - Function that loads configuration and returns Services instance
112
112
  * @returns MCP tool for component analysis and recommendation
113
113
  */
114
- export declare function createGenerateComponentTool(loadServices: () => Services): McpTool;
114
+ export declare function createGenerateComponentTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -58,4 +58,4 @@ export declare function mapFigmaTokensToTheme(args: MapTokensToThemeInput, works
58
58
  * @param loadServices - Function that loads configuration and returns Services instance
59
59
  * @returns MCP tool for token mapping
60
60
  */
61
- export declare function createMapTokensToThemeTool(loadServices: () => Services): McpTool;
61
+ export declare function createMapTokensToThemeTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -27,4 +27,4 @@ import type { Services } from '../../services.js';
27
27
  * @param loadServices - Function that loads configuration and returns Services instance
28
28
  * @returns Array of MCP tools
29
29
  */
30
- export declare function createStorefrontNextTools(loadServices: () => Services): McpTool[];
30
+ export declare function createStorefrontNextTools(loadServices: () => Promise<Services> | Services): McpTool[];
@@ -7,6 +7,21 @@ import { existsSync, readFileSync } from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { globSync } from 'glob';
9
9
  import { Project, InterfaceDeclaration, PropertySignature } from 'ts-morph';
10
+ /**
11
+ * Lazily-initialized, reusable ts-morph Project for component analysis.
12
+ * Creating a new Project (~40ms) on every analyzeComponent call is expensive;
13
+ * reusing one with an in-memory file system avoids repeated TypeScript compiler init.
14
+ */
15
+ let cachedProject;
16
+ function getProject() {
17
+ if (!cachedProject) {
18
+ cachedProject = new Project({
19
+ useInMemoryFileSystem: true,
20
+ skipAddingFilesFromTsConfig: true,
21
+ });
22
+ }
23
+ return cachedProject;
24
+ }
10
25
  // ============================================================================
11
26
  // TYPE INFERENCE
12
27
  // ============================================================================
@@ -279,43 +294,45 @@ function parseComponentFile(filePath) {
279
294
  filePath,
280
295
  };
281
296
  }
282
- const project = new Project({
283
- useInMemoryFileSystem: true,
284
- skipAddingFilesFromTsConfig: true,
285
- });
286
- const sourceFile = project.createSourceFile(filePath, content);
287
- const interfaces = sourceFile.getInterfaces();
288
- const propsInterface = interfaces.find((i) => i.getName().includes('Props'));
289
- if (!propsInterface) {
297
+ const project = getProject();
298
+ const sourceFile = project.createSourceFile(filePath, content, { overwrite: true });
299
+ try {
300
+ const interfaces = sourceFile.getInterfaces();
301
+ const propsInterface = interfaces.find((i) => i.getName().includes('Props'));
302
+ if (!propsInterface) {
303
+ return {
304
+ componentName: extractComponentName(content),
305
+ interfaceName: null,
306
+ hasDecorators: false,
307
+ props: [],
308
+ exportType: detectExportType(content),
309
+ filePath,
310
+ };
311
+ }
312
+ const props = propsInterface.getProperties().map((prop) => {
313
+ const name = prop.getName();
314
+ const type = prop.getType().getText();
315
+ const optional = prop.hasQuestionToken();
316
+ return {
317
+ name,
318
+ type,
319
+ optional,
320
+ isComplex: isComplexType(type),
321
+ isUIOnly: isUIOnlyProp(name),
322
+ };
323
+ });
290
324
  return {
291
325
  componentName: extractComponentName(content),
292
- interfaceName: null,
326
+ interfaceName: propsInterface.getName(),
293
327
  hasDecorators: false,
294
- props: [],
328
+ props,
295
329
  exportType: detectExportType(content),
296
330
  filePath,
297
331
  };
298
332
  }
299
- const props = propsInterface.getProperties().map((prop) => {
300
- const name = prop.getName();
301
- const type = prop.getType().getText();
302
- const optional = prop.hasQuestionToken();
303
- return {
304
- name,
305
- type,
306
- optional,
307
- isComplex: isComplexType(type),
308
- isUIOnly: isUIOnlyProp(name),
309
- };
310
- });
311
- return {
312
- componentName: extractComponentName(content),
313
- interfaceName: propsInterface.getName(),
314
- hasDecorators: false,
315
- props,
316
- exportType: detectExportType(content),
317
- filePath,
318
- };
333
+ finally {
334
+ project.removeSourceFile(sourceFile);
335
+ }
319
336
  }
320
337
  // ============================================================================
321
338
  // COMPONENT ANALYZER
@@ -249,4 +249,4 @@ export type PageDesignerDecoratorInput = z.infer<typeof pageDesignerDecoratorSch
249
249
  * @param loadServices - Function that loads configuration and returns Services instance
250
250
  * @returns The configured MCP tool
251
251
  */
252
- export declare function createPageDesignerDecoratorTool(loadServices: () => Services): McpTool;
252
+ export declare function createPageDesignerDecoratorTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -542,7 +542,7 @@ export function createPageDesignerDecoratorTool(loadServices) {
542
542
  const validatedArgs = pageDesignerDecoratorSchema.parse(args);
543
543
  // Use projectDirectory from services to ensure we search in the correct project directory
544
544
  // This prevents searches in the home folder when MCP clients spawn servers from ~
545
- const services = loadServices();
545
+ const services = await loadServices();
546
546
  const workspaceRoot = services.resolveWithProjectDirectory();
547
547
  if (validatedArgs.autoMode === undefined && !validatedArgs.conversationContext) {
548
548
  const fullPath = resolveComponent(validatedArgs.component, workspaceRoot, validatedArgs.searchPaths);
@@ -6,4 +6,4 @@ import type { Services } from '../../services.js';
6
6
  * @param loadServices - Function that loads configuration and returns Services instance
7
7
  * @returns The configured MCP tool
8
8
  */
9
- export declare function createDeveloperGuidelinesTool(loadServices: () => Services): McpTool;
9
+ export declare function createDeveloperGuidelinesTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -11,4 +11,4 @@ export type { ColorEntry, ColorMapping, CollectedAnswers, ConversationContext, F
11
11
  * @param loadServices - Function that loads configuration and returns Services instance
12
12
  * @returns The configured MCP tool
13
13
  */
14
- export declare function createSiteThemingTool(loadServices: () => Services): McpTool;
14
+ export declare function createSiteThemingTool(loadServices: () => Promise<Services> | Services): McpTool;
@@ -390,5 +390,5 @@
390
390
  "enableJsonFlag": false
391
391
  }
392
392
  },
393
- "version": "0.4.13"
393
+ "version": "0.4.14"
394
394
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@salesforce/b2c-dx-mcp",
3
3
  "description": "MCP server for B2C Commerce developer experience tools",
4
- "version": "0.4.13",
4
+ "version": "0.4.14",
5
5
  "author": "Salesforce",
6
6
  "license": "Apache-2.0",
7
7
  "repository": "SalesforceCommerceCloud/b2c-developer-tooling",
@@ -80,7 +80,7 @@
80
80
  "yaml": "2.8.1",
81
81
  "postcss": "8.5.6",
82
82
  "zod": "3.25.76",
83
- "@salesforce/b2c-tooling-sdk": "0.8.3"
83
+ "@salesforce/b2c-tooling-sdk": "0.9.0"
84
84
  },
85
85
  "devDependencies": {
86
86
  "@eslint/compat": "^1",