Skip to main content
Glama
RecompileScriptsTool.cs9.42 kB
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using McpUnity.Utils; using Newtonsoft.Json.Linq; using UnityEditor; using UnityEditor.Compilation; using UnityEngine; namespace McpUnity.Tools { /// <summary> /// Tool to recompile all scripts in the Unity project /// </summary> public class RecompileScriptsTool : McpToolBase { private class CompilationRequest { public readonly bool ReturnWithLogs; public readonly int LogsLimit; public readonly TaskCompletionSource<JObject> CompletionSource; public CompilationRequest(bool returnWithLogs, int logsLimit, TaskCompletionSource<JObject> completionSource) { ReturnWithLogs = returnWithLogs; LogsLimit = logsLimit; CompletionSource = completionSource; } } private class CompilationResult { public readonly List<CompilerMessage> SortedLogs; public readonly int WarningsCount; public readonly int ErrorsCount; public bool HasErrors => ErrorsCount > 0; public CompilationResult(List<CompilerMessage> sortedLogs, int warningsCount, int errorsCount) { SortedLogs = sortedLogs; WarningsCount = warningsCount; ErrorsCount = errorsCount; } } private readonly List<CompilationRequest> _pendingRequests = new List<CompilationRequest>(); private readonly List<CompilerMessage> _compilationLogs = new List<CompilerMessage>(); private int _processedAssemblies = 0; public RecompileScriptsTool() { Name = "recompile_scripts"; Description = "Recompiles all scripts in the Unity project"; IsAsync = true; // Compilation is asynchronous } /// <summary> /// Execute the Recompile tool asynchronously /// </summary> /// <param name="parameters">Tool parameters as a JObject</param> /// <param name="tcs">TaskCompletionSource to set the result or exception</param> public override void ExecuteAsync(JObject parameters, TaskCompletionSource<JObject> tcs) { // Extract and store parameters var returnWithLogs = GetBoolParameter(parameters, "returnWithLogs", true); var logsLimit = Mathf.Clamp(GetIntParameter(parameters, "logsLimit", 100), 0, 1000); var request = new CompilationRequest(returnWithLogs, logsLimit, tcs); bool hasActiveRequest = false; lock (_pendingRequests) { hasActiveRequest = _pendingRequests.Count > 0; _pendingRequests.Add(request); } if (hasActiveRequest) { McpLogger.LogInfo("Recompilation already in progress. Waiting for completion..."); return; } // On first request, initialize compilation listeners and start compilation StartCompilationTracking(); if (EditorApplication.isCompiling == false) { McpLogger.LogInfo("Recompiling all scripts in the Unity project"); CompilationPipeline.RequestScriptCompilation(); } } /// <summary> /// Subscribe to compilation events, reset tracked state /// </summary> private void StartCompilationTracking() { _compilationLogs.Clear(); _processedAssemblies = 0; CompilationPipeline.assemblyCompilationFinished += OnAssemblyCompilationFinished; CompilationPipeline.compilationFinished += OnCompilationFinished; } /// <summary> /// Unsubscribe from compilation events /// </summary> private void StopCompilationTracking() { CompilationPipeline.assemblyCompilationFinished -= OnAssemblyCompilationFinished; CompilationPipeline.compilationFinished -= OnCompilationFinished; } /// <summary> /// Record compilation logs for every single assembly /// </summary> private void OnAssemblyCompilationFinished(string assemblyPath, CompilerMessage[] messages) { _processedAssemblies++; _compilationLogs.AddRange(messages); } /// <summary> /// Stop tracking and complete all pending requests /// </summary> private void OnCompilationFinished(object _) { McpLogger.LogInfo($"Recompilation completed. Processed {_processedAssemblies} assemblies with {_compilationLogs.Count} compiler messages"); // Sort logs by type: first errors, then warnings and info List<CompilerMessage> sortedLogs = _compilationLogs.OrderBy(x => x.type).ToList(); int errorsCount = _compilationLogs.Count(l => l.type == CompilerMessageType.Error); int warningsCount = _compilationLogs.Count(l => l.type == CompilerMessageType.Warning); CompilationResult result = new CompilationResult(sortedLogs, warningsCount, errorsCount); // Stop tracking before completing requests StopCompilationTracking(); // Complete all requests received before compilation end, the next received request will start a new compilation List<CompilationRequest> requestsToComplete = new List<CompilationRequest>(); lock (_pendingRequests) { requestsToComplete.AddRange(_pendingRequests); _pendingRequests.Clear(); } foreach (var request in requestsToComplete) { CompleteRequest(request, result); } } /// <summary> /// Process a completed compilation request /// </summary> private static void CompleteRequest(CompilationRequest request, CompilationResult result) { JArray logsArray = new JArray(); IEnumerable<CompilerMessage> logsToReturn = request.ReturnWithLogs ? result.SortedLogs.Take(request.LogsLimit) : Enumerable.Empty<CompilerMessage>(); foreach (var message in logsToReturn) { var logObject = new JObject { ["message"] = message.message, ["type"] = message.type.ToString() }; // Add file information if available if (!string.IsNullOrEmpty(message.file)) { logObject["file"] = message.file; logObject["line"] = message.line; logObject["column"] = message.column; } logsArray.Add(logObject); } string summaryMessage = result.HasErrors ? $"Recompilation completed with {result.ErrorsCount} error(s) and {result.WarningsCount} warning(s)" : $"Successfully recompiled all scripts with {result.WarningsCount} warning(s)"; summaryMessage += $" (returnWithLogs: {request.ReturnWithLogs}, logsLimit: {request.LogsLimit})"; var response = new JObject { ["success"] = true, ["type"] = "text", ["message"] = summaryMessage, ["logs"] = logsArray }; request.CompletionSource.SetResult(response); } /// <summary> /// Helper method to safely extract integer parameters with default values /// </summary> /// <param name="parameters">JObject containing parameters</param> /// <param name="key">Parameter key to extract</param> /// <param name="defaultValue">Default value if parameter is missing or invalid</param> /// <returns>Extracted integer value or default</returns> private static int GetIntParameter(JObject parameters, string key, int defaultValue) { if (parameters?[key] != null && int.TryParse(parameters[key].ToString(), out int value)) return value; return defaultValue; } /// <summary> /// Helper method to safely extract boolean parameters with default values /// </summary> /// <param name="parameters">JObject containing parameters</param> /// <param name="key">Parameter key to extract</param> /// <param name="defaultValue">Default value if parameter is missing or invalid</param> /// <returns>Extracted boolean value or default</returns> private static bool GetBoolParameter(JObject parameters, string key, bool defaultValue) { if (parameters?[key] != null && bool.TryParse(parameters[key].ToString(), out bool value)) return value; return defaultValue; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/CoderGamester/mcp-unity'

If you have feedback or need assistance with the MCP directory API, please join our Discord server