@@ -29,7 +29,7 @@ const BASE_TOOLSET: Toolset = 'SCAPI';
2929const PROJECT_TYPE_TOOLSETS : Record < ProjectType , Toolset [ ] > = {
3030 cartridges : [ 'CARTRIDGES' ] ,
3131 'pwa-kit-v3' : [ 'PWAV3' , 'MRT' ] ,
32- 'storefront-next' : [ 'STOREFRONTNEXT' , 'MRT' ] ,
32+ 'storefront-next' : [ 'STOREFRONTNEXT' , 'MRT' , 'CARTRIDGES' ] ,
3333} ;
3434
3535/**
@@ -107,14 +107,60 @@ export function createToolRegistry(services: Services): ToolRegistry {
107107 return registry ;
108108}
109109
110+ /**
111+ * Performs workspace auto-discovery and returns appropriate toolsets.
112+ * Always includes BASE_TOOLSET even if no project types are detected.
113+ *
114+ * @param flags - Startup flags containing workingDirectory
115+ * @param reason - Reason for triggering auto-discovery (for logging)
116+ * @returns Array of toolsets to enable
117+ */
118+ async function performAutoDiscovery ( flags : StartupFlags , reason : string ) : Promise < Toolset [ ] > {
119+ const logger = getLogger ( ) ;
120+
121+ // Working directory from --working-directory flag or SFCC_WORKING_DIRECTORY env var
122+ const workingDirectory = flags . workingDirectory ?? process . cwd ( ) ;
123+
124+ // Warn if working directory wasn't explicitly configured
125+ if ( ! flags . workingDirectory ) {
126+ logger . warn (
127+ { cwd : workingDirectory } ,
128+ 'No --working-directory flag or SFCC_WORKING_DIRECTORY env var provided. ' +
129+ 'MCP clients like Cursor and Claude Desktop often spawn servers from ~ instead of the project directory. ' +
130+ 'Set --working-directory or SFCC_WORKING_DIRECTORY for reliable auto-discovery.' ,
131+ ) ;
132+ }
133+
134+ const detectionResult = await detectWorkspaceType ( workingDirectory ) ;
135+
136+ // Map all detected project types to MCP toolsets (union)
137+ // Note: getToolsetsForProjectTypes always includes BASE_TOOLSET
138+ const mappedToolsets = getToolsetsForProjectTypes ( detectionResult . projectTypes ) ;
139+
140+ logger . info (
141+ {
142+ reason,
143+ projectTypes : detectionResult . projectTypes ,
144+ matchedPatterns : detectionResult . matchedPatterns ,
145+ enabledToolsets : mappedToolsets ,
146+ } ,
147+ `Auto-discovery (${ reason } ): project types: ${ detectionResult . projectTypes . join ( ', ' ) || 'none' } ` ,
148+ ) ;
149+
150+ return mappedToolsets ;
151+ }
152+
110153/**
111154 * Register tools with the MCP server based on startup flags.
112155 *
113156 * Tool selection logic:
114- * 1. If neither --toolsets nor --tools are provided , perform auto-discovery
157+ * 1. If no valid tools result from --toolsets and --tools, perform auto-discovery
115158 * 2. Start with all tools from --toolsets (or auto-discovered toolsets)
116159 * 3. Add individual tools from --tools (can be from any toolset)
117160 *
161+ * Auto-discovery always enables at least the BASE_TOOLSET (SCAPI), even if no
162+ * project types are detected in the workspace.
163+ *
118164 * Example:
119165 * --toolsets STOREFRONTNEXT,MRT --tools cartridge_deploy
120166 * This enables STOREFRONTNEXT and MRT toolsets, plus adds cartridge_deploy from CARTRIDGES.
@@ -124,44 +170,11 @@ export function createToolRegistry(services: Services): ToolRegistry {
124170 * @param services - Services instance
125171 */
126172export async function registerToolsets ( flags : StartupFlags , server : B2CDxMcpServer , services : Services ) : Promise < void > {
127- let toolsets = flags . toolsets ?? [ ] ;
173+ const toolsets = flags . toolsets ?? [ ] ;
128174 const individualTools = flags . tools ?? [ ] ;
129175 const allowNonGaTools = flags . allowNonGaTools ?? false ;
130176 const logger = getLogger ( ) ;
131177
132- // Auto-discovery: When no --toolsets or --tools flags are provided,
133- // detect project type and enable appropriate toolsets automatically.
134- if ( toolsets . length === 0 && individualTools . length === 0 ) {
135- // Working directory from --working-directory flag or SFCC_WORKING_DIRECTORY env var
136- const workingDirectory = flags . workingDirectory ?? process . cwd ( ) ;
137-
138- // Warn if working directory wasn't explicitly configured
139- if ( ! flags . workingDirectory ) {
140- logger . warn (
141- { cwd : workingDirectory } ,
142- 'No --working-directory flag or SFCC_WORKING_DIRECTORY env var provided. ' +
143- 'MCP clients like Cursor and Claude Desktop often spawn servers from ~ instead of the project directory. ' +
144- 'Set --working-directory or SFCC_WORKING_DIRECTORY for reliable auto-discovery.' ,
145- ) ;
146- }
147-
148- const detectionResult = await detectWorkspaceType ( workingDirectory ) ;
149-
150- // Map all detected project types to MCP toolsets (union)
151- const mappedToolsets = getToolsetsForProjectTypes ( detectionResult . projectTypes ) ;
152-
153- logger . info (
154- {
155- projectTypes : detectionResult . projectTypes ,
156- matchedPatterns : detectionResult . matchedPatterns ,
157- enabledToolsets : mappedToolsets ,
158- } ,
159- `Auto-discovered project types: ${ detectionResult . projectTypes . join ( ', ' ) || 'none' } ` ,
160- ) ;
161-
162- toolsets = mappedToolsets ;
163- }
164-
165178 // Create the tool registry (all available tools)
166179 const toolRegistry = createToolRegistry ( services ) ;
167180
@@ -170,8 +183,11 @@ export async function registerToolsets(flags: StartupFlags, server: B2CDxMcpServ
170183 const allToolsByName = new Map ( allTools . map ( ( tool ) => [ tool . name , tool ] ) ) ;
171184 const existingToolNames = new Set ( allToolsByName . keys ( ) ) ;
172185
173- // Warn about invalid -- tools names (but continue with valid ones)
186+ // Determine valid individual tools
174187 const invalidTools = individualTools . filter ( ( name ) => ! existingToolNames . has ( name ) ) ;
188+ const validIndividualTools = individualTools . filter ( ( name ) => existingToolNames . has ( name ) ) ;
189+
190+ // Warn about invalid --tools names (but continue with valid ones)
175191 if ( invalidTools . length > 0 ) {
176192 logger . warn (
177193 { invalidTools, validTools : [ ...existingToolNames ] } ,
@@ -194,6 +210,17 @@ export async function registerToolsets(flags: StartupFlags, server: B2CDxMcpServ
194210 const validToolsets = toolsets . filter ( ( t ) : t is Toolset => TOOLSETS . includes ( t as Toolset ) ) ;
195211 const toolsetsToEnable = new Set < Toolset > ( toolsets . includes ( ALL_TOOLSETS ) ? TOOLSETS : validToolsets ) ;
196212
213+ // Auto-discovery: If no valid toolsets AND no valid individual tools, detect workspace type.
214+ // This handles both: (1) no flags provided, and (2) all provided flags are invalid.
215+ // Auto-discovery enables appropriate toolsets based on workspace type,
216+ // or at minimum BASE_TOOLSET if no project types are detected.
217+ if ( toolsetsToEnable . size === 0 && validIndividualTools . length === 0 ) {
218+ const discoveredToolsets = await performAutoDiscovery ( flags , 'no valid toolsets or tools' ) ;
219+ for ( const toolset of discoveredToolsets ) {
220+ toolsetsToEnable . add ( toolset ) ;
221+ }
222+ }
223+
197224 // Build the set of tools to register:
198225 // 1. Start with tools from enabled toolsets
199226 // 2. Add individual tools from --tools
@@ -211,7 +238,7 @@ export async function registerToolsets(flags: StartupFlags, server: B2CDxMcpServ
211238 }
212239
213240 // Step 2: Add individual tools from --tools (can be from any toolset)
214- for ( const toolName of individualTools ) {
241+ for ( const toolName of validIndividualTools ) {
215242 const tool = allToolsByName . get ( toolName ) ;
216243 if ( tool && ! registeredToolNames . has ( toolName ) ) {
217244 toolsToRegister . push ( tool ) ;
0 commit comments