1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Threading . Tasks ;
5+ using McpUnity . Unity ;
6+ using McpUnity . Utils ;
7+ using Newtonsoft . Json . Linq ;
8+ using UnityEditor ;
9+ using UnityEditor . Compilation ;
10+ using UnityEngine ;
11+
12+ namespace McpUnity . Tools {
13+ public class RecompileScriptsTool : McpToolBase
14+ {
15+ private readonly List < CompilerMessage > _compilationLogs = new List < CompilerMessage > ( ) ;
16+ private TaskCompletionSource < JObject > _completionSource ;
17+ private int _processedAssemblies = 0 ;
18+ private bool _returnWithLogs = true ;
19+ private int _logsLimit = 100 ;
20+
21+ public RecompileScriptsTool ( )
22+ {
23+ 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." ;
25+ IsAsync = true ; // Compilation is asynchronous
26+ }
27+
28+ /// <summary>
29+ /// Execute the Recompile tool asynchronously
30+ /// </summary>
31+ /// <param name="parameters">Tool parameters as a JObject</param>
32+ /// <param name="tcs">TaskCompletionSource to set the result or exception</param>
33+ public override void ExecuteAsync ( JObject parameters , TaskCompletionSource < JObject > tcs )
34+ {
35+ _completionSource = tcs ;
36+ _compilationLogs . Clear ( ) ;
37+ _processedAssemblies = 0 ;
38+
39+ // 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 ( ) ;
49+ }
50+ else {
51+ McpLogger . LogInfo ( "Recompilation already in progress. Waiting for completion..." ) ;
52+ }
53+ }
54+
55+ private void OnAssemblyCompilationFinished ( string assemblyPath , CompilerMessage [ ] messages )
56+ {
57+ _processedAssemblies ++ ;
58+ _compilationLogs . AddRange ( messages ) ;
59+ }
60+
61+ private void OnCompilationFinished ( object _ )
62+ {
63+ McpLogger . LogInfo ( $ "Recompilation completed. Processed { _processedAssemblies } assemblies with { _compilationLogs . Count } compiler messages") ;
64+
65+ CompilationPipeline . compilationFinished -= OnCompilationFinished ;
66+ CompilationPipeline . assemblyCompilationFinished -= OnAssemblyCompilationFinished ;
67+
68+ try
69+ {
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 ( ) ;
77+
78+ int errorCount = errors . Count ;
79+ int warningCount = warnings . Count ;
80+
81+ // Sort logs and apply logsLimit - prioritize errors if logsLimit is restrictive
82+ IEnumerable < CompilerMessage > sortedLogs ;
83+ if ( ! _returnWithLogs || _logsLimit <= 0 )
84+ {
85+ sortedLogs = Enumerable . Empty < CompilerMessage > ( ) ;
86+ }
87+ else
88+ {
89+ // Always include all errors, then warnings, then other messages up to the logsLimit
90+ var selectedLogs = errors . ToList ( ) ;
91+ var remainingSlots = _logsLimit - selectedLogs . Count ;
92+
93+ if ( remainingSlots > 0 )
94+ {
95+ selectedLogs . AddRange ( warnings . Take ( remainingSlots ) ) ;
96+ remainingSlots = _logsLimit - selectedLogs . Count ;
97+ }
98+
99+ if ( remainingSlots > 0 )
100+ {
101+ selectedLogs . AddRange ( others . Take ( remainingSlots ) ) ;
102+ }
103+
104+ sortedLogs = selectedLogs ;
105+ }
106+
107+ foreach ( var message in sortedLogs )
108+ {
109+ var logObject = new JObject
110+ {
111+ [ "message" ] = message . message ,
112+ [ "type" ] = message . type . ToString ( ) ,
113+ [ "timestamp" ] = DateTime . Now . ToString ( "yyyy-MM-dd HH:mm:ss.fff" )
114+ } ;
115+
116+ // Add file information if available
117+ if ( ! string . IsNullOrEmpty ( message . file ) )
118+ {
119+ logObject [ "file" ] = message . file ;
120+ logObject [ "line" ] = message . line ;
121+ logObject [ "column" ] = message . column ;
122+ }
123+
124+ logsArray . Add ( logObject ) ;
125+ }
126+
127+ bool hasErrors = errorCount > 0 ;
128+ 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 } )";
133+
134+ var response = new JObject
135+ {
136+ [ "success" ] = true ,
137+ [ "type" ] = "text" ,
138+ [ "message" ] = summaryMessage ,
139+ [ "logs" ] = logsArray
140+ } ;
141+
142+ McpLogger . LogInfo ( $ "Setting recompilation result: success={ ! hasErrors } , errors={ errorCount } , warnings={ warningCount } ") ;
143+ _completionSource . SetResult ( response ) ;
144+ }
145+ catch ( Exception ex )
146+ {
147+ McpLogger . LogError ( $ "Error creating recompilation response: { ex . Message } \n { ex . StackTrace } ") ;
148+ _completionSource . SetResult ( McpUnitySocketHandler . CreateErrorResponse (
149+ $ "Error creating recompilation response: { ex . Message } ",
150+ "response_creation_error"
151+ ) ) ;
152+ }
153+ }
154+
155+ /// <summary>
156+ /// Helper method to safely extract integer parameters with default values
157+ /// </summary>
158+ /// <param name="parameters">JObject containing parameters</param>
159+ /// <param name="key">Parameter key to extract</param>
160+ /// <param name="defaultValue">Default value if parameter is missing or invalid</param>
161+ /// <returns>Extracted integer value or default</returns>
162+ private static int GetIntParameter ( JObject parameters , string key , int defaultValue )
163+ {
164+ if ( parameters ? [ key ] != null && int . TryParse ( parameters [ key ] . ToString ( ) , out int value ) )
165+ return value ;
166+ return defaultValue ;
167+ }
168+
169+ /// <summary>
170+ /// Helper method to safely extract boolean parameters with default values
171+ /// </summary>
172+ /// <param name="parameters">JObject containing parameters</param>
173+ /// <param name="key">Parameter key to extract</param>
174+ /// <param name="defaultValue">Default value if parameter is missing or invalid</param>
175+ /// <returns>Extracted boolean value or default</returns>
176+ private static bool GetBoolParameter ( JObject parameters , string key , bool defaultValue )
177+ {
178+ if ( parameters ? [ key ] != null && bool . TryParse ( parameters [ key ] . ToString ( ) , out bool value ) )
179+ return value ;
180+ return defaultValue ;
181+ }
182+ }
183+ }
0 commit comments