1010using UnityEngine ;
1111
1212namespace McpUnity . Tools {
13+ /// <summary>
14+ /// Tool to recompile all scripts in the Unity project
15+ /// </summary>
1316 public class RecompileScriptsTool : McpToolBase
1417 {
18+ private class CompilationRequest {
19+ public readonly bool ReturnWithLogs ;
20+ public readonly int LogsLimit ;
21+ public readonly TaskCompletionSource < JObject > CompletionSource ;
22+
23+ public CompilationRequest ( bool returnWithLogs , int logsLimit , TaskCompletionSource < JObject > completionSource )
24+ {
25+ ReturnWithLogs = returnWithLogs ;
26+ LogsLimit = logsLimit ;
27+ CompletionSource = completionSource ;
28+ }
29+ }
30+
31+ private readonly List < CompilationRequest > _pendingRequests = new List < CompilationRequest > ( ) ;
1532 private readonly List < CompilerMessage > _compilationLogs = new List < CompilerMessage > ( ) ;
16- private TaskCompletionSource < JObject > _completionSource ;
1733 private int _processedAssemblies = 0 ;
18- private bool _returnWithLogs = true ;
19- private int _logsLimit = 100 ;
2034
2135 public RecompileScriptsTool ( )
2236 {
2337 Name = "recompile_scripts" ;
24- Description = "Recompiles all scripts in the Unity project. Use returnWithLogs parameter to control whether compilation logs are returned. Use logsLimit parameter to limit the number of logs returned when returnWithLogs is true. " ;
38+ Description = "Recompiles all scripts in the Unity project" ;
2539 IsAsync = true ; // Compilation is asynchronous
2640 }
2741
@@ -32,68 +46,119 @@ public RecompileScriptsTool()
3246 /// <param name="tcs">TaskCompletionSource to set the result or exception</param>
3347 public override void ExecuteAsync ( JObject parameters , TaskCompletionSource < JObject > tcs )
3448 {
35- _completionSource = tcs ;
36- _compilationLogs . Clear ( ) ;
37- _processedAssemblies = 0 ;
38-
3949 // Extract and store parameters
40- _returnWithLogs = GetBoolParameter ( parameters , "returnWithLogs" , true ) ;
41- _logsLimit = Mathf . Clamp ( GetIntParameter ( parameters , "logsLimit" , 100 ) , 0 , 1000 ) ;
42-
43- CompilationPipeline . assemblyCompilationFinished += OnAssemblyCompilationFinished ;
44- CompilationPipeline . compilationFinished += OnCompilationFinished ;
45-
46- if ( EditorApplication . isCompiling == false ) {
47- McpLogger . LogInfo ( $ "Recompiling all scripts in the Unity project (logsLimit: { _logsLimit } )" ) ;
48- CompilationPipeline . RequestScriptCompilation ( ) ;
50+ var returnWithLogs = GetBoolParameter ( parameters , "returnWithLogs" , true ) ;
51+ var logsLimit = Mathf . Clamp ( GetIntParameter ( parameters , "logsLimit" , 100 ) , 0 , 1000 ) ;
52+ var request = new CompilationRequest ( returnWithLogs , logsLimit , tcs ) ;
53+
54+ bool hasActiveRequest = false ;
55+ lock ( _pendingRequests )
56+ {
57+ hasActiveRequest = _pendingRequests . Count > 0 ;
58+ _pendingRequests . Add ( request ) ;
4959 }
50- else {
60+
61+ if ( hasActiveRequest )
62+ {
5163 McpLogger . LogInfo ( "Recompilation already in progress. Waiting for completion..." ) ;
64+ return ;
65+ }
66+
67+ // On first request, initialize compilation listeners and start compilation
68+ StartCompilationTracking ( ) ;
69+
70+ if ( EditorApplication . isCompiling == false )
71+ {
72+ McpLogger . LogInfo ( $ "Recompiling all scripts in the Unity project (logsLimit: { logsLimit } )") ;
73+ CompilationPipeline . RequestScriptCompilation ( ) ;
5274 }
5375 }
5476
77+ /// <summary>
78+ /// Subscribe to compilation events, reset tracked state
79+ /// </summary>
80+ private void StartCompilationTracking ( )
81+ {
82+ _compilationLogs . Clear ( ) ;
83+ _processedAssemblies = 0 ;
84+ CompilationPipeline . assemblyCompilationFinished += OnAssemblyCompilationFinished ;
85+ CompilationPipeline . compilationFinished += OnCompilationFinished ;
86+ }
87+
88+ /// <summary>
89+ /// Unsubscribe from compilation events
90+ /// </summary>
91+ private void StopCompilationTracking ( )
92+ {
93+ CompilationPipeline . assemblyCompilationFinished -= OnAssemblyCompilationFinished ;
94+ CompilationPipeline . compilationFinished -= OnCompilationFinished ;
95+ }
96+
97+ /// <summary>
98+ /// Record compilation logs for every single assembly
99+ /// </summary>
100+ /// <param name="assemblyPath"></param>
101+ /// <param name="messages"></param>
55102 private void OnAssemblyCompilationFinished ( string assemblyPath , CompilerMessage [ ] messages )
56103 {
57104 _processedAssemblies ++ ;
58105 _compilationLogs . AddRange ( messages ) ;
59106 }
60107
108+ /// <summary>
109+ /// Complete all pending requests and stop tracking
110+ /// </summary>
111+ /// <param name="_"></param>
61112 private void OnCompilationFinished ( object _ )
62113 {
63114 McpLogger . LogInfo ( $ "Recompilation completed. Processed { _processedAssemblies } assemblies with { _compilationLogs . Count } compiler messages") ;
64115
65- CompilationPipeline . compilationFinished -= OnCompilationFinished ;
66- CompilationPipeline . assemblyCompilationFinished -= OnAssemblyCompilationFinished ;
116+ // Separate errors, warnings, and other messages
117+ List < CompilerMessage > errors = _compilationLogs . Where ( m => m . type == CompilerMessageType . Error ) . ToList ( ) ;
118+ List < CompilerMessage > warnings = _compilationLogs . Where ( m => m . type == CompilerMessageType . Warning ) . ToList ( ) ;
119+ List < CompilerMessage > others = _compilationLogs . Where ( m => m . type != CompilerMessageType . Error && m . type != CompilerMessageType . Warning ) . ToList ( ) ;
120+
121+ // Stop tracking before completing requests
122+ StopCompilationTracking ( ) ;
123+
124+ // Complete all requests received before compilation end, the next received request will start a new compilation
125+ List < CompilationRequest > requestsToComplete = new List < CompilationRequest > ( ) ;
126+
127+ lock ( _pendingRequests )
128+ {
129+ requestsToComplete . AddRange ( _pendingRequests ) ;
130+ _pendingRequests . Clear ( ) ;
131+ }
67132
68- try
133+ foreach ( var request in requestsToComplete )
69134 {
70- // Format logs as JSON array similar to ConsoleLogsService
71- JArray logsArray = new JArray ( ) ;
72-
73- // Separate errors, warnings, and other messages
74- List < CompilerMessage > errors = _compilationLogs . Where ( m => m . type == CompilerMessageType . Error ) . ToList ( ) ;
75- List < CompilerMessage > warnings = _compilationLogs . Where ( m => m . type == CompilerMessageType . Warning ) . ToList ( ) ;
76- List < CompilerMessage > others = _compilationLogs . Where ( m => m . type != CompilerMessageType . Error && m . type != CompilerMessageType . Warning ) . ToList ( ) ;
135+ CompleteRequest ( request , errors , warnings , others ) ;
136+ }
137+ }
77138
78- int errorCount = errors . Count ;
79- int warningCount = warnings . Count ;
139+ /// <summary>
140+ /// Process a completed compilation request
141+ /// </summary>
142+ private static void CompleteRequest ( CompilationRequest request , List < CompilerMessage > errors , List < CompilerMessage > warnings , List < CompilerMessage > others )
143+ {
144+ try {
145+ JArray logsArray = new JArray ( ) ;
80146
81147 // Sort logs and apply logsLimit - prioritize errors if logsLimit is restrictive
82148 IEnumerable < CompilerMessage > sortedLogs ;
83- if ( ! _returnWithLogs || _logsLimit <= 0 )
149+ if ( ! request . ReturnWithLogs || request . LogsLimit <= 0 )
84150 {
85151 sortedLogs = Enumerable . Empty < CompilerMessage > ( ) ;
86152 }
87- else
88- {
153+ else {
89154 // Always include all errors, then warnings, then other messages up to the logsLimit
90155 var selectedLogs = errors . ToList ( ) ;
91- var remainingSlots = _logsLimit - selectedLogs . Count ;
156+ var remainingSlots = request . LogsLimit - selectedLogs . Count ;
92157
93158 if ( remainingSlots > 0 )
94159 {
95160 selectedLogs . AddRange ( warnings . Take ( remainingSlots ) ) ;
96- remainingSlots = _logsLimit - selectedLogs . Count ;
161+ remainingSlots = request . LogsLimit - selectedLogs . Count ;
97162 }
98163
99164 if ( remainingSlots > 0 )
@@ -106,7 +171,7 @@ private void OnCompilationFinished(object _)
106171
107172 foreach ( var message in sortedLogs )
108173 {
109- var logObject = new JObject
174+ var logObject = new JObject
110175 {
111176 [ "message" ] = message . message ,
112177 [ "type" ] = message . type . ToString ( ) ,
@@ -124,28 +189,28 @@ private void OnCompilationFinished(object _)
124189 logsArray . Add ( logObject ) ;
125190 }
126191
127- bool hasErrors = errorCount > 0 ;
192+ bool hasErrors = errors . Count > 0 ;
128193 string summaryMessage = hasErrors
129- ? $ "Recompilation completed with { errorCount } error(s) and { warningCount } warning(s)"
130- : $ "Successfully recompiled all scripts with { warningCount } warning(s)";
131-
132- summaryMessage += $ " (returnWithLogs: { _returnWithLogs } , logsLimit: { _logsLimit } )";
194+ ? $ "Recompilation completed with { errors . Count } error(s) and { warnings . Count } warning(s)"
195+ : $ "Successfully recompiled all scripts with { warnings . Count } warning(s)";
196+
197+ summaryMessage += $ " (returnWithLogs: { request . ReturnWithLogs } , logsLimit: { request . LogsLimit } )";
133198
134- var response = new JObject
199+ var response = new JObject
135200 {
136201 [ "success" ] = true ,
137202 [ "type" ] = "text" ,
138203 [ "message" ] = summaryMessage ,
139204 [ "logs" ] = logsArray
140205 } ;
141206
142- McpLogger . LogInfo ( $ "Setting recompilation result: success={ ! hasErrors } , errors={ errorCount } , warnings={ warningCount } ") ;
143- _completionSource . SetResult ( response ) ;
144- }
145- catch ( Exception ex )
207+ McpLogger . LogInfo ( $ "Setting recompilation result: success={ ! hasErrors } , errors={ errors . Count } , warnings={ warnings . Count } ") ;
208+ request . CompletionSource . SetResult ( response ) ;
209+ }
210+ catch ( Exception ex )
146211 {
147212 McpLogger . LogError ( $ "Error creating recompilation response: { ex . Message } \n { ex . StackTrace } ") ;
148- _completionSource . SetResult ( McpUnitySocketHandler . CreateErrorResponse (
213+ request . CompletionSource . SetResult ( McpUnitySocketHandler . CreateErrorResponse (
149214 $ "Error creating recompilation response: { ex . Message } ",
150215 "response_creation_error"
151216 ) ) ;
0 commit comments