@@ -406,6 +406,7 @@ private static bool TryGetEmbeddedServerSource(out string srcPath)
406406 /// <summary>
407407 /// Searches Unity project for MCPForUnityTools folders and copies .py files to server tools directory.
408408 /// Only copies if the tool's version.txt has changed (or doesn't exist).
409+ /// Files are copied into per-folder subdirectories to avoid conflicts.
409410 /// </summary>
410411 private static void CopyUnityProjectTools ( string destToolsDir )
411412 {
@@ -418,18 +419,51 @@ private static void CopyUnityProjectTools(string destToolsDir)
418419 return ;
419420 }
420421
421- // Find all MCPForUnityTools folders
422- var toolsFolders = Directory . GetDirectories ( projectRoot , "MCPForUnityTools" , SearchOption . AllDirectories ) ;
422+ // Ensure destToolsDir exists
423+ Directory . CreateDirectory ( destToolsDir ) ;
424+
425+ // Limit scan to specific directories to avoid deep recursion
426+ var searchRoots = new List < string > ( ) ;
427+ var assetsPath = Path . Combine ( projectRoot , "Assets" ) ;
428+ var packagesPath = Path . Combine ( projectRoot , "Packages" ) ;
429+ var packageCachePath = Path . Combine ( projectRoot , "Library" , "PackageCache" ) ;
430+
431+ if ( Directory . Exists ( assetsPath ) ) searchRoots . Add ( assetsPath ) ;
432+ if ( Directory . Exists ( packagesPath ) ) searchRoots . Add ( packagesPath ) ;
433+ if ( Directory . Exists ( packageCachePath ) ) searchRoots . Add ( packageCachePath ) ;
434+
435+ // Find all MCPForUnityTools folders in limited search roots
436+ var toolsFolders = new List < string > ( ) ;
437+ foreach ( var searchRoot in searchRoots )
438+ {
439+ try
440+ {
441+ toolsFolders . AddRange ( Directory . GetDirectories ( searchRoot , "MCPForUnityTools" , SearchOption . AllDirectories ) ) ;
442+ }
443+ catch ( Exception ex )
444+ {
445+ McpLog . Warn ( $ "Failed to search { searchRoot } : { ex . Message } ") ;
446+ }
447+ }
423448
424449 int copiedCount = 0 ;
425450 int skippedCount = 0 ;
426451
452+ // Track all active folder identifiers (for cleanup)
453+ var activeFolderIdentifiers = new HashSet < string > ( ) ;
454+
427455 foreach ( var folder in toolsFolders )
428456 {
429457 // Generate unique identifier for this tools folder based on its parent directory structure
430458 // e.g., "MooseRunner_MCPForUnityTools" or "MyPackage_MCPForUnityTools"
431459 string folderIdentifier = GetToolsFolderIdentifier ( folder ) ;
432- string versionTrackingFile = Path . Combine ( destToolsDir , $ "{ folderIdentifier } _version.txt") ;
460+ activeFolderIdentifiers . Add ( folderIdentifier ) ;
461+
462+ // Create per-folder subdirectory in destToolsDir
463+ string destFolderSubdir = Path . Combine ( destToolsDir , folderIdentifier ) ;
464+ Directory . CreateDirectory ( destFolderSubdir ) ;
465+
466+ string versionTrackingFile = Path . Combine ( destFolderSubdir , "version.txt" ) ;
433467
434468 // Read source version
435469 string sourceVersionFile = Path . Combine ( folder , "version.txt" ) ;
@@ -450,7 +484,7 @@ private static void CopyUnityProjectTools(string destToolsDir)
450484 foreach ( var pyFile in pyFiles )
451485 {
452486 string fileName = Path . GetFileName ( pyFile ) ;
453- string destFile = Path . Combine ( destToolsDir , fileName ) ;
487+ string destFile = Path . Combine ( destFolderSubdir , fileName ) ;
454488
455489 try
456490 {
@@ -480,6 +514,9 @@ private static void CopyUnityProjectTools(string destToolsDir)
480514 }
481515 }
482516
517+ // Clean up stale subdirectories (folders removed from upstream)
518+ CleanupStaleToolFolders ( destToolsDir , activeFolderIdentifiers ) ;
519+
483520 if ( copiedCount > 0 )
484521 {
485522 McpLog . Info ( $ "Copied { copiedCount } Unity project tool(s) to server") ;
@@ -491,6 +528,43 @@ private static void CopyUnityProjectTools(string destToolsDir)
491528 }
492529 }
493530
531+ /// <summary>
532+ /// Removes stale tool subdirectories that are no longer present in the Unity project.
533+ /// </summary>
534+ private static void CleanupStaleToolFolders ( string destToolsDir , HashSet < string > activeFolderIdentifiers )
535+ {
536+ try
537+ {
538+ if ( ! Directory . Exists ( destToolsDir ) ) return ;
539+
540+ // Get all subdirectories in destToolsDir
541+ var existingSubdirs = Directory . GetDirectories ( destToolsDir ) ;
542+
543+ foreach ( var subdir in existingSubdirs )
544+ {
545+ string subdirName = Path . GetFileName ( subdir ) ;
546+
547+ // Check if this subdirectory corresponds to an active tools folder
548+ if ( ! activeFolderIdentifiers . Contains ( subdirName ) )
549+ {
550+ try
551+ {
552+ Directory . Delete ( subdir , recursive : true ) ;
553+ McpLog . Info ( $ "Cleaned up stale tools folder: { subdirName } ") ;
554+ }
555+ catch ( Exception ex )
556+ {
557+ McpLog . Warn ( $ "Failed to delete stale folder { subdirName } : { ex . Message } ") ;
558+ }
559+ }
560+ }
561+ }
562+ catch ( Exception ex )
563+ {
564+ McpLog . Warn ( $ "Failed to cleanup stale tool folders: { ex . Message } ") ;
565+ }
566+ }
567+
494568 /// <summary>
495569 /// Generates a unique identifier for a MCPForUnityTools folder based on its parent directory.
496570 /// Example: "Assets/MooseRunner/Editor/MCPForUnityTools" → "MooseRunner_MCPForUnityTools"
0 commit comments