55 */
66
77import { getLogger } from '@salesforce/b2c-tooling-sdk/logging' ;
8+ import { detectWorkspaceType , type ProjectType } from '@salesforce/b2c-tooling-sdk/discovery' ;
89import type { McpTool , Toolset , StartupFlags } from './utils/index.js' ;
910import { ALL_TOOLSETS , TOOLSETS , VALID_TOOLSET_NAMES } from './utils/index.js' ;
1011import type { B2CDxMcpServer } from './server.js' ;
@@ -15,6 +16,59 @@ import {createPwav3Tools} from './tools/pwav3/index.js';
1516import { createScapiTools } from './tools/scapi/index.js' ;
1617import { createStorefrontNextTools } from './tools/storefrontnext/index.js' ;
1718
19+ /**
20+ * Maps a single project type to its associated MCP toolsets.
21+ */
22+ function getToolsetsForProjectType ( projectType : ProjectType ) : Toolset [ ] {
23+ switch ( projectType ) {
24+ case 'custom-api' : {
25+ return [ 'CARTRIDGES' , 'SCAPI' ] ;
26+ }
27+ case 'headless' : {
28+ return [ 'SCAPI' ] ;
29+ }
30+ case 'pwa-kit-v3' : {
31+ return [ 'PWAV3' , 'MRT' , 'SCAPI' ] ;
32+ }
33+ case 'sfra' : {
34+ return [ 'CARTRIDGES' , 'SCAPI' ] ;
35+ }
36+ case 'storefront-next' : {
37+ return [ 'STOREFRONTNEXT' , 'MRT' , 'SCAPI' ] ;
38+ }
39+ default : {
40+ // Fallback: provide basic SCAPI tools for unknown projects
41+ return [ 'SCAPI' ] ;
42+ }
43+ }
44+ }
45+
46+ /**
47+ * Maps multiple detected project types to a union of MCP toolsets.
48+ *
49+ * Combines toolsets from all matched project types, enabling hybrid
50+ * project support (e.g., SFRA + Custom API gets both CARTRIDGES and SCAPI).
51+ *
52+ * @param projectTypes - Array of detected project types
53+ * @returns Union of all toolsets for the detected project types
54+ */
55+ function getToolsetsForProjectTypes ( projectTypes : ProjectType [ ] ) : Toolset [ ] {
56+ // Fallback to SCAPI when no project types detected
57+ if ( projectTypes . length === 0 ) {
58+ return [ 'SCAPI' ] ;
59+ }
60+
61+ const toolsetSet = new Set < Toolset > ( ) ;
62+
63+ for ( const projectType of projectTypes ) {
64+ for ( const toolset of getToolsetsForProjectType ( projectType ) ) {
65+ toolsetSet . add ( toolset ) ;
66+ }
67+ }
68+
69+ return [ ...toolsetSet ] ;
70+ }
71+
1872/**
1973 * Registry of tools organized by toolset.
2074 * Tools can belong to multiple toolsets via their `toolsets` array.
@@ -61,8 +115,9 @@ export function createToolRegistry(services: Services): ToolRegistry {
61115 * Register tools with the MCP server based on startup flags.
62116 *
63117 * Tool selection logic:
64- * 1. Start with all tools from --toolsets
65- * 2. Add individual tools from --tools (can be from any toolset)
118+ * 1. If neither --toolsets nor --tools are provided, perform auto-discovery
119+ * 2. Start with all tools from --toolsets (or auto-discovered toolsets)
120+ * 3. Add individual tools from --tools (can be from any toolset)
66121 *
67122 * Example:
68123 * --toolsets STOREFRONTNEXT,MRT --tools cartridge_deploy
@@ -73,12 +128,43 @@ export function createToolRegistry(services: Services): ToolRegistry {
73128 * @param services - Services instance
74129 */
75130export async function registerToolsets ( flags : StartupFlags , server : B2CDxMcpServer , services : Services ) : Promise < void > {
76- const toolsets = flags . toolsets ?? [ ] ;
131+ let toolsets = flags . toolsets ?? [ ] ;
77132 const individualTools = flags . tools ?? [ ] ;
78133 const allowNonGaTools = flags . allowNonGaTools ?? false ;
134+ const logger = getLogger ( ) ;
135+
136+ // Auto-discovery: When no --toolsets or --tools flags are provided,
137+ // detect project type and enable appropriate toolsets automatically.
138+ if ( toolsets . length === 0 && individualTools . length === 0 ) {
139+ // Working directory from --working-directory flag or SFCC_WORKING_DIRECTORY env var
140+ const workingDirectory = flags . workingDirectory ?? process . cwd ( ) ;
141+
142+ // Warn if working directory wasn't explicitly configured
143+ if ( ! flags . workingDirectory ) {
144+ logger . warn (
145+ { cwd : workingDirectory } ,
146+ 'No --working-directory flag or SFCC_WORKING_DIRECTORY env var provided. ' +
147+ 'MCP clients like Cursor and Claude Desktop often spawn servers from ~ instead of the project directory. ' +
148+ 'Set --working-directory or SFCC_WORKING_DIRECTORY for reliable auto-discovery.' ,
149+ ) ;
150+ }
151+
152+ const detectionResult = await detectWorkspaceType ( workingDirectory ) ;
153+
154+ // Map all detected project types to MCP toolsets (union)
155+ const mappedToolsets = getToolsetsForProjectTypes ( detectionResult . projectTypes ) ;
156+
157+ logger . info (
158+ {
159+ projectTypes : detectionResult . projectTypes ,
160+ matchedPatterns : detectionResult . matchedPatterns ,
161+ enabledToolsets : mappedToolsets ,
162+ } ,
163+ `Auto-discovered project types: ${ detectionResult . projectTypes . join ( ', ' ) || 'none' } ` ,
164+ ) ;
79165
80- // NOTE: When no -- toolsets or --tools flags are provided, auto-discovery
81- // will detect project type and enable appropriate toolsets automatically.
166+ toolsets = mappedToolsets ;
167+ }
82168
83169 // Create the tool registry (all available tools)
84170 const toolRegistry = createToolRegistry ( services ) ;
@@ -88,8 +174,6 @@ export async function registerToolsets(flags: StartupFlags, server: B2CDxMcpServ
88174 const allToolsByName = new Map ( allTools . map ( ( tool ) => [ tool . name , tool ] ) ) ;
89175 const existingToolNames = new Set ( allToolsByName . keys ( ) ) ;
90176
91- const logger = getLogger ( ) ;
92-
93177 // Warn about invalid --tools names (but continue with valid ones)
94178 const invalidTools = individualTools . filter ( ( name ) => ! existingToolNames . has ( name ) ) ;
95179 if ( invalidTools . length > 0 ) {
0 commit comments